styling yang menggunakan data API
This commit is contained in:
parent
7cf8029368
commit
c31bd10c5c
3
.env
3
.env
@ -1 +1,2 @@
|
||||
VITE_API_URL = http://localhost:3000
|
||||
VITE_API_URL = http://localhost:3000
|
||||
VITE_API_TEST_URL = http://localhost:3001
|
||||
12
package-lock.json
generated
12
package-lock.json
generated
@ -15,6 +15,7 @@
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.7",
|
||||
"express-validator": "^7.2.1",
|
||||
"idb": "^8.0.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"react": "^19.0.0",
|
||||
"react-bootstrap": "^2.10.9",
|
||||
@ -3038,6 +3039,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/idb": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/idb/-/idb-8.0.2.tgz",
|
||||
"integrity": "sha512-CX70rYhx7GDDQzwwQMDwF6kDRQi5vVs6khHUumDrMecBylKkwvZ8HWvKV08AGb7VbpoGCWUQ4aHzNDgoUiOIUg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
@ -7088,6 +7095,11 @@
|
||||
"function-bind": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"idb": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/idb/-/idb-8.0.2.tgz",
|
||||
"integrity": "sha512-CX70rYhx7GDDQzwwQMDwF6kDRQi5vVs6khHUumDrMecBylKkwvZ8HWvKV08AGb7VbpoGCWUQ4aHzNDgoUiOIUg=="
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.7",
|
||||
"express-validator": "^7.2.1",
|
||||
"idb": "^8.0.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"react": "^19.0.0",
|
||||
"react-bootstrap": "^2.10.9",
|
||||
|
||||
BIN
queueapp.zip
Normal file
BIN
queueapp.zip
Normal file
Binary file not shown.
@ -24,7 +24,7 @@ export const updateQueueStatus = async (queueId, status) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const createQueue = async (customerData) => {
|
||||
export const createTicketApi = async (customerData) => {
|
||||
try {
|
||||
const response = await axios.post(`${API_URL}/queues`, customerData);
|
||||
return response.data;
|
||||
|
||||
@ -34,12 +34,12 @@ const AdminSidebar = () => {
|
||||
{/* <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-light shadow-sm 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-light shadow-sm d-flex align-items-center">
|
||||
<FaTools className="me-2" /> Pengaturan Menu
|
||||
</NavLink>
|
||||
</NavLink> */}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
|
||||
// FILE INI UNTUK TEST TAMPILAN DATA DENGAN API
|
||||
|
||||
import { fetchQueues, updateQueueStatus } from "../../api/queueApi";
|
||||
import { fetchQueues, updateQueueStatus, createTicketApi } from "../../api/queueApi";
|
||||
|
||||
export const getProcessedQueues = async () => {
|
||||
const data = await fetchQueues();
|
||||
@ -28,3 +27,7 @@ export const getProcessedQueues = async () => {
|
||||
export const changeQueueStatus = async (queueId, newStatus) => {
|
||||
return await updateQueueStatus(queueId, newStatus);
|
||||
};
|
||||
|
||||
export const createTicket = async (ticketData) => {
|
||||
return await createTicketApi(ticketData);
|
||||
};
|
||||
|
||||
@ -45,7 +45,7 @@ const TestQueueTable = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Container className="mt-4">
|
||||
<Container>
|
||||
<Card className="shadow">
|
||||
<Card.Header className="bg-primary text-white">
|
||||
<h5 className="mb-0">List Antrian</h5>
|
||||
@ -85,7 +85,7 @@ const TestQueueTable = () => {
|
||||
</span>
|
||||
</td>
|
||||
<td>{new Date(queue.createdAt).toLocaleString()}</td>
|
||||
<td>
|
||||
<td className="text-center p-2">
|
||||
{queue.status !== "In Progress" && queue.status !== "Completed" && (
|
||||
<Button
|
||||
variant="info"
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
|
||||
// FILE INI UNTUK TEST TAMPILAN DATA DENGAN API
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { getProcessedQueues } from "../Admin/TestQueueActions";
|
||||
import { Card, Container } from "react-bootstrap";
|
||||
import { Card, Container, Row, Col, ListGroup } from "react-bootstrap";
|
||||
import ReactPlayer from "react-player";
|
||||
|
||||
const TestQueueDisplay = () => {
|
||||
const [queues, setQueues] = useState([]);
|
||||
const [currentTime, setCurrentTime] = useState(new Date());
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
@ -25,21 +24,81 @@ const TestQueueDisplay = () => {
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setCurrentTime(new Date());
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Container className="mt-4 text-center">
|
||||
<h2 className="mb-4">Layar Antrian</h2>
|
||||
{queues.length > 0 ? (
|
||||
<Card className="shadow p-4">
|
||||
<h1 className="display-3 text-primary">{queues[0].id}</h1>
|
||||
<h4>{queues[0].customerName}</h4>
|
||||
<p>{queues[0].serviceName}</p>
|
||||
<Container fluid className="p-3" style={{ overflowX: "hidden" }}>
|
||||
<Row className="mb-3 g-0">
|
||||
<Col md={4} className="px-3 d-flex flex-column justify-content-between">
|
||||
<div
|
||||
className="shadow mb-3 p-3 d-flex justify-content-between align-items-center position-relative"
|
||||
style={{ borderRadius: "10px" }}
|
||||
>
|
||||
<div className="flex-grow-1">
|
||||
<h5>Pandawa24Jam</h5>
|
||||
<p>CS: 081234567891</p>
|
||||
</div>
|
||||
<div className="position-absolute top-50 end-0 translate-middle-y me-3">
|
||||
<h4 className="mb-0">{currentTime.toLocaleTimeString()}</h4>
|
||||
</div>
|
||||
</div>
|
||||
{queues.length > 0 ? (
|
||||
<Card className="shadow flex-grow-1 d-flex align-items-center justify-content-center text-center"
|
||||
style={{ borderRadius: "10px", padding: "30px", width: "100%", minHeight: "250px" }}>
|
||||
<h1 className="display-3 text-primary">{queues[0].id}</h1>
|
||||
<h4>{queues[0].customerName}</h4>
|
||||
<p>{queues[0].serviceName}</p>
|
||||
</Card>
|
||||
) : (
|
||||
<p className="text-muted">Tidak ada antrian aktif</p>
|
||||
)}
|
||||
</Col>
|
||||
<Col md={8} className="px-3 d-flex align-items-stretch">
|
||||
<div className="w-100 d-flex">
|
||||
<ReactPlayer
|
||||
url="https://www.youtube.com/watch?v=FaU8BkqmXzo&pp=ygULc3RvY2sgdmlkZW8%3D"
|
||||
controls
|
||||
width="100%"
|
||||
height="100%"
|
||||
className="react-player"
|
||||
style={{ borderRadius: "10px", overflow: "hidden", flexGrow: 1 }}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="d-flex flex-wrap g-0">
|
||||
<Col className="d-flex p-3">
|
||||
<Card className="flex-grow-1"
|
||||
style={{ width: "100%" }}>
|
||||
<Card.Header>
|
||||
<h2>
|
||||
List Layanan antrian
|
||||
</h2>
|
||||
</Card.Header>
|
||||
<Row className="m-2">
|
||||
{Array.from(new Set(queues.map((queue) => queue.serviceName))).map((serviceName, index) => (
|
||||
<Col key={index} md={2}>
|
||||
<h3>{serviceName}</h3>
|
||||
{queues
|
||||
.filter((q) => q.serviceName === serviceName)
|
||||
.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt))
|
||||
.map((q, i) => (
|
||||
<span key={i}>{q.serviceId} - {q.id} - {new Date(q.createdAt).toLocaleTimeString()}</span>
|
||||
))}
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
</Card>
|
||||
) : (
|
||||
<p className="text-muted">Tidak ada antrian aktif</p>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default TestQueueDisplay;
|
||||
|
||||
|
||||
@ -1,34 +1,155 @@
|
||||
|
||||
// FILE INI UNTUK TEST TAMPILAN DATA DENGAN API
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { getProcessedQueues } from "../Admin/TestQueueActions";
|
||||
import { Container, Card, Row, Col, Table } from "react-bootstrap";
|
||||
import { getProcessedQueues, createTicket } from "../Admin/TestQueueActions";
|
||||
import { Container, Card, Row, Col, Form, Button, Carousel } from "react-bootstrap";
|
||||
import { FaPrint, FaPalette, FaFileAlt, FaUndo, FaTruck, FaUser } from 'react-icons/fa';
|
||||
|
||||
const services = [
|
||||
{ id: "j0001", name: "Siap Print", icon: <FaPrint size={50} /> },
|
||||
{ id: "j0002", name: "Design/Edit/Kreatif", icon: <FaPalette size={50} /> },
|
||||
{ id: "j0003", name: "FotoCopy/Jilid/Scan", icon: <FaFileAlt size={50} /> },
|
||||
{ id: "j0004", name: "Retur Penjualan", icon: <FaUndo size={50} /> },
|
||||
{ id: "j0005", name: "Online Pickup", icon: <FaTruck size={50} /> },
|
||||
{ id: "j0006", name: "Tamu", icon: <FaUser size={50} /> }
|
||||
];
|
||||
|
||||
const ServiceSelection = () => {
|
||||
const [selectedService, setSelectedService] = useState(null);
|
||||
const [name, setName] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [ticket, setTicket] = useState(null);
|
||||
const [enableForm, setEnableForm] = useState(false);
|
||||
const [queues, setQueues] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedService && !enableForm) {
|
||||
generateTicket();
|
||||
}
|
||||
getProcessedQueues().then(setQueues);
|
||||
}, []);
|
||||
}, [selectedService]);
|
||||
|
||||
const handleServiceSelect = (serviceId) => {
|
||||
setSelectedService(services.find(service => service.id === serviceId));
|
||||
};
|
||||
|
||||
const generateTicket = async () => {
|
||||
if (selectedService) {
|
||||
const queueNumber = `${selectedService.id}-${Math.floor(1000 + Math.random() * 9000)}`;
|
||||
const newTicket = {
|
||||
number: queueNumber,
|
||||
serviceId: selectedService.id,
|
||||
service: selectedService.name,
|
||||
name: enableForm ? name : "-",
|
||||
phone: enableForm ? phone : "-",
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await createTicket(newTicket);
|
||||
setTicket(response.data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
generateTicket();
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
{queues.map((operator) => (
|
||||
<Col key={operator.operatorId}>
|
||||
<h3>{operator.operatorName}</h3>
|
||||
<ul>
|
||||
{operator.queues.map((queue) => (
|
||||
<li key={queue.id}>{queue.serviceName}</li>
|
||||
))}
|
||||
</ul>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
<Container fluid className="bg-dark text-light">
|
||||
{!ticket ? (
|
||||
<Row className="vh-100">
|
||||
<Col md={6} className="d-flex flex-column">
|
||||
<Row className="flex-grow-1">
|
||||
{services.map(service => (
|
||||
<Col key={service.id} md={6} className="my-2">
|
||||
<Card
|
||||
className="h-100 shadow rounded-3 bg-secondary"
|
||||
onClick={() => handleServiceSelect(service.id)}
|
||||
>
|
||||
<Card.Body className="d-flex flex-column justify-content-center align-items-center">
|
||||
{service.icon}
|
||||
<Card.Text className="text-center mt-5">
|
||||
{service.name}
|
||||
</Card.Text>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
</Col>
|
||||
<Col md={6} className="d-flex flex-column my-2">
|
||||
<Card className={`flex-grow-1 shadow rounded-3 ${enableForm ? "bg-secondary" : "bg-dark"}`}>
|
||||
<Card.Body className="d-flex flex-column">
|
||||
<div className="d-flex align-items-center ">
|
||||
<h3 className="mb-0 me-3 text-uppercase text-white">Data Diri</h3>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
onChange={() => setEnableForm(!enableForm)}
|
||||
style={{ transform: "scale(1.5)" }}
|
||||
/>
|
||||
<p className="my-2 text-white" style={{ transform: "scale(0.9)" }}>
|
||||
Beri tanda centang untuk mengisi form
|
||||
</p>
|
||||
</div>
|
||||
<Form onSubmit={handleSubmit} className="flex-grow-1">
|
||||
<Form.Group className="mb-3" controlId="formName">
|
||||
<Form.Label className={enableForm ? "text-light" : "text-muted"}>Nama</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder="@example: pandawa"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
disabled={!enableForm}
|
||||
className={enableForm ? "bg-light text-dark" : "bg-dark text-muted"}
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group className="mb-3" controlId="formPhone">
|
||||
<Form.Label className={enableForm ? "text-light" : "text-muted"}>No Telepon</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder="@example: 08xxxxxxxxxxx"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
disabled={!enableForm}
|
||||
className={enableForm ? "bg-light text-dark" : "bg-dark text-muted"}
|
||||
/>
|
||||
</Form.Group>
|
||||
<Button variant={enableForm ? "primary" : "secondary"} type="submit" disabled={!enableForm} className="p-4">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
<div className="d-flex flex-grow-1 align-items-center justify-content-center">
|
||||
<Carousel>
|
||||
{["c1.png", "c2.jpg", "c3.jpg"].map((image, index) => (
|
||||
<Carousel.Item key={index} interval={3000} className='rounded'>
|
||||
<img className="d-block img-fluid rounded" src={`/public/${image}`} alt={`Slide ${index + 1}`}/>
|
||||
</Carousel.Item>
|
||||
))}
|
||||
</Carousel>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
) : (
|
||||
<Container className="d-flex justify-content-center align-items-center vh-100">
|
||||
<Card className="p-4 text-center shadow-lg rounded-3 bg-secondary" style={{ maxWidth: "400px", width: "100%" }}>
|
||||
<Card.Body>
|
||||
<Card.Title className="fw-bold fs-3 text-uppercase">Tiket Antrian</Card.Title>
|
||||
<hr />
|
||||
<Card.Text className="fw-bold text-uppercase fs-2 bg-light p-3 rounded">{ticket.number}</Card.Text>
|
||||
<Card.Text><strong>Layanan :</strong> {ticket.service}</Card.Text>
|
||||
<Card.Text><strong>Nama :</strong> {ticket.name}</Card.Text>
|
||||
<Card.Text><strong>No Telepon :</strong> {ticket.phone}</Card.Text>
|
||||
<Button variant="success" onClick={() => window.print()}>Cetak Tiket</Button>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Container>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServiceSelection;
|
||||
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
import TestQueueTable from "../../components/Admin/TestQueueTable";
|
||||
import QueueTable from "../../components/Admin/QueueTable";
|
||||
// import QueueTable from "../../components/Admin/QueueTable";
|
||||
// import TestComponent from "../../components/test";
|
||||
|
||||
const QueueListPage = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
{/* <TestQueueTable/> INI ADALAH UNTUK TEST DATA DENGAN API */}
|
||||
|
||||
<QueueTable/>
|
||||
<TestQueueTable/>
|
||||
{/* Atas Data dengan API bawah Data Static */}
|
||||
{/* <QueueTable/>
|
||||
<TestComponent/> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import TestQueueDisplay from "../../components/Display/TestQueueDisplay"
|
||||
import QueueDisplay from "../../components/Display/QueueDisplay"
|
||||
// import QueueDisplay from "../../components/Display/QueueDisplay"
|
||||
|
||||
const QueueDisplayPage = () => {
|
||||
return (
|
||||
<>
|
||||
{/* <TestQueueDisplay/> INI ADALAH UNTUK TEST DATA DENGAN API */}
|
||||
<QueueDisplay/>
|
||||
<TestQueueDisplay/>
|
||||
{/* Atas Data dengan API bawah Data Static */}
|
||||
{/* <QueueDisplay/> */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import TestServiceSelection from "../../components/Display/TestServiceSelection"
|
||||
import ServiceSelection from "../../components/Display/ServiceSelection"
|
||||
// import ServiceSelection from "../../components/Display/ServiceSelection"
|
||||
|
||||
const QueueMenuPage = () => {
|
||||
return (
|
||||
<>
|
||||
{/* <TestServiceSelection/> INI ADALAH UNTUK TEST DATA DENGAN API */}
|
||||
<ServiceSelection/>
|
||||
<TestServiceSelection/>
|
||||
{/* Atas Data dengan API bawah Data Static */}
|
||||
{/* <ServiceSelection/> */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ const AppRoutes = () => {
|
||||
<Router>
|
||||
<Routes>
|
||||
{/* Redirect default ke queue-list */}
|
||||
<Route path="/admin" element={<Navigate to="/admin/queue-list" replace />} />
|
||||
<Route path="/" element={<Navigate to="/admin/queue-list" replace />} />
|
||||
|
||||
{/* Rute untuk Admin */}
|
||||
<Route path="/admin" element={<AdminLayout />}>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user