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,264 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import api from '../services/api';
3
+
4
+ function DailyReport() {
5
+ const [report, setReport] = useState(null);
6
+ const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]);
7
+ const [loading, setLoading] = useState(false);
8
+ const [dateRange, setDateRange] = useState({ startDate: '', endDate: '' });
9
+ const [revenueReport, setRevenueReport] = useState(null);
10
+
11
+ useEffect(() => {
12
+ fetchDailyReport();
13
+ fetchRevenueReport();
14
+ }, []);
15
+
16
+ const fetchDailyReport = async () => {
17
+ setLoading(true);
18
+ try {
19
+ const response = await api.get(`/reports/daily?date=${selectedDate}`);
20
+ setReport(response.data.data);
21
+ } catch (error) {
22
+ console.error('Error fetching report:', error);
23
+ } finally {
24
+ setLoading(false);
25
+ }
26
+ };
27
+
28
+ const fetchRevenueReport = async () => {
29
+ try {
30
+ let url = '/reports/revenue';
31
+ if (dateRange.startDate && dateRange.endDate) {
32
+ url += `?startDate=${dateRange.startDate}&endDate=${dateRange.endDate}`;
33
+ }
34
+ const response = await api.get(url);
35
+ setRevenueReport(response.data);
36
+ } catch (error) {
37
+ console.error('Error fetching revenue report:', error);
38
+ }
39
+ };
40
+
41
+ const handleDateChange = (e) => {
42
+ setSelectedDate(e.target.value);
43
+ };
44
+
45
+ const handleDateRangeChange = (e) => {
46
+ setDateRange({ ...dateRange, [e.target.name]: e.target.value });
47
+ };
48
+
49
+ const applyDateRange = () => {
50
+ fetchRevenueReport();
51
+ };
52
+
53
+ const exportToCSV = () => {
54
+ if (!report?.transactions) return;
55
+
56
+ const headers = ['Plate Number', 'Driver', 'Package', 'Amount Paid', 'Payment Date', 'Service Date'];
57
+ const csvData = report.transactions.map(t => [
58
+ t.PlateNumber,
59
+ t.DriverName,
60
+ t.PackageName,
61
+ t.AmountPaid,
62
+ new Date(t.PaymentDate).toLocaleDateString(),
63
+ new Date(t.ServiceDate).toLocaleDateString()
64
+ ]);
65
+
66
+ const csvContent = [headers, ...csvData].map(row => row.join(',')).join('\n');
67
+ const blob = new Blob([csvContent], { type: 'text/csv' });
68
+ const url = URL.createObjectURL(blob);
69
+ const a = document.createElement('a');
70
+ a.href = url;
71
+ a.download = `report_${selectedDate}.csv`;
72
+ a.click();
73
+ URL.revokeObjectURL(url);
74
+ };
75
+
76
+ return (
77
+ <div className="max-w-7xl mx-auto animate-fade-in space-y-6">
78
+ {/* Daily Report Section */}
79
+ <div className="glass-card p-8">
80
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-center mb-6 space-y-4 md:space-y-0">
81
+ <div className="flex items-center space-x-3">
82
+ <div className="w-12 h-12 bg-gradient-to-br from-orange-500 to-red-600 rounded-xl flex items-center justify-center">
83
+ <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
84
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="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" />
85
+ </svg>
86
+ </div>
87
+ <div>
88
+ <h2 className="text-2xl font-bold text-white">Daily Report</h2>
89
+ <p className="text-white/60 text-sm">View daily sales and transactions</p>
90
+ </div>
91
+ </div>
92
+
93
+ <div className="flex space-x-3">
94
+ <input
95
+ type="date"
96
+ value={selectedDate}
97
+ onChange={handleDateChange}
98
+ className="glass-input"
99
+ />
100
+ <button onClick={fetchDailyReport} className="glass-button">
101
+ View Report
102
+ </button>
103
+ <button onClick={exportToCSV} className="glass-button-secondary">
104
+ Export CSV
105
+ </button>
106
+ </div>
107
+ </div>
108
+
109
+ {loading ? (
110
+ <div className="text-center py-12">
111
+ <div className="text-white text-xl animate-pulse">Loading report...</div>
112
+ </div>
113
+ ) : report ? (
114
+ <>
115
+ {/* Summary Cards */}
116
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
117
+ <div className="bg-white/5 rounded-lg p-4">
118
+ <div className="text-white/60 text-sm">Date</div>
119
+ <div className="text-2xl font-bold text-white">{new Date(report.date).toLocaleDateString()}</div>
120
+ </div>
121
+ <div className="bg-white/5 rounded-lg p-4">
122
+ <div className="text-white/60 text-sm">Total Services</div>
123
+ <div className="text-2xl font-bold text-blue-400">{report.totalServices}</div>
124
+ </div>
125
+ <div className="bg-white/5 rounded-lg p-4">
126
+ <div className="text-white/60 text-sm">Total Revenue</div>
127
+ <div className="text-2xl font-bold text-green-400">{report.totalRevenue.toLocaleString()} RWF</div>
128
+ </div>
129
+ </div>
130
+
131
+ {/* Transactions Table */}
132
+ <div className="overflow-x-auto">
133
+ <table className="glass-table w-full">
134
+ <thead>
135
+ <tr>
136
+ <th>Plate Number</th>
137
+ <th>Driver</th>
138
+ <th>Package</th>
139
+ <th>Amount Paid</th>
140
+ <th>Payment Date</th>
141
+ <th>Service Date</th>
142
+ </tr>
143
+ </thead>
144
+ <tbody>
145
+ {report.transactions.length === 0 ? (
146
+ <tr>
147
+ <td colSpan="6" className="text-center text-white/40 py-8">
148
+ No transactions found for this date
149
+ </td>
150
+ </tr>
151
+ ) : (
152
+ report.transactions.map((transaction, index) => (
153
+ <tr key={index}>
154
+ <td className="font-mono">{transaction.PlateNumber}</td>
155
+ <td>{transaction.DriverName}</td>
156
+ <td>{transaction.PackageName}</td>
157
+ <td className="text-green-400">{transaction.AmountPaid.toLocaleString()} RWF</td>
158
+ <td>{new Date(transaction.PaymentDate).toLocaleDateString()}</td>
159
+ <td>{new Date(transaction.ServiceDate).toLocaleDateString()}</td>
160
+ </tr>
161
+ ))
162
+ )}
163
+ </tbody>
164
+ </table>
165
+ </div>
166
+ </>
167
+ ) : (
168
+ <div className="text-center py-12 text-white/40">
169
+ No data available
170
+ </div>
171
+ )}
172
+ </div>
173
+
174
+ {/* Revenue Report Section */}
175
+ <div className="glass-card p-8">
176
+ <h3 className="text-xl font-bold text-white mb-4">Revenue Analysis</h3>
177
+
178
+ <div className="flex flex-col md:flex-row gap-4 mb-6">
179
+ <div className="flex-1">
180
+ <label className="text-white/80 text-sm block mb-2">Start Date</label>
181
+ <input
182
+ type="date"
183
+ name="startDate"
184
+ value={dateRange.startDate}
185
+ onChange={handleDateRangeChange}
186
+ className="glass-input w-full"
187
+ />
188
+ </div>
189
+ <div className="flex-1">
190
+ <label className="text-white/80 text-sm block mb-2">End Date</label>
191
+ <input
192
+ type="date"
193
+ name="endDate"
194
+ value={dateRange.endDate}
195
+ onChange={handleDateRangeChange}
196
+ className="glass-input w-full"
197
+ />
198
+ </div>
199
+ <div className="flex items-end">
200
+ <button onClick={applyDateRange} className="glass-button">
201
+ Apply Filter
202
+ </button>
203
+ </div>
204
+ </div>
205
+
206
+ {revenueReport && (
207
+ <>
208
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
209
+ <div className="bg-white/5 rounded-lg p-4">
210
+ <div className="text-white/60 text-sm">Period Total Revenue</div>
211
+ <div className="text-2xl font-bold text-green-400">
212
+ {revenueReport.summary?.totalRevenue?.toLocaleString() || 0} RWF
213
+ </div>
214
+ </div>
215
+ <div className="bg-white/5 rounded-lg p-4">
216
+ <div className="text-white/60 text-sm">Total Services</div>
217
+ <div className="text-2xl font-bold text-blue-400">
218
+ {revenueReport.summary?.totalServices || 0}
219
+ </div>
220
+ </div>
221
+ <div className="bg-white/5 rounded-lg p-4">
222
+ <div className="text-white/60 text-sm">Average Daily Revenue</div>
223
+ <div className="text-2xl font-bold text-purple-400">
224
+ {Math.round(revenueReport.summary?.averageDaily || 0).toLocaleString()} RWF
225
+ </div>
226
+ </div>
227
+ </div>
228
+
229
+ <div className="overflow-x-auto">
230
+ <table className="glass-table w-full">
231
+ <thead>
232
+ <tr>
233
+ <th>Date</th>
234
+ <th>Services</th>
235
+ <th>Revenue</th>
236
+ </tr>
237
+ </thead>
238
+ <tbody>
239
+ {revenueReport.data?.length === 0 ? (
240
+ <tr>
241
+ <td colSpan="3" className="text-center text-white/40 py-8">
242
+ No revenue data for selected period
243
+ </td>
244
+ </tr>
245
+ ) : (
246
+ revenueReport.data?.map((item, index) => (
247
+ <tr key={index}>
248
+ <td>{new Date(item.date).toLocaleDateString()}</td>
249
+ <td>{item.totalServices}</td>
250
+ <td className="text-green-400">{parseFloat(item.dailyRevenue).toLocaleString()} RWF</td>
251
+ </tr>
252
+ ))
253
+ )}
254
+ </tbody>
255
+ </table>
256
+ </div>
257
+ </>
258
+ )}
259
+ </div>
260
+ </div>
261
+ );
262
+ }
263
+
264
+ export default DailyReport;
@@ -0,0 +1,153 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Link } from 'react-router-dom';
3
+ import api from '../services/api';
4
+
5
+ function Dashboard() {
6
+ const [stats, setStats] = useState({
7
+ totalServices: 0,
8
+ totalRevenue: 0,
9
+ todayServices: 0,
10
+ todayRevenue: 0
11
+ });
12
+ const [recentServices, setRecentServices] = useState([]);
13
+ const [loading, setLoading] = useState(true);
14
+
15
+ useEffect(() => {
16
+ fetchDashboardData();
17
+ }, []);
18
+
19
+ const fetchDashboardData = async () => {
20
+ try {
21
+ const [servicesRes, todayReport] = await Promise.all([
22
+ api.get('/services'),
23
+ api.get('/reports/daily')
24
+ ]);
25
+
26
+ const services = servicesRes.data.data || [];
27
+ const todayData = todayReport.data.data;
28
+
29
+ setStats({
30
+ totalServices: services.length,
31
+ totalRevenue: services.reduce((sum, s) => sum + (parseFloat(s.AmountPaid) || 0), 0),
32
+ todayServices: todayData?.totalServices || 0,
33
+ todayRevenue: todayData?.totalRevenue || 0
34
+ });
35
+
36
+ setRecentServices(services.slice(0, 5));
37
+ } catch (error) {
38
+ console.error('Error fetching dashboard data:', error);
39
+ } finally {
40
+ setLoading(false);
41
+ }
42
+ };
43
+
44
+ const statCards = [
45
+ { title: 'Total Services', value: stats.totalServices, 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', color: 'from-blue-500 to-cyan-500' },
46
+ { title: 'Total Revenue', value: `${stats.totalRevenue.toLocaleString()} RWF`, icon: 'M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z', color: 'from-green-500 to-emerald-500' },
47
+ { title: "Today's Services", value: stats.todayServices, icon: 'M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z', color: 'from-purple-500 to-pink-500' },
48
+ { title: "Today's Revenue", value: `${stats.todayRevenue.toLocaleString()} RWF`, icon: 'M9 8h6m-5 4h4m-2 4h2M3 12a9 9 0 1018 0 9 9 0 00-18 0z', color: 'from-orange-500 to-red-500' },
49
+ ];
50
+
51
+ if (loading) {
52
+ return (
53
+ <div className="flex items-center justify-center h-96">
54
+ <div className="text-white text-xl animate-pulse">Loading dashboard...</div>
55
+ </div>
56
+ );
57
+ }
58
+
59
+ return (
60
+ <div className="space-y-6 animate-fade-in">
61
+ {/* Welcome Section */}
62
+ <div className="glass-card p-6">
63
+ <h1 className="text-3xl font-bold text-white mb-2">Welcome Back!</h1>
64
+ <p className="text-white/60">Here's what's happening with your car wash business today.</p>
65
+ </div>
66
+
67
+ {/* Stats Grid */}
68
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
69
+ {statCards.map((stat, index) => (
70
+ <div key={index} className="glass-card p-6 hover:scale-105 transition-transform duration-300">
71
+ <div className="flex items-center justify-between mb-4">
72
+ <div className={`w-12 h-12 bg-gradient-to-r ${stat.color} rounded-xl flex items-center justify-center`}>
73
+ <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
74
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d={stat.icon} />
75
+ </svg>
76
+ </div>
77
+ </div>
78
+ <h3 className="text-white/60 text-sm mb-1">{stat.title}</h3>
79
+ <p className="text-white text-2xl font-bold">{stat.value}</p>
80
+ </div>
81
+ ))}
82
+ </div>
83
+
84
+ {/* Recent Services */}
85
+ <div className="glass-card p-6">
86
+ <div className="flex justify-between items-center mb-4">
87
+ <h2 className="text-xl font-bold text-white">Recent Services</h2>
88
+ <Link to="/services" className="text-blue-300 hover:text-blue-200 text-sm">
89
+ View All →
90
+ </Link>
91
+ </div>
92
+
93
+ {recentServices.length > 0 ? (
94
+ <div className="overflow-x-auto">
95
+ <table className="glass-table">
96
+ <thead>
97
+ <tr>
98
+ <th>Date</th>
99
+ <th>Plate Number</th>
100
+ <th>Driver</th>
101
+ <th>Package</th>
102
+ <th>Amount</th>
103
+ </tr>
104
+ </thead>
105
+ <tbody>
106
+ {recentServices.map((service) => (
107
+ <tr key={service.RecordNumber}>
108
+ <td>{new Date(service.ServiceDate).toLocaleDateString()}</td>
109
+ <td className="font-mono">{service.PlateNumber}</td>
110
+ <td>{service.DriverName}</td>
111
+ <td>{service.PackageName}</td>
112
+ <td>{service.AmountPaid?.toLocaleString()} RWF</td>
113
+ </tr>
114
+ ))}
115
+ </tbody>
116
+ </table>
117
+ </div>
118
+ ) : (
119
+ <p className="text-white/40 text-center py-8">No services recorded yet</p>
120
+ )}
121
+ </div>
122
+
123
+ {/* Quick Actions */}
124
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
125
+ <Link to="/services" className="glass-card p-6 hover:bg-white/20 transition-all duration-300 text-center group">
126
+ <svg className="w-12 h-12 text-white mx-auto mb-3 group-hover:scale-110 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
127
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
128
+ </svg>
129
+ <h3 className="text-white font-semibold">New Service</h3>
130
+ <p className="text-white/40 text-sm mt-1">Record a car wash service</p>
131
+ </Link>
132
+
133
+ <Link to="/payments" className="glass-card p-6 hover:bg-white/20 transition-all duration-300 text-center group">
134
+ <svg className="w-12 h-12 text-white mx-auto mb-3 group-hover:scale-110 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
135
+ <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" />
136
+ </svg>
137
+ <h3 className="text-white font-semibold">Record Payment</h3>
138
+ <p className="text-white/40 text-sm mt-1">Process customer payment</p>
139
+ </Link>
140
+
141
+ <Link to="/reports" className="glass-card p-6 hover:bg-white/20 transition-all duration-300 text-center group">
142
+ <svg className="w-12 h-12 text-white mx-auto mb-3 group-hover:scale-110 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
143
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="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" />
144
+ </svg>
145
+ <h3 className="text-white font-semibold">View Reports</h3>
146
+ <p className="text-white/40 text-sm mt-1">See daily analytics</p>
147
+ </Link>
148
+ </div>
149
+ </div>
150
+ );
151
+ }
152
+
153
+ export default Dashboard;
@@ -0,0 +1,163 @@
1
+ import React, { useState } from 'react';
2
+ import api from '../services/api';
3
+
4
+ function Login({ onLogin }) {
5
+ const [username, setUsername] = useState('');
6
+ const [password, setPassword] = useState('');
7
+ const [error, setError] = useState('');
8
+ const [loading, setLoading] = useState(false);
9
+
10
+ const handleSubmit = async (e) => {
11
+ e.preventDefault();
12
+ setLoading(true);
13
+ setError('');
14
+
15
+ try {
16
+ const response = await api.post('/auth/login', { username, password });
17
+ if (response.data.success && response.data.user) {
18
+ onLogin(response.data.user);
19
+ }
20
+ } catch (err) {
21
+ setError(err.response?.data?.error || 'Invalid credentials');
22
+ } finally {
23
+ setLoading(false);
24
+ }
25
+ };
26
+
27
+ return (
28
+ <div style={{
29
+ minHeight: '100vh',
30
+ position: 'relative',
31
+ overflow: 'hidden',
32
+ background: 'linear-gradient(135deg, #4c1d95, #1e3a8a, #312e81)',
33
+ display: 'flex',
34
+ alignItems: 'center',
35
+ justifyContent: 'center',
36
+ padding: '1.5rem'
37
+ }}>
38
+ {/* Animated Background Circles */}
39
+ <div style={{ position: 'absolute', inset: 0, overflow: 'hidden' }}>
40
+ <div style={{
41
+ position: 'absolute',
42
+ top: '-10%',
43
+ left: '-10%',
44
+ width: '400px',
45
+ height: '400px',
46
+ background: '#9333ea',
47
+ borderRadius: '50%',
48
+ filter: 'blur(80px)',
49
+ opacity: 0.3,
50
+ animation: 'float 6s ease-in-out infinite'
51
+ }}></div>
52
+ <div style={{
53
+ position: 'absolute',
54
+ bottom: '-10%',
55
+ right: '-10%',
56
+ width: '400px',
57
+ height: '400px',
58
+ background: '#3b82f6',
59
+ borderRadius: '50%',
60
+ filter: 'blur(80px)',
61
+ opacity: 0.3,
62
+ animation: 'float 6s ease-in-out infinite',
63
+ animationDelay: '2s'
64
+ }}></div>
65
+ <div style={{
66
+ position: 'absolute',
67
+ top: '50%',
68
+ left: '50%',
69
+ transform: 'translate(-50%, -50%)',
70
+ width: '500px',
71
+ height: '500px',
72
+ background: '#ec4899',
73
+ borderRadius: '50%',
74
+ filter: 'blur(100px)',
75
+ opacity: 0.15,
76
+ animation: 'float 6s ease-in-out infinite',
77
+ animationDelay: '4s'
78
+ }}></div>
79
+ </div>
80
+
81
+ {/* Login Card */}
82
+ <div className="glass-card" style={{ maxWidth: '500px', width: '100%' }}>
83
+ <div style={{ textAlign: 'center', marginBottom: '2.5rem' }}>
84
+ <div style={{
85
+ width: '112px',
86
+ height: '112px',
87
+ margin: '0 auto 1.5rem',
88
+ background: 'linear-gradient(135deg, #3b82f6, #9333ea)',
89
+ borderRadius: '1.5rem',
90
+ display: 'flex',
91
+ alignItems: 'center',
92
+ justifyContent: 'center',
93
+ boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)'
94
+ }}>
95
+ <svg style={{ width: '56px', height: '56px', color: 'white' }} fill="none" stroke="currentColor" viewBox="0 0 24 24">
96
+ <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" />
97
+ </svg>
98
+ </div>
99
+ <h1 style={{ fontSize: '3rem', fontWeight: 'bold', color: 'white', marginBottom: '0.5rem' }}>SmartPark</h1>
100
+ <p style={{ fontSize: '1.25rem', color: 'rgba(255,255,255,0.7)' }}>Car Wash Management System</p>
101
+ </div>
102
+
103
+ <form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
104
+ <div>
105
+ <label style={{ color: 'rgba(255,255,255,0.9)', fontSize: '0.875rem', fontWeight: 500, display: 'block', marginBottom: '0.5rem' }}>Username</label>
106
+ <input
107
+ type="text"
108
+ placeholder="Enter your username"
109
+ value={username}
110
+ onChange={(e) => setUsername(e.target.value)}
111
+ className="glass-input"
112
+ required
113
+ disabled={loading}
114
+ />
115
+ </div>
116
+
117
+ <div>
118
+ <label style={{ color: 'rgba(255,255,255,0.9)', fontSize: '0.875rem', fontWeight: 500, display: 'block', marginBottom: '0.5rem' }}>Password</label>
119
+ <input
120
+ type="password"
121
+ placeholder="Enter your password"
122
+ value={password}
123
+ onChange={(e) => setPassword(e.target.value)}
124
+ className="glass-input"
125
+ required
126
+ disabled={loading}
127
+ />
128
+ </div>
129
+
130
+ {error && (
131
+ <div style={{
132
+ background: 'rgba(239, 68, 68, 0.3)',
133
+ backdropFilter: 'blur(8px)',
134
+ border: '1px solid rgba(239, 68, 68, 0.5)',
135
+ borderRadius: '0.5rem',
136
+ padding: '1rem',
137
+ color: '#fecaca',
138
+ textAlign: 'center'
139
+ }}>
140
+ {error}
141
+ </div>
142
+ )}
143
+
144
+ <button
145
+ type="submit"
146
+ disabled={loading}
147
+ className="glass-button"
148
+ style={{ fontSize: '1.125rem' }}
149
+ >
150
+ {loading ? 'Logging in...' : 'Login to Dashboard'}
151
+ </button>
152
+ </form>
153
+
154
+ <div style={{ marginTop: '2rem', paddingTop: '1.5rem', borderTop: '1px solid rgba(255,255,255,0.2)', textAlign: 'center' }}>
155
+ <p style={{ color: 'rgba(255,255,255,0.5)', fontSize: '0.875rem' }}>Demo Credentials</p>
156
+ <p style={{ color: 'rgba(255,255,255,0.7)', fontSize: '0.875rem', marginTop: '0.25rem', fontFamily: 'monospace' }}>Username: admin | Password: admin123</p>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ );
161
+ }
162
+
163
+ export default Login;
@@ -0,0 +1,99 @@
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" onClick={() => setIsMobileMenuOpen(false)}>
76
+ {navItems.map((item) => (
77
+ <Link
78
+ key={item.path}
79
+ to={item.path}
80
+ className={`flex items-center space-x-3 px-4 py-3 rounded-lg transition-all duration-300 ${
81
+ location.pathname === item.path
82
+ ? 'bg-white/20 text-white'
83
+ : 'text-white/70 hover:bg-white/10'
84
+ }`}
85
+ >
86
+ <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
87
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d={item.icon} />
88
+ </svg>
89
+ <span>{item.name}</span>
90
+ </Link>
91
+ ))}
92
+ </div>
93
+ </div>
94
+ )}
95
+ </>
96
+ );
97
+ }
98
+
99
+ export default Navbar;