24/02/2025
This commit is contained in:
parent
3fd2931404
commit
11d418602a
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
38
eslint.config.js
Normal file
38
eslint.config.js
Normal file
@ -0,0 +1,38 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import react from 'eslint-plugin-react'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
|
||||
export default [
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
files: ['**/*.{js,jsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
ecmaFeatures: { jsx: true },
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
settings: { react: { version: '18.3' } },
|
||||
plugins: {
|
||||
react,
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...js.configs.recommended.rules,
|
||||
...react.configs.recommended.rules,
|
||||
...react.configs['jsx-runtime'].rules,
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react/jsx-no-target-blank': 'off',
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
17
index.html
Normal file
17
index.html
Normal file
@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<!-- Poppins Font -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">
|
||||
<title>Vite + React</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
8496
package-lock.json
generated
Normal file
8496
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
41
package.json
Normal file
41
package.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "queue-app",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^2.6.0",
|
||||
"axios": "^1.7.9",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"bootstrap": "^5.3.3",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.7",
|
||||
"express-validator": "^7.2.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"react": "^19.0.0",
|
||||
"react-bootstrap": "^2.10.9",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-player": "^2.16.0",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router-dom": "^6.29.0",
|
||||
"redux": "^5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.19.0",
|
||||
"@types/react": "^19.0.8",
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"@vitejs/plugin-react": "^1.3.2",
|
||||
"eslint": "^9.19.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.18",
|
||||
"globals": "^15.14.0",
|
||||
"vite": "^6.1.1"
|
||||
}
|
||||
}
|
||||
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
0
src/components/AdminPage/ListQueue.jsx
Normal file
0
src/components/AdminPage/ListQueue.jsx
Normal file
70
src/components/Display/QueueDisplay.jsx
Normal file
70
src/components/Display/QueueDisplay.jsx
Normal file
@ -0,0 +1,70 @@
|
||||
import { Container, Row, Col, Card } from "react-bootstrap";
|
||||
import ReactPlayer from "react-player";
|
||||
|
||||
const sampleTickets = [
|
||||
{ number: "A1", counter: "1" },
|
||||
{ number: "A2", counter: "2" },
|
||||
{ number: "A3", counter: "3" },
|
||||
{ number: "A4", counter: "4" },
|
||||
{ number: "A5", counter: "5" },
|
||||
{ number: "A6", counter: "6" },
|
||||
{ number: "A7", counter: "7" },
|
||||
{ number: "A8", counter: "8" },
|
||||
{ number: "A9", counter: "9" },
|
||||
{ number: "A10", counter: "10" },
|
||||
];
|
||||
|
||||
const QueueDisplay = () => {
|
||||
return (
|
||||
<Container fluid className="p-3" style={{ overflowX: "hidden" }}>
|
||||
<Row className="mb-3 g-0">
|
||||
{/* Informasi Bisnis & Nomor Antrian Utama */}
|
||||
<Col md={4} className="px-3 d-flex flex-column justify-content-between">
|
||||
<div className="mb-3 p-3 text-white bg-dark text-center" style={{ borderRadius: "10px" }}>
|
||||
<h4 className="mb-1">Jagowebdev.com</h4>
|
||||
<p className="mb-1">Jl. Zebra III No. 32, Pedurungan Kidul, Semarang</p>
|
||||
<p>Telp: 08561363962</p>
|
||||
</div>
|
||||
<Card className="flex-grow-1 d-flex align-items-center justify-content-center text-center"
|
||||
style={{ borderRadius: "10px", padding: "20px", width: "100%", minHeight: "250px" }}>
|
||||
<Card.Body>
|
||||
<h5 className="mb-2"><strong>NOMOR ANTRIAN</strong></h5>
|
||||
<h1 style={{ fontSize: "80px", fontWeight: "bold", margin: "10px 0" }}>A0</h1>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
{/* Video Player */}
|
||||
<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>
|
||||
|
||||
{/* Loket Antrian */}
|
||||
<Row className="text-center d-flex flex-wrap justify-content-center row-cols-2 row-cols-md-3 row-cols-lg-5 g-0 px-2">
|
||||
{sampleTickets.map((ticket, index) => (
|
||||
<Col key={index} className="d-flex justify-content-center p-2">
|
||||
<Card className="d-flex align-items-center justify-content-center text-center flex-grow-1"
|
||||
style={{ borderRadius: "10px", width: "100%", minHeight: "150px" }}>
|
||||
<Card.Body>
|
||||
<Card.Title>Nomor Antrian</Card.Title>
|
||||
<h2 style={{ fontSize: "40px", fontWeight: "bold" }}>{ticket.number}</h2>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default QueueDisplay;
|
||||
135
src/components/Display/ServiceSelection.jsx
Normal file
135
src/components/Display/ServiceSelection.jsx
Normal file
@ -0,0 +1,135 @@
|
||||
import { Container, Card, Button, Row, Col, Form, } from 'react-bootstrap';
|
||||
import { useState } from 'react';
|
||||
import ReactPlayer from 'react-player';
|
||||
|
||||
const services = [
|
||||
{id: "TSP1", name: "test1"},
|
||||
{id: "TSP2", name: "test2"},
|
||||
{id: "TSP3", name: "test3"},
|
||||
{id: "TSP4", name: "test4"},
|
||||
{id: "TSP5", name: "test5"},
|
||||
{id: "TSP6", name: "test6"}
|
||||
];
|
||||
|
||||
const ServiceSelection = () => {
|
||||
|
||||
const [selectedService, setSelectedService] = useState (null);
|
||||
const [name, setName] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [ticket, setTicket] = useState (null);
|
||||
|
||||
const handleServiceSelect = (serviceId) => {
|
||||
setSelectedService(services.find(service => service.id === serviceId));
|
||||
}
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
if (selectedService && name && phone) {
|
||||
const queueNumber = `${selectedService.id}-${Math.floor(1000 + Math.random() * 9000)}`;
|
||||
const newTicket = {
|
||||
number : queueNumber,
|
||||
serviceId : selectedService.id,
|
||||
service : selectedService.name,
|
||||
name,
|
||||
phone
|
||||
};
|
||||
setTicket(newTicket);
|
||||
setTimeout(() => window.print(), 500);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{!ticket ? (
|
||||
<Row className='vh-100'>
|
||||
<Col md={6} className='d-flex flex-column'>
|
||||
<h3 className='my-4'>Pilih Layanan</h3>
|
||||
<Row className='flex-grow-1'>
|
||||
{services.map(service => {
|
||||
const isSelected = selectedService?.id === service.id;
|
||||
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"}`}>
|
||||
<Card.Body className='d-flex flex-column justify-content-between'>
|
||||
<Card.Title className='text-center'>{service.name}</Card.Title>
|
||||
<Button
|
||||
variant={isSelected ? "light" : "outline-primary"}
|
||||
onClick={() => handleServiceSelect(service.id)}>
|
||||
{isSelected ? "Dipilih" : "Pilih Layanan"}
|
||||
</Button>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Col>
|
||||
)
|
||||
})}
|
||||
</Row>
|
||||
</Col>
|
||||
<Col md={6} className='d-flex flex-column'>
|
||||
<h3 className='my-4'>Masukan Data Diri</h3>
|
||||
<Card className='flex-grow-1'>
|
||||
<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'>
|
||||
<Form.Label>Nama</Form.Label>
|
||||
<Form.Control
|
||||
type='text'
|
||||
placeholder='@example: pandawa'
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group className='mb-3' controlId='formPhone'>
|
||||
<Form.Label>No Telepon</Form.Label>
|
||||
<Form.Control
|
||||
type='text'
|
||||
placeholder='@example: 08xxxxxxxxxxx'
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
<Button variant='primary' type='submit'>Submit</Button>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
<div className='d-flex flex-grow-1 my-3'>
|
||||
<ReactPlayer
|
||||
url="https://www.youtube.com/watch?v=FaU8BkqmXzo&pp=ygULc3RvY2sgdmlkZW8%3D"
|
||||
controls
|
||||
width="100%"
|
||||
height="400px"
|
||||
/>
|
||||
</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.Body>
|
||||
<Card.Title className="fw-bold fs-3">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
|
||||
11
src/main.jsx
Normal file
11
src/main.jsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import AppRoutes from './routes/AppRoutes.jsx'
|
||||
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||
import './styles/style.css'
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<StrictMode>
|
||||
<AppRoutes/>
|
||||
</StrictMode>,
|
||||
)
|
||||
11
src/pages/Display/QueueDisplayPage.jsx
Normal file
11
src/pages/Display/QueueDisplayPage.jsx
Normal file
@ -0,0 +1,11 @@
|
||||
import QueueDisplay from "../../components/Display/QueueDisplay"
|
||||
|
||||
const QueueDisplayPage = () => {
|
||||
return (
|
||||
<>
|
||||
<QueueDisplay/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default QueueDisplayPage
|
||||
11
src/pages/Display/QueueMenuPage.jsx
Normal file
11
src/pages/Display/QueueMenuPage.jsx
Normal file
@ -0,0 +1,11 @@
|
||||
import ServiceSelection from "../../components/Display/ServiceSelection"
|
||||
|
||||
const QueueMenuPage = () => {
|
||||
return (
|
||||
<>
|
||||
<ServiceSelection/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default QueueMenuPage
|
||||
10
src/pages/Display/QueueSettingsDisplayPage.jsx
Normal file
10
src/pages/Display/QueueSettingsDisplayPage.jsx
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
const QueueSettingsDisplayPage = () => {
|
||||
return (
|
||||
<div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default QueueSettingsDisplayPage
|
||||
10
src/pages/Display/QueueSettingsMenuPage.jsx
Normal file
10
src/pages/Display/QueueSettingsMenuPage.jsx
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
const QueueSettingsMenuPage = () => {
|
||||
return (
|
||||
<div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default QueueSettingsMenuPage
|
||||
10
src/pages/Queue/CallQueuePage.jsx
Normal file
10
src/pages/Queue/CallQueuePage.jsx
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
const CallQueuePage = () => {
|
||||
return (
|
||||
<div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CallQueuePage
|
||||
10
src/pages/Queue/QueueListPage.jsx
Normal file
10
src/pages/Queue/QueueListPage.jsx
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
const QueueListPage = () => {
|
||||
return (
|
||||
<div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default QueueListPage
|
||||
10
src/pages/Queue/QueueReportPage.jsx
Normal file
10
src/pages/Queue/QueueReportPage.jsx
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
const QueueReportPage = () => {
|
||||
return (
|
||||
<div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default QueueReportPage
|
||||
19
src/redux/queueSlice.js
Normal file
19
src/redux/queueSlice.js
Normal file
@ -0,0 +1,19 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
const queueSlice = createSlice({
|
||||
name: "queue",
|
||||
initialState: {
|
||||
tickets: [],
|
||||
},
|
||||
reducers: {
|
||||
addTicket: (state, action) => {
|
||||
state.tickets.push(action.payload);
|
||||
},
|
||||
removeTicket: (state) => {
|
||||
state.tickets.shift();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const {addTicket, removeTicket } = queueSlice.actions;
|
||||
export default queueSlice.reducer;
|
||||
12
src/redux/store.js
Normal file
12
src/redux/store.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import queueReducer from "./queueSlice";
|
||||
import themeReducer from "./themeSlice";
|
||||
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
queue: queueReducer,
|
||||
theme: themeReducer,
|
||||
}
|
||||
});
|
||||
|
||||
export default store;
|
||||
16
src/redux/themeSlice.js
Normal file
16
src/redux/themeSlice.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
const themeSlice = createSlice({
|
||||
name: "theme",
|
||||
initialState: {
|
||||
darkMode: false,
|
||||
},
|
||||
reducers: {
|
||||
toggleTheme: (state) => {
|
||||
state.darkMode = !state.darkMode;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const {toggleTheme} = themeSlice.actions;
|
||||
export default themeSlice.reducer;
|
||||
29
src/routes/AppRoutes.jsx
Normal file
29
src/routes/AppRoutes.jsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { BrowserRouter as Router, Routes, Route } from "react-router-dom"
|
||||
import QueueDisplayPage from "../pages/Display/QueueDisplayPage"
|
||||
import QueueSettingsDisplayPage from "../pages/Display/QueueSettingsDisplayPage"
|
||||
import QueueSettingsMenuPage from "../pages/Display/QueueSettingsMenuPage"
|
||||
import QueueMenuPage from "../pages/Display/QueueMenuPage"
|
||||
import CallQueuePage from "../pages/Queue/CallQueuePage"
|
||||
import QueueListPage from "../pages/Queue/QueueListPage"
|
||||
import QueueReportPage from "../pages/Queue/QueueReportPage"
|
||||
|
||||
const AppRoutes = () => {
|
||||
return (
|
||||
<Router>
|
||||
<Routes>
|
||||
{/* Queue Pages */}
|
||||
<Route path="/" element={<QueueListPage/>}/>
|
||||
<Route path="/queue-list" element={<QueueListPage/>}/>
|
||||
<Route path="/call-queue" element={<CallQueuePage/>}/>
|
||||
<Route path="/queue-report" element={<QueueReportPage/>}/>
|
||||
{/* Display Pages */}
|
||||
<Route path="/queue-menu-settings" element={<QueueSettingsMenuPage/>}/>
|
||||
<Route path="/queue-menu" element={<QueueMenuPage/>}/>
|
||||
<Route path="/queue-display-settings" element={<QueueSettingsDisplayPage/>}/>
|
||||
<Route path="/queue-display" element={<QueueDisplayPage/>}/>
|
||||
</Routes>
|
||||
</Router>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppRoutes
|
||||
3
src/styles/style.css
Normal file
3
src/styles/style.css
Normal file
@ -0,0 +1,3 @@
|
||||
:root {
|
||||
--bs-body-font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
7
vite.config.js
Normal file
7
vite.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
Loading…
Reference in New Issue
Block a user