create-bus-project 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/PUBLISHING.md +35 -0
- package/README.md +51 -0
- package/bin/create-bus-project.js +113 -0
- package/package.json +31 -0
- package/template/README.md +35 -0
- package/template/backend/.env +8 -0
- package/template/backend/.env.example +8 -0
- package/template/backend/README.md +29 -0
- package/template/backend/config/db.js +21 -0
- package/template/backend/controllers/authController.js +109 -0
- package/template/backend/controllers/bookingController.js +99 -0
- package/template/backend/controllers/busController.js +34 -0
- package/template/backend/controllers/scheduleController.js +52 -0
- package/template/backend/controllers/userController.js +39 -0
- package/template/backend/index.js +39 -0
- package/template/backend/package.json +22 -0
- package/template/backend/routes/authRoutes.js +17 -0
- package/template/backend/routes/bookingRoutes.js +20 -0
- package/template/backend/routes/busRoutes.js +10 -0
- package/template/backend/routes/scheduleRoutes.js +10 -0
- package/template/backend/routes/userRoutes.js +10 -0
- package/template/backend/schema.sql +59 -0
- package/template/backend/seeders/seedUsers.js +35 -0
- package/template/frontend/.env +1 -0
- package/template/frontend/.env.example +1 -0
- package/template/frontend/README.md +14 -0
- package/template/frontend/index.html +12 -0
- package/template/frontend/package.json +21 -0
- package/template/frontend/src/App.jsx +55 -0
- package/template/frontend/src/api.js +1 -0
- package/template/frontend/src/main.jsx +10 -0
- package/template/frontend/src/pages/Bookings.jsx +129 -0
- package/template/frontend/src/pages/Buses.jsx +100 -0
- package/template/frontend/src/pages/Dashboard.jsx +10 -0
- package/template/frontend/src/pages/Login.jsx +141 -0
- package/template/frontend/src/pages/ManagerDashboard.jsx +39 -0
- package/template/frontend/src/pages/PassengerDashboard.jsx +248 -0
- package/template/frontend/src/pages/Report.jsx +158 -0
- package/template/frontend/src/pages/Schedules.jsx +142 -0
- package/template/frontend/src/pages/Users.jsx +105 -0
- package/template/frontend/src/style.css +236 -0
- package/template/frontend/vite.config.js +10 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import cors from "cors";
|
|
3
|
+
import session from "express-session";
|
|
4
|
+
import dotenv from "dotenv";
|
|
5
|
+
|
|
6
|
+
import authRoutes from "./routes/authRoutes.js";
|
|
7
|
+
import userRoutes from "./routes/userRoutes.js";
|
|
8
|
+
import busRoutes from "./routes/busRoutes.js";
|
|
9
|
+
import scheduleRoutes from "./routes/scheduleRoutes.js";
|
|
10
|
+
import bookingRoutes from "./routes/bookingRoutes.js";
|
|
11
|
+
|
|
12
|
+
dotenv.config();
|
|
13
|
+
|
|
14
|
+
const app = express();
|
|
15
|
+
|
|
16
|
+
app.use(cors({
|
|
17
|
+
origin: process.env.FRONTEND_URL || "http://localhost:__FRONTEND_PORT__",
|
|
18
|
+
credentials: true
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
app.use(express.json());
|
|
22
|
+
|
|
23
|
+
app.use(session({
|
|
24
|
+
secret: process.env.SESSION_SECRET || "y_bus_secret",
|
|
25
|
+
resave: false,
|
|
26
|
+
saveUninitialized: false
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
app.use("/api/auth", authRoutes);
|
|
30
|
+
app.use("/api/users", userRoutes);
|
|
31
|
+
app.use("/api/buses", busRoutes);
|
|
32
|
+
app.use("/api/schedules", scheduleRoutes);
|
|
33
|
+
app.use("/api/bookings", bookingRoutes);
|
|
34
|
+
|
|
35
|
+
const PORT = process.env.PORT || __BACKEND_PORT__;
|
|
36
|
+
|
|
37
|
+
app.listen(PORT, () => {
|
|
38
|
+
console.log(`Server running on port ${PORT}`);
|
|
39
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__PROJECT_NAME__-backend",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node index.js",
|
|
8
|
+
"dev": "nodemon index.js",
|
|
9
|
+
"seed": "node seeders/seedUsers.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"bcryptjs": "^2.4.3",
|
|
13
|
+
"cors": "^2.8.5",
|
|
14
|
+
"dotenv": "^16.4.5",
|
|
15
|
+
"express": "^4.19.2",
|
|
16
|
+
"express-session": "^1.18.0",
|
|
17
|
+
"mysql2": "^3.11.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"nodemon": "^3.1.14"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
register,
|
|
5
|
+
login,
|
|
6
|
+
logout,
|
|
7
|
+
me
|
|
8
|
+
} from "../controllers/authController.js";
|
|
9
|
+
|
|
10
|
+
const router = express.Router();
|
|
11
|
+
|
|
12
|
+
router.post("/register", register);
|
|
13
|
+
router.post("/login", login);
|
|
14
|
+
router.post("/logout", logout);
|
|
15
|
+
router.get("/me", me);
|
|
16
|
+
|
|
17
|
+
export default router;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import {
|
|
3
|
+
getBookings,
|
|
4
|
+
getMyBookings,
|
|
5
|
+
getBookedSeats,
|
|
6
|
+
createBooking,
|
|
7
|
+
deleteBooking,
|
|
8
|
+
bookingReport
|
|
9
|
+
} from "../controllers/bookingController.js";
|
|
10
|
+
|
|
11
|
+
const router = express.Router();
|
|
12
|
+
|
|
13
|
+
router.get("/", getBookings);
|
|
14
|
+
router.get("/my/:userid", getMyBookings);
|
|
15
|
+
router.get("/seats/:scheduleid", getBookedSeats);
|
|
16
|
+
router.post("/", createBooking);
|
|
17
|
+
router.delete("/:id", deleteBooking);
|
|
18
|
+
router.get("/report", bookingReport);
|
|
19
|
+
|
|
20
|
+
export default router;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { getBuses, createBus, deleteBus } from "../controllers/busController.js";
|
|
3
|
+
|
|
4
|
+
const router = express.Router();
|
|
5
|
+
|
|
6
|
+
router.get("/", getBuses);
|
|
7
|
+
router.post("/", createBus);
|
|
8
|
+
router.delete("/:id", deleteBus);
|
|
9
|
+
|
|
10
|
+
export default router;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { getSchedules, createSchedule, deleteSchedule } from "../controllers/scheduleController.js";
|
|
3
|
+
|
|
4
|
+
const router = express.Router();
|
|
5
|
+
|
|
6
|
+
router.get("/", getSchedules);
|
|
7
|
+
router.post("/", createSchedule);
|
|
8
|
+
router.delete("/:id", deleteSchedule);
|
|
9
|
+
|
|
10
|
+
export default router;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { getUsers, createUser, deleteUser } from "../controllers/userController.js";
|
|
3
|
+
|
|
4
|
+
const router = express.Router();
|
|
5
|
+
|
|
6
|
+
router.get("/", getUsers);
|
|
7
|
+
router.post("/", createUser);
|
|
8
|
+
router.delete("/:id", deleteUser);
|
|
9
|
+
|
|
10
|
+
export default router;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
CREATE DATABASE IF NOT EXISTS __DB_NAME__;
|
|
2
|
+
USE __DB_NAME__;
|
|
3
|
+
|
|
4
|
+
DROP TABLE IF EXISTS yk_bookings;
|
|
5
|
+
DROP TABLE IF EXISTS yk_schedules;
|
|
6
|
+
DROP TABLE IF EXISTS yk_buses;
|
|
7
|
+
DROP TABLE IF EXISTS yk_users;
|
|
8
|
+
|
|
9
|
+
CREATE TABLE yk_users (
|
|
10
|
+
userid INT AUTO_INCREMENT PRIMARY KEY,
|
|
11
|
+
username VARCHAR(100) NOT NULL UNIQUE,
|
|
12
|
+
password VARCHAR(255) NOT NULL,
|
|
13
|
+
userRole ENUM('passenger','manager','driver') NOT NULL DEFAULT 'passenger'
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
CREATE TABLE yk_buses (
|
|
17
|
+
busid INT AUTO_INCREMENT PRIMARY KEY,
|
|
18
|
+
platenumber VARCHAR(50) NOT NULL UNIQUE,
|
|
19
|
+
totalseats INT NOT NULL,
|
|
20
|
+
bustype VARCHAR(100) NOT NULL
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE TABLE yk_schedules (
|
|
24
|
+
scheduleid INT AUTO_INCREMENT PRIMARY KEY,
|
|
25
|
+
busid INT NOT NULL,
|
|
26
|
+
routename VARCHAR(100) NOT NULL,
|
|
27
|
+
departurepoint VARCHAR(100) NOT NULL,
|
|
28
|
+
destination VARCHAR(100) NOT NULL,
|
|
29
|
+
departuretime DATETIME NOT NULL,
|
|
30
|
+
estimatedarrivaltime DATETIME NOT NULL,
|
|
31
|
+
ticketprice DECIMAL(10,2) NOT NULL,
|
|
32
|
+
scheduleStatus ENUM('active','cancelled','completed') DEFAULT 'active',
|
|
33
|
+
FOREIGN KEY (busid) REFERENCES yk_buses(busid) ON DELETE CASCADE
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
CREATE TABLE yk_bookings (
|
|
37
|
+
bookingid INT AUTO_INCREMENT PRIMARY KEY,
|
|
38
|
+
scheduleid INT NOT NULL,
|
|
39
|
+
userid INT NOT NULL,
|
|
40
|
+
passengername VARCHAR(100) NOT NULL,
|
|
41
|
+
passengergender VARCHAR(20) NOT NULL,
|
|
42
|
+
passengerphone VARCHAR(30) NOT NULL,
|
|
43
|
+
seatnumber VARCHAR(20) NOT NULL,
|
|
44
|
+
paymentstatus ENUM('pending','paid') DEFAULT 'pending',
|
|
45
|
+
bookingdate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
46
|
+
FOREIGN KEY (scheduleid) REFERENCES yk_schedules(scheduleid) ON DELETE CASCADE,
|
|
47
|
+
FOREIGN KEY (userid) REFERENCES yk_users(userid) ON DELETE CASCADE,
|
|
48
|
+
UNIQUE(scheduleid, seatnumber)
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
INSERT INTO yk_buses (platenumber, totalseats, bustype) VALUES
|
|
52
|
+
('RAB 123A', 30, 'Coaster'),
|
|
53
|
+
('RAC 456B', 45, 'Express Bus');
|
|
54
|
+
|
|
55
|
+
INSERT INTO yk_schedules
|
|
56
|
+
(busid, routename, departurepoint, destination, departuretime, estimatedarrivaltime, ticketprice, scheduleStatus)
|
|
57
|
+
VALUES
|
|
58
|
+
(1, 'Kigali - Huye', 'Kigali', 'Huye', '2026-06-01 08:00:00', '2026-06-01 11:00:00', 5000, 'active'),
|
|
59
|
+
(2, 'Kigali - Musanze', 'Kigali', 'Musanze', '2026-06-01 09:00:00', '2026-06-01 12:00:00', 4500, 'active');
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import bcrypt from "bcryptjs";
|
|
2
|
+
import db from "../config/db.js";
|
|
3
|
+
|
|
4
|
+
const users = [
|
|
5
|
+
{ username: "manager1", password: "123456", userRole: "manager" },
|
|
6
|
+
{ username: "driver1", password: "123456", userRole: "driver" },
|
|
7
|
+
{ username: "passenger1", password: "123456", userRole: "passenger" }
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
const seedUsers = async () => {
|
|
11
|
+
for (const user of users) {
|
|
12
|
+
const hashedPassword = await bcrypt.hash(user.password, 10);
|
|
13
|
+
|
|
14
|
+
const sql = `
|
|
15
|
+
INSERT INTO yk_users(username, password, userRole)
|
|
16
|
+
VALUES(?,?,?)
|
|
17
|
+
ON DUPLICATE KEY UPDATE username=username
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
db.query(sql, [user.username, hashedPassword, user.userRole], (err) => {
|
|
21
|
+
if (err) {
|
|
22
|
+
console.log(err.message);
|
|
23
|
+
} else {
|
|
24
|
+
console.log(`${user.userRole} seeded`);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
console.log("Seed completed");
|
|
31
|
+
process.exit();
|
|
32
|
+
}, 1500);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
seedUsers();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VITE_API_URL=http://localhost:__BACKEND_PORT__
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VITE_API_URL=http://localhost:__BACKEND_PORT__
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Y Bus Booking</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.jsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__PROJECT_NAME__-frontend",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"start": "vite",
|
|
9
|
+
"build": "vite build",
|
|
10
|
+
"preview": "vite preview"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
14
|
+
"jspdf": "^4.2.1",
|
|
15
|
+
"jspdf-autotable": "^5.0.8",
|
|
16
|
+
"lucide-react": "^0.468.0",
|
|
17
|
+
"react": "^18.3.1",
|
|
18
|
+
"react-dom": "^18.3.1",
|
|
19
|
+
"vite": "^5.4.2"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import Login from "./pages/Login.jsx";
|
|
4
|
+
import ManagerDashboard from "./pages/ManagerDashboard.jsx";
|
|
5
|
+
import PassengerDashboard from "./pages/PassengerDashboard.jsx";
|
|
6
|
+
import { API_BASE_URL } from "./api.js";
|
|
7
|
+
|
|
8
|
+
export default function App() {
|
|
9
|
+
const [user, setUser] = useState(null);
|
|
10
|
+
const [loading, setLoading] = useState(true);
|
|
11
|
+
|
|
12
|
+
const checkLoggedUser = async () => {
|
|
13
|
+
try {
|
|
14
|
+
const res = await fetch(`${API_BASE_URL}/api/auth/me`, {
|
|
15
|
+
credentials: "include"
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
if (res.ok) {
|
|
19
|
+
const data = await res.json();
|
|
20
|
+
setUser(data);
|
|
21
|
+
}
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.log(error);
|
|
24
|
+
} finally {
|
|
25
|
+
setLoading(false);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
checkLoggedUser();
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
const logout = async () => {
|
|
34
|
+
await fetch(`${API_BASE_URL}/api/auth/logout`, {
|
|
35
|
+
method: "POST",
|
|
36
|
+
credentials: "include"
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
setUser(null);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
if (loading) {
|
|
43
|
+
return <div className="center-page">Loading...</div>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!user) {
|
|
47
|
+
return <Login setUser={setUser} />;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (user.userRole === "manager") {
|
|
51
|
+
return <ManagerDashboard user={user} logout={logout} />;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return <PassengerDashboard user={user} logout={logout} />;
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const API_BASE_URL = import.meta.env.VITE_API_URL || "http://localhost:__BACKEND_PORT__";
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { API_BASE_URL } from "../api.js";
|
|
4
|
+
|
|
5
|
+
export default function Bookings() {
|
|
6
|
+
const [bookings, setBookings] = useState([]);
|
|
7
|
+
const [schedules, setSchedules] = useState([]);
|
|
8
|
+
const [users, setUsers] = useState([]);
|
|
9
|
+
|
|
10
|
+
const [form, setForm] = useState({
|
|
11
|
+
scheduleid: "",
|
|
12
|
+
userid: "",
|
|
13
|
+
passengername: "",
|
|
14
|
+
passengergender: "Female",
|
|
15
|
+
passengerphone: "",
|
|
16
|
+
seatnumber: "",
|
|
17
|
+
paymentstatus: "pending"
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const handleChange = (e) => {
|
|
21
|
+
setForm({
|
|
22
|
+
...form,
|
|
23
|
+
[e.target.name]: e.target.value
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const fetchBookings = async () => {
|
|
28
|
+
const res = await fetch(`${API_BASE_URL}/api/bookings`);
|
|
29
|
+
const data = await res.json();
|
|
30
|
+
setBookings(data);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const fetchSchedules = async () => {
|
|
34
|
+
const res = await fetch(`${API_BASE_URL}/api/schedules`);
|
|
35
|
+
const data = await res.json();
|
|
36
|
+
setSchedules(data);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const fetchUsers = async () => {
|
|
40
|
+
const res = await fetch(`${API_BASE_URL}/api/users`);
|
|
41
|
+
const data = await res.json();
|
|
42
|
+
setUsers(data);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
fetchBookings();
|
|
47
|
+
fetchSchedules();
|
|
48
|
+
fetchUsers();
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
const handleSubmit = async (e) => {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
|
|
54
|
+
const res = await fetch(`${API_BASE_URL}/api/bookings`, {
|
|
55
|
+
method: "POST",
|
|
56
|
+
headers: {
|
|
57
|
+
"Content-Type": "application/json"
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify(form)
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const data = await res.json();
|
|
63
|
+
|
|
64
|
+
if (!res.ok) {
|
|
65
|
+
alert(data.sqlMessage || data.message || "Booking failed`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
setForm({
|
|
70
|
+
scheduleid: "",
|
|
71
|
+
userid: "",
|
|
72
|
+
passengername: "",
|
|
73
|
+
passengergender: "Female",
|
|
74
|
+
passengerphone: "",
|
|
75
|
+
seatnumber: "",
|
|
76
|
+
paymentstatus: "pending"
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
fetchBookings();
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handleDelete = async (id) => {
|
|
83
|
+
await fetch(`${API_BASE_URL}/api/bookings/${id}`, {
|
|
84
|
+
method: "DELETE"
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
fetchBookings();
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<div>
|
|
92
|
+
<div className="card">
|
|
93
|
+
<h1>Bookings</h1>
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<table>
|
|
99
|
+
<thead>
|
|
100
|
+
<tr>
|
|
101
|
+
<th>Route</th>
|
|
102
|
+
<th>Passenger</th>
|
|
103
|
+
<th>Phone</th>
|
|
104
|
+
<th>Seat</th>
|
|
105
|
+
<th>Payment</th>
|
|
106
|
+
<th>Action</th>
|
|
107
|
+
</tr>
|
|
108
|
+
</thead>
|
|
109
|
+
|
|
110
|
+
<tbody>
|
|
111
|
+
{bookings.map((booking) => (
|
|
112
|
+
<tr key={booking.bookingid}>
|
|
113
|
+
<td>{booking.routename}</td>
|
|
114
|
+
<td>{booking.passengername}</td>
|
|
115
|
+
<td>{booking.passengerphone}</td>
|
|
116
|
+
<td>{booking.seatnumber}</td>
|
|
117
|
+
<td>{booking.paymentstatus}</td>
|
|
118
|
+
<td>
|
|
119
|
+
<button className="delete" onClick={() => handleDelete(booking.bookingid)}>
|
|
120
|
+
Delete
|
|
121
|
+
</button>
|
|
122
|
+
</td>
|
|
123
|
+
</tr>
|
|
124
|
+
))}
|
|
125
|
+
</tbody>
|
|
126
|
+
</table>
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { API_BASE_URL } from "../api.js";
|
|
4
|
+
|
|
5
|
+
export default function Buses() {
|
|
6
|
+
const [buses, setBuses] = useState([]);
|
|
7
|
+
|
|
8
|
+
const [form, setForm] = useState({
|
|
9
|
+
platenumber: "",
|
|
10
|
+
totalseats: "",
|
|
11
|
+
bustype: ""
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const handleChange = (e) => {
|
|
15
|
+
setForm({
|
|
16
|
+
...form,
|
|
17
|
+
[e.target.name]: e.target.value
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const fetchBuses = async () => {
|
|
22
|
+
const res = await fetch(`${API_BASE_URL}/api/buses`);
|
|
23
|
+
const data = await res.json();
|
|
24
|
+
setBuses(data);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
fetchBuses();
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
const handleSubmit = async (e) => {
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
|
|
34
|
+
await fetch(`${API_BASE_URL}/api/buses`, {
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers: {
|
|
37
|
+
"Content-Type": "application/json"
|
|
38
|
+
},
|
|
39
|
+
body: JSON.stringify(form)
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
setForm({
|
|
43
|
+
platenumber: "",
|
|
44
|
+
totalseats: "",
|
|
45
|
+
bustype: ""
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
fetchBuses();
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const handleDelete = async (id) => {
|
|
52
|
+
await fetch(`${API_BASE_URL}/api/buses/${id}`, {
|
|
53
|
+
method: "DELETE"
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
fetchBuses();
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div>
|
|
61
|
+
<div className="card">
|
|
62
|
+
<h1>Buses</h1>
|
|
63
|
+
|
|
64
|
+
<form className="form-grid" onSubmit={handleSubmit}>
|
|
65
|
+
<input name="platenumber" placeholder="Plate Number" value={form.platenumber} onChange={handleChange} />
|
|
66
|
+
<input name="totalseats" placeholder="Total Seats" value={form.totalseats} onChange={handleChange} />
|
|
67
|
+
<input name="bustype" placeholder="Bus Type" value={form.bustype} onChange={handleChange} />
|
|
68
|
+
<button type="submit">Add Bus</button>
|
|
69
|
+
</form>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<table>
|
|
73
|
+
<thead>
|
|
74
|
+
<tr>
|
|
75
|
+
|
|
76
|
+
<th>Plate Number</th>
|
|
77
|
+
<th>Total Seats</th>
|
|
78
|
+
<th>Bus Type</th>
|
|
79
|
+
<th>Action</th>
|
|
80
|
+
</tr>
|
|
81
|
+
</thead>
|
|
82
|
+
|
|
83
|
+
<tbody>
|
|
84
|
+
{buses.map((bus) => (
|
|
85
|
+
<tr key={bus.busid}>
|
|
86
|
+
<td>{bus.platenumber}</td>
|
|
87
|
+
<td>{bus.totalseats}</td>
|
|
88
|
+
<td>{bus.bustype}</td>
|
|
89
|
+
<td>
|
|
90
|
+
<button className="delete" onClick={() => handleDelete(bus.busid)}>
|
|
91
|
+
Delete
|
|
92
|
+
</button>
|
|
93
|
+
</td>
|
|
94
|
+
</tr>
|
|
95
|
+
))}
|
|
96
|
+
</tbody>
|
|
97
|
+
</table>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export default function Dashboard({ user }) {
|
|
3
|
+
return (
|
|
4
|
+
<div className="card">
|
|
5
|
+
<h1>Ticket Booking and Fleet Management System</h1>
|
|
6
|
+
<p>This system manages users, buses, schedules, bookings, and passenger reports.</p>
|
|
7
|
+
{user && <p>Logged in as: <b>{user.username}</b> ({user.userRole})</p>}
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
}
|