krispdev-business 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 (45) hide show
  1. package/Dev-Business/.vscode/settings.json +3 -0
  2. package/Dev-Business/backend-project/config/db.js +16 -0
  3. package/Dev-Business/backend-project/controllers/authController.js +41 -0
  4. package/Dev-Business/backend-project/controllers/carController.js +62 -0
  5. package/Dev-Business/backend-project/controllers/packageController.js +49 -0
  6. package/Dev-Business/backend-project/controllers/paymentController.js +63 -0
  7. package/Dev-Business/backend-project/controllers/reportController.js +129 -0
  8. package/Dev-Business/backend-project/controllers/serviceController.js +155 -0
  9. package/Dev-Business/backend-project/middleware/auth.js +16 -0
  10. package/Dev-Business/backend-project/package-lock.json +1507 -0
  11. package/Dev-Business/backend-project/package.json +21 -0
  12. package/Dev-Business/backend-project/routes/authRoutes.js +10 -0
  13. package/Dev-Business/backend-project/routes/carRoutes.js +11 -0
  14. package/Dev-Business/backend-project/routes/packageRoutes.js +11 -0
  15. package/Dev-Business/backend-project/routes/paymentRoutes.js +10 -0
  16. package/Dev-Business/backend-project/routes/reportRoutes.js +11 -0
  17. package/Dev-Business/backend-project/routes/serviceRoutes.js +19 -0
  18. package/Dev-Business/backend-project/server.js +72 -0
  19. package/Dev-Business/frontend-project/README.md +18 -0
  20. package/Dev-Business/frontend-project/eslint.config.js +21 -0
  21. package/Dev-Business/frontend-project/index.html +14 -0
  22. package/Dev-Business/frontend-project/package-lock.json +3655 -0
  23. package/Dev-Business/frontend-project/package.json +34 -0
  24. package/Dev-Business/frontend-project/postcss.config.js +6 -0
  25. package/Dev-Business/frontend-project/public/favicon.svg +1 -0
  26. package/Dev-Business/frontend-project/public/icons.svg +24 -0
  27. package/Dev-Business/frontend-project/src/App.css +184 -0
  28. package/Dev-Business/frontend-project/src/App.jsx +84 -0
  29. package/Dev-Business/frontend-project/src/assets/hero.png +0 -0
  30. package/Dev-Business/frontend-project/src/assets/react.svg +1 -0
  31. package/Dev-Business/frontend-project/src/assets/vite.svg +1 -0
  32. package/Dev-Business/frontend-project/src/components/CarForm.jsx +146 -0
  33. package/Dev-Business/frontend-project/src/components/DailyReport.jsx +264 -0
  34. package/Dev-Business/frontend-project/src/components/Dashboard.jsx +153 -0
  35. package/Dev-Business/frontend-project/src/components/Login.jsx +163 -0
  36. package/Dev-Business/frontend-project/src/components/Navbar.jsx +99 -0
  37. package/Dev-Business/frontend-project/src/components/Navibar.jsx +100 -0
  38. package/Dev-Business/frontend-project/src/components/PaymentForm.jsx +211 -0
  39. package/Dev-Business/frontend-project/src/components/ServiceRecord.jsx +384 -0
  40. package/Dev-Business/frontend-project/src/index.css +155 -0
  41. package/Dev-Business/frontend-project/src/main.jsx +10 -0
  42. package/Dev-Business/frontend-project/src/services/api.js +23 -0
  43. package/Dev-Business/frontend-project/tailwind.config.js +31 -0
  44. package/Dev-Business/frontend-project/vite.config.js +15 -0
  45. package/package.json +17 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ "git.ignoreLimitWarning": true
3
+ }
@@ -0,0 +1,16 @@
1
+ import mysql from 'mysql2/promise';
2
+ import dotenv from 'dotenv';
3
+
4
+ dotenv.config();
5
+
6
+ const pool = mysql.createPool({
7
+ host: process.env.DB_HOST,
8
+ user: process.env.DB_USER,
9
+ password: process.env.DB_PASSWORD,
10
+ database: process.env.DB_NAME,
11
+ waitForConnections: true,
12
+ connectionLimit: 10,
13
+ queueLimit: 0
14
+ });
15
+
16
+ export default pool;
@@ -0,0 +1,41 @@
1
+ export const login = async (req, res) => {
2
+ const { username, password } = req.body;
3
+
4
+ // Demo credentials (in production, check database)
5
+ if (username === 'admin' && password === 'admin123') {
6
+ req.session.user = {
7
+ username,
8
+ role: 'admin',
9
+ loginTime: new Date()
10
+ };
11
+ return res.json({
12
+ success: true,
13
+ message: 'Login successful',
14
+ user: req.session.user
15
+ });
16
+ }
17
+
18
+
19
+
20
+ res.status(401).json({
21
+ success: false,
22
+ error: 'Invalid username or password'
23
+ });
24
+ };
25
+
26
+ export const logout = (req, res) => {
27
+ req.session.destroy((err) => {
28
+ if (err) {
29
+ return res.status(500).json({ error: 'Logout failed' });
30
+ }
31
+ res.json({ success: true, message: 'Logged out successfully' });
32
+ });
33
+ };
34
+
35
+ export const checkAuth = (req, res) => {
36
+ if (req.session.user) {
37
+ res.json({ authenticated: true, user: req.session.user });
38
+ } else {
39
+ res.json({ authenticated: false });
40
+ }
41
+ };
@@ -0,0 +1,62 @@
1
+ import pool from '../config/db.js';
2
+
3
+ export const createCar = async (req, res) => {
4
+ const { PlateNumber, CarType, CarSize, DriverName, PhoneNumber } = req.body;
5
+
6
+ // Validation
7
+ if (!PlateNumber || !DriverName || !PhoneNumber) {
8
+ return res.status(400).json({
9
+ success: false,
10
+ error: 'PlateNumber, DriverName, and PhoneNumber are required'
11
+ });
12
+ }
13
+
14
+ try {
15
+ // Check if car already exists
16
+ const [existing] = await pool.execute(
17
+ 'SELECT PlateNumber FROM Car WHERE PlateNumber = ?',
18
+ [PlateNumber]
19
+ );
20
+
21
+ if (existing.length > 0) {
22
+ return res.status(400).json({
23
+ success: false,
24
+ error: 'Car with this plate number already exists'
25
+ });
26
+ }
27
+ car
28
+ const [result] = await pool.execute(
29
+ 'INSERT INTO Car (PlateNumber, CarType, CarSize, DriverName, PhoneNumber) VALUES (?, ?, ?, ?, ?)',
30
+ [PlateNumber, CarType || null, CarSize || null, DriverName, PhoneNumber]
31
+ );
32
+
33
+ res.status(201).json({
34
+ success: true,
35
+ message: 'Car registered successfully',
36
+ data: { PlateNumber, CarType, CarSize, DriverName, PhoneNumber }
37
+ });
38
+ } catch (error) {
39
+ console.error(error);
40
+ res.status(500).json({ success: false, error: error.message });
41
+ }
42
+ };
43
+ export const getAllCars = async (req, res) => {
44
+ try {
45
+ const [rows] = await pool.execute('SELECT * FROM Car ORDER BY DriverName');
46
+ res.json({ success: true, data: rows });
47
+ } catch (error) {
48
+ res.status(500).json({ success: false, error: error.message });
49
+ }
50
+ };
51
+ export const getCarByPlate = async (req, res) => {
52
+ const { plateNumber } = req.params;
53
+ try {
54
+ const [rows] = await pool.execute('SELECT * FROM Car WHERE PlateNumber = ?', [plateNumber]);
55
+ if (rows.length === 0) {
56
+ return res.status(404).json({ success: false, error: 'Car not found' });
57
+ }
58
+ res.json({ success: true, data: rows[0] });
59
+ } catch (error) {
60
+ res.status(500).json({ success: false, error: error.message });
61
+ }
62
+ };
@@ -0,0 +1,49 @@
1
+ import pool from '../config/db.js';
2
+
3
+ export const createPackage = async (req, res) => {
4
+ const { PackageName, PackageDescription, PackagePrice } = req.body;
5
+
6
+ if (!PackageName || !PackagePrice) {
7
+ return res.status(400).json({
8
+ success: false,
9
+ error: 'PackageName and PackagePrice are required'
10
+ });
11
+ }
12
+
13
+ try {
14
+ const [result] = await pool.execute(
15
+ 'INSERT INTO Package (PackageName, PackageDescription, PackagePrice) VALUES (?, ?, ?)',
16
+ [PackageName, PackageDescription || null, PackagePrice]
17
+ );
18
+
19
+ res.status(201).json({
20
+ success: true,
21
+ message: 'Package created successfully',
22
+ data: { PackageNumber: result.insertId, PackageName, PackageDescription, PackagePrice }
23
+ });
24
+ } catch (error) {
25
+ res.status(500).json({ success: false, error: error.message });
26
+ }
27
+ };
28
+
29
+ export const getAllPackages = async (req, res) => {
30
+ try {
31
+ const [rows] = await pool.execute('SELECT * FROM Package ORDER BY PackagePrice');
32
+ res.json({ success: true, data: rows });
33
+ } catch (error) {
34
+ res.status(500).json({ success: false, error: error.message });
35
+ }
36
+ };
37
+
38
+ export const getPackageById = async (req, res) => {
39
+ const { id } = req.params;
40
+ try {
41
+ const [rows] = await pool.execute('SELECT * FROM Package WHERE PackageNumber = ?', [id]);
42
+ if (rows.length === 0) {
43
+ return res.status(404).json({ success: false, error: 'Package not found' });
44
+ }
45
+ res.json({ success: true, data: rows[0] });
46
+ } catch (error) {
47
+ res.status(500).json({ success: false, error: error.message });
48
+ }
49
+ };
@@ -0,0 +1,63 @@
1
+ import pool from '../config/db.js';
2
+
3
+ export const createPayment = async (req, res) => {
4
+ const { AmountPaid, PaymentDate, RecordNumber } = req.body;
5
+
6
+ if (!AmountPaid || !PaymentDate || !RecordNumber) {
7
+ return res.status(400).json({
8
+ success: false,
9
+ error: 'AmountPaid, PaymentDate, and RecordNumber are required'
10
+ });
11
+ }
12
+
13
+ try {
14
+ // Verify service record exists
15
+ const [service] = await pool.execute('SELECT RecordNumber, PackageNumber FROM ServicePackage WHERE RecordNumber = ?', [RecordNumber]);
16
+ if (service.length === 0) {
17
+ return res.status(400).json({ success: false, error: 'Service record not found' });
18
+ }
19
+
20
+ // Get package price for validation
21
+ const [pkg] = await pool.execute('SELECT PackagePrice FROM Package WHERE PackageNumber = ?', [service[0].PackageNumber]);
22
+ const expectedAmount = pkg[0].PackagePrice;
23
+
24
+ if (AmountPaid < expectedAmount) {
25
+ return res.status(400).json({
26
+ success: false,
27
+ error: `Amount paid (${AmountPaid}) is less than package price (${expectedAmount})`
28
+ });
29
+ }
30
+
31
+ // Check if payment already exists for this record
32
+ const [existing] = await pool.execute('SELECT PaymentNumber FROM Payment WHERE RecordNumber = ?', [RecordNumber]);
33
+ if (existing.length > 0) {
34
+ return res.status(400).json({ success: false, error: 'Payment already recorded for this service' });
35
+ }
36
+
37
+ const [result] = await pool.execute(
38
+ 'INSERT INTO Payment (AmountPaid, PaymentDate, RecordNumber) VALUES (?, ?, ?)',
39
+ [AmountPaid, PaymentDate, RecordNumber]
40
+ );
41
+
42
+ res.status(201).json({
43
+ success: true,
44
+ message: 'Payment recorded successfully',
45
+ data: { PaymentNumber: result.insertId, AmountPaid, PaymentDate, RecordNumber }
46
+ });
47
+ } catch (error) {
48
+ res.status(500).json({ success: false, error: error.message });
49
+ }
50
+ };
51
+
52
+ export const getPaymentByRecord = async (req, res) => {
53
+ const { recordNumber } = req.params;
54
+ try {
55
+ const [rows] = await pool.execute('SELECT * FROM Payment WHERE RecordNumber = ?', [recordNumber]);
56
+ if (rows.length === 0) {
57
+ return res.status(404).json({ success: false, error: 'Payment not found' });
58
+ }
59
+ res.json({ success: true, data: rows[0] });
60
+ } catch (error) {
61
+ res.status(500).json({ success: false, error: error.message });
62
+ }
63
+ };
@@ -0,0 +1,129 @@
1
+ import pool from '../config/db.js';
2
+
3
+ export const getDailyReport = async (req, res) => {
4
+ try {
5
+ const { date } = req.query;
6
+ const reportDate = date || new Date().toISOString().split('T')[0];
7
+
8
+ const [rows] = await pool.execute(`
9
+ SELECT
10
+ c.PlateNumber,
11
+ c.DriverName,
12
+ p.PackageName,
13
+ p.PackageDescription,
14
+ p.PackagePrice,
15
+ pay.AmountPaid,
16
+ pay.PaymentDate,
17
+ sp.ServiceDate,
18
+ (pay.AmountPaid - p.PackagePrice) as ChangeAmount
19
+ FROM ServicePackage sp
20
+ JOIN Car c ON sp.PlateNumber = c.PlateNumber
21
+ JOIN Package p ON sp.PackageNumber = p.PackageNumber
22
+ JOIN Payment pay ON sp.RecordNumber = pay.RecordNumber
23
+ WHERE DATE(pay.PaymentDate) = ?
24
+ ORDER BY pay.PaymentDate DESC
25
+ `, [reportDate]);
26
+
27
+ const totalRevenue = rows.reduce((sum, r) => sum + parseFloat(r.AmountPaid), 0);
28
+ const totalServices = rows.length;
29
+
30
+ res.json({
31
+ success: true,
32
+ data: {
33
+ date: reportDate,
34
+ totalRevenue,
35
+ totalServices,
36
+ transactions: rows
37
+ }
38
+ });
39
+ } catch (error) {
40
+ console.error('Daily report error:', error);
41
+ res.status(500).json({ success: false, error: error.message });
42
+ }
43
+ };
44
+
45
+ export const generateBill = async (req, res) => {
46
+ try {
47
+ const { recordNumber } = req.params;
48
+
49
+ const [rows] = await pool.execute(`
50
+ SELECT
51
+ sp.RecordNumber,
52
+ sp.ServiceDate,
53
+ c.PlateNumber,
54
+ c.DriverName,
55
+ c.PhoneNumber,
56
+ c.CarType,
57
+ c.CarSize,
58
+ p.PackageName,
59
+ p.PackageDescription,
60
+ p.PackagePrice,
61
+ pay.AmountPaid,
62
+ pay.PaymentDate,
63
+ (pay.AmountPaid - p.PackagePrice) as ChangeAmount
64
+ FROM ServicePackage sp
65
+ JOIN Car c ON sp.PlateNumber = c.PlateNumber
66
+ JOIN Package p ON sp.PackageNumber = p.PackageNumber
67
+ JOIN Payment pay ON sp.RecordNumber = pay.RecordNumber
68
+ WHERE sp.RecordNumber = ?
69
+ `, [recordNumber]);
70
+
71
+ if (rows.length === 0) {
72
+ return res.status(404).json({ success: false, error: 'Bill not found' });
73
+ }
74
+
75
+ const bill = rows[0];
76
+ res.json({
77
+ success: true,
78
+ data: {
79
+ ...bill,
80
+ companyName: 'SmartPark Car Wash',
81
+ companyAddress: 'Rubavu District, Western Province, Rwanda',
82
+ receiptNumber: `INV-${Date.now()}`,
83
+ timestamp: new Date().toISOString()
84
+ }
85
+ });
86
+ } catch (error) {
87
+ console.error('Generate bill error:', error);
88
+ res.status(500).json({ success: false, error: error.message });
89
+ }
90
+ };
91
+
92
+ export const getRevenueReport = async (req, res) => {
93
+ try {
94
+ const { startDate, endDate } = req.query;
95
+
96
+ let query = `
97
+ SELECT
98
+ DATE(pay.PaymentDate) as date,
99
+ COUNT(*) as totalServices,
100
+ SUM(pay.AmountPaid) as dailyRevenue
101
+ FROM Payment pay
102
+ JOIN ServicePackage sp ON pay.RecordNumber = sp.RecordNumber
103
+ WHERE 1=1
104
+ `;
105
+ const params = [];
106
+
107
+ if (startDate && endDate) {
108
+ query += ` AND DATE(pay.PaymentDate) BETWEEN ? AND ?`;
109
+ params.push(startDate, endDate);
110
+ }
111
+
112
+ query += ` GROUP BY DATE(pay.PaymentDate) ORDER BY date DESC`;
113
+
114
+ const [rows] = await pool.execute(query, params);
115
+
116
+ res.json({
117
+ success: true,
118
+ data: rows,
119
+ summary: {
120
+ totalRevenue: rows.reduce((sum, r) => sum + parseFloat(r.dailyRevenue), 0),
121
+ totalServices: rows.reduce((sum, r) => sum + r.totalServices, 0),
122
+ averageDaily: rows.length > 0 ? rows.reduce((sum, r) => sum + parseFloat(r.dailyRevenue), 0) / rows.length : 0
123
+ }
124
+ });
125
+ } catch (error) {
126
+ console.error('Revenue report error:', error);
127
+ res.status(500).json({ success: false, error: error.message });
128
+ }
129
+ };
@@ -0,0 +1,155 @@
1
+ import pool from '../config/db.js';
2
+
3
+ export const createService = async (req, res) => {
4
+ const { ServiceDate, PlateNumber, PackageNumber } = req.body;
5
+
6
+ if (!ServiceDate || !PlateNumber || !PackageNumber) {
7
+ return res.status(400).json({
8
+ success: false,
9
+ error: 'ServiceDate, PlateNumber, and PackageNumber are required'
10
+ });
11
+ }
12
+
13
+ try {
14
+ // Verify car exists
15
+ const [car] = await pool.execute('SELECT PlateNumber FROM Car WHERE PlateNumber = ?', [PlateNumber]);
16
+ if (car.length === 0) {
17
+ return res.status(400).json({ success: false, error: 'Car not found. Register car first.' });
18
+ }
19
+
20
+ // Verify package exists
21
+ const [pkg] = await pool.execute('SELECT PackageNumber FROM Package WHERE PackageNumber = ?', [PackageNumber]);
22
+ if (pkg.length === 0) {
23
+ return res.status(400).json({ success: false, error: 'Package not found' });
24
+ }
25
+
26
+ const [result] = await pool.execute(
27
+ 'INSERT INTO ServicePackage (ServiceDate, PlateNumber, PackageNumber) VALUES (?, ?, ?)',
28
+ [ServiceDate, PlateNumber, PackageNumber]
29
+ );
30
+
31
+ res.status(201).json({
32
+ success: true,
33
+ message: 'Service record created successfully',
34
+ data: { RecordNumber: result.insertId, ServiceDate, PlateNumber, PackageNumber }
35
+ });
36
+ } catch (error) {
37
+ res.status(500).json({ success: false, error: error.message });
38
+ }
39
+ };
40
+
41
+ export const getAllServices = async (req, res) => {
42
+ try {
43
+ const [rows] = await pool.execute(`
44
+ SELECT
45
+ sp.RecordNumber,
46
+ sp.ServiceDate,
47
+ sp.PlateNumber,
48
+ sp.PackageNumber,
49
+ c.DriverName,
50
+ c.PhoneNumber,
51
+ p.PackageName,
52
+ p.PackageDescription,
53
+ p.PackagePrice,
54
+ pay.AmountPaid,
55
+ pay.PaymentDate
56
+ FROM ServicePackage sp
57
+ LEFT JOIN Car c ON sp.PlateNumber = c.PlateNumber
58
+ LEFT JOIN Package p ON sp.PackageNumber = p.PackageNumber
59
+ LEFT JOIN Payment pay ON sp.RecordNumber = pay.RecordNumber
60
+ ORDER BY sp.ServiceDate DESC
61
+ `);
62
+ res.json({ success: true, data: rows });
63
+ } catch (error) {
64
+ res.status(500).json({ success: false, error: error.message });
65
+ }
66
+ };
67
+
68
+ export const getServiceById = async (req, res) => {
69
+ const { recordNumber } = req.params;
70
+ try {
71
+ const [rows] = await pool.execute(`
72
+ SELECT
73
+ sp.RecordNumber,
74
+ sp.ServiceDate,
75
+ sp.PlateNumber,
76
+ sp.PackageNumber,
77
+ c.DriverName,
78
+ c.CarType,
79
+ c.CarSize,
80
+ c.PhoneNumber,
81
+ p.PackageName,
82
+ p.PackageDescription,
83
+ p.PackagePrice
84
+ FROM ServicePackage sp
85
+ JOIN Car c ON sp.PlateNumber = c.PlateNumber
86
+ JOIN Package p ON sp.PackageNumber = p.PackageNumber
87
+ WHERE sp.RecordNumber = ?
88
+ `, [recordNumber]);
89
+
90
+ if (rows.length === 0) {
91
+ return res.status(404).json({ success: false, error: 'Service record not found' });
92
+ }
93
+ res.json({ success: true, data: rows[0] });
94
+ } catch (error) {
95
+ res.status(500).json({ success: false, error: error.message });
96
+ }
97
+ };
98
+
99
+ export const updateService = async (req, res) => {
100
+ const { recordNumber } = req.params;
101
+ const { ServiceDate, PlateNumber, PackageNumber } = req.body;
102
+
103
+ if (!ServiceDate || !PlateNumber || !PackageNumber) {
104
+ return res.status(400).json({
105
+ success: false,
106
+ error: 'ServiceDate, PlateNumber, and PackageNumber are required'
107
+ });
108
+ }
109
+
110
+ try {
111
+ // Check if record exists
112
+ const [existing] = await pool.execute('SELECT RecordNumber FROM ServicePackage WHERE RecordNumber = ?', [recordNumber]);
113
+ if (existing.length === 0) {
114
+ return res.status(404).json({ success: false, error: 'Service record not found' });
115
+ }
116
+
117
+ const [result] = await pool.execute(
118
+ 'UPDATE ServicePackage SET ServiceDate = ?, PlateNumber = ?, PackageNumber = ? WHERE RecordNumber = ?',
119
+ [ServiceDate, PlateNumber, PackageNumber, recordNumber]
120
+ );
121
+
122
+ res.json({
123
+ success: true,
124
+ message: 'Service record updated successfully',
125
+ data: { RecordNumber: recordNumber, ServiceDate, PlateNumber, PackageNumber }
126
+ });
127
+ } catch (error) {
128
+ res.status(500).json({ success: false, error: error.message });
129
+ }
130
+ };
131
+
132
+ export const deleteService = async (req, res) => {
133
+ const { recordNumber } = req.params;
134
+
135
+ try {
136
+ // Check if record exists
137
+ const [existing] = await pool.execute('SELECT RecordNumber FROM ServicePackage WHERE RecordNumber = ?', [recordNumber]);
138
+ if (existing.length === 0) {
139
+ return res.status(404).json({ success: false, error: 'Service record not found' });
140
+ }
141
+
142
+ // Delete payment first (foreign key constraint)
143
+ await pool.execute('DELETE FROM Payment WHERE RecordNumber = ?', [recordNumber]);
144
+
145
+ // Delete service record
146
+ const [result] = await pool.execute('DELETE FROM ServicePackage WHERE RecordNumber = ?', [recordNumber]);
147
+
148
+ res.json({
149
+ success: true,
150
+ message: 'Service record and associated payment deleted successfully'
151
+ });
152
+ } catch (error) {
153
+ res.status(500).json({ success: false, error: error.message });
154
+ }
155
+ };
@@ -0,0 +1,16 @@
1
+ export const requireAuth = (req, res, next) => {
2
+ if (!req.session.user) {
3
+ return res.status(401).json({
4
+ success: false,
5
+ error: 'Unauthorized - Please login first'
6
+ });
7
+ }
8
+ next();
9
+ };
10
+
11
+ export const isLoggedIn = (req, res, next) => {
12
+ if (req.session.user) {
13
+ return res.status(200).json({ user: req.session.user });
14
+ }
15
+ next();
16
+ };