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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/PUBLISHING.md +35 -0
  3. package/README.md +51 -0
  4. package/bin/create-bus-project.js +113 -0
  5. package/package.json +31 -0
  6. package/template/README.md +35 -0
  7. package/template/backend/.env +8 -0
  8. package/template/backend/.env.example +8 -0
  9. package/template/backend/README.md +29 -0
  10. package/template/backend/config/db.js +21 -0
  11. package/template/backend/controllers/authController.js +109 -0
  12. package/template/backend/controllers/bookingController.js +99 -0
  13. package/template/backend/controllers/busController.js +34 -0
  14. package/template/backend/controllers/scheduleController.js +52 -0
  15. package/template/backend/controllers/userController.js +39 -0
  16. package/template/backend/index.js +39 -0
  17. package/template/backend/package.json +22 -0
  18. package/template/backend/routes/authRoutes.js +17 -0
  19. package/template/backend/routes/bookingRoutes.js +20 -0
  20. package/template/backend/routes/busRoutes.js +10 -0
  21. package/template/backend/routes/scheduleRoutes.js +10 -0
  22. package/template/backend/routes/userRoutes.js +10 -0
  23. package/template/backend/schema.sql +59 -0
  24. package/template/backend/seeders/seedUsers.js +35 -0
  25. package/template/frontend/.env +1 -0
  26. package/template/frontend/.env.example +1 -0
  27. package/template/frontend/README.md +14 -0
  28. package/template/frontend/index.html +12 -0
  29. package/template/frontend/package.json +21 -0
  30. package/template/frontend/src/App.jsx +55 -0
  31. package/template/frontend/src/api.js +1 -0
  32. package/template/frontend/src/main.jsx +10 -0
  33. package/template/frontend/src/pages/Bookings.jsx +129 -0
  34. package/template/frontend/src/pages/Buses.jsx +100 -0
  35. package/template/frontend/src/pages/Dashboard.jsx +10 -0
  36. package/template/frontend/src/pages/Login.jsx +141 -0
  37. package/template/frontend/src/pages/ManagerDashboard.jsx +39 -0
  38. package/template/frontend/src/pages/PassengerDashboard.jsx +248 -0
  39. package/template/frontend/src/pages/Report.jsx +158 -0
  40. package/template/frontend/src/pages/Schedules.jsx +142 -0
  41. package/template/frontend/src/pages/Users.jsx +105 -0
  42. package/template/frontend/src/style.css +236 -0
  43. package/template/frontend/vite.config.js +10 -0
@@ -0,0 +1,142 @@
1
+ import React from "react";
2
+ import { useEffect, useState } from "react";
3
+ import { API_BASE_URL } from "../api.js";
4
+
5
+ export default function Schedules() {
6
+ const [schedules, setSchedules] = useState([]);
7
+ const [buses, setBuses] = useState([]);
8
+
9
+ const [form, setForm] = useState({
10
+ busid: "",
11
+ routename: "",
12
+ departurepoint: "",
13
+ destination: "",
14
+ departuretime: "",
15
+ estimatedarrivaltime: "",
16
+ ticketprice: "",
17
+ scheduleStatus: "active"
18
+ });
19
+
20
+ const handleChange = (e) => {
21
+ setForm({
22
+ ...form,
23
+ [e.target.name]: e.target.value
24
+ });
25
+ };
26
+
27
+ const fetchSchedules = async () => {
28
+ const res = await fetch(`${API_BASE_URL}/api/schedules`);
29
+ const data = await res.json();
30
+ setSchedules(data);
31
+ };
32
+
33
+ const fetchBuses = async () => {
34
+ const res = await fetch(`${API_BASE_URL}/api/buses`);
35
+ const data = await res.json();
36
+ setBuses(data);
37
+ };
38
+
39
+ useEffect(() => {
40
+ fetchSchedules();
41
+ fetchBuses();
42
+ }, []);
43
+
44
+ const handleSubmit = async (e) => {
45
+ e.preventDefault();
46
+
47
+ await fetch(`${API_BASE_URL}/api/schedules`, {
48
+ method: "POST",
49
+ headers: {
50
+ "Content-Type": "application/json"
51
+ },
52
+ body: JSON.stringify(form)
53
+ });
54
+
55
+ setForm({
56
+ busid: "",
57
+ routename: "",
58
+ departurepoint: "",
59
+ destination: "",
60
+ departuretime: "",
61
+ estimatedarrivaltime: "",
62
+ ticketprice: "",
63
+ scheduleStatus: "active"
64
+ });
65
+
66
+ fetchSchedules();
67
+ };
68
+
69
+ const handleDelete = async (id) => {
70
+ await fetch(`${API_BASE_URL}/api/schedules/${id}`, {
71
+ method: "DELETE"
72
+ });
73
+
74
+ fetchSchedules();
75
+ };
76
+
77
+ return (
78
+ <div>
79
+ <div className="card">
80
+ <h1>Schedules</h1>
81
+
82
+ <form className="form-grid" onSubmit={handleSubmit}>
83
+ <select name="busid" value={form.busid} onChange={handleChange}>
84
+ <option value="">Select Bus</option>
85
+ {buses.map((bus) => (
86
+ <option key={bus.busid} value={bus.busid}>
87
+ {bus.platenumber} - {bus.bustype}
88
+ </option>
89
+ ))}
90
+ </select>
91
+
92
+ <input name="routename" placeholder="Route Name" value={form.routename} onChange={handleChange} />
93
+ <input name="departurepoint" placeholder="Departure Point" value={form.departurepoint} onChange={handleChange} />
94
+ <input name="destination" placeholder="Destination" value={form.destination} onChange={handleChange} />
95
+ <input name="departuretime" type="datetime-local" value={form.departuretime} onChange={handleChange} />
96
+ <input name="estimatedarrivaltime" type="datetime-local" value={form.estimatedarrivaltime} onChange={handleChange} />
97
+ <input name="ticketprice" placeholder="Ticket Price" value={form.ticketprice} onChange={handleChange} />
98
+
99
+ <select name="scheduleStatus" value={form.scheduleStatus} onChange={handleChange}>
100
+ <option value="active">Active</option>
101
+ <option value="cancelled">Cancelled</option>
102
+ <option value="completed">Completed</option>
103
+ </select>
104
+
105
+ <button type="submit">Add Schedule</button>
106
+ </form>
107
+ </div>
108
+
109
+ <table>
110
+ <thead>
111
+ <tr>
112
+ <th>Bus</th>
113
+ <th>Route</th>
114
+ <th>From</th>
115
+ <th>To</th>
116
+ <th>Price</th>
117
+ <th>Status</th>
118
+ <th>Action</th>
119
+ </tr>
120
+ </thead>
121
+
122
+ <tbody>
123
+ {schedules.map((schedule) => (
124
+ <tr key={schedule.scheduleid}>
125
+ <td>{schedule.platenumber}</td>
126
+ <td>{schedule.routename}</td>
127
+ <td>{schedule.departurepoint}</td>
128
+ <td>{schedule.destination}</td>
129
+ <td>{schedule.ticketprice}</td>
130
+ <td>{schedule.scheduleStatus}</td>
131
+ <td>
132
+ <button className="delete" onClick={() => handleDelete(schedule.scheduleid)}>
133
+ Delete
134
+ </button>
135
+ </td>
136
+ </tr>
137
+ ))}
138
+ </tbody>
139
+ </table>
140
+ </div>
141
+ );
142
+ }
@@ -0,0 +1,105 @@
1
+ import React from "react";
2
+ import { useEffect, useState } from "react";
3
+ import { API_BASE_URL } from "../api.js";
4
+
5
+ export default function Users() {
6
+ const [users, setUsers] = useState([]);
7
+
8
+ const [form, setForm] = useState({
9
+ username: "",
10
+ password: "",
11
+ userRole: "passenger"
12
+ });
13
+
14
+ const handleChange = (e) => {
15
+ setForm({
16
+ ...form,
17
+ [e.target.name]: e.target.value
18
+ });
19
+ };
20
+
21
+ const fetchUsers = async () => {
22
+ const res = await fetch(`${API_BASE_URL}/api/users`);
23
+ const data = await res.json();
24
+ setUsers(data);
25
+ };
26
+
27
+ useEffect(() => {
28
+ fetchUsers();
29
+ }, []);
30
+
31
+ const handleSubmit = async (e) => {
32
+ e.preventDefault();
33
+
34
+ await fetch(`${API_BASE_URL}/api/users`, {
35
+ method: "POST",
36
+ headers: {
37
+ "Content-Type": "application/json"
38
+ },
39
+ body: JSON.stringify(form)
40
+ });
41
+
42
+ setForm({
43
+ username: "",
44
+ password: "",
45
+ userRole: "passenger"
46
+ });
47
+
48
+ fetchUsers();
49
+ };
50
+
51
+ const handleDelete = async (id) => {
52
+ await fetch(`${API_BASE_URL}/api/users/${id}`, {
53
+ method: "DELETE"
54
+ });
55
+
56
+ fetchUsers();
57
+ };
58
+
59
+ return (
60
+ <div>
61
+ <div className="card">
62
+ <h1>Users</h1>
63
+
64
+ <form className="form-grid" onSubmit={handleSubmit}>
65
+ <input name="username" placeholder="Username" value={form.username} onChange={handleChange} />
66
+ <input name="password" placeholder="Password" value={form.password} onChange={handleChange} />
67
+
68
+ <select name="userRole" value={form.userRole} onChange={handleChange}>
69
+ <option value="passenger">Passenger</option>
70
+ <option value="manager">Manager</option>
71
+ <option value="driver">Driver</option>
72
+ </select>
73
+
74
+ <button type="submit">Add User</button>
75
+ </form>
76
+ </div>
77
+
78
+ <table>
79
+ <thead>
80
+ <tr>
81
+
82
+ <th>Username</th>
83
+ <th>Role</th>
84
+ <th>Action</th>
85
+ </tr>
86
+ </thead>
87
+
88
+ <tbody>
89
+ {users.map((user) => (
90
+ <tr key={user.userid}>
91
+
92
+ <td>{user.username}</td>
93
+ <td><span className="badge">{user.userRole}</span></td>
94
+ <td>
95
+ <button className="delete" onClick={() => handleDelete(user.userid)}>
96
+ Delete
97
+ </button>
98
+ </td>
99
+ </tr>
100
+ ))}
101
+ </tbody>
102
+ </table>
103
+ </div>
104
+ );
105
+ }
@@ -0,0 +1,236 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ }
4
+
5
+ body {
6
+ margin: 0;
7
+ font-family: Arial, sans-serif;
8
+ background: white;
9
+ color: black;
10
+ }
11
+
12
+ .login-page,
13
+ .center-page {
14
+ min-height: 100vh;
15
+ display: grid;
16
+ place-items: center;
17
+ background: #064e3b;
18
+ padding: 20px;
19
+ }
20
+
21
+ .login-card {
22
+ width: 100%;
23
+ max-width: 430px;
24
+ background: white;
25
+ color: black;
26
+ padding: 32px;
27
+ border-radius: 24px;
28
+ box-shadow: 0 20px 50px rgba(0,0,0,0.2);
29
+ }
30
+
31
+ .login-card h1 {
32
+ margin: 0 0 8px;
33
+ }
34
+
35
+ .login-card label {
36
+ display: block;
37
+ margin-top: 16px;
38
+ margin-bottom: 6px;
39
+ font-weight: bold;
40
+ }
41
+
42
+ .role-switch {
43
+ display: grid;
44
+ grid-template-columns: 1fr 1fr;
45
+ gap: 10px;
46
+ background: #ecfdf5;
47
+ padding: 8px;
48
+ border-radius: 14px;
49
+ margin: 22px 0;
50
+ }
51
+
52
+ .role-switch button {
53
+ background: transparent;
54
+ color: #064e3b;
55
+ }
56
+
57
+ .role-switch .active-role {
58
+ background: #064e3b;
59
+ color: white;
60
+ }
61
+
62
+ .full-btn {
63
+ width: 100%;
64
+ margin-top: 20px;
65
+ }
66
+
67
+ .hint,
68
+ .muted {
69
+ color: #6b7280;
70
+ }
71
+
72
+ .hint {
73
+ margin-top: 18px;
74
+ line-height: 1.7;
75
+ font-size: 14px;
76
+ }
77
+
78
+ .topbar {
79
+ background: #064e3b;
80
+ color: white;
81
+ padding: 18px 35px;
82
+ display: flex;
83
+ align-items: center;
84
+ justify-content: space-between;
85
+ gap: 16px;
86
+ }
87
+
88
+ .topbar h2,
89
+ .topbar p {
90
+ margin: 0;
91
+ }
92
+
93
+ nav {
94
+ display: flex;
95
+ gap: 8px;
96
+ flex-wrap: wrap;
97
+ }
98
+
99
+ button {
100
+ border: none;
101
+ background: #064e3b;
102
+ color: white;
103
+ padding: 10px 14px;
104
+ border-radius: 8px;
105
+ cursor: pointer;
106
+ }
107
+
108
+ button:disabled {
109
+ cursor: not-allowed;
110
+ opacity: 0.7;
111
+ }
112
+
113
+ nav button {
114
+ background: white;
115
+ color: black;
116
+ }
117
+
118
+ button:hover {
119
+ opacity: 0.85;
120
+ }
121
+
122
+ .container {
123
+ padding: 30px;
124
+ }
125
+
126
+ .card {
127
+ background: white;
128
+ color: black;
129
+ padding: 24px;
130
+ border-radius: 16px;
131
+ box-shadow: 0 8px 24px rgba(0,0,0,0.08);
132
+ margin-bottom: 24px;
133
+ }
134
+
135
+ .form-grid {
136
+ display: grid;
137
+ grid-template-columns: repeat(3, 1fr);
138
+ gap: 15px;
139
+ }
140
+
141
+ .mt {
142
+ margin-top: 20px;
143
+ }
144
+
145
+ input, select {
146
+ padding: 12px;
147
+ border: 1px solid #ddd;
148
+ border-radius: 9px;
149
+ width: 100%;
150
+ color: black;
151
+ }
152
+
153
+ table {
154
+ width: 100%;
155
+ border-collapse: collapse;
156
+ background: white;
157
+ color: black;
158
+ border-radius: 12px;
159
+ overflow: hidden;
160
+ }
161
+
162
+ th {
163
+ background: #064e3b;
164
+ color: white;
165
+ }
166
+
167
+ th, td {
168
+ padding: 12px;
169
+ border-bottom: 1px solid #eee;
170
+ text-align: left;
171
+ }
172
+
173
+ .delete {
174
+ background: #065f46;
175
+ }
176
+
177
+ .badge {
178
+ padding: 5px 10px;
179
+ border-radius: 20px;
180
+ background: #d1fae5;
181
+ color: #064e3b;
182
+ }
183
+
184
+ .seat-grid {
185
+ display: grid;
186
+ grid-template-columns: repeat(10, 1fr);
187
+ gap: 10px;
188
+ margin-top: 18px;
189
+ }
190
+
191
+ .seat {
192
+ background: #ecfdf5;
193
+ color: #064e3b;
194
+ }
195
+
196
+ .seat.selected {
197
+ background: #064e3b;
198
+ color: white;
199
+ }
200
+
201
+ .seat.booked {
202
+ background: #065f46;
203
+ color: white;
204
+ }
205
+
206
+ @media (max-width: 850px) {
207
+ .form-grid {
208
+ grid-template-columns: 1fr;
209
+ }
210
+
211
+ .topbar {
212
+ display: block;
213
+ }
214
+
215
+ .seat-grid {
216
+ grid-template-columns: repeat(5, 1fr);
217
+ }
218
+ }
219
+
220
+ .switch-auth {
221
+ margin-top: 18px;
222
+ text-align: center;
223
+ }
224
+
225
+ .switch-auth p {
226
+ margin: 0;
227
+ color: #4b5563;
228
+ }
229
+
230
+ .switch-auth button {
231
+ margin-left: 8px;
232
+ background: transparent;
233
+ color: #064e3b;
234
+ font-weight: bold;
235
+ padding: 4px 8px;
236
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ server: {
7
+ port: __FRONTEND_PORT__,
8
+ host: true
9
+ }
10
+ });