From 7cf80293684779b6555c5c42f2ef3793521b1e18 Mon Sep 17 00:00:00 2001 From: damarrsyh Date: Thu, 27 Feb 2025 16:32:05 +0700 Subject: [PATCH] 27/02/2025 --- .env | 2 +- db.json | 125 ++++++++- package-lock.json | 14 +- package.json | 2 +- src/api/queueApi.js | 71 ++--- src/components/Admin/AdminSidebar.jsx | 4 +- src/components/Admin/QueueActions.jsx | 74 ++--- src/components/Admin/QueueTable.jsx | 261 +++++++++--------- src/components/Admin/TestQueueActions.jsx | 30 ++ src/components/Admin/TestQueueTable.jsx | 133 +++++++++ src/components/Display/QueueDisplay.jsx | 58 +++- src/components/Display/ServiceSelection.jsx | 66 ++--- src/components/Display/TestQueueDisplay.jsx | 45 +++ .../Display/TestServiceSelection.jsx | 34 +++ src/pages/Admin/CallQueuePage.jsx | 4 +- src/pages/Admin/QueueListPage.jsx | 7 +- src/pages/Display/QueueDisplayPage.jsx | 2 + src/pages/Display/QueueMenuPage.jsx | 2 + src/routes/AppRoutes.jsx | 8 +- 19 files changed, 658 insertions(+), 284 deletions(-) create mode 100644 src/components/Admin/TestQueueActions.jsx create mode 100644 src/components/Admin/TestQueueTable.jsx create mode 100644 src/components/Display/TestQueueDisplay.jsx create mode 100644 src/components/Display/TestServiceSelection.jsx diff --git a/.env b/.env index 223030e..345a9b1 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -VITE_API_URL = http://localhost:3000/queue \ No newline at end of file +VITE_API_URL = http://localhost:3000 \ No newline at end of file diff --git a/db.json b/db.json index 25e1eb3..983ca76 100644 --- a/db.json +++ b/db.json @@ -1,16 +1,115 @@ { - "queue": [ - { "id": 1, "number": "P001", "counter": 1, "service": "Siap Print", "customer": { "name": "John Doe", "phone": "081234567890" }, "startTime": "2025-02-26T08:00:00", "endTime": null, "status": "waiting" }, - { "id": 2, "number": "D001", "counter": 2, "service": "Design", "customer": { "name": "Jane Smith", "phone": "081298765432" }, "startTime": "2025-02-26T08:05:00", "endTime": null, "status": "called" }, - { "id": 3, "number": "F001", "counter": 3, "service": "Fotocopy", "customer": { "name": "Emily Davis", "phone": "081678901234" }, "startTime": "2025-02-26T08:30:00", "endTime": "2025-02-26T08:45:00", "status": "completed" }, - { "id": 4, "number": "R001", "counter": 4, "service": "Retur Barang", "customer": { "name": "Sophia Lee", "phone": "081789012345" }, "startTime": "2025-02-26T08:35:00", "endTime": null, "status": "called" }, - { "id": 5, "number": "O001", "counter": 5, "service": "Online Pickup", "customer": { "name": "Daniel Martinez", "phone": "081890123456" }, "startTime": "2025-02-26T08:40:00", "endTime": null, "status": "waiting" }, - { "id": 6, "number": "T001", "counter": 6, "service": "Tamu", "customer": { "name": "Henry Adams", "phone": "081901234567" }, "startTime": "2025-02-26T08:50:00", "endTime": null, "status": "waiting" }, - { "id": 7, "number": "P002", "counter": 1, "service": "Siap Print", "customer": { "name": "James Thompson", "phone": "081234567891" }, "startTime": "2025-02-26T09:00:00", "endTime": null, "status": "waiting" }, - { "id": 8, "number": "D002", "counter": 2, "service": "Design", "customer": { "name": "Benjamin Clark", "phone": "081456789013" }, "startTime": "2025-02-26T09:10:00", "endTime": "2025-02-26T09:20:00", "status": "completed" }, - { "id": 9, "number": "F002", "counter": 3, "service": "Fotocopy", "customer": { "name": "Olivia Brown", "phone": "081567890124" }, "startTime": "2025-02-26T09:15:00", "endTime": null, "status": "waiting" }, - { "id": 10, "number": "R002", "counter": 4, "service": "Retur Barang", "customer": { "name": "William White", "phone": "081678901235" }, "startTime": "2025-02-26T09:20:00", "endTime": null, "status": "called" }, - { "id": 11, "number": "O002", "counter": 5, "service": "Online Pickup", "customer": { "name": "Sophia Harris", "phone": "081789012346" }, "startTime": "2025-02-26T09:25:00", "endTime": null, "status": "waiting" }, - { "id": 12, "number": "T002", "counter": 6, "service": "Tamu", "customer": { "name": "Alexander Nelson", "phone": "081890123457" }, "startTime": "2025-02-26T09:30:00", "endTime": null, "status": "waiting" } + "operators": [ + { + "id": "O001", + "name": "Rina Wijaya", + "email": "rina@example.com", + "queues": [ + { + "queue_id": "Q12345", + "customer": { + "id": "C001", + "name": "Budi Santoso", + "phone": "+628123456789", + "email": "budi@example.com" + }, + "service": { + "id": "J0001", + "name": "siap_print" + }, + "status": "In Progress", + "created_at": "2025-02-27T08:30:00Z" + }, + { + "queue_id": "Q12346", + "customer": { + "id": "C002", + "name": "Siti Aminah", + "phone": "+628987654321", + "email": "siti@example.com" + }, + "service": { + "id": "J0001", + "name": "siap_print" + }, + "status": "Waiting", + "created_at": "2025-02-27T09:00:00Z" + } + ] + }, + { + "id": "O002", + "name": "Doni Saputra", + "email": "doni@example.com", + "queues": [ + { + "queue_id": "Q12347", + "customer": { + "id": "C003", + "name": "Ahmad Fauzi", + "phone": "+6285566778899", + "email": "ahmad@example.com" + }, + "service": { + "id": "J0002", + "name": "design" + }, + "status": "Completed", + "created_at": "2025-02-27T09:30:00Z" + }, + { + "queue_id": "Q12348", + "customer": { + "id": "C004", + "name": "Lina Kusuma", + "phone": "+6289988776655", + "email": "lina@example.com" + }, + "service": { + "id": "J0003", + "name": "fotocopy" + }, + "status": "In Progress", + "created_at": "2025-02-27T10:00:00Z" + } + ] + }, + { + "id": "O003", + "name": "Sari Ningsih", + "email": "sari@example.com", + "queues": [ + { + "queue_id": "Q12349", + "customer": { + "id": "j000", + "name": "Rudi Hartono", + "phone": "+6287711223344", + "email": "rudi@example.com" + }, + "service": { + "id": "J0002", + "name": "design" + }, + "status": "Waiting", + "created_at": "2025-02-27T10:30:00Z" + }, + { + "queue_id": "Q12350", + "customer": { + "id": "C006", + "name": "Dewi Anggraini", + "phone": "+6286677889900", + "email": "dewi@example.com" + }, + "service": { + "id": "J0001", + "name": "siap_print" + }, + "status": "Completed", + "created_at": "2025-02-27T11:00:00Z" + } + ] + } ] } diff --git a/package-lock.json b/package-lock.json index a1b3cf0..01f6a05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "@reduxjs/toolkit": "^2.6.0", - "axios": "^1.7.9", + "axios": "^1.8.1", "bcryptjs": "^3.0.2", "bootstrap": "^5.3.3", "cors": "^2.8.5", @@ -1772,9 +1772,9 @@ } }, "node_modules/axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz", + "integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -6220,9 +6220,9 @@ } }, "axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz", + "integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==", "requires": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", diff --git a/package.json b/package.json index 030ff14..d9f8607 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@reduxjs/toolkit": "^2.6.0", - "axios": "^1.7.9", + "axios": "^1.8.1", "bcryptjs": "^3.0.2", "bootstrap": "^5.3.3", "cors": "^2.8.5", diff --git a/src/api/queueApi.js b/src/api/queueApi.js index 0773931..2ba5fac 100644 --- a/src/api/queueApi.js +++ b/src/api/queueApi.js @@ -1,56 +1,35 @@ import axios from "axios"; -const API_URL = "http://localhost:5000/queue"; +const API_URL = import.meta.env.VITE_API_URL; -export const fetchOperators = async () => { - try { - const response = await axios.get(API_URL); - return response.data; - } catch (error) { - console.error("Error fetching operators", error); - return []; - } +export const fetchQueues = async () => { + try { + const response = await axios.get(`${API_URL}/operators`); + return response.data; + } catch (error) { + console.error("Error fetching queues:", error); + return []; + } }; -export const addCustomerToOperator = async (operatorId, customerData) => { - try { - const response = await axios.get(`${API_URL}/${operatorId}`); - const operator = response.data; - - operator.customers.push(customerData); - - const updateResponse = await axios.put(`${API_URL}/${operatorId}`, operator); - return updateResponse.data; - } catch (error) { - console.error("Error adding customer", error); - } +export const updateQueueStatus = async (queueId, status) => { + try { + const response = await axios.put(`${API_URL}/queues/${queueId}`, { + status, + }); + return response.data; + } catch (error) { + console.error("Error updating queue status:", error); + return null; + } }; -export const updateCustomerStatus = async (operatorId, customerId, updatedData) => { +export const createQueue = async (customerData) => { try { - const response = await axios.get(`${API_URL}/${operatorId}`); - const operator = response.data; - - operator.customers = operator.customers.map(customer => - customer.id === customerId ? { ...customer, ...updatedData } : customer - ); - - const updateResponse = await axios.put(`${API_URL}/${operatorId}`, operator); - return updateResponse.data; + const response = await axios.post(`${API_URL}/queues`, customerData); + return response.data; } catch (error) { - console.error("Error updating customer status", error); + console.error("Error creating queue:", error); + return null; } -}; - -export const deleteCustomerFromOperator = async (operatorId, customerId) => { - try { - const response = await axios.get(`${API_URL}/${operatorId}`); - const operator = response.data; - - operator.customers = operator.customers.filter(customer => customer.id !== customerId); - - await axios.put(`${API_URL}/${operatorId}`, operator); - } catch (error) { - console.error("Error deleting customer", error); - } -}; + }; diff --git a/src/components/Admin/AdminSidebar.jsx b/src/components/Admin/AdminSidebar.jsx index 9aea3b6..2def27b 100644 --- a/src/components/Admin/AdminSidebar.jsx +++ b/src/components/Admin/AdminSidebar.jsx @@ -28,9 +28,9 @@ const AdminSidebar = () => { Daftar Antrian - + {/* Panggil Antrian - + */} {/* Laporan Antrian */} diff --git a/src/components/Admin/QueueActions.jsx b/src/components/Admin/QueueActions.jsx index 25cab5f..fea1539 100644 --- a/src/components/Admin/QueueActions.jsx +++ b/src/components/Admin/QueueActions.jsx @@ -1,29 +1,29 @@ const queueData = [ - { id: 1, name: "John Doe", phone: "08123456789", service: "Siap Print", serviceId: "J0001", locket: "01", startAt: "10:30 AM", completedAt: "10:30 AM", queueNumber: "001", status: "Menunggu", called: false, active: true, audio: "Default"}, - { id: 2, name: "Jane Smith", phone: "08234567890", service: "Design", serviceId: "J0002", locket: "04", startAt: "11:15 AM", completedAt: "11:15 AM", queueNumber: "002", status: "Dilayani", called: false, active: true, audio: "Beep"}, - { id: 3, name: "Michael Johnson", phone: "08345678901", service: "FotoCopy", serviceId: "J0003", locket: "08", startAt: "11:50 AM", completedAt: "11:50 AM", queueNumber: "003", status: "Selesai", called: false, active: true, audio: "Bell"}, - { id: 4, name: "Emily Brown", phone: "08456789012", service: "Tamu", serviceId: "J0006", locket: "02", startAt: "12:10 PM", completedAt: "12:10 PM", queueNumber: "004", status: "Menunggu", called: false, active: true, audio: "Chime"}, - { id: 5, name: "David Wilson", phone: "08567890123", service: "Online", serviceId: "J0005", locket: "05", startAt: "12:50 PM", completedAt: "12:50 PM", queueNumber: "005", status: "Dilayani", called: false, active: true, audio: "Ding"}, - { id: 6, name: "Sophia Martinez", phone: "08678901234", service: "Online", serviceId: "J0005", locket: "06", startAt: "01:30 PM", completedAt: "01:30 PM", queueNumber: "006", status: "Selesai", called: false, active: true, audio: "Ping"}, - { id: 7, name: "James Anderson", phone: "08789012345", service: "Siap Print", serviceId: "J0001", locket: "03", startAt: "02:15 PM", completedAt: "02:15 PM", queueNumber: "007", status: "Menunggu", called: false, active: true, audio: "Chirp"}, - { id: 8, name: "Olivia Thomas", phone: "08890123456", service: "Design", serviceId: "J0002", locket: "07", startAt: "02:50 PM", completedAt: "02:50 PM", queueNumber: "008", status: "Dilayani", called: false, active: true, audio: "Ring"}, - { id: 9, name: "Liam White", phone: "08901234567", service: "FotoCopy", serviceId: "J0003", locket: "09", startAt: "03:20 PM", completedAt: "03:20 PM", queueNumber: "009", status: "Selesai", called: false, active: true, audio: "Tone"}, - { id: 10, name: "Emma Harris", phone: "08012345678", service: "Siap Print", serviceId: "J0001", locket: "10", startAt: "03:50 PM", completedAt: "03:50 PM", queueNumber: "010", status: "Menunggu", called: false, active: true, audio: "Alarm" }, - { id: 11, name: "John Doe", phone: "08123456789", service: "Siap Print", serviceId: "J0001", locket: "01", startAt: "10:30 AM", completedAt: "10:30 AM", queueNumber: "001", status: "Menunggu", called: false, active: true, audio: "Default"}, - { id: 12, name: "Jane Smith", phone: "08234567890", service: "Design", serviceId: "J0002", locket: "04", startAt: "11:15 AM", completedAt: "11:15 AM", queueNumber: "002", status: "Dilayani", called: false, active: true, audio: "Beep"}, - { id: 13, name: "Michael Johnson", phone: "08345678901", service: "FotoCopy", serviceId: "J0003", locket: "08", startAt: "11:50 AM", completedAt: "11:50 AM", queueNumber: "003", status: "Selesai", called: false, active: true, audio: "Bell"}, - { id: 14, name: "Emily Brown", phone: "08456789012", service: "Online", serviceId: "J0005", locket: "02", startAt: "12:10 PM", completedAt: "12:10 PM", queueNumber: "004", status: "Menunggu", called: false, active: true, audio: "Chime"}, - { id: 15, name: "David Wilson", phone: "08567890123", service: "Design", serviceId: "J0002", locket: "05", startAt: "12:50 PM", completedAt: "12:50 PM", queueNumber: "005", status: "Dilayani", called: false, active: true, audio: "Ding"}, - { id: 16, name: "John Doe", phone: "08123456789", service: "Online", serviceId: "J0005", locket: "01", startAt: "10:30 AM", completedAt: "10:30 AM", queueNumber: "001", status: "Menunggu", called: false, active: true, audio: "Default"}, - { id: 17, name: "Jane Smith", phone: "08234567890", service: "Design", serviceId: "J0002", locket: "04", startAt: "11:15 AM", completedAt: "11:15 AM", queueNumber: "002", status: "Dilayani", called: false, active: true, audio: "Beep"}, - { id: 18, name: "Michael Johnson", phone: "08345678901", service: "Retur", serviceId: "J0004", locket: "08", startAt: "11:50 AM", completedAt: "11:50 AM", queueNumber: "003", status: "Selesai", called: false, active: true, audio: "Bell"}, - { id: 19, name: "Emily Brown", phone: "08456789012", service: "Online", serviceId: "J0005", locket: "02", startAt: "12:10 PM", completedAt: "12:10 PM", queueNumber: "004", status: "Menunggu", called: false, active: true, audio: "Chime"}, - { id: 20, name: "David Wilson", phone: "08567890123", service: "Design", serviceId: "J0002", locket: "05", startAt: "12:50 PM", completedAt: "12:50 PM", queueNumber: "005", status: "Dilayani", called: false, active: true, audio: "Ding"}, - { id: 21, name: "Sophia Martinez", phone: "08678901234", service: "Retur", serviceId: "J0004", locket: "06", startAt: "01:30 PM", completedAt: "01:30 PM", queueNumber: "006", status: "Selesai", called: false, active: true, audio: "Ping"}, - { id: 22, name: "James Anderson", phone: "08789012345", service: "Online", serviceId: "J0005", locket: "03", startAt: "02:15 PM", completedAt: "02:15 PM", queueNumber: "007", status: "Menunggu", called: false, active: true, audio: "Chirp"}, - { id: 23, name: "Olivia Thomas", phone: "08890123456", service: "Design", serviceId: "J0002", locket: "07", startAt: "02:50 PM", completedAt: "02:50 PM", queueNumber: "008", status: "Dilayani", called: false, active: true, audio: "Ring"}, - { id: 24, name: "Liam White", phone: "08901234567", service: "Tamu", serviceId: "J0006", locket: "09", startAt: "03:20 PM", completedAt: "03:20 PM", queueNumber: "009", status: "Selesai", called: false, active: true, audio: "Tone"}, - { id: 25, name: "Emma Harris", phone: "08012345678", service: "Siap Print", serviceId: "J0001", locket: "10", startAt: "03:50 PM", completedAt: "03:50 PM", queueNumber: "010", status: "Menunggu", called: false, active: true, audio: "Alarm"}, + { id: 1, name: "John Doe", phone: "08123456789", service: "Siap Print", serviceId: "J0001", locket: "loket 01", startAt: "10:30 AM", completedAt: "10:30 AM", queueNumber: "001", status: "Menunggu", called: false, active: true}, + { id: 2, name: "Jane Smith", phone: "08234567890", service: "Design", serviceId: "J0002", locket: "loket 04", startAt: "11:15 AM", completedAt: "11:15 AM", queueNumber: "002", status: "Menunggu", called: false, active: true}, + { id: 3, name: "Michael Johnson", phone: "08345678901", service: "FotoCopy", serviceId: "J0003", locket: "loket 08", startAt: "11:50 AM", completedAt: "11:50 AM", queueNumber: "003", status: "Menunggu", called: false, active: true}, + { id: 4, name: "Emily Brown", phone: "08456789012", service: "Tamu", serviceId: "J0006", locket: "loket 02", startAt: "12:10 PM", completedAt: "12:10 PM", queueNumber: "004", status: "Menunggu", called: false, active: true}, + { id: 5, name: "David Wilson", phone: "08567890123", service: "Online", serviceId: "J0005", locket: "loket 05", startAt: "04:00 PM", completedAt: "12:50 PM", queueNumber: "005", status: "Menunggu", called: false, active: true}, + { id: 6, name: "Sophia Martinez", phone: "08678901234", service: "Online", serviceId: "J0005", locket: "loket 06", startAt: "01:30 PM", completedAt: "01:30 PM", queueNumber: "006", status: "Menunggu", called: false, active: true}, + { id: 7, name: "James Anderson", phone: "08789012345", service: "Siap Print", serviceId: "J0001", locket: "loket 03", startAt: "02:15 PM", completedAt: "02:15 PM", queueNumber: "007", status: "Menunggu", called: false, active: true}, + { id: 8, name: "Olivia Thomas", phone: "08890123456", service: "Design", serviceId: "J0002", locket: "loket 07", startAt: "02:50 PM", completedAt: "02:50 PM", queueNumber: "008", status: "Menunggu", called: false, active: true}, + { id: 9, name: "Liam White", phone: "08901234567", service: "FotoCopy", serviceId: "J0003", locket: "loket 09", startAt: "03:20 PM", completedAt: "03:20 PM", queueNumber: "009", status: "Menunggu", called: false, active: true}, + { id: 10, name: "Emma Harris", phone: "08012345678", service: "Siap Print", serviceId: "J0001", locket: "loket 10", startAt: "03:50 PM", completedAt: "03:50 PM", queueNumber: "010", status: "Menunggu", called: false, active: true}, + { id: 11, name: "John Doe", phone: "08123456789", service: "Siap Print", serviceId: "J0001", locket: "loket 01", startAt: "10:30 AM", completedAt: "10:30 AM", queueNumber: "001", status: "Menunggu", called: false, active: true}, + { id: 12, name: "Jane Smith", phone: "08234567890", service: "Design", serviceId: "J0002", locket: "loket 04", startAt: "11:15 AM", completedAt: "11:15 AM", queueNumber: "002", status: "Menunggu", called: false, active: true}, + { id: 13, name: "Michael Johnson", phone: "08345678901", service: "FotoCopy", serviceId: "J0003", locket: "loket 08", startAt: "11:50 AM", completedAt: "11:50 AM", queueNumber: "003", status: "Menunggu", called: false, active: true}, + { id: 14, name: "Emily Brown", phone: "08456789012", service: "Online", serviceId: "J0005", locket: "loket 02", startAt: "12:10 PM", completedAt: "12:10 PM", queueNumber: "004", status: "Menunggu", called: false, active: true}, + { id: 15, name: "David Wilson", phone: "08567890123", service: "Design", serviceId: "J0002", locket: "loket 05", startAt: "04:00 PM", completedAt: "12:50 PM", queueNumber: "005", status: "Menunggu", called: false, active: true}, + { id: 16, name: "John Doe", phone: "08123456789", service: "Online", serviceId: "J0005", locket: "loket 01", startAt: "10:30 AM", completedAt: "10:30 AM", queueNumber: "001", status: "Menunggu", called: false, active: true}, + { id: 17, name: "Jane Smith", phone: "08234567890", service: "Design", serviceId: "J0002", locket: "loket 04", startAt: "11:15 AM", completedAt: "11:15 AM", queueNumber: "002", status: "Menunggu", called: false, active: true}, + { id: 18, name: "Michael Johnson", phone: "08345678901", service: "Retur", serviceId: "J0004", locket: "loket 08", startAt: "11:50 AM", completedAt: "11:50 AM", queueNumber: "003", status: "Menunggu", called: false, active: true}, + { id: 19, name: "Emily Brown", phone: "08456789012", service: "Online", serviceId: "J0005", locket: "loket 02", startAt: "12:10 PM", completedAt: "12:10 PM", queueNumber: "004", status: "Menunggu", called: false, active: true}, + { id: 20, name: "David Wilson", phone: "08567890123", service: "Design", serviceId: "J0002", locket: "loket 05", startAt: "04:00 PM", completedAt: "12:50 PM", queueNumber: "005", status: "Menunggu", called: false, active: true}, + { id: 21, name: "Sophia Martinez", phone: "08678901234", service: "Retur", serviceId: "J0004", locket: "loket 06", startAt: "01:30 PM", completedAt: "01:30 PM", queueNumber: "006", status: "Menunggu", called: false, active: true}, + { id: 22, name: "James Anderson", phone: "08789012345", service: "Online", serviceId: "J0005", locket: "loket 03", startAt: "02:15 PM", completedAt: "02:15 PM", queueNumber: "007", status: "Menunggu", called: false, active: true}, + { id: 23, name: "Olivia Thomas", phone: "08890123456", service: "Design", serviceId: "J0002", locket: "loket 07", startAt: "02:50 PM", completedAt: "02:50 PM", queueNumber: "008", status: "Menunggu", called: false, active: true}, + { id: 24, name: "Liam White", phone: "08901234567", service: "Tamu", serviceId: "J0006", locket: "loket 09", startAt: "03:20 PM", completedAt: "03:20 PM", queueNumber: "009", status: "Menunggu", called: false, active: true}, + { id: 25, name: "Emma Harris", phone: "08012345678", service: "Siap Print", serviceId: "J0001", locket: "loket 10", startAt: "03:50 PM", completedAt: "03:50 PM", queueNumber: "010", status: "Menunggu", called: false, active: true}, ]; export const getQueueSettingsData = () => { @@ -61,10 +61,21 @@ export const toggleServiceStatus = (id) => { }; export const getFilteredQueueData = (callQueueView = false) => { - if (callQueueView) { - return queueData.filter((item) => item.status === "Menunggu"); - } - return queueData; + let filteredQueue = queueData.filter((item) => + callQueueView ? item.status === "Menunggu" : true + ); + + // Urutkan berdasarkan status "Menunggu" terlebih dahulu, lalu berdasarkan startAt + filteredQueue.sort((a, b) => { + if (a.status === "Menunggu" && b.status !== "Menunggu") return -1; + if (b.status === "Menunggu" && a.status !== "Menunggu") return 1; + + const timeA = new Date(`1970/01/01 ${a.startAt}`); + const timeB = new Date(`1970/01/01 ${b.startAt}`); + return timeA - timeB; + }); + + return filteredQueue; }; export const getQueueDisplayData = () => { @@ -79,9 +90,6 @@ export const getQueueDisplayData = () => { })); }; - - -// QueueActions.js import { useState } from "react"; export const useQueueData = (settingsView, callQueueView) => { @@ -113,7 +121,7 @@ export const useQueueData = (settingsView, callQueueView) => { searchQuery, setSearchQuery, setCurrentPage, - indexOfFirstItem, // ✅ Tambahkan ini + indexOfFirstItem, }; }; diff --git a/src/components/Admin/QueueTable.jsx b/src/components/Admin/QueueTable.jsx index 4e221d8..08e2dce 100644 --- a/src/components/Admin/QueueTable.jsx +++ b/src/components/Admin/QueueTable.jsx @@ -1,142 +1,143 @@ -import { Table, Button, Form, Pagination, InputGroup, FormControl } from "react-bootstrap"; +import { useState } from "react"; +import { Card, Table, Button, Form, Pagination, InputGroup, FormControl, Modal } from "react-bootstrap"; import { FaSearch } from "react-icons/fa"; -import { handleCall, toggleServiceStatus, handleDelete, handleEdit, useQueueData } from "./QueueActions"; +import { handleCall, useQueueData } from "./QueueActions"; // eslint-disable-next-line react/prop-types -const QueueTable = ({ showActions = true, reportView = false, settingsView = false, displayView = false, callQueueView = false }) => { - - const { currentItems, currentPage, totalPages, searchQuery, setSearchQuery, setCurrentPage, indexOfFirstItem } = useQueueData(settingsView, callQueueView); +const QueueTable = ({ settingsView = false, displayView = false }) => { + const { currentItems, currentPage, totalPages, searchQuery, setSearchQuery, setCurrentPage, indexOfFirstItem } = useQueueData(settingsView); + + // State untuk modal + const [showModal, setShowModal] = useState(false); + const [selectedQueue, setSelectedQueue] = useState(null); + const [selectedLocket, setSelectedLocket] = useState(""); + + // Data loket operator (bisa diganti dengan API) + const availableLockets = ["Loket 1", "Loket 2", "Loket 3", "Loket 4", "Loket 5", "Loket 6", "Loket 7", "Loket 8", "Loket 9"]; + + // Fungsi membuka modal dan menyimpan antrian yang dipilih + const handleOpenModal = (queue) => { + setSelectedQueue(queue); + setShowModal(true); + }; + + // Fungsi menutup modal + const handleCloseModal = () => { + setShowModal(false); + setSelectedQueue(null); + setSelectedLocket(""); + }; + + // Fungsi memilih loket dan panggil antrian + const handleSelectLocket = () => { + if (selectedLocket) { + handleCall(selectedQueue.id, selectedLocket); // Panggil dengan loket + handleCloseModal(); // Tutup modal setelah memilih + } + }; return ( <> - {(callQueueView || reportView || (!settingsView && !displayView)) && ( - - - - - setSearchQuery(e.target.value)} - /> - - )} - - - - - {displayView ? ( - <> - - - - - ) : settingsView ? ( - <> - - - - - - ) : reportView ? ( - <> - - - - - - - - - ) : ( - <> - - - - - - - {showActions && } - - )} - - - - {currentItems.map((item, index) => ( - - - {displayView ? ( - <> - - - - - ) : settingsView ? ( - <> - - - - - - ) : reportView ? ( - <> - - - - - - - - - ) : ( - <> - - - - - - - {showActions && ( + + + {(!settingsView && !displayView) && ( + + + + + setSearchQuery(e.target.value)} + /> + + )} + + setCurrentPage(prev => Math.max(prev - 1, 1))} disabled={currentPage === 1} /> + {[...Array(totalPages)].map((_, index) => ( + setCurrentPage(index + 1)}> + {index + 1} + + ))} + setCurrentPage(prev => Math.min(prev + 1, totalPages))} disabled={currentPage === totalPages} /> + + + + +
+
#Pengaturan SuaraVideo KomersilAksiNama LayananID LayananStatusAksiNamaNo. TeleponLayananLoketCetak AntrianSelesai AntrianAksiNamaLayananKode LayananNomor AntrianLoketStatusAksi
{index + 1 + indexOfFirstItem} - - - - - - {item.video} - - - - {item.service}{item.serviceId}{item.active ? "Aktif" : "Nonaktif"} - - {item.name}{item.phone}{item.service}{item.locket}{item.startAt}{item.completedAt} - - - {item.name}{item.service}{item.serviceId}{item.queueNumber}{item.status === "Dilayani" ? item.locket : "-"}{item.status}
+ + + + + + + + + + + + + + + {currentItems.map((item, index) => ( + + + + + + + + + - )} - - )} - - ))} - -
#NamaLayananKode LayananNomor AntrianPengambilanLoketStatusAksi
{index + 1 + indexOfFirstItem}{item.name}{item.service}{item.serviceId}{item.queueNumber}{item.startAt}{item.status === "Dilayani" ? item.locket : "-"}{item.status} - -
- - setCurrentPage(prev => Math.max(prev - 1, 1))} disabled={currentPage === 1} /> - {[...Array(totalPages)].map((_, index) => ( - setCurrentPage(index + 1)}> - {index + 1} - - ))} - setCurrentPage(prev => Math.min(prev + 1, totalPages))} disabled={currentPage === totalPages} /> - + + ))} + {Array.from({ length: Math.max(0, 10 - currentItems.length) }).map((_, index) => ( + +   + + ))} + + + + + + + {/* Modal untuk memilih loket */} + + + Pilih Loket Operator + + +
+ {availableLockets.map((locket, index) => ( + setSelectedLocket(e.target.value)} + /> + ))} + +
+ + + + +
); }; diff --git a/src/components/Admin/TestQueueActions.jsx b/src/components/Admin/TestQueueActions.jsx new file mode 100644 index 0000000..bdafbc7 --- /dev/null +++ b/src/components/Admin/TestQueueActions.jsx @@ -0,0 +1,30 @@ + +// FILE INI UNTUK TEST TAMPILAN DATA DENGAN API + +import { fetchQueues, updateQueueStatus } from "../../api/queueApi"; + +export const getProcessedQueues = async () => { + const data = await fetchQueues(); + + // Ubah struktur data jika diperlukan + const processedData = data.map((operator) => ({ + operatorId: operator.id, + operatorName: operator.name, + queues: operator.queues.map((queue) => ({ + id: queue.queue_id, + customerName: queue.customer.name, + customerEmail: queue.customer.email, + customerPhone: queue.customer.phone, + serviceId: queue.service.id, + serviceName: queue.service.name, + status: queue.status, + createdAt: queue.created_at, + })), + })); + + return processedData; +}; + +export const changeQueueStatus = async (queueId, newStatus) => { + return await updateQueueStatus(queueId, newStatus); +}; diff --git a/src/components/Admin/TestQueueTable.jsx b/src/components/Admin/TestQueueTable.jsx new file mode 100644 index 0000000..2f71cc6 --- /dev/null +++ b/src/components/Admin/TestQueueTable.jsx @@ -0,0 +1,133 @@ + +// FILE INI UNTUK TEST TAMPILAN DATA DENGAN API + +import { useEffect, useState } from "react"; +import { getProcessedQueues } from "./TestQueueActions"; +import { Card, Table, Container, Button, Modal, ListGroup } from "react-bootstrap"; + +const TestQueueTable = () => { + const [queues, setQueues] = useState([]); + const [selectedQueue, setSelectedQueue] = useState(null); + const [showModal, setShowModal] = useState(false); + + useEffect(() => { + const fetchData = async () => { + const data = await getProcessedQueues(); + // Flatten data agar tidak dikelompokkan per operator + const allQueues = data.flatMap((operator) => + operator.queues.map((queue) => ({ + ...queue, + operatorName: operator.operatorName, // Menyimpan nama operator di setiap antrian + })) + ); + // Sort data: Prioritaskan yang Waiting dan urutkan berdasarkan createdAt + allQueues.sort((a, b) => { + if (a.status === "Waiting" && b.status !== "Waiting") return -1; + if (a.status !== "Waiting" && b.status === "Waiting") return 1; + return new Date(a.createdAt) - new Date(b.createdAt); + }); + setQueues(allQueues); + }; + fetchData(); + }, []); + + // Data dummy untuk loket operator yang tersedia + const availableCounters = [ + { id: "LKT001", name: "Loket 1" }, + { id: "LKT002", name: "Loket 2" }, + { id: "LKT003", name: "Loket 3" }, + ]; + + // Handler untuk membuka modal dan memilih queue + const handleCallQueue = (queue) => { + setSelectedQueue(queue); + setShowModal(true); + }; + + return ( + + + +
List Antrian
+
+ + + + + + + + + + + + + + + {queues.length > 0 ? ( + queues.map((queue) => ( + + + + + + + + + + )) + ) : ( + + + + )} + +
CustomerEmailPhoneServiceStatusCreated AtAction
{queue.customerName}{queue.customerEmail}{queue.customerPhone}{queue.serviceName} + + {queue.status} + + {new Date(queue.createdAt).toLocaleString()} + {queue.status !== "In Progress" && queue.status !== "Completed" && ( + + )} +
+ No queues available +
+
+
+ + {/* Modal Pilih Loket */} + setShowModal(false)} centered> + + Pilih Loket Operator + + +

Memanggil antrian {selectedQueue?.customerName}

+ + {availableCounters.map((counter) => ( + + {counter.name} + + ))} + +
+
+
+ ); +}; + +export default TestQueueTable; diff --git a/src/components/Display/QueueDisplay.jsx b/src/components/Display/QueueDisplay.jsx index ecc0997..7e3710d 100644 --- a/src/components/Display/QueueDisplay.jsx +++ b/src/components/Display/QueueDisplay.jsx @@ -80,19 +80,53 @@ const QueueDisplay = () => { {/* Loket Antrian */} - - - + + + + +

+ List Antrian Layanan +

+
+ + + List Antrian Layanan - - - - List Antrian Layanan -

-
- -
+

data antrian

+
+ + + + List Antrian Layanan +

data antrian

+
+ + + + List Antrian Layanan +

data antrian

+
+ + + + List Antrian Layanan +

data antrian

+
+ + + + List Antrian Layanan +

data antrian

+
+ + + + List Antrian Layanan +

data antrian

+
+ +
diff --git a/src/components/Display/ServiceSelection.jsx b/src/components/Display/ServiceSelection.jsx index d0f66ad..4558f0e 100644 --- a/src/components/Display/ServiceSelection.jsx +++ b/src/components/Display/ServiceSelection.jsx @@ -1,5 +1,5 @@ import { Container, Card, Button, Row, Col, Form, Carousel } from 'react-bootstrap'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { FaPrint, FaPalette, FaFileAlt, FaUndo, FaTruck, FaUser } from 'react-icons/fa'; const services = [ @@ -18,15 +18,17 @@ const ServiceSelection = () => { const [ticket, setTicket] = useState(null); const [enableForm, setEnableForm] = useState(false); + useEffect(() => { + if (selectedService && !enableForm) { + generateTicket(); + } + }, [selectedService]); + const handleServiceSelect = (serviceId) => { setSelectedService(services.find(service => service.id === serviceId)); - if (!enableForm) { - handleSubmit(new Event("submit")); - } }; - const handleSubmit = (e) => { - e.preventDefault(); + const generateTicket = () => { if (selectedService) { const queueNumber = `${selectedService.id}-${Math.floor(1000 + Math.random() * 9000)}`; const newTicket = { @@ -41,31 +43,35 @@ const ServiceSelection = () => { } }; + const handleSubmit = (e) => { + e.preventDefault(); + generateTicket(); + }; + return ( {!ticket ? ( -

Pilih Layanan

+
+

Pilih Layanan

+
- {services.map(service => { - const isSelected = selectedService?.id === service.id; - return ( - - handleServiceSelect(service.id)} - > - - {service.icon} - -
{service.name}
-
-
-
- - ); - })} + {services.map(service => ( + + handleServiceSelect(service.id)} + > + + {service.icon} + +
{service.name}
+
+
+
+ + ))}
@@ -91,7 +97,6 @@ const ServiceSelection = () => { value={name} onChange={(e) => setName(e.target.value)} disabled={!enableForm} - className={`border ${enableForm ? "border-primary" : "border-secondary"} ${enableForm ? "" : "bg-light text-secondary"}`} /> @@ -102,7 +107,6 @@ const ServiceSelection = () => { value={phone} onChange={(e) => setPhone(e.target.value)} disabled={!enableForm} - className={`border ${enableForm ? "border-primary" : "border-secondary"} ${enableForm ? "" : "bg-light text-secondary"}`} />