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.
- package/Dev-Business/.vscode/settings.json +3 -0
- package/Dev-Business/backend-project/config/db.js +16 -0
- package/Dev-Business/backend-project/controllers/authController.js +41 -0
- package/Dev-Business/backend-project/controllers/carController.js +62 -0
- package/Dev-Business/backend-project/controllers/packageController.js +49 -0
- package/Dev-Business/backend-project/controllers/paymentController.js +63 -0
- package/Dev-Business/backend-project/controllers/reportController.js +129 -0
- package/Dev-Business/backend-project/controllers/serviceController.js +155 -0
- package/Dev-Business/backend-project/middleware/auth.js +16 -0
- package/Dev-Business/backend-project/package-lock.json +1507 -0
- package/Dev-Business/backend-project/package.json +21 -0
- package/Dev-Business/backend-project/routes/authRoutes.js +10 -0
- package/Dev-Business/backend-project/routes/carRoutes.js +11 -0
- package/Dev-Business/backend-project/routes/packageRoutes.js +11 -0
- package/Dev-Business/backend-project/routes/paymentRoutes.js +10 -0
- package/Dev-Business/backend-project/routes/reportRoutes.js +11 -0
- package/Dev-Business/backend-project/routes/serviceRoutes.js +19 -0
- package/Dev-Business/backend-project/server.js +72 -0
- package/Dev-Business/frontend-project/README.md +18 -0
- package/Dev-Business/frontend-project/eslint.config.js +21 -0
- package/Dev-Business/frontend-project/index.html +14 -0
- package/Dev-Business/frontend-project/package-lock.json +3655 -0
- package/Dev-Business/frontend-project/package.json +34 -0
- package/Dev-Business/frontend-project/postcss.config.js +6 -0
- package/Dev-Business/frontend-project/public/favicon.svg +1 -0
- package/Dev-Business/frontend-project/public/icons.svg +24 -0
- package/Dev-Business/frontend-project/src/App.css +184 -0
- package/Dev-Business/frontend-project/src/App.jsx +84 -0
- package/Dev-Business/frontend-project/src/assets/hero.png +0 -0
- package/Dev-Business/frontend-project/src/assets/react.svg +1 -0
- package/Dev-Business/frontend-project/src/assets/vite.svg +1 -0
- package/Dev-Business/frontend-project/src/components/CarForm.jsx +146 -0
- package/Dev-Business/frontend-project/src/components/DailyReport.jsx +264 -0
- package/Dev-Business/frontend-project/src/components/Dashboard.jsx +153 -0
- package/Dev-Business/frontend-project/src/components/Login.jsx +163 -0
- package/Dev-Business/frontend-project/src/components/Navbar.jsx +99 -0
- package/Dev-Business/frontend-project/src/components/Navibar.jsx +100 -0
- package/Dev-Business/frontend-project/src/components/PaymentForm.jsx +211 -0
- package/Dev-Business/frontend-project/src/components/ServiceRecord.jsx +384 -0
- package/Dev-Business/frontend-project/src/index.css +155 -0
- package/Dev-Business/frontend-project/src/main.jsx +10 -0
- package/Dev-Business/frontend-project/src/services/api.js +23 -0
- package/Dev-Business/frontend-project/tailwind.config.js +31 -0
- package/Dev-Business/frontend-project/vite.config.js +15 -0
- package/package.json +17 -0
|
@@ -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
|
+
};
|