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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Umwali Christa
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/PUBLISHING.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Publishing `create-bus-project` to npm
|
|
2
|
+
|
|
3
|
+
1. Create or log in to your npm account:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm login
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
2. From this package folder, test the package locally:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm pack
|
|
13
|
+
npm install -g ./create-bus-project-1.0.0.tgz
|
|
14
|
+
create-bus-project
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
3. Check the package name is available:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm view create-bus-project
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
If npm returns a 404, the name is available. If it already exists, rename the package in `package.json`, for example `@your-username/create-bus-project`.
|
|
24
|
+
|
|
25
|
+
4. Publish:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm publish --access public
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
5. Use it after publishing:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx create-bus-project
|
|
35
|
+
```
|
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# create-bus-project
|
|
2
|
+
|
|
3
|
+
Create a full-stack Bus Booking System with React + Vite frontend, Express backend, and MySQL.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx create-bus-project
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The CLI asks for:
|
|
12
|
+
|
|
13
|
+
- Project name
|
|
14
|
+
- Backend port
|
|
15
|
+
- Frontend port
|
|
16
|
+
- MySQL database name
|
|
17
|
+
|
|
18
|
+
## Generated app
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
my-bus-app/
|
|
22
|
+
├── backend/
|
|
23
|
+
│ ├── config/
|
|
24
|
+
│ ├── controllers/
|
|
25
|
+
│ ├── routes/
|
|
26
|
+
│ ├── seeders/
|
|
27
|
+
│ ├── schema.sql
|
|
28
|
+
│ ├── .env
|
|
29
|
+
│ └── package.json
|
|
30
|
+
└── frontend/
|
|
31
|
+
├── src/
|
|
32
|
+
├── .env
|
|
33
|
+
├── vite.config.js
|
|
34
|
+
└── package.json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Run generated project
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
cd my-bus-app/backend
|
|
41
|
+
npm install
|
|
42
|
+
mysql -u root -p < schema.sql
|
|
43
|
+
npm run seed
|
|
44
|
+
npm run dev
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
cd my-bus-app/frontend
|
|
49
|
+
npm install
|
|
50
|
+
npm run dev
|
|
51
|
+
```
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import readline from "node:readline/promises";
|
|
5
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
const packageRoot = path.resolve(__dirname, "..");
|
|
11
|
+
const templateDir = path.join(packageRoot, "template");
|
|
12
|
+
|
|
13
|
+
const pipedAnswers = !process.stdin.isTTY ? fs.readFileSync(0, "utf8").split(/\r?\n/) : [];
|
|
14
|
+
let pipedIndex = 0;
|
|
15
|
+
const rl = process.stdin.isTTY ? readline.createInterface({ input, output }) : null;
|
|
16
|
+
|
|
17
|
+
const slugify = (value) =>
|
|
18
|
+
value
|
|
19
|
+
.trim()
|
|
20
|
+
.toLowerCase()
|
|
21
|
+
.replace(/[^a-z0-9-_]+/g, "-")
|
|
22
|
+
.replace(/^-+|-+$/g, "") || "bus-project";
|
|
23
|
+
|
|
24
|
+
const sanitizeDbName = (value) =>
|
|
25
|
+
value.trim().replace(/[^a-zA-Z0-9_]/g, "_") || "bus_booking";
|
|
26
|
+
|
|
27
|
+
const ask = async (question, defaultValue, validator) => {
|
|
28
|
+
while (true) {
|
|
29
|
+
const raw = rl
|
|
30
|
+
? await rl.question(`${question} (${defaultValue}): `)
|
|
31
|
+
: (pipedAnswers[pipedIndex++] ?? "");
|
|
32
|
+
const answer = raw.trim() || defaultValue;
|
|
33
|
+
if (!validator || validator(answer)) return answer;
|
|
34
|
+
console.log("Please enter a valid value.");
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const copyRecursive = (source, destination) => {
|
|
39
|
+
const stat = fs.statSync(source);
|
|
40
|
+
if (stat.isDirectory()) {
|
|
41
|
+
fs.mkdirSync(destination, { recursive: true });
|
|
42
|
+
for (const item of fs.readdirSync(source)) {
|
|
43
|
+
copyRecursive(path.join(source, item), path.join(destination, item));
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
fs.copyFileSync(source, destination);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const replaceInFiles = (dir, replacements) => {
|
|
51
|
+
for (const item of fs.readdirSync(dir)) {
|
|
52
|
+
const fullPath = path.join(dir, item);
|
|
53
|
+
const stat = fs.statSync(fullPath);
|
|
54
|
+
if (stat.isDirectory()) {
|
|
55
|
+
replaceInFiles(fullPath, replacements);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const binaryExt = new Set([".png", ".jpg", ".jpeg", ".gif", ".ico", ".pdf", ".zip"]);
|
|
59
|
+
if (binaryExt.has(path.extname(fullPath).toLowerCase())) continue;
|
|
60
|
+
let content = fs.readFileSync(fullPath, "utf8");
|
|
61
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
62
|
+
content = content.split(key).join(value);
|
|
63
|
+
}
|
|
64
|
+
fs.writeFileSync(fullPath, content);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const main = async () => {
|
|
69
|
+
console.log("\nCreate Bus Project\n");
|
|
70
|
+
|
|
71
|
+
const defaultProjectName = process.argv[2] ? slugify(process.argv[2]) : "bus-project";
|
|
72
|
+
const projectName = slugify(await ask("Project name", defaultProjectName));
|
|
73
|
+
const backendPort = await ask("Backend port", "5000", (v) => /^\d+$/.test(v));
|
|
74
|
+
const frontendPort = await ask("Frontend port", "5173", (v) => /^\d+$/.test(v));
|
|
75
|
+
const dbName = sanitizeDbName(await ask("MySQL database name", "bus_booking"));
|
|
76
|
+
|
|
77
|
+
const destination = path.resolve(process.cwd(), projectName);
|
|
78
|
+
if (fs.existsSync(destination) && fs.readdirSync(destination).length > 0) {
|
|
79
|
+
console.error(`\nCannot create project. Folder already exists and is not empty: ${destination}`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
copyRecursive(templateDir, destination);
|
|
84
|
+
|
|
85
|
+
replaceInFiles(destination, {
|
|
86
|
+
"__PROJECT_NAME__": projectName,
|
|
87
|
+
"__PROJECT_TITLE__": projectName.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
88
|
+
"__BACKEND_PORT__": backendPort,
|
|
89
|
+
"__FRONTEND_PORT__": frontendPort,
|
|
90
|
+
"__DB_NAME__": dbName
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
console.log(`\nSuccess! Created ${projectName}.`);
|
|
94
|
+
console.log("\nNext steps:");
|
|
95
|
+
console.log(` cd ${projectName}/backend`);
|
|
96
|
+
console.log(" npm install");
|
|
97
|
+
console.log(` mysql -u root -p < schema.sql`);
|
|
98
|
+
console.log(" npm run seed");
|
|
99
|
+
console.log(" npm run dev");
|
|
100
|
+
console.log("\nIn a second terminal:");
|
|
101
|
+
console.log(` cd ${projectName}/frontend`);
|
|
102
|
+
console.log(" npm install");
|
|
103
|
+
console.log(" npm run dev");
|
|
104
|
+
console.log(`\nFrontend: http://localhost:${frontendPort}`);
|
|
105
|
+
console.log(`Backend: http://localhost:${backendPort}\n`);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
main()
|
|
109
|
+
.catch((error) => {
|
|
110
|
+
console.error(error);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
})
|
|
113
|
+
.finally(() => rl?.close());
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-bus-project",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI generator for a full-stack Bus Booking System using React, Vite, Express, and MySQL.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-bus-project": "bin/create-bus-project.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"template",
|
|
12
|
+
"README.md",
|
|
13
|
+
"PUBLISHING.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"keywords": [
|
|
17
|
+
"bus",
|
|
18
|
+
"booking",
|
|
19
|
+
"react",
|
|
20
|
+
"vite",
|
|
21
|
+
"express",
|
|
22
|
+
"mysql",
|
|
23
|
+
"starter",
|
|
24
|
+
"scaffold"
|
|
25
|
+
],
|
|
26
|
+
"author": "Umwali Christa",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# __PROJECT_TITLE__
|
|
2
|
+
|
|
3
|
+
Full-stack Bus Booking System generated with `create-bus-project`.
|
|
4
|
+
|
|
5
|
+
## Included
|
|
6
|
+
- React + Vite frontend
|
|
7
|
+
- Node.js + Express backend
|
|
8
|
+
- MySQL database schema
|
|
9
|
+
- Manager and passenger workflows
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cd backend
|
|
15
|
+
npm install
|
|
16
|
+
cp .env.example .env
|
|
17
|
+
# create the database in MySQL, then import schema.sql
|
|
18
|
+
npm run seed
|
|
19
|
+
npm run dev
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Open a second terminal:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
cd frontend
|
|
26
|
+
npm install
|
|
27
|
+
npm run dev
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Backend: http://localhost:__BACKEND_PORT__
|
|
31
|
+
Frontend: http://localhost:__FRONTEND_PORT__
|
|
32
|
+
Database: `__DB_NAME__`
|
|
33
|
+
|
|
34
|
+
## Default users
|
|
35
|
+
Run `npm run seed` in the backend folder to create starter users.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Backend Project
|
|
2
|
+
|
|
3
|
+
## Steps
|
|
4
|
+
|
|
5
|
+
1. Open MySQL Workbench or phpMyAdmin.
|
|
6
|
+
2. Run `schema.sql`.
|
|
7
|
+
3. Install dependencies:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
4. Seed users:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm run seed
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
5. Start backend:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm start
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Default users:
|
|
26
|
+
|
|
27
|
+
- manager1 / 123456
|
|
28
|
+
- driver1 / 123456
|
|
29
|
+
- passenger1 / 123456
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import mysql from "mysql2";
|
|
2
|
+
import dotenv from "dotenv";
|
|
3
|
+
|
|
4
|
+
dotenv.config();
|
|
5
|
+
|
|
6
|
+
const db = mysql.createConnection({
|
|
7
|
+
host: process.env.DB_HOST || "localhost",
|
|
8
|
+
user: process.env.DB_USER || "root",
|
|
9
|
+
password: process.env.DB_PASSWORD || "",
|
|
10
|
+
database: process.env.DB_NAME || "__DB_NAME__"
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
db.connect((err) => {
|
|
14
|
+
if (err) {
|
|
15
|
+
console.log("Database connection failed:", err.message);
|
|
16
|
+
} else {
|
|
17
|
+
console.log("Database connected successfully");
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export default db;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import bcrypt from "bcryptjs";
|
|
2
|
+
import db from "../config/db.js";
|
|
3
|
+
|
|
4
|
+
export const register = async (req, res) => {
|
|
5
|
+
const { username, password, userRole } = req.body;
|
|
6
|
+
|
|
7
|
+
if (!username || !password || !userRole) {
|
|
8
|
+
return res.status(400).json({
|
|
9
|
+
message: "Username and password are required"
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const allowedRoles = ["passenger", "manager"];
|
|
14
|
+
|
|
15
|
+
if (!allowedRoles.includes(userRole)) {
|
|
16
|
+
return res.status(400).json({
|
|
17
|
+
message: "Only passenger and manager registration is allowed"
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const checkSql = "SELECT * FROM yk_users WHERE username=?";
|
|
22
|
+
|
|
23
|
+
db.query(checkSql, [username], async (err, result) => {
|
|
24
|
+
if (err) return res.status(500).json(err);
|
|
25
|
+
|
|
26
|
+
if (result.length > 0) {
|
|
27
|
+
return res.status(409).json({
|
|
28
|
+
message: "Username already exists"
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const hashedPassword = await bcrypt.hash(password, 10);
|
|
33
|
+
|
|
34
|
+
const sql = `
|
|
35
|
+
INSERT INTO yk_users(username, password, userRole)
|
|
36
|
+
VALUES(?,?,?)
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
db.query(sql, [username, hashedPassword, userRole], (err, result) => {
|
|
40
|
+
if (err) return res.status(500).json(err);
|
|
41
|
+
|
|
42
|
+
res.status(201).json({
|
|
43
|
+
message: `${userRole} registered successfully`,
|
|
44
|
+
userid: result.insertId
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const login = (req, res) => {
|
|
51
|
+
const { username, password, userRole } = req.body;
|
|
52
|
+
|
|
53
|
+
if (!username || !password || !userRole) {
|
|
54
|
+
return res.status(400).json({
|
|
55
|
+
message: "Username and password are required"
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const sql = "SELECT * FROM yk_users WHERE username=? AND userRole=?";
|
|
60
|
+
|
|
61
|
+
db.query(sql, [username, userRole], async (err, result) => {
|
|
62
|
+
if (err) return res.status(500).json(err);
|
|
63
|
+
|
|
64
|
+
if (result.length === 0) {
|
|
65
|
+
return res.status(404).json({
|
|
66
|
+
message: `No ${userRole} account found with that username`
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const user = result[0];
|
|
71
|
+
|
|
72
|
+
const isMatch = await bcrypt.compare(password, user.password);
|
|
73
|
+
|
|
74
|
+
if (!isMatch) {
|
|
75
|
+
return res.status(400).json({
|
|
76
|
+
message: "Wrong password"
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
req.session.user = {
|
|
81
|
+
userid: user.userid,
|
|
82
|
+
username: user.username,
|
|
83
|
+
userRole: user.userRole
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
res.json({
|
|
87
|
+
message: "Login successful",
|
|
88
|
+
user: req.session.user
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const logout = (req, res) => {
|
|
94
|
+
req.session.destroy(() => {
|
|
95
|
+
res.json({
|
|
96
|
+
message: "Logged out successfully"
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const me = (req, res) => {
|
|
102
|
+
if (!req.session.user) {
|
|
103
|
+
return res.status(401).json({
|
|
104
|
+
message: "Not logged in"
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
res.json(req.session.user);
|
|
109
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import db from "../config/db.js";
|
|
2
|
+
|
|
3
|
+
export const getBookings = (req, res) => {
|
|
4
|
+
const sql = `SELECT yk_bookings.*, yk_schedules.routename, yk_schedules.departurepoint, yk_schedules.destination FROM yk_bookings INNER JOIN yk_schedules ON yk_bookings.scheduleid = yk_schedules.scheduleid ORDER BY bookingid DESC`;
|
|
5
|
+
|
|
6
|
+
db.query(sql, (err, result) => {
|
|
7
|
+
if (err) {
|
|
8
|
+
res.status(500).json(err);
|
|
9
|
+
}
|
|
10
|
+
res.json(result);
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const getMyBookings = (req, res) => {
|
|
15
|
+
const { userid } = req.params;
|
|
16
|
+
|
|
17
|
+
const sql = `
|
|
18
|
+
SELECT yk_bookings.*, yk_schedules.routename, yk_schedules.departurepoint, yk_schedules.destination, yk_schedules.departuretime, yk_schedules.ticketprice FROM yk_bookings INNER JOIN yk_schedules ON yk_bookings.scheduleid = yk_schedules.scheduleid WHERE yk_bookings.userid = ? ORDER BY bookingid DESC`;
|
|
19
|
+
|
|
20
|
+
db.query(sql, [userid], (err, result) => {
|
|
21
|
+
if (err) {
|
|
22
|
+
res.status(500).json(err);
|
|
23
|
+
}
|
|
24
|
+
res.json(result);
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const getBookedSeats = (req, res) => {
|
|
29
|
+
const { scheduleid } = req.params;
|
|
30
|
+
|
|
31
|
+
const sql = "SELECT seatnumber FROM yk_bookings WHERE scheduleid=?";
|
|
32
|
+
|
|
33
|
+
db.query(sql, [scheduleid], (err, result) => {
|
|
34
|
+
if (err) {
|
|
35
|
+
res.status(500).json(err);
|
|
36
|
+
}
|
|
37
|
+
res.json(result.map((row) => row.seatnumber));
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const createBooking = (req, res) => {
|
|
42
|
+
const { scheduleid,userid,passengername,passengergender,passengerphone,seatnumber,paymentstatus} = req.body;
|
|
43
|
+
|
|
44
|
+
if (!scheduleid || !userid || !passengername || !passengergender || !passengerphone || !seatnumber) {
|
|
45
|
+
return res.status(400).json({ message: "All booking fields are required" });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const sql = `INSERT INTO yk_bookings(scheduleid, userid, passengername, passengergender, passengerphone, seatnumber, paymentstatus)VALUES(?,?,?,?,?,?,?)`;
|
|
49
|
+
|
|
50
|
+
db.query(
|
|
51
|
+
sql,
|
|
52
|
+
[scheduleid, userid, passengername, passengergender, passengerphone, seatnumber, paymentstatus || "pending"],
|
|
53
|
+
(err) => {
|
|
54
|
+
if (err) {
|
|
55
|
+
if (err.code === "ER_DUP_ENTRY") {
|
|
56
|
+
return res.status(409).json({ message: "This seat is already booked. Choose another seat." });
|
|
57
|
+
}
|
|
58
|
+
return res.status(500).json(err);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
res.json({ message: "Booking created successfully" });
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const deleteBooking = (req, res) => {
|
|
67
|
+
const { id } = req.params;
|
|
68
|
+
|
|
69
|
+
db.query("DELETE FROM yk_bookings WHERE bookingid=?", [id], (err) => {
|
|
70
|
+
if (err) {
|
|
71
|
+
res.status(500).json(err);
|
|
72
|
+
}
|
|
73
|
+
res.json({ message: "Booking deleted successfully" });
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const bookingReport = (req, res) => {
|
|
78
|
+
const sql = `
|
|
79
|
+
SELECT
|
|
80
|
+
yk_schedules.scheduleid,
|
|
81
|
+
yk_schedules.routename,
|
|
82
|
+
yk_schedules.departurepoint,
|
|
83
|
+
yk_schedules.destination,
|
|
84
|
+
yk_schedules.departuretime,
|
|
85
|
+
yk_bookings.passengername,
|
|
86
|
+
yk_bookings.passengergender,
|
|
87
|
+
yk_bookings.passengerphone,
|
|
88
|
+
yk_bookings.seatnumber,
|
|
89
|
+
yk_bookings.paymentstatus
|
|
90
|
+
FROM yk_bookings
|
|
91
|
+
INNER JOIN yk_schedules ON yk_bookings.scheduleid = yk_schedules.scheduleid
|
|
92
|
+
ORDER BY yk_schedules.routename, yk_bookings.seatnumber
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
db.query(sql, (err, result) => {
|
|
96
|
+
if (err) return res.status(500).json(err);
|
|
97
|
+
res.json(result);
|
|
98
|
+
});
|
|
99
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import db from "../config/db.js";
|
|
2
|
+
|
|
3
|
+
export const getBuses = (req, res) => {
|
|
4
|
+
db.query("SELECT * FROM yk_buses ORDER BY busid DESC", (err, result) => {
|
|
5
|
+
if (err) {
|
|
6
|
+
res.status(500).json(err);
|
|
7
|
+
}
|
|
8
|
+
res.json(result);
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const createBus = (req, res) => {
|
|
13
|
+
const { platenumber, totalseats, bustype } = req.body;
|
|
14
|
+
|
|
15
|
+
const sql = "INSERT INTO yk_buses(platenumber, totalseats, bustype) VALUES(?,?,?)";
|
|
16
|
+
|
|
17
|
+
db.query(sql, [platenumber, totalseats, bustype], (err) => {
|
|
18
|
+
if (err) {
|
|
19
|
+
res.status(500).json(err);
|
|
20
|
+
}
|
|
21
|
+
res.json({ message: "Bus added successfully" });
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const deleteBus = (req, res) => {
|
|
26
|
+
const { id } = req.params;
|
|
27
|
+
|
|
28
|
+
db.query("DELETE FROM yk_buses WHERE busid=?", [id], (err) => {
|
|
29
|
+
if (err) {
|
|
30
|
+
res.status(500).json(err);
|
|
31
|
+
}
|
|
32
|
+
res.json({ message: "Bus deleted successfully" });
|
|
33
|
+
});
|
|
34
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import db from "../config/db.js";
|
|
2
|
+
|
|
3
|
+
export const getSchedules = (req, res) => {
|
|
4
|
+
const sql = `
|
|
5
|
+
SELECT yk_schedules.*, yk_buses.platenumber, yk_buses.totalseats
|
|
6
|
+
FROM yk_schedules
|
|
7
|
+
INNER JOIN yk_buses ON yk_schedules.busid = yk_buses.busid
|
|
8
|
+
ORDER BY scheduleid DESC
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
db.query(sql, (err, result) => {
|
|
12
|
+
if (err) return res.status(500).json(err);
|
|
13
|
+
res.json(result);
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const createSchedule = (req, res) => {
|
|
18
|
+
const {
|
|
19
|
+
busid,
|
|
20
|
+
routename,
|
|
21
|
+
departurepoint,
|
|
22
|
+
destination,
|
|
23
|
+
departuretime,
|
|
24
|
+
estimatedarrivaltime,
|
|
25
|
+
ticketprice,
|
|
26
|
+
scheduleStatus
|
|
27
|
+
} = req.body;
|
|
28
|
+
|
|
29
|
+
const sql = `
|
|
30
|
+
INSERT INTO yk_schedules
|
|
31
|
+
(busid, routename, departurepoint, destination, departuretime, estimatedarrivaltime, ticketprice, scheduleStatus)
|
|
32
|
+
VALUES(?,?,?,?,?,?,?,?)
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
db.query(
|
|
36
|
+
sql,
|
|
37
|
+
[busid, routename, departurepoint, destination, departuretime, estimatedarrivaltime, ticketprice, scheduleStatus],
|
|
38
|
+
(err) => {
|
|
39
|
+
if (err) return res.status(500).json(err);
|
|
40
|
+
res.json({ message: "Schedule added successfully" });
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const deleteSchedule = (req, res) => {
|
|
46
|
+
const { id } = req.params;
|
|
47
|
+
|
|
48
|
+
db.query("DELETE FROM yk_schedules WHERE scheduleid=?", [id], (err) => {
|
|
49
|
+
if (err) return res.status(500).json(err);
|
|
50
|
+
res.json({ message: "Schedule deleted successfully" });
|
|
51
|
+
});
|
|
52
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import bcrypt from "bcryptjs";
|
|
2
|
+
import db from "../config/db.js";
|
|
3
|
+
|
|
4
|
+
export const getUsers = (req, res) => {
|
|
5
|
+
const sql = "SELECT userid, username, userRole FROM yk_users ORDER BY userid DESC";
|
|
6
|
+
|
|
7
|
+
db.query(sql, (err, result) => {
|
|
8
|
+
if (err) return res.status(500).json(err);
|
|
9
|
+
res.json(result);
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const createUser = async (req, res) => {
|
|
14
|
+
const { username, password, userRole } = req.body;
|
|
15
|
+
|
|
16
|
+
if (!username || !password || !userRole) {
|
|
17
|
+
return res.status(400).json({ message: "All fields are required" });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const hashedPassword = await bcrypt.hash(password, 10);
|
|
21
|
+
|
|
22
|
+
const sql = "INSERT INTO yk_users(username, password, userRole) VALUES(?,?,?)";
|
|
23
|
+
|
|
24
|
+
db.query(sql, [username, hashedPassword, userRole], (err, result) => {
|
|
25
|
+
if (err) return res.status(500).json(err);
|
|
26
|
+
|
|
27
|
+
res.json({ message: "User created successfully" });
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const deleteUser = (req, res) => {
|
|
32
|
+
const { id } = req.params;
|
|
33
|
+
|
|
34
|
+
db.query("DELETE FROM yk_users WHERE userid=?", [id], (err) => {
|
|
35
|
+
if (err) return res.status(500).json(err);
|
|
36
|
+
|
|
37
|
+
res.json({ message: "User deleted successfully" });
|
|
38
|
+
});
|
|
39
|
+
};
|