From a432d8e24fbe2745b292dc5fc207244df509a954 Mon Sep 17 00:00:00 2001 From: Saipramodh033 Date: Tue, 12 May 2026 12:51:51 +0530 Subject: [PATCH] feat(visitors-hostel): refactor module and add features --- src/Modules/Visitors_Hostel/ARCHITECTURE.md | 197 +++ src/Modules/Visitors_Hostel/CheckoutForm.jsx | 41 +- .../Visitors_Hostel/MIGRATION_CHECKLIST.md | 0 .../Visitors_Hostel/STAGE3_4_CHECKLIST.js | 410 ++++++ .../Visitors_Hostel/VisitorsNavbar.jsx | 109 +- .../Visitors_Hostel/accountStatements.jsx | 580 --------- .../Visitors_Hostel/activeBookings.jsx | 29 +- src/Modules/Visitors_Hostel/addItems.jsx | 175 --- src/Modules/Visitors_Hostel/bookingForm.jsx | 473 ------- src/Modules/Visitors_Hostel/bookings.jsx | 389 ------ .../Visitors_Hostel/cancellationRequest.jsx | 215 ---- .../Visitors_Hostel/completedBookings.jsx | 201 --- .../components/InventoryManagement.jsx | 58 + .../components/OverstayDashboard.jsx | 166 +++ .../components/OverstayMonitor.jsx | 280 ++++ .../components/RoomAvailabilityContainer.jsx | 139 ++ .../RoomAvailabilityDetails.jsx} | 142 +-- .../common/ActionConfirmationProvider.jsx | 83 ++ .../components/common/ErrorBoundary.jsx | 128 ++ .../components/common/LoadingSpinner.jsx | 97 ++ .../components/common/MealBookingHistory.jsx | 206 +++ .../components/common/RoleBasedAccess.jsx | 85 ++ .../common/ViewActiveBookingModal.jsx | 622 +++++++++ .../components/common/ViewBookingModal.jsx | 254 ++++ .../forms/AddInventoryItemsForm.jsx | 371 ++++++ .../components/forms/BookingRequestForm.jsx | 524 ++++++++ .../components/forms/CheckoutForm.jsx | 25 + .../forms/ConfirmBookingForm.jsx} | 159 +-- .../forms/ForwardBookingForm.jsx} | 99 +- .../components/forms/InventoryForm.jsx | 11 + .../components/forms/MealBookingForm.jsx | 406 ++++++ .../components/forms/OfflineBookingForm.jsx | 558 ++++++++ .../components/forms/UpdateBookingForm.jsx | 425 +++++++ .../components/tables/ActiveBookingsTable.jsx | 346 +++++ .../tables/CancelledRequestsTable.jsx | 237 ++++ .../tables/CompletedBookingsTable.jsx | 288 +++++ .../components/tables/InventoryTable.jsx | 594 +++++++++ .../tables/OverstayDetectionTable.jsx | 387 ++++++ .../tables/PendingRequestsTable.jsx | 452 +++++++ .../tables/ReplenishmentRequestsTable.jsx | 441 +++++++ src/Modules/Visitors_Hostel/index.js | 52 + src/Modules/Visitors_Hostel/inventory.jsx | 73 +- .../pages/ActiveBookingsPage.jsx | 44 + .../pages/CancelledBookingsPage.jsx | 44 + .../pages/CompletedBookingsPage.jsx | 44 + .../pages/FinancialReportsPage.jsx | 727 +++++++++++ .../GuidelinesPage.jsx} | 4 +- .../pages/InventoryManagementPage.jsx | 51 + .../pages/PendingBookingsPage.jsx | 46 + .../Visitors_Hostel/pendingRequests.jsx | 47 +- .../Visitors_Hostel/roomsAvailability.jsx | 138 -- .../services/visitorHostelApi.js | 1132 +++++++++++++++++ .../styles/visitorHostel.module.css | 230 ++++ src/Modules/Visitors_Hostel/updateBooking.jsx | 416 ------ .../Visitors_Hostel/utils/constants.js | 85 ++ .../Visitors_Hostel/utils/dateHelpers.js | 94 ++ .../Visitors_Hostel/utils/errorHandler.js | 276 ++++ .../Visitors_Hostel/utils/validators.js | 174 +++ .../Visitors_Hostel/viewActiveBooking.jsx | 593 --------- src/Modules/Visitors_Hostel/viewBooking.jsx | 349 ----- .../Visitors_Hostel/visitorsContent.jsx | 158 ++- src/routes/visitorsHostelRoutes/index.jsx | 61 +- 62 files changed, 11282 insertions(+), 3958 deletions(-) create mode 100644 src/Modules/Visitors_Hostel/ARCHITECTURE.md create mode 100644 src/Modules/Visitors_Hostel/MIGRATION_CHECKLIST.md create mode 100644 src/Modules/Visitors_Hostel/STAGE3_4_CHECKLIST.js delete mode 100644 src/Modules/Visitors_Hostel/accountStatements.jsx delete mode 100644 src/Modules/Visitors_Hostel/addItems.jsx delete mode 100644 src/Modules/Visitors_Hostel/bookingForm.jsx delete mode 100644 src/Modules/Visitors_Hostel/bookings.jsx delete mode 100644 src/Modules/Visitors_Hostel/cancellationRequest.jsx delete mode 100644 src/Modules/Visitors_Hostel/completedBookings.jsx create mode 100644 src/Modules/Visitors_Hostel/components/InventoryManagement.jsx create mode 100644 src/Modules/Visitors_Hostel/components/OverstayDashboard.jsx create mode 100644 src/Modules/Visitors_Hostel/components/OverstayMonitor.jsx create mode 100644 src/Modules/Visitors_Hostel/components/RoomAvailabilityContainer.jsx rename src/Modules/Visitors_Hostel/{roomsAvailibilityDetails.jsx => components/RoomAvailabilityDetails.jsx} (59%) create mode 100644 src/Modules/Visitors_Hostel/components/common/ActionConfirmationProvider.jsx create mode 100644 src/Modules/Visitors_Hostel/components/common/ErrorBoundary.jsx create mode 100644 src/Modules/Visitors_Hostel/components/common/LoadingSpinner.jsx create mode 100644 src/Modules/Visitors_Hostel/components/common/MealBookingHistory.jsx create mode 100644 src/Modules/Visitors_Hostel/components/common/RoleBasedAccess.jsx create mode 100644 src/Modules/Visitors_Hostel/components/common/ViewActiveBookingModal.jsx create mode 100644 src/Modules/Visitors_Hostel/components/common/ViewBookingModal.jsx create mode 100644 src/Modules/Visitors_Hostel/components/forms/AddInventoryItemsForm.jsx create mode 100644 src/Modules/Visitors_Hostel/components/forms/BookingRequestForm.jsx create mode 100644 src/Modules/Visitors_Hostel/components/forms/CheckoutForm.jsx rename src/Modules/Visitors_Hostel/{confirmBooking_Incharge.jsx => components/forms/ConfirmBookingForm.jsx} (65%) rename src/Modules/Visitors_Hostel/{forwardBooking.jsx => components/forms/ForwardBookingForm.jsx} (71%) create mode 100644 src/Modules/Visitors_Hostel/components/forms/InventoryForm.jsx create mode 100644 src/Modules/Visitors_Hostel/components/forms/MealBookingForm.jsx create mode 100644 src/Modules/Visitors_Hostel/components/forms/OfflineBookingForm.jsx create mode 100644 src/Modules/Visitors_Hostel/components/forms/UpdateBookingForm.jsx create mode 100644 src/Modules/Visitors_Hostel/components/tables/ActiveBookingsTable.jsx create mode 100644 src/Modules/Visitors_Hostel/components/tables/CancelledRequestsTable.jsx create mode 100644 src/Modules/Visitors_Hostel/components/tables/CompletedBookingsTable.jsx create mode 100644 src/Modules/Visitors_Hostel/components/tables/InventoryTable.jsx create mode 100644 src/Modules/Visitors_Hostel/components/tables/OverstayDetectionTable.jsx create mode 100644 src/Modules/Visitors_Hostel/components/tables/PendingRequestsTable.jsx create mode 100644 src/Modules/Visitors_Hostel/components/tables/ReplenishmentRequestsTable.jsx create mode 100644 src/Modules/Visitors_Hostel/index.js create mode 100644 src/Modules/Visitors_Hostel/pages/ActiveBookingsPage.jsx create mode 100644 src/Modules/Visitors_Hostel/pages/CancelledBookingsPage.jsx create mode 100644 src/Modules/Visitors_Hostel/pages/CompletedBookingsPage.jsx create mode 100644 src/Modules/Visitors_Hostel/pages/FinancialReportsPage.jsx rename src/Modules/Visitors_Hostel/{vhGuidelines.jsx => pages/GuidelinesPage.jsx} (98%) create mode 100644 src/Modules/Visitors_Hostel/pages/InventoryManagementPage.jsx create mode 100644 src/Modules/Visitors_Hostel/pages/PendingBookingsPage.jsx delete mode 100644 src/Modules/Visitors_Hostel/roomsAvailability.jsx create mode 100644 src/Modules/Visitors_Hostel/services/visitorHostelApi.js create mode 100644 src/Modules/Visitors_Hostel/styles/visitorHostel.module.css delete mode 100644 src/Modules/Visitors_Hostel/updateBooking.jsx create mode 100644 src/Modules/Visitors_Hostel/utils/constants.js create mode 100644 src/Modules/Visitors_Hostel/utils/dateHelpers.js create mode 100644 src/Modules/Visitors_Hostel/utils/errorHandler.js create mode 100644 src/Modules/Visitors_Hostel/utils/validators.js delete mode 100644 src/Modules/Visitors_Hostel/viewActiveBooking.jsx delete mode 100644 src/Modules/Visitors_Hostel/viewBooking.jsx diff --git a/src/Modules/Visitors_Hostel/ARCHITECTURE.md b/src/Modules/Visitors_Hostel/ARCHITECTURE.md new file mode 100644 index 000000000..dba078504 --- /dev/null +++ b/src/Modules/Visitors_Hostel/ARCHITECTURE.md @@ -0,0 +1,197 @@ +# Visitor Hostel Module - Frontend Architecture + +## Folder Structure + +``` +Visitors_Hostel/ +├── pages/ +│ ├── PendingBookingsPage.jsx +│ ├── ActiveBookingsPage.jsx +│ ├── InventoryManagementPage.jsx +│ ├── CancelledBookingsPage.jsx +│ └── CompletedBookingsPage.jsx +│ +├── components/ +│ ├── forms/ +│ │ ├── BookingRequestForm.jsx +│ │ ├── ConfirmBookingForm.jsx +│ │ ├── ForwardBookingForm.jsx +│ │ ├── CheckoutForm.jsx +│ │ └── InventoryForm.jsx +│ │ +│ ├── tables/ +│ │ ├── PendingRequestsTable.jsx +│ │ ├── ActiveBookingsTable.jsx +│ │ ├── CancelledRequestsTable.jsx +│ │ ├── CompletedBookingsTable.jsx +│ │ └── InventoryTable.jsx +│ │ +│ └── common/ +│ ├── ViewBookingModal.jsx +│ ├── ViewActiveBookingModal.jsx +│ ├── RoleBasedAccess.jsx +│ └── LoadingSpinner.jsx +│ +├── services/ +│ └── visitorHostelApi.js +│ +├── utils/ +│ ├── validators.js +│ ├── dateHelpers.js +│ └── constants.js +│ +├── styles/ +│ └── visitorHostel.module.css +│ +└── index.js +``` + +## Component Responsibilities + +### pages/ +- **Route-level components** representing screens +- Assemble page-level components +- Don't contain heavy UI logic +- Handle page state and data fetching + +### components/forms/ +- **Form components** for user input +- Handle form validation +- Submit data to API via services +- Show success/error messages + +### components/tables/ +- **Table components** for data display +- Handle sorting, filtering, search +- Support pagination +- Include action buttons + +### components/common/ +- **Shared UI components** used across module +- Modal wrappers +- Reusable UI elements +- Role-based access control + +### services/ +- **Centralized API communication** +- RESTful endpoint definitions +- Request/response handling +- Token management via interceptors + +### utils/ +- **Helper functions** used across components +- Date formatting and validation +- Form validation utilities +- Constants and enumerations + +### styles/ +- **CSS modules** for component styling +- Responsive design utilities +- Print styles + +## Migration Status + +### ✅ Completed +- `pages/` - All page components created +- `components/tables/` - All table components created +- `components/forms/` - Form component references created +- `components/common/` - Modal component references created +- `services/visitorHostelApi.js` - Service layer completed +- `utils/` - Validators, helpers, and constants created +- `styles/` - CSS modules created + +### 🔄 In Progress +- Moving form implementations from root to `components/forms/` +- Moving modal implementations from root to `components/common/` + +### ⏳ TODO +- Create `RoleBasedAccess.jsx` component +- Create `LoadingSpinner.jsx` component +- Create page for Account Statements +- Full integration testing + +## API Endpoints Mapping + +| Feature | Endpoint | Status | +|---------|----------|--------| +| Pending Bookings | GET /api/bookings/pending/ | ✅ | +| Active Bookings | GET /api/bookings/active/ | ✅ | +| Confirm Booking | POST /api/bookings/confirm/ | ✅ | +| Reject Booking | POST /api/bookings/reject/ | ✅ | +| Forward Booking | POST /api/bookings/forward/ | ✅ | +| Cancel Booking | POST /api/bookings/cancel/ | ✅ | +| Checkout | POST /api/bookings/checkout/ | ✅ | +| Room Availability | GET /api/rooms/availability/ | ✅ | +| Add Inventory | POST /api/inventory/add/ | ✅ | +| Get Inventory | GET /api/inventory/ | ⏳ NEEDED | +| Cancelled Bookings | GET /api/bookings/cancelled/ | ⏳ NEEDED | +| Completed Bookings | GET /api/bookings/completed/ | ⏳ NEEDED | +| Get Booking Details | GET /api/bookings/{id}/ | ⏳ NEEDED | + +## Usage Examples + +### Using Pages +```javascript +// In routes or navigation +import PendingBookingsPage from './pages/PendingBookingsPage'; + + +``` + +### Using Components +```javascript +// In page components +import PendingRequestsTable from '../components/tables/PendingRequestsTable'; +import BookingRequestForm from '../components/forms/BookingRequestForm'; + + + {}} /> +``` + +### Using Services +```javascript +// In components +import { bookingsAPI } from '../../services/visitorHostelApi'; + +const pendingBookings = await bookingsAPI.getPendingBookings(); +const result = await bookingsAPI.confirmBooking(bookingId, category, rooms); +``` + +### Using Utils +```javascript +import { validateEmail, formatDate, getDaysBetween } from '../../utils/'; +import { USER_ROLES, BOOKING_STATUS } from '../../utils/constants'; + +if (validateEmail(email)) { + const formatted = formatDate(date); + const days = getDaysBetween(startDate, endDate); +} +``` + +## Next Steps + +1. **Move existing implementations** + - Move `bookingForm.jsx` → `components/forms/BookingRequestForm.jsx` + - Move `confirmBooking_Incharge.jsx` → `components/forms/ConfirmBookingForm.jsx` + - (and other forms) + +2. **Create shared components** + - `components/common/RoleBasedAccess.jsx` + - `components/common/LoadingSpinner.jsx` + +3. **Backend coordination** + - Create missing endpoints: + - GET /api/inventory/ + - GET /api/bookings/cancelled/ + - GET /api/bookings/completed/ + - GET /api/bookings/{id}/ + +4. **Integration testing** + - End-to-end workflow for all 3 roles + - Browser console validation + - API contract validation + +5. **Performance optimization** + - Add pagination to tables + - Implement lazy loading + - Optimize re-renders diff --git a/src/Modules/Visitors_Hostel/CheckoutForm.jsx b/src/Modules/Visitors_Hostel/CheckoutForm.jsx index f18425921..23bd3e331 100644 --- a/src/Modules/Visitors_Hostel/CheckoutForm.jsx +++ b/src/Modules/Visitors_Hostel/CheckoutForm.jsx @@ -12,9 +12,8 @@ import { NumberInput, Divider, } from "@mantine/core"; -import axios from "axios"; import PropTypes from "prop-types"; -import { host } from "../../routes/globalRoutes"; +import { bookingsAPI } from "./services/visitorHostelApi"; function CheckoutForm({ modalOpened, onClose, bookingId, bookingDetails }) { const [items, setItems] = useState([ @@ -53,43 +52,15 @@ function CheckoutForm({ modalOpened, onClose, bookingId, bookingDetails }) { }; const handleCompleteCheckout = async () => { - const token = localStorage.getItem("authToken"); - if (!token) { - return console.error("No authentication token found!"); - } - try { - const now = new Date(); - const formattedTime = now.toTimeString().split(" ")[0]; // Extract time in HH:MM:SS format - - const data = { - booking_id: bookingId, - inventory_items: items.map((item) => ({ - name: item.name, - quantity: item.quantity, - cost: item.cost, - })), - meal_bill: 0, // Add meal bill if applicable - room_bill: totalAmount, // Total amount as room bill - check_out_time: formattedTime, // Send time in HH:MM:SS format - }; - - console.log("Data being sent to backend:", data); // Log the data + // Use bookingsAPI service for checkout + const mealBill = 0; // Default meal bill + const roomBill = totalAmount; // Total amount as room bill - // Send request to the backend - const response = await axios.post( - `${host}/visitorhostel/check-out-with-inventory/`, - data, - { - headers: { - Authorization: `Token ${token}`, - "Content-Type": "application/json", - }, - }, - ); + const response = await bookingsAPI.checkOut(bookingId, mealBill, roomBill); console.log("Successfully completed checkout for booking ID:", bookingId); - console.log("Response from backend:", response.data); + console.log("Response from backend:", response); alert("Checkout completed successfully!"); onClose(); // Close the modal after checkout diff --git a/src/Modules/Visitors_Hostel/MIGRATION_CHECKLIST.md b/src/Modules/Visitors_Hostel/MIGRATION_CHECKLIST.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/Modules/Visitors_Hostel/STAGE3_4_CHECKLIST.js b/src/Modules/Visitors_Hostel/STAGE3_4_CHECKLIST.js new file mode 100644 index 000000000..7c86c3eba --- /dev/null +++ b/src/Modules/Visitors_Hostel/STAGE3_4_CHECKLIST.js @@ -0,0 +1,410 @@ +/** + * STAGE 3 & 4: COMPONENT MIGRATION & TESTING CHECKLIST + * + * Status: Current progress ~2.5/10 hours + * Remaining: Component migration (3-4 hrs) + Testing (2-3 hrs) + * + * This file tracks all components requiring migration and testing + */ + +// ============================================================================ +// STAGE 3: COMPONENT MIGRATION CHECKLIST +// ============================================================================ + +/** + * PRIORITY 1: CRITICAL COMPONENTS (Migrate First) + * These are active in current UI and used by all roles + */ + +// ✅ COMPLETED +const COMPLETED_COMPONENTS = [ + { + name: "visitorsContent.jsx", + status: "✅ DONE", + task: "Added Routes/Route configuration", + file: "Modules/Visitors_Hostel/visitorsContent.jsx", + notes: "Now routes to bookings, room-availability, inventory, bills", + }, + { + name: "visitorHostelApi.js", + status: "✅ DONE", + task: "Created centralized service layer", + file: "Modules/Visitors_Hostel/services/visitorHostelApi.js", + notes: "19 API methods, proper error handling, auth interceptor", + }, +]; + +// 🟡 IN PROGRESS / READY TO START +const PRIORITY_1_COMPONENTS = [ + { + name: "bookings.jsx", + status: "🟡 READY TO START", + task: "Migrate to use bookingsAPI service", + location: "Modules/Visitors_Hostel/bookings.jsx", + targetLocation: "Modules/Visitors_Hostel/pages/BookingsPage.jsx (NEW)", + requirements: [ + "Remove axios imports and hardcoded URLs", + "Import { bookingsAPI } from ../services/visitorHostelApi", + "Use useSelector for Redux user role", + "Add loading/error/filter states", + "Replace POST /get-booking-requests/ with GET /api/bookings/pending/", + "Replace POST /get-active-bookings/ with GET /api/bookings/active/", + "Implement role-based tab filtering (pending/active/completed)", + "Test with VhIncharge (see all) and Student (see own)", + ], + estimate: "45 minutes", + apiMethods: ["getPendingBookings()", "getActiveBookings()", "confirmBooking()", "rejectBooking()"], + }, + { + name: "bookingForm.jsx", + status: "🟡 READY TO START", + task: "Migrate form to use bookingsAPI.requestBooking()", + location: "Modules/Visitors_Hostel/bookingForm.jsx", + targetLocation: "Modules/Visitors_Hostel/components/forms/BookingRequestForm.jsx (NEW)", + requirements: [ + "Remove axios post to /visitorhostel/request-booking/", + "Use bookingsAPI.requestBooking({ intender, category, dates, ... })", + "Move file upload handling (if any) to be compatible with multipart requests", + "Add form validation via utils/validators.js", + "Add success/error notifications", + "Test form submission and file upload", + ], + estimate: "30 minutes", + apiMethods: ["requestBooking()"], + }, + { + name: "roomsAvailability.jsx", + status: "🟡 READY TO START", + task: "Migrate to use roomsAPI service", + location: "Modules/Visitors_Hostel/roomsAvailability.jsx", + targetLocation: "Modules/Visitors_Hostel/pages/RoomAvailabilityPage.jsx (NEW)", + requirements: [ + "Remove POST to /visitorhostel/room_availabity_new/", + "Use roomsAPI.getAvailableRooms(startDate, endDate)", + "Add date range picker component", + "Add loading state during room fetch", + "Display available room numbers in table/grid", + "Add room filtering by category", + ], + estimate: "35 minutes", + apiMethods: ["getAvailableRooms()"], + }, + { + name: "inventory.jsx", + status: "🟡 READY TO START", + task: "Migrate to use inventoryAPI service", + location: "Modules/Visitors_Hostel/inventory.jsx", + targetLocation: "Modules/Visitors_Hostel/pages/InventoryPage.jsx (NEW)", + requirements: [ + "Remove POST to /visitorhostel/api/inventory_add/", + "Use inventoryAPI.addInventory() and updateInventory()", + "Add form for adding items (ItemForm.jsx)", + "Add table for displaying inventory (InventoryTable.jsx)", + "Add role check: VhIncharge only", + "Test add/update operations", + ], + estimate: "40 minutes", + apiMethods: ["addInventory()", "updateInventory()"], + }, + { + name: "accountStatements.jsx", + status: "🟡 READY TO START", + task: "Migrate to use reportsAPI service", + location: "Modules/Visitors_Hostel/accountStatements.jsx", + targetLocation: "Modules/Visitors_Hostel/pages/BillsPage.jsx (NEW)", + requirements: [ + "Remove GET to /visitorhostel/accounts-income/", + "Use reportsAPI.getBillsBetweenDates(startDate, endDate)", + "Add date range filter", + "Display bill totals (meal_total, room_total, total)", + "Add table for bill records", + "Add generate bill button: reportsAPI.generateBill()", + "Role check: VhIncharge only", + ], + estimate: "40 minutes", + apiMethods: ["getBillsBetweenDates()", "generateBill()"], + }, +]; + +/** + * PRIORITY 2: CHECK-IN/OUT & FORMS + * Support components for role-specific operations + */ +const PRIORITY_2_COMPONENTS = [ + { + name: "CheckoutForm.jsx", + task: "Migrate to use bookingsAPI.checkOut()", + requirements: [ + "Import { bookingsAPI } from ../services/visitorHostelApi", + "Replace POST to /check-out/ with checkOut(bookingId, mealBill, roomBill)", + "Add validation for meal/room bill amounts", + "Test with VhCaretaker role", + ], + estimate: "20 minutes", + }, + { + name: "check-in logic (in existing components)", + task: "Refactor check-in to use bookingsAPI.checkIn()", + requirements: [ + "Find check-in form/handler (may be in bookingForm.jsx or separate)", + "Replace POST to /check-in/ with checkIn(bookingId, name, phone, ...)", + "Create CheckInForm.jsx component", + ], + estimate: "25 minutes", + }, + { + name: "confirmBooking_Incharge.jsx", + task: "Migrate to use bookingsAPI.confirmBooking()", + requirements: [ + "Replace POST to /confirm-booking-new/ with confirmBooking(bookingId, category, rooms)", + "Add room selector component (RoomSelector.jsx)", + "Add category dropdown", + "Test confirmation flow", + ], + estimate: "30 minutes", + }, + { + name: "forwardBooking.jsx", + task: "Migrate to use bookingsAPI.forwardBooking()", + requirements: [ + "Replace POST to /forward-booking/ with forwardBooking(bookingId, category, rooms, remark)", + "Add room selector for new rooms", + "Add remark field", + ], + estimate: "20 minutes", + }, +]; + +/** + * PRIORITY 3: SUPPORTING COMPONENTS + * Shared UI components for consistent UX + */ +const PRIORITY_3_COMPONENTS = [ + { + name: "RoleBasedAccess.jsx", + task: "Create role access wrapper component", + location: "Modules/Visitors_Hostel/components/common/RoleBasedAccess.jsx (NEW)", + requirements: [ + "Wrap content based on user role", + "Usage: content ", + "Show message if user doesn't have access", + ], + estimate: "15 minutes", + }, + { + name: "DateRangePicker.jsx", + task: "Create reusable date range picker", + location: "Modules/Visitors_Hostel/components/common/DateRangePicker.jsx (NEW)", + requirements: [ + "Accept onDateChange callback", + "Validate end_date > start_date", + "Use Mantine or native input", + ], + estimate: "20 minutes", + }, + { + name: "RoomSelector.jsx", + task: "Create room selection component", + location: "Modules/Visitors_Hostel/components/common/RoomSelector.jsx (NEW)", + requirements: [ + "Display available rooms as checkboxes/tags", + "Accept onChange callback", + "Show room count", + ], + estimate: "15 minutes", + }, + { + name: "ErrorBoundary.jsx", + task: "Create error boundary for graceful failures", + location: "Modules/Visitors_Hostel/components/common/ErrorBoundary.jsx (NEW)", + requirements: [ + "Catch errors in child components", + "Display user-friendly error message", + "Add retry button", + ], + estimate: "20 minutes", + }, +]; + +/** + * PRIORITY 4: UTILITIES & STYLES + * Supporting files for consistency + */ +const PRIORITY_4_FILES = [ + { + name: "validators.js", + task: "Create validation utilities", + location: "Modules/Visitors_Hostel/utils/validators.js (NEW)", + functions: [ + "validateDateRange(startDate, endDate)", + "validatePhoneNumber(phone)", + "validateEmail(email)", + "validateRoomCount(numberOfRooms, maxRooms)", + "validateBookingDates(fromDate, toDate, minDays)", + ], + estimate: "25 minutes", + }, + { + name: "formatters.js", + task: "Create formatting utilities", + location: "Modules/Visitors_Hostel/utils/formatters.js (NEW)", + functions: [ + "formatDate(date) → YYYY-MM-DD", + "formatCurrency(amount) → Rs. X,XXX", + "formatPhone(phone) → +91-XXXX-XXXXX", + "formatStatus(status) → Title Case", + ], + estimate: "15 minutes", + }, + { + name: "constants.js", + task: "Centralize all constants", + location: "Modules/Visitors_Hostel/utils/constants.js (NEW)", + constants: [ + "ROOM_CATEGORIES: { 'RP': 'Regular', 'DLX': 'Deluxe' }", + "BOOKING_STATUSES: ['pending', 'confirmed', 'active', 'completed', 'cancelled']", + "USER_ROLES: ['VhIncharge', 'VhCaretaker', 'Student', 'Intender']", + "MEAL_TYPES: ['m_tea', 'breakfast', 'lunch', 'eve_tea', 'dinner']", + "MEAL_RATES: { ... }", + ], + estimate: "15 minutes", + }, + { + name: "CSS Modules", + task: "Create modularized CSS files", + location: "Modules/Visitors_Hostel/styles/", + files: [ + "pages.module.css (page layouts)", + "forms.module.css (form styling)", + "tables.module.css (table styling)", + "common.module.css (shared components)", + ], + estimate: "30 minutes", + }, +]; + +// ============================================================================ +// STAGE 4: TESTING CHECKLIST +// ============================================================================ + +const TESTING_PHASES = { + phase1_smoke_tests: { + name: "Phase 1: Smoke Tests (Postman/Browser)", + tasks: [ + "✓ GET /api/visitorhostel/health/ → 200 OK", + "✓ GET /api/visitorhostel/bookings/pending/ → 200 OK (with token)", + "✓ GET /api/visitorhostel/bookings/active/ → 200 OK (with token)", + "✓ GET /api/visitorhostel/rooms/availability/?start_date=...&end_date=... → 200 OK", + "✓ POST /api/visitorhostel/bookings/request/ → 200 OK (valid payload)", + "✓ POST /api/visitorhostel/bookings/request/ → 400 (invalid payload)", + ], + estimate: "30 minutes", + }, + phase2_component_tests: { + name: "Phase 2: Component Migration Tests", + tasks: [ + "✓ BookingsPage loads pending/active bookings", + "✓ BookingRequestForm submits and shows success", + "✓ RoomAvailabilityPage fetches and displays rooms", + "✓ InventoryPage CRUD operations work", + "✓ BillsPage generates and displays reports", + "✓ CheckIn/Out form submissions work", + "✓ No console errors (check browser DevTools)", + "✓ Loading states appear and disappear correctly", + "✓ Error messages display on API failures", + ], + estimate: "1 hour", + }, + phase3_e2e_workflow: { + name: "Phase 3: End-to-End Workflow Tests", + scenarios: [ + { + name: "Student Booking Workflow", + steps: [ + "1. Student logs in", + "2. Navigates to /visitors_hostel/", + "3. Clicks 'Request Booking'", + "4. Fills out booking form (dates, purpose, rooms)", + "5. Submits form → POST /api/bookings/request/", + "6. Sees success message", + "7. Booking appears in 'My Bookings'", + ], + }, + { + name: "VhIncharge Confirmation Workflow", + steps: [ + "1. VhIncharge logs in", + "2. Navigates to Pending Bookings tab", + "3. Sees student booking request", + "4. Clicks 'Confirm'", + "5. Selects room category and room numbers", + "6. Submits → POST /api/bookings/confirm/", + "7. Booking status changes to 'confirmed'", + "8. Student can see confirmed rooms", + ], + }, + { + name: "Check-In/Out Workflow", + steps: [ + "1. VhCaretaker logs in", + "2. Finds confirmed booking", + "3. Clicks 'Check In'", + "4. Enters visitor details (name, phone)", + "5. Submits → POST /api/bookings/checkin/", + "6. Booking status changes to 'active/checked-in'", + "7. Later: Click 'Check Out'", + "8. Enter meal/room bills", + "9. Submits → POST /api/bookings/checkout/", + "10. Bill finalized", + ], + }, + ], + estimate: "1.5 hours", + }, + phase4_regression: { + name: "Phase 4: Regression Testing", + tasks: [ + "✓ All 19 API endpoints respond correctly", + "✓ Role-based access works (VhIncharge ≠ Student visibility)", + "✓ No feature loss from legacy → REST migration", + "✓ No broken links or routing", + "✓ File uploads work (if applicable)", + "✓ Notifications trigger on status changes", + "✓ Browser back/forward navigation works", + "✓ Token refresh on expiry (if applicable)", + "✓ Mobile responsiveness (if required)", + ], + estimate: "1 hour", + }, +}; + +// ============================================================================ +// SUMMARY & QUICK START +// ============================================================================ + +const EXECUTION_SUMMARY = { + completed: [ + "✅ Backend deprecation comments added (Phase 1)", + "✅ Services/visitorHostelApi.js created (Phase 2)", + "✅ Folder structure created (Phase 2)", + "✅ visitorsContent.jsx updated with Routes (Phase 2)", + "✅ Migration pattern template created (Phase 2)", + ], + remaining: [ + "🟡 Component migrations (5 priority-1 components)", + "🟡 Supporting components (4 common components)", + "🟡 Utilities & styles (constants, validators, CSS)", + "🟡 Testing phases (4 phases: smoke → E2E → regression)", + ], + nextSteps: [ + "1. START WITH: bookings.jsx → BookingsPage.jsx (45 min)", + "2. FOLLOW BY: bookingForm.jsx → BookingRequestForm.jsx (30 min)", + "3. CONTINUE: roomsAvailability, inventory, accountStatements (2-3 hrs)", + "4. CREATE: Common components (RoleBasedAccess, DateRangePicker, etc. - 1 hr)", + "5. CREATE: Utilities (validators, formatters, constants - 55 min)", + "6. TEST: Run full test suite (4+ hours)", + ], + totalRemaining: "6-8 hours", +}; + +export { COMPLETED_COMPONENTS, PRIORITY_1_COMPONENTS, PRIORITY_2_COMPONENTS, PRIORITY_3_COMPONENTS, PRIORITY_4_FILES, TESTING_PHASES, EXECUTION_SUMMARY }; diff --git a/src/Modules/Visitors_Hostel/VisitorsNavbar.jsx b/src/Modules/Visitors_Hostel/VisitorsNavbar.jsx index 187ab79e5..38a9b9994 100644 --- a/src/Modules/Visitors_Hostel/VisitorsNavbar.jsx +++ b/src/Modules/Visitors_Hostel/VisitorsNavbar.jsx @@ -1,36 +1,32 @@ import React from "react"; import { Tabs, MantineProvider, Button } from "@mantine/core"; import { IconChevronLeft, IconChevronRight } from "@tabler/icons-react"; -import { useNavigate } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router-dom"; import { useSelector } from "react-redux"; import CustomBreadcrumbs from "./components/BreadCrumbs"; // Import the CustomBreadcrumbs component +import { VH_ABSOLUTE_PATHS } from "../../routes/visitorsHostelRoutes"; const TabsModules = [ { label: "Manage Bookings", id: "manage-bookings", - url: "/visitors_hostel", + url: VH_ABSOLUTE_PATHS.ROOT, }, { label: "Room Availability", id: "room-availability", - url: "/visitors_hostel/room-availability", + url: VH_ABSOLUTE_PATHS.ROOM_AVAILABILITY, }, - // { - // label: "Mess Record", - // id: "mess-record", - // url: "/visitors_hostel/mess-record", - // }, - { label: "Inventory", id: "inventory", url: "/visitors_hostel/inventory" }, + { label: "Inventory", id: "inventory", url: VH_ABSOLUTE_PATHS.INVENTORY }, { label: "Account Statement", id: "account-statement", - url: "/visitors_hostel/account-statement", + url: VH_ABSOLUTE_PATHS.ACCOUNT_STATEMENTS, }, { label: "Rules and Regulations", id: "rules", - url: "/visitors_hostel/rules", + url: VH_ABSOLUTE_PATHS.GUIDELINES, }, ]; @@ -38,27 +34,27 @@ const ManageBookingsTabs = [ { label: "Bookings", id: "bookings", - url: "/visitors_hostel/", + url: VH_ABSOLUTE_PATHS.BOOKINGS, }, { label: "Pending Requests", id: "pending-requests", - url: "/visitors_hostel/pending_requests", + url: VH_ABSOLUTE_PATHS.PENDING_BOOKINGS, }, { label: "Cancelled Requests", id: "cancel-request", - url: "/visitors_hostel/cancel_request", + url: VH_ABSOLUTE_PATHS.CANCELLED_BOOKINGS, }, { label: "Active Bookings", id: "active-bookings", - url: "/visitors_hostel/active_bookings", + url: VH_ABSOLUTE_PATHS.ACTIVE_BOOKINGS, }, { label: "Completed Bookings", id: "completed-bookings", - url: "/visitors_hostel/completed_bookings", + url: VH_ABSOLUTE_PATHS.COMPLETED_BOOKINGS, }, ]; @@ -66,6 +62,32 @@ export default function BookingManagement() { const [activeTab, setActiveTab] = React.useState("manage-bookings"); const [activeSubTab, setActiveSubTab] = React.useState("bookings"); const navigate = useNavigate(); + const location = useLocation(); + + React.useEffect(() => { + const path = location.pathname; + + if ( + path.includes(VH_ABSOLUTE_PATHS.ROOM_AVAILABILITY) || + path.includes(VH_ABSOLUTE_PATHS.INVENTORY) || + path.includes(VH_ABSOLUTE_PATHS.ACCOUNT_STATEMENTS) || + path.includes(VH_ABSOLUTE_PATHS.GUIDELINES) + ) { + if (path.includes(VH_ABSOLUTE_PATHS.ROOM_AVAILABILITY)) setActiveTab("room-availability"); + else if (path.includes(VH_ABSOLUTE_PATHS.INVENTORY)) setActiveTab("inventory"); + else if (path.includes(VH_ABSOLUTE_PATHS.ACCOUNT_STATEMENTS)) setActiveTab("account-statement"); + else if (path.includes(VH_ABSOLUTE_PATHS.GUIDELINES)) setActiveTab("rules"); + return; + } + + setActiveTab("manage-bookings"); + + if (path.includes(VH_ABSOLUTE_PATHS.PENDING_BOOKINGS)) setActiveSubTab("pending-requests"); + else if (path.includes(VH_ABSOLUTE_PATHS.CANCELLED_BOOKINGS)) setActiveSubTab("cancel-request"); + else if (path.includes(VH_ABSOLUTE_PATHS.ACTIVE_BOOKINGS)) setActiveSubTab("active-bookings"); + else if (path.includes(VH_ABSOLUTE_PATHS.COMPLETED_BOOKINGS)) setActiveSubTab("completed-bookings"); + else setActiveSubTab("bookings"); + }, [location.pathname]); const handleTabChange = (tabId) => { const tab = TabsModules.find((t) => t.id === tabId); @@ -84,20 +106,36 @@ export default function BookingManagement() { }; const role = useSelector((state) => state.user.role); + const currentAccessibleModules = useSelector( + (state) => state.user.currentAccessibleModules + ); + const isIncharge = role === "VhIncharge"; + const isCaretaker = role === "VhCaretaker"; + + // Check if user has access to visitor_hostel module + const hasVHAccess = currentAccessibleModules?.visitor_hostel === true; const filteredTabs = TabsModules.filter((tab) => { - if (role === "VhCaretaker" || role === "VhIncharge") { + // VhIncharge has full module access. + if (isIncharge) { return true; } - return ["manage-bookings", "booking-form", "rules"].includes(tab.id); + // VhCaretaker cannot access financial reports. + if (isCaretaker) { + return tab.id !== "account-statement"; + } + // Other users see tabs only if they have visitor_hostel module access + if (hasVHAccess) { + return ["manage-bookings", "booking-form", "rules"].includes(tab.id); + } + // If no access, show nothing + return false; }); - const activeTabLabel = filteredTabs.find( - (tab) => tab.id === activeTab, - )?.label; - const activeSubTabLabel = ManageBookingsTabs.find( - (tab) => tab.id === activeSubTab, - )?.label; + const activeTabLabel = + TabsModules.find((tab) => tab.id === activeTab)?.label || "Manage Bookings"; + const activeSubTabLabel = + ManageBookingsTabs.find((tab) => tab.id === activeSubTab)?.label || "Bookings"; const activeTabIndex = filteredTabs.findIndex((tab) => tab.id === activeTab); const activeSubTabIndex = ManageBookingsTabs.findIndex( @@ -136,14 +174,14 @@ export default function BookingManagement() {
{/* Main Tab Navigation */}
- + {filteredTabs.map((tab) => (
- + - {ManageBookingsTabs.map((tab) => ( + {ManageBookingsTabs.filter((tab) => { + // Pending Requests only for VhIncharge/VhCaretaker + if (tab.id === "pending-requests") { + return role === "VhIncharge" || role === "VhCaretaker"; + } + // All other tabs visible to everyone + return true; + }).map((tab) => ( - - - - ); -} - -export default AddItems; diff --git a/src/Modules/Visitors_Hostel/bookingForm.jsx b/src/Modules/Visitors_Hostel/bookingForm.jsx deleted file mode 100644 index d9e7ec936..000000000 --- a/src/Modules/Visitors_Hostel/bookingForm.jsx +++ /dev/null @@ -1,473 +0,0 @@ -import React, { useEffect, useState } from "react"; -import PropTypes from "prop-types"; -import { - MantineProvider, - TextInput, - Select, - NumberInput, - Button, - Textarea, - Group, - Grid, - Modal, - Text, - Box, -} from "@mantine/core"; -import axios from "axios"; -import { useSelector } from "react-redux"; -import { useForm } from "@mantine/form"; -import { showNotification } from "@mantine/notifications"; -import { requestBookingRoute } from "../../routes/visitorsHostelRoutes"; -import { countries } from "./data/countries"; - -function CombinedBookingForm({ modalOpened, onClose }) { - const form = useForm({ - initialValues: { - intender: "", - arrivalDate: "", - arrivalHour: "", - arrivalMinutes: "", - arrivalAMPM: "", - departureDate: "", - departureHour: "", - departureMinutes: "", - departureAMPM: "", - numberOfPeople: 1, - numberOfRooms: 1, - category: "", - purpose: "", - remarks: "", - billsBy: "", - visitor_name: "", - visitor_email: "", - visitor_phone: "", - visitor_organization: "", - visitor_address: "", - nationality: "", - }, - }); - - // Function to get today's date in yyyy-mm-dd format - const getTodayDate = () => { - const today = new Date(); - const year = today.getFullYear(); - const month = String(today.getMonth() + 1).padStart(2, "0"); - const day = String(today.getDate()).padStart(2, "0"); - return `${year}-${month}-${day}`; - }; - - function getCookie(name) { - let cookieValue = null; - if (document.cookie && document.cookie !== "") { - const cookies = document.cookie.split(";"); - for (let i = 0; i < cookies.length; i += 1) { - const cookie = cookies[i].trim(); - if (cookie.substring(0, name.length + 1) === `${name}=`) { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; - } - - const csrfToken = getCookie("csrftoken"); - console.log("CSRF TOKEN: ", csrfToken); - - const handleSubmit = async (values) => { - const token = localStorage.getItem("authToken"); - console.log(" Token : ", token); - - const emailRegex = - /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9-]+\.[a-zA-Z]{2,}(?:\.[a-zA-Z]{2,})?$/; - - if (!emailRegex.test(values.visitor_email)) { - form.setFieldError("visitor_email", "Invalid email format"); - - // Show notification for invalid email - showNotification({ - title: "Invalid Email", - message: "Please enter a valid email address.", - color: "red", - }); - - return; // Stop form submission - } - - const requestData = { - intender: values.intender, - category: values.category, - booking_from: values.arrivalDate, - booking_to: values.departureDate, - "number-of-people": values.numberOfPeople.toString(), - "purpose-of-visit": values.purpose, - "number-of-rooms": values.numberOfRooms.toString(), - booking_from_time: `${values.arrivalHour}:${values.arrivalMinutes} ${values.arrivalAMPM}`, - booking_to_time: `${values.departureHour}:${values.departureMinutes} ${values.departureAMPM}`, - remarks_during_booking_request: values.remarks, - bill_settlement: values.billsBy, - visitor_name: values.visitor_name, - visitor_phone: values.visitor_phone, - visitor_email: values.visitor_email, - visitor_address: values.visitor_address, - nationality: values.nationality, - visitor_organization: values.visitor_organization, - csrfmiddlewaretoken: csrfToken, - }; - - try { - const response = await axios.post(requestBookingRoute, requestData, { - headers: { - Authorization: `Token ${token}`, - "X-CSRFToken": csrfToken, - "Content-Type": "application/json", - }, - }); - console.log("Form submitted", response.data); - onClose(); // Close the modal on successful submission - window.location.reload(); // Reload the page - } catch (error) { - console.error("Error submitting form", error); - } - }; - - const username = useSelector((state) => state); - console.log("IntenderID: ", username); - const role = useSelector((state) => state.user.role); - - const [todayDate, setTodayDate] = useState(getTodayDate()); - - useEffect(() => { - setTodayDate(getTodayDate()); - }, []); - - return ( - - - - Place a Booking Request - - - } - > -
- - {/* {username} */} - {/* Conditionally render Intender ID field */} - {role !== "student" && - role !== "Professor" && - role !== "VhCaretaker" && - role !== "VhIncharge" && ( - - - form.setFieldValue("intender", event.currentTarget.value) - } - required - /> - - )} - - - form.setFieldValue("visitor_name", event.currentTarget.value) - } - required - /> - - - {/* Arrival Details */} - - { - form.setFieldValue("arrivalDate", event.currentTarget.value); - }} - required - min={todayDate} // Ensures that the arrival date can't be in the past (yyyy-mm-dd format) - /> - - - - form.setFieldValue("arrivalHour", value)} - min={1} - max={12} - required - /> - - - - form.setFieldValue("arrivalMinutes", value) - } - min={0} - max={59} - required - /> - - - form.setFieldValue("departureAMPM", value)} - data={["AM", "PM"]} - required - /> - - - {/* Number of People and Rooms */} - - - form.setFieldValue("numberOfPeople", value) - } - min={1} - required - /> - - - form.setFieldValue("numberOfRooms", value)} - min={1} - required - /> - - - {/* Category, Purpose, Remarks, Bills By */} - -