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,100 @@
1
+ import React, { useState } from 'react';
2
+ import { Link, useLocation } from 'react-router-dom';
3
+
4
+ function Navbar({ user, onLogout }) {
5
+ const location = useLocation();
6
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
7
+
8
+ const navItems = [
9
+ { path: '/', name: 'Dashboard', icon: 'M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6' },
10
+ { path: '/cars', name: 'Cars', icon: 'M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z' },
11
+ { path: '/services', name: 'Services', icon: 'M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' },
12
+ { path: '/payments', name: 'Payments', icon: 'M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z' },
13
+ { path: '/reports', name: 'Reports', icon: 'M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z' },
14
+ ];
15
+
16
+ return (
17
+ <>
18
+ <nav className="glass-card fixed top-4 left-4 right-4 z-50 px-6 py-3 animate-fade-in">
19
+ <div className="flex justify-between items-center">
20
+ <Link to="/" className="flex items-center space-x-3">
21
+ <div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-purple-600 rounded-xl flex items-center justify-center">
22
+ <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
23
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
24
+ </svg>
25
+ </div>
26
+ <span className="text-white font-bold text-xl hidden md:block">SmartPark</span>
27
+ </Link>
28
+
29
+ {/* Desktop Menu */}
30
+ <div className="hidden md:flex space-x-2">
31
+ {navItems.map((item) => (
32
+ <Link
33
+ key={item.path}
34
+ to={item.path}
35
+ className={`px-4 py-2 rounded-lg transition-all duration-300 flex items-center space-x-2 ${
36
+ location.pathname === item.path
37
+ ? 'bg-white/20 text-white'
38
+ : 'text-white/70 hover:bg-white/10 hover:text-white'
39
+ }`}
40
+ >
41
+ <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
42
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d={item.icon} />
43
+ </svg>
44
+ <span>{item.name}</span>
45
+ </Link>
46
+ ))}
47
+ </div>
48
+
49
+ <div className="flex items-center space-x-3">
50
+ <div className="hidden md:block text-right">
51
+ <div className="text-white text-sm font-medium">{user?.username}</div>
52
+ <div className="text-white/40 text-xs">Administrator</div>
53
+ </div>
54
+ <button
55
+ onClick={onLogout}
56
+ className="glass-button-secondary text-sm px-4 py-2"
57
+ >
58
+ Logout
59
+ </button>
60
+ <button
61
+ onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
62
+ className="md:hidden text-white p-2"
63
+ >
64
+ <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
65
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
66
+ </svg>
67
+ </button>
68
+ </div>
69
+ </div>
70
+ </nav>
71
+
72
+ {/* Mobile Menu */}
73
+ {isMobileMenuOpen && (
74
+ <div className="fixed inset-0 z-40 pt-20 animate-fade-in md:hidden">
75
+ <div className="glass-card mx-4 p-4 space-y-2">
76
+ {navItems.map((item) => (
77
+ <Link
78
+ key={item.path}
79
+ to={item.path}
80
+ onClick={() => setIsMobileMenuOpen(false)}
81
+ className={`flex items-center space-x-3 px-4 py-3 rounded-lg transition-all duration-300 ${
82
+ location.pathname === item.path
83
+ ? 'bg-white/20 text-white'
84
+ : 'text-white/70 hover:bg-white/10'
85
+ }`}
86
+ >
87
+ <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
88
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d={item.icon} />
89
+ </svg>
90
+ <span>{item.name}</span>
91
+ </Link>
92
+ ))}
93
+ </div>
94
+ </div>
95
+ )}
96
+ </>
97
+ );
98
+ }
99
+
100
+ export default Navbar;
@@ -0,0 +1,211 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { useSearchParams } from 'react-router-dom';
3
+ import api from '../services/api';
4
+
5
+ function PaymentForm() {
6
+ const [searchParams] = useSearchParams();
7
+ const [formData, setFormData] = useState({
8
+ AmountPaid: '',
9
+ PaymentDate: new Date().toISOString().split('T')[0],
10
+ RecordNumber: searchParams.get('record') || ''
11
+ });
12
+ const [services, setServices] = useState([]);
13
+ const [selectedService, setSelectedService] = useState(null);
14
+ const [message, setMessage] = useState(null);
15
+ const [loading, setLoading] = useState(false);
16
+
17
+ useEffect(() => {
18
+ fetchUnpaidServices();
19
+ }, []);
20
+
21
+ useEffect(() => {
22
+ if (formData.RecordNumber) {
23
+ fetchServiceDetails(formData.RecordNumber);
24
+ }
25
+ }, [formData.RecordNumber]);
26
+
27
+ const fetchUnpaidServices = async () => {
28
+ try {
29
+ const response = await api.get('/services');
30
+ const allServices = response.data.data || [];
31
+ const unpaid = allServices.filter(service => !service.AmountPaid);
32
+ setServices(unpaid);
33
+ } catch (error) {
34
+ console.error('Error fetching services:', error);
35
+ }
36
+ };
37
+
38
+ const fetchServiceDetails = async (recordNumber) => {
39
+ try {
40
+ const response = await api.get(`/services/${recordNumber}`);
41
+ setSelectedService(response.data.data);
42
+ if (response.data.data.PackagePrice) {
43
+ setFormData(prev => ({
44
+ ...prev,
45
+ AmountPaid: response.data.data.PackagePrice
46
+ }));
47
+ }
48
+ } catch (error) {
49
+ console.error('Error fetching service details:', error);
50
+ }
51
+ };
52
+
53
+ const handleChange = (e) => {
54
+ setFormData({ ...formData, [e.target.name]: e.target.value });
55
+ };
56
+
57
+ const handleSubmit = async (e) => {
58
+ e.preventDefault();
59
+ setLoading(true);
60
+ setMessage(null);
61
+
62
+ try {
63
+ await api.post('/payments', formData);
64
+ setMessage({ type: 'success', text: '✅ Payment recorded successfully!' });
65
+ setFormData({
66
+ AmountPaid: '',
67
+ PaymentDate: new Date().toISOString().split('T')[0],
68
+ RecordNumber: ''
69
+ });
70
+ setSelectedService(null);
71
+ fetchUnpaidServices();
72
+ setTimeout(() => setMessage(null), 3000);
73
+ } catch (err) {
74
+ setMessage({ type: 'error', text: err.response?.data?.error || 'Payment failed' });
75
+ } finally {
76
+ setLoading(false);
77
+ }
78
+ };
79
+
80
+ return (
81
+ <div className="max-w-4xl mx-auto animate-fade-in space-y-6">
82
+ <div className="glass-card p-8">
83
+ <div className="flex items-center space-x-3 mb-6">
84
+ <div className="w-12 h-12 bg-gradient-to-br from-green-500 to-emerald-600 rounded-xl flex items-center justify-center">
85
+ <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
86
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z" />
87
+ </svg>
88
+ </div>
89
+ <div>
90
+ <h2 className="text-2xl font-bold text-white">Record Payment</h2>
91
+ <p className="text-white/60 text-sm">Process customer payment for services</p>
92
+ </div>
93
+ </div>
94
+
95
+ {message && (
96
+ <div className={`mb-6 p-4 rounded-lg backdrop-blur-sm ${
97
+ message.type === 'success'
98
+ ? 'bg-green-500/20 border border-green-500/50 text-green-200'
99
+ : 'bg-red-500/20 border border-red-500/50 text-red-200'
100
+ } animate-fade-in`}>
101
+ {message.text}
102
+ </div>
103
+ )}
104
+
105
+ <form onSubmit={handleSubmit} className="space-y-4">
106
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
107
+ <div>
108
+ <label className="text-white/80 text-sm block mb-2">Select Service *</label>
109
+ <select
110
+ name="RecordNumber"
111
+ value={formData.RecordNumber}
112
+ onChange={handleChange}
113
+ className="glass-input w-full"
114
+ required
115
+ >
116
+ <option value="">Select unpaid service</option>
117
+ {services.map(service => (
118
+ <option key={service.RecordNumber} value={service.RecordNumber}>
119
+ {service.PlateNumber} - {service.DriverName} - {new Date(service.ServiceDate).toLocaleDateString()}
120
+ </option>
121
+ ))}
122
+ </select>
123
+ </div>
124
+
125
+ <div>
126
+ <label className="text-white/80 text-sm block mb-2">Payment Date *</label>
127
+ <input
128
+ type="date"
129
+ name="PaymentDate"
130
+ value={formData.PaymentDate}
131
+ onChange={handleChange}
132
+ className="glass-input w-full"
133
+ required
134
+ />
135
+ </div>
136
+
137
+ <div>
138
+ <label className="text-white/80 text-sm block mb-2">Amount Paid (RWF) *</label>
139
+ <input
140
+ type="number"
141
+ name="AmountPaid"
142
+ value={formData.AmountPaid}
143
+ onChange={handleChange}
144
+ placeholder="Enter amount"
145
+ className="glass-input w-full"
146
+ required
147
+ min="0"
148
+ step="100"
149
+ />
150
+ </div>
151
+ </div>
152
+
153
+ {selectedService && (
154
+ <div className="bg-white/5 rounded-lg p-4 mt-4">
155
+ <h4 className="text-white font-semibold mb-2">Service Details</h4>
156
+ <div className="grid grid-cols-2 gap-2 text-sm">
157
+ <div className="text-white/60">Package:</div>
158
+ <div className="text-white">{selectedService.PackageName}</div>
159
+ <div className="text-white/60">Package Price:</div>
160
+ <div className="text-white">{selectedService.PackagePrice?.toLocaleString()} RWF</div>
161
+ <div className="text-white/60">Driver:</div>
162
+ <div className="text-white">{selectedService.DriverName}</div>
163
+ <div className="text-white/60">Vehicle:</div>
164
+ <div className="text-white">{selectedService.PlateNumber}</div>
165
+ </div>
166
+ {parseFloat(formData.AmountPaid) < selectedService.PackagePrice && (
167
+ <div className="mt-3 text-yellow-400 text-sm">
168
+ ⚠️ Amount paid is less than package price ({selectedService.PackagePrice.toLocaleString()} RWF)
169
+ </div>
170
+ )}
171
+ {parseFloat(formData.AmountPaid) > selectedService.PackagePrice && (
172
+ <div className="mt-3 text-green-400 text-sm">
173
+ ✓ Change to return: {(formData.AmountPaid - selectedService.PackagePrice).toLocaleString()} RWF
174
+ </div>
175
+ )}
176
+ </div>
177
+ )}
178
+
179
+ <div className="flex justify-end pt-4">
180
+ <button
181
+ type="submit"
182
+ disabled={loading}
183
+ className="glass-button disabled:opacity-50"
184
+ >
185
+ {loading ? 'Processing...' : 'Record Payment'}
186
+ </button>
187
+ </div>
188
+ </form>
189
+ </div>
190
+
191
+ {/* Recent Payments Summary */}
192
+ <div className="glass-card p-8">
193
+ <h3 className="text-xl font-bold text-white mb-4">Payment Summary</h3>
194
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
195
+ <div className="bg-white/5 rounded-lg p-4">
196
+ <div className="text-white/60 text-sm">Unpaid Services</div>
197
+ <div className="text-2xl font-bold text-yellow-400">{services.length}</div>
198
+ </div>
199
+ <div className="bg-white/5 rounded-lg p-4">
200
+ <div className="text-white/60 text-sm">Total Due Amount</div>
201
+ <div className="text-2xl font-bold text-white">
202
+ {services.reduce((sum, s) => sum + (s.PackagePrice || 0), 0).toLocaleString()} RWF
203
+ </div>
204
+ </div>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ );
209
+ }
210
+
211
+ export default PaymentForm;