Minor Update 26/02/2025

This commit is contained in:
damarrsyh 2025-02-26 23:52:34 +07:00
parent 492552d373
commit 167f0f4e0a
11 changed files with 102 additions and 47 deletions

1
.env Normal file
View File

@ -0,0 +1 @@
VITE_API_URL = http://localhost:3000/queue

16
db.json Normal file
View File

@ -0,0 +1,16 @@
{
"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" }
]
}

BIN
public/c1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 MiB

BIN
public/c2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 KiB

BIN
public/c3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 KiB

56
src/api/queueApi.js Normal file
View File

@ -0,0 +1,56 @@
import axios from "axios";
const API_URL = "http://localhost:5000/queue";
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 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 updateCustomerStatus = async (operatorId, customerId, updatedData) => {
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;
} catch (error) {
console.error("Error updating customer status", error);
}
};
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);
}
};

View File

@ -2,7 +2,7 @@ import { Container } from "react-bootstrap";
const AdminFooter = () => {
return (
<footer className="bg-dark text-center text-light py-3 mt-auto">
<footer className="bg-light text-center py-3 mt-auto shadow-sm" style={{ borderTop: "1px solid #D3D3D3" }}>
<Container>
<p className="mb-0">&copy; {new Date().getFullYear()} Admin Dashboard</p>
</Container>

View File

@ -2,7 +2,7 @@ import { Navbar, Container } from "react-bootstrap";
const AdminNavbar = () => {
return (
<Navbar bg="dark" variant="dark" expand="lg">
<Navbar bg="light" variant="light" expand="lg" className="shadow-sm">
<Container>
<Navbar.Brand href="/admin">Admin Dashboard</Navbar.Brand>
</Container>

View File

@ -11,33 +11,33 @@ const AdminSidebar = () => {
};
return (
<div className="d-flex flex-column bg-dark text-white vh-100 p-3" style={{ width: "250px" }}>
<div className="d-flex flex-column bg-light p-3" style={{ width: "250px", borderRight: "1px solid #D3D3D3" }}>
<h5 className="mb-4">Admin Menu</h5>
{/* Antrian Menu */}
<div>
<button
onClick={() => toggleMenu("antrian")}
className="btn btn-secondary w-100 text-start mb-2 d-flex align-items-center justify-content-between"
className="btn btn-light shadow-sm w-100 text-start mb-2 d-flex align-items-center justify-content-between"
>
<span><FaList className="me-2" /> Antrian</span>
{openMenu === "antrian" ? <FaChevronDown /> : <FaChevronRight />}
</button>
{openMenu === "antrian" && (
<div className="ms-3 my-2 d-grid gap-2">
<NavLink to="/admin/queue-list" className="btn btn-outline-light d-flex align-items-center">
<NavLink to="/admin/queue-list" className="btn btn-light shadow-sm d-flex align-items-center">
<FaList className="me-2" /> Daftar Antrian
</NavLink>
<NavLink to="/admin/call-queue" className="btn btn-outline-light d-flex align-items-center">
<NavLink to="/admin/call-queue" className="btn btn-light shadow-sm d-flex align-items-center">
<FaPhone className="me-2" /> Panggil Antrian
</NavLink>
{/* <NavLink to="/admin/queue-report" className="btn btn-outline-light d-flex align-items-center">
{/* <NavLink to="/admin/queue-report" className="btn btn-light shadow-sm d-flex align-items-center">
<FaChartBar className="me-2" /> Laporan Antrian
</NavLink> */}
<NavLink to="/admin/queue-settings-display" className="btn btn-outline-light d-flex align-items-center">
<NavLink to="/admin/queue-settings-display" className="btn btn-light shadow-sm d-flex align-items-center">
<FaCog className="me-2" /> Pengaturan Layar
</NavLink>
<NavLink to="/admin/queue-settings-menu" className="btn btn-outline-light d-flex align-items-center">
<NavLink to="/admin/queue-settings-menu" className="btn btn-light shadow-sm d-flex align-items-center">
<FaTools className="me-2" /> Pengaturan Menu
</NavLink>
</div>
@ -48,7 +48,7 @@ const AdminSidebar = () => {
<div>
<button
onClick={() => toggleMenu("monitor")}
className="btn btn-secondary w-100 text-start mb-2 d-flex align-items-center justify-content-between"
className="btn btn-light shadow-sm w-100 text-start mb-2 d-flex align-items-center justify-content-between"
>
<span><FaTv className="me-2" /> Monitor</span>
{openMenu === "monitor" ? <FaChevronDown /> : <FaChevronRight />}
@ -57,13 +57,13 @@ const AdminSidebar = () => {
<div className="ms-3 my-2 d-grid gap-2">
<button
onClick={() => window.open("/queue-display", "_blank")}
className="btn btn-outline-light d-flex align-items-center"
className="btn btn-light shadow-sm d-flex align-items-center"
>
<FaTv className="me-2" /> Tampilan Antrian
</button>
<button
onClick={() => window.open("/queue-menu", "_blank")}
className="btn btn-outline-light d-flex align-items-center"
className="btn btn-light shadow-sm d-flex align-items-center"
>
<FaThList className="me-2" /> Menu Antrian
</button>

View File

@ -42,11 +42,11 @@ const QueueDisplay = () => {
style={{ borderRadius: "10px" }}
>
<div className="flex-grow-1">
<h3>Pandawa24Jam</h3>
<p>Call Center: 081234567891</p>
<h5>Pandawa24Jam</h5>
<p>CS: 081234567891</p>
</div>
<div className="position-absolute top-50 end-0 translate-middle-y me-3">
<h2 className="mb-0">{currentTime.toLocaleTimeString()}</h2>
<h4 className="mb-0">{currentTime.toLocaleTimeString()}</h4>
</div>
</div>

View File

@ -20,6 +20,9 @@ const ServiceSelection = () => {
const handleServiceSelect = (serviceId) => {
setSelectedService(services.find(service => service.id === serviceId));
if (!enableForm) {
handleSubmit(new Event("submit"));
}
};
const handleSubmit = (e) => {
@ -50,13 +53,8 @@ const ServiceSelection = () => {
return (
<Col key={service.id} md={6} className="mb-3">
<Card
className={`h-100 ${isSelected ? "bg-primary text-white border-primary shadow-sm" : "bg-light"}`}
onClick={() => {
handleServiceSelect(service.id);
if (!enableForm) {
handleSubmit(new Event("submit"));
}
}}
className={`h-100 shadow rounded-3 ${isSelected ? "bg-primary text-white border-primary" : "bg-light"}`}
onClick={() => handleServiceSelect(service.id)}
>
<Card.Body className="d-flex flex-column justify-content-center align-items-center">
{service.icon}
@ -82,7 +80,7 @@ const ServiceSelection = () => {
Beri tanda centang untuk mengisi form
</p>
</div>
<Card className={`flex-grow-1 ${enableForm ? "" : "bg-secondary bg-opacity-10"}`}>
<Card className={`flex-grow-1 shadow rounded-3 ${enableForm ? "" : "bg-secondary bg-opacity-10"}`}>
<Card.Body className="d-flex flex-column">
<Form onSubmit={handleSubmit} className="flex-grow-1 d-flex flex-column">
<Form.Group className="mb-3" controlId="formName">
@ -115,34 +113,18 @@ const ServiceSelection = () => {
</Card>
<div className="d-flex flex-grow-1 my-3 align-items-center justify-content-center">
<Carousel>
<Carousel.Item interval={3000}>
<img className="d-block w-100" src="https://images.unsplash.com/photo-1519389950473-47ba0277781c?w=800&h=400&fit=crop" alt="First slide" height={400} />
<Carousel.Caption>
<h3>First Slide Label</h3>
<p>Nulla vitae elit libero, a pharetra augue mollis interdum.</p>
</Carousel.Caption>
</Carousel.Item>
<Carousel.Item interval={3000}>
<img className="d-block w-100" src="https://images.unsplash.com/photo-1519389950473-47ba0277781c?w=800&h=400&fit=crop" alt="Second slide" height={400} />
<Carousel.Caption>
<h3>Second Slide Label</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</Carousel.Caption>
</Carousel.Item>
<Carousel.Item interval={3000}>
<img className="d-block w-100" src="https://images.unsplash.com/photo-1519389950473-47ba0277781c?w=800&h=400&fit=crop" alt="Third slide" height={400} />
<Carousel.Caption>
<h3>Third Slide Label</h3>
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur.</p>
</Carousel.Caption>
</Carousel.Item>
{["c1.png", "c2.jpg", "c3.jpg"].map((image, index) => (
<Carousel.Item key={index} interval={3000}>
<img className="d-block w-100" src={`/public/${image}`} alt={`Slide ${index + 1}`} height={300} />
</Carousel.Item>
))}
</Carousel>
</div>
</Col>
</Row>
) : (
<Container className="d-flex justify-content-center align-items-center vh-100">
<Card className="p-4 text-center" style={{ maxWidth: "400px", width: "100%" }}>
<Card className="p-4 text-center shadow-lg rounded-3" style={{ maxWidth: "400px", width: "100%" }}>
<Card.Body>
<Card.Title className="fw-bold fs-3">Tiket Antrian</Card.Title>
<hr />