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,384 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import api from '../services/api';
3
+
4
+ function ServiceRecord() {
5
+ const [services, setServices] = useState([]);
6
+ const [packages, setPackages] = useState([]);
7
+ const [cars, setCars] = useState([]);
8
+ const [formData, setFormData] = useState({
9
+ ServiceDate: new Date().toISOString().split('T')[0],
10
+ PlateNumber: '',
11
+ PackageNumber: ''
12
+ });
13
+ const [editingId, setEditingId] = useState(null);
14
+ const [message, setMessage] = useState(null);
15
+ const [loading, setLoading] = useState(false);
16
+ const [showBill, setShowBill] = useState(null);
17
+
18
+ useEffect(() => {
19
+ fetchData();
20
+ }, []);
21
+
22
+ const fetchData = async () => {
23
+ try {
24
+ const [servicesRes, packagesRes, carsRes] = await Promise.all([
25
+ api.get('/services'),
26
+ api.get('/packages'),
27
+ api.get('/cars')
28
+ ]);
29
+
30
+ setServices(servicesRes.data.data || []);
31
+ setPackages(packagesRes.data.data || []);
32
+ setCars(carsRes.data.data || []);
33
+ } catch (error) {
34
+ console.error('Error fetching data:', error);
35
+ }
36
+ };
37
+
38
+ const handleChange = (e) => {
39
+ setFormData({ ...formData, [e.target.name]: e.target.value });
40
+ };
41
+
42
+ const handleSubmit = async (e) => {
43
+ e.preventDefault();
44
+ setLoading(true);
45
+ setMessage(null);
46
+
47
+ try {
48
+ if (editingId) {
49
+ await api.put(`/services/${editingId}`, formData);
50
+ setMessage({ type: 'success', text: '✅ Service record updated successfully!' });
51
+ setEditingId(null);
52
+ } else {
53
+ const response = await api.post('/services', formData);
54
+ setMessage({ type: 'success', text: '✅ Service record created successfully!' });
55
+
56
+ // Auto-redirect to payment if needed
57
+ if (response.data.RecordNumber) {
58
+ setTimeout(() => {
59
+ if (window.confirm('Would you like to record payment for this service?')) {
60
+ window.location.href = `/payments?record=${response.data.RecordNumber}`;
61
+ }
62
+ }, 500);
63
+ }
64
+ }
65
+
66
+ setFormData({
67
+ ServiceDate: new Date().toISOString().split('T')[0],
68
+ PlateNumber: '',
69
+ PackageNumber: ''
70
+ });
71
+ fetchData();
72
+ setTimeout(() => setMessage(null), 3000);
73
+ } catch (err) {
74
+ setMessage({ type: 'error', text: err.response?.data?.error || 'Operation failed' });
75
+ } finally {
76
+ setLoading(false);
77
+ }
78
+ };
79
+
80
+ const handleEdit = (service) => {
81
+ setEditingId(service.RecordNumber);
82
+ setFormData({
83
+ ServiceDate: service.ServiceDate.split('T')[0],
84
+ PlateNumber: service.PlateNumber,
85
+ PackageNumber: service.PackageNumber
86
+ });
87
+ window.scrollTo({ top: 0, behavior: 'smooth' });
88
+ };
89
+
90
+ const handleDelete = async (id) => {
91
+ if (window.confirm('⚠️ Delete this service record? This will also delete associated payment.')) {
92
+ setLoading(true);
93
+ try {
94
+ await api.delete(`/services/${id}`);
95
+ setMessage({ type: 'success', text: '🗑️ Service record deleted successfully!' });
96
+ fetchData();
97
+ setTimeout(() => setMessage(null), 3000);
98
+ } catch (err) {
99
+ setMessage({ type: 'error', text: err.response?.data?.error || 'Delete failed' });
100
+ } finally {
101
+ setLoading(false);
102
+ }
103
+ }
104
+ };
105
+
106
+ const handleViewBill = async (recordNumber) => {
107
+ try {
108
+ const response = await api.get(`/reports/bill/${recordNumber}`);
109
+ setShowBill(response.data.data);
110
+ } catch (err) {
111
+ setMessage({ type: 'error', text: 'Bill not found. Please record payment first.' });
112
+ setTimeout(() => setMessage(null), 3000);
113
+ }
114
+ };
115
+
116
+ const closeBill = () => {
117
+ setShowBill(null);
118
+ };
119
+
120
+ const printBill = () => {
121
+ window.print();
122
+ };
123
+
124
+ return (
125
+ <div className="max-w-7xl mx-auto animate-fade-in space-y-6">
126
+ {/* Bill Modal */}
127
+ {showBill && (
128
+ <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm animate-fade-in" onClick={closeBill}>
129
+ <div className="glass-card max-w-2xl w-full p-8 animate-slide-up" onClick={(e) => e.stopPropagation()}>
130
+ <div className="text-center mb-6" id="bill-content">
131
+ <div className="w-16 h-16 mx-auto mb-4 bg-gradient-to-br from-blue-500 to-purple-600 rounded-2xl flex items-center justify-center">
132
+ <svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
133
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
134
+ </svg>
135
+ </div>
136
+ <h2 className="text-2xl font-bold text-white mb-2">SmartPark Car Wash</h2>
137
+ <p className="text-white/60 text-sm">{showBill.companyAddress}</p>
138
+ <p className="text-white/40 text-xs mt-2">Receipt: {showBill.receiptNumber}</p>
139
+ </div>
140
+
141
+ <div className="space-y-3 text-white border-t border-b border-white/20 py-4 my-4">
142
+ <div className="flex justify-between">
143
+ <span className="text-white/60">Plate Number:</span>
144
+ <span className="font-mono font-bold">{showBill.PlateNumber}</span>
145
+ </div>
146
+ <div className="flex justify-between">
147
+ <span className="text-white/60">Driver:</span>
148
+ <span>{showBill.DriverName}</span>
149
+ </div>
150
+ <div className="flex justify-between">
151
+ <span className="text-white/60">Phone:</span>
152
+ <span>{showBill.PhoneNumber}</span>
153
+ </div>
154
+ <div className="flex justify-between">
155
+ <span className="text-white/60">Service Date:</span>
156
+ <span>{new Date(showBill.ServiceDate).toLocaleDateString()}</span>
157
+ </div>
158
+ <div className="flex justify-between">
159
+ <span className="text-white/60">Package:</span>
160
+ <span className="font-semibold">{showBill.PackageName}</span>
161
+ </div>
162
+ <div className="flex justify-between">
163
+ <span className="text-white/60">Package Price:</span>
164
+ <span>{showBill.PackagePrice.toLocaleString()} RWF</span>
165
+ </div>
166
+ <div className="flex justify-between pt-2 border-t border-white/20">
167
+ <span className="text-white/60">Amount Paid:</span>
168
+ <span className="text-green-400 font-bold">{showBill.AmountPaid.toLocaleString()} RWF</span>
169
+ </div>
170
+ {showBill.ChangeAmount > 0 && (
171
+ <div className="flex justify-between">
172
+ <span className="text-white/60">Change:</span>
173
+ <span className="text-yellow-400">{showBill.ChangeAmount.toLocaleString()} RWF</span>
174
+ </div>
175
+ )}
176
+ <div className="flex justify-between">
177
+ <span className="text-white/60">Payment Date:</span>
178
+ <span>{new Date(showBill.PaymentDate).toLocaleDateString()}</span>
179
+ </div>
180
+ </div>
181
+
182
+ <div className="text-center text-white/40 text-sm mt-4">
183
+ Thank you for choosing SmartPark!
184
+ </div>
185
+
186
+ <div className="flex space-x-3 mt-6">
187
+ <button onClick={printBill} className="glass-button flex-1">
188
+ 🖨️ Print
189
+ </button>
190
+ <button onClick={closeBill} className="glass-button-secondary flex-1">
191
+ Close
192
+ </button>
193
+ </div>
194
+ </div>
195
+ </div>
196
+ )}
197
+
198
+ {/* Form Section */}
199
+ <div className="glass-card p-8">
200
+ <div className="flex items-center space-x-3 mb-6">
201
+ <div className="w-12 h-12 bg-gradient-to-br from-purple-500 to-pink-600 rounded-xl flex items-center justify-center">
202
+ <svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
203
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="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" />
204
+ </svg>
205
+ </div>
206
+ <div>
207
+ <h2 className="text-2xl font-bold text-white">
208
+ {editingId ? 'Edit Service Record' : 'New Service Record'}
209
+ </h2>
210
+ <p className="text-white/60 text-sm">
211
+ {editingId ? 'Update service information' : 'Record a car wash service'}
212
+ </p>
213
+ </div>
214
+ </div>
215
+
216
+ {message && (
217
+ <div className={`mb-6 p-4 rounded-lg backdrop-blur-sm ${
218
+ message.type === 'success'
219
+ ? 'bg-green-500/20 border border-green-500/50 text-green-200'
220
+ : 'bg-red-500/20 border border-red-500/50 text-red-200'
221
+ } animate-fade-in`}>
222
+ {message.text}
223
+ </div>
224
+ )}
225
+
226
+ <form onSubmit={handleSubmit} className="space-y-4">
227
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
228
+ <div>
229
+ <label className="text-white/80 text-sm block mb-2">Service Date *</label>
230
+ <input
231
+ type="date"
232
+ name="ServiceDate"
233
+ value={formData.ServiceDate}
234
+ onChange={handleChange}
235
+ className="glass-input w-full"
236
+ required
237
+ />
238
+ </div>
239
+
240
+ <div>
241
+ <label className="text-white/80 text-sm block mb-2">Plate Number *</label>
242
+ <select
243
+ name="PlateNumber"
244
+ value={formData.PlateNumber}
245
+ onChange={handleChange}
246
+ className="glass-input w-full"
247
+ required
248
+ >
249
+ <option value="">Select car</option>
250
+ {cars.map(car => (
251
+ <option key={car.PlateNumber} value={car.PlateNumber}>
252
+ {car.PlateNumber} - {car.DriverName}
253
+ </option>
254
+ ))}
255
+ </select>
256
+ </div>
257
+
258
+ <div>
259
+ <label className="text-white/80 text-sm block mb-2">Service Package *</label>
260
+ <select
261
+ name="PackageNumber"
262
+ value={formData.PackageNumber}
263
+ onChange={handleChange}
264
+ className="glass-input w-full"
265
+ required
266
+ >
267
+ <option value="">Select package</option>
268
+ {packages.map(pkg => (
269
+ <option key={pkg.PackageNumber} value={pkg.PackageNumber}>
270
+ {pkg.PackageName} - {pkg.PackagePrice.toLocaleString()} RWF
271
+ </option>
272
+ ))}
273
+ </select>
274
+ </div>
275
+ </div>
276
+
277
+ <div className="flex justify-end space-x-3 pt-4">
278
+ {editingId && (
279
+ <button
280
+ type="button"
281
+ onClick={() => {
282
+ setEditingId(null);
283
+ setFormData({
284
+ ServiceDate: new Date().toISOString().split('T')[0],
285
+ PlateNumber: '',
286
+ PackageNumber: ''
287
+ });
288
+ }}
289
+ className="glass-button-secondary"
290
+ >
291
+ Cancel
292
+ </button>
293
+ )}
294
+ <button
295
+ type="submit"
296
+ disabled={loading}
297
+ className="glass-button disabled:opacity-50"
298
+ >
299
+ {loading ? 'Processing...' : (editingId ? 'Update Record' : 'Create Record')}
300
+ </button>
301
+ </div>
302
+ </form>
303
+ </div>
304
+
305
+ {/* Services List */}
306
+ <div className="glass-card p-8">
307
+ <div className="flex justify-between items-center mb-6">
308
+ <h3 className="text-xl font-bold text-white">Service Records</h3>
309
+ <div className="text-white/60 text-sm">
310
+ Total: {services.length} records
311
+ </div>
312
+ </div>
313
+
314
+ <div className="overflow-x-auto">
315
+ <table className="glass-table w-full">
316
+ <thead>
317
+ <tr>
318
+ <th>Date</th>
319
+ <th>Plate</th>
320
+ <th>Driver</th>
321
+ <th>Package</th>
322
+ <th>Price</th>
323
+ <th>Payment</th>
324
+ <th>Actions</th>
325
+ </tr>
326
+ </thead>
327
+ <tbody>
328
+ {services.length === 0 ? (
329
+ <tr>
330
+ <td colSpan="7" className="text-center text-white/40 py-8">
331
+ No service records found
332
+ </td>
333
+ </tr>
334
+ ) : (
335
+ services.map((service) => (
336
+ <tr key={service.RecordNumber}>
337
+ <td>{new Date(service.ServiceDate).toLocaleDateString()}</td>
338
+ <td className="font-mono">{service.PlateNumber}</td>
339
+ <td>{service.DriverName}</td>
340
+ <td>{service.PackageName}</td>
341
+ <td>{service.PackagePrice?.toLocaleString()} RWF</td>
342
+ <td>
343
+ {service.AmountPaid ? (
344
+ <span className="text-green-400">✓ {service.AmountPaid.toLocaleString()} RWF</span>
345
+ ) : (
346
+ <span className="text-yellow-400">Pending</span>
347
+ )}
348
+ </td>
349
+ <td>
350
+ <div className="flex space-x-2">
351
+ <button
352
+ onClick={() => handleEdit(service)}
353
+ className="text-blue-300 hover:text-blue-200 text-sm"
354
+ >
355
+ Edit
356
+ </button>
357
+ <button
358
+ onClick={() => handleDelete(service.RecordNumber)}
359
+ className="text-red-300 hover:text-red-200 text-sm"
360
+ >
361
+ Delete
362
+ </button>
363
+ {service.AmountPaid && (
364
+ <button
365
+ onClick={() => handleViewBill(service.RecordNumber)}
366
+ className="text-green-300 hover:text-green-200 text-sm"
367
+ >
368
+ Bill
369
+ </button>
370
+ )}
371
+ </div>
372
+ </td>
373
+ </tr>
374
+ ))
375
+ )}
376
+ </tbody>
377
+ </table>
378
+ </div>
379
+ </div>
380
+ </div>
381
+ );
382
+ }
383
+
384
+ export default ServiceRecord;
@@ -0,0 +1,155 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer base {
6
+ body {
7
+ background: linear-gradient(135deg, #4c1d95, #1e3a8a, #312e81);
8
+ min-height: 100vh;
9
+ font-family: 'Inter', system-ui, -apple-system, sans-serif;
10
+ }
11
+ }
12
+
13
+ @layer components {
14
+ .glass-card {
15
+ background: rgba(255, 255, 255, 0.2);
16
+ backdrop-filter: blur(20px);
17
+ border-radius: 1rem;
18
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
19
+ border: 1px solid rgba(255, 255, 255, 0.3);
20
+ padding: 2rem;
21
+ }
22
+
23
+ .glass-input {
24
+ background: rgba(255, 255, 255, 0.2);
25
+ backdrop-filter: blur(12px);
26
+ border: 1px solid rgba(255, 255, 255, 0.3);
27
+ border-radius: 0.5rem;
28
+ padding: 0.75rem 1rem;
29
+ color: white;
30
+ width: 100%;
31
+ transition: all 0.3s ease;
32
+ }
33
+
34
+ .glass-input:focus {
35
+ outline: none;
36
+ border-color: #60a5fa;
37
+ box-shadow: 0 0 0 2px rgba(96, 165, 250, 0.3);
38
+ }
39
+
40
+ .glass-input::placeholder {
41
+ color: rgba(255, 255, 255, 0.5);
42
+ }
43
+
44
+ .glass-button {
45
+ background: linear-gradient(135deg, #3b82f6, #9333ea);
46
+ color: white;
47
+ font-weight: 600;
48
+ padding: 0.75rem 2rem;
49
+ border-radius: 0.5rem;
50
+ transition: all 0.3s ease;
51
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
52
+ width: 100%;
53
+ border: none;
54
+ cursor: pointer;
55
+ }
56
+
57
+ .glass-button:hover {
58
+ transform: scale(1.05);
59
+ background: linear-gradient(135deg, #2563eb, #7e22ce);
60
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
61
+ }
62
+
63
+ .glass-button:disabled {
64
+ opacity: 0.5;
65
+ cursor: not-allowed;
66
+ }
67
+
68
+ .glass-button-secondary {
69
+ background: rgba(255, 255, 255, 0.2);
70
+ backdrop-filter: blur(12px);
71
+ border: 1px solid rgba(255, 255, 255, 0.3);
72
+ color: white;
73
+ font-weight: 600;
74
+ padding: 0.5rem 1.5rem;
75
+ border-radius: 0.5rem;
76
+ transition: all 0.3s ease;
77
+ cursor: pointer;
78
+ }
79
+
80
+ .glass-button-secondary:hover {
81
+ background: rgba(255, 255, 255, 0.3);
82
+ transform: scale(1.05);
83
+ }
84
+
85
+ .glass-table {
86
+ width: 100%;
87
+ color: white;
88
+ border-collapse: collapse;
89
+ }
90
+
91
+ .glass-table th {
92
+ background: rgba(255, 255, 255, 0.2);
93
+ backdrop-filter: blur(12px);
94
+ padding: 0.75rem;
95
+ text-align: left;
96
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
97
+ }
98
+
99
+ .glass-table td {
100
+ padding: 0.75rem;
101
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
102
+ }
103
+
104
+ .glass-table tr:hover {
105
+ background: rgba(255, 255, 255, 0.1);
106
+ }
107
+ }
108
+
109
+ /* Custom animations */
110
+ @keyframes fadeIn {
111
+ from {
112
+ opacity: 0;
113
+ transform: translateY(-20px);
114
+ }
115
+ to {
116
+ opacity: 1;
117
+ transform: translateY(0);
118
+ }
119
+ }
120
+
121
+ @keyframes float {
122
+ 0%, 100% {
123
+ transform: translateY(0px);
124
+ }
125
+ 50% {
126
+ transform: translateY(-20px);
127
+ }
128
+ }
129
+
130
+ .animate-fade-in {
131
+ animation: fadeIn 0.5s ease-in-out;
132
+ }
133
+
134
+ .animate-float {
135
+ animation: float 6s ease-in-out infinite;
136
+ }
137
+
138
+ /* Custom scrollbar */
139
+ ::-webkit-scrollbar {
140
+ width: 8px;
141
+ }
142
+
143
+ ::-webkit-scrollbar-track {
144
+ background: rgba(255, 255, 255, 0.1);
145
+ border-radius: 10px;
146
+ }
147
+
148
+ ::-webkit-scrollbar-thumb {
149
+ background: rgba(255, 255, 255, 0.3);
150
+ border-radius: 10px;
151
+ }
152
+
153
+ ::-webkit-scrollbar-thumb:hover {
154
+ background: rgba(255, 255, 255, 0.5);
155
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom/client'
3
+ import App from './App.jsx'
4
+ import './index.css'
5
+
6
+ ReactDOM.createRoot(document.getElementById('root')).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>
10
+ )
@@ -0,0 +1,23 @@
1
+ import axios from 'axios';
2
+
3
+ const api = axios.create({
4
+ baseURL: 'http://localhost:5000/api',
5
+ withCredentials: true,
6
+ headers: {
7
+ 'Content-Type': 'application/json',
8
+ },
9
+ });
10
+
11
+ // Add response interceptor for error handling
12
+ api.interceptors.response.use(
13
+ (response) => response,
14
+ (error) => {
15
+ if (error.response?.status === 401) {
16
+ // Redirect to login if unauthorized
17
+ window.location.href = '/login';
18
+ }
19
+ return Promise.reject(error);
20
+ }
21
+ );
22
+
23
+ export default api;
@@ -0,0 +1,31 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ export default {
3
+ content: [
4
+ "./index.html",
5
+ "./src/**/*.{js,ts,jsx,tsx}",
6
+ ],
7
+ theme: {
8
+ extend: {
9
+ animation: {
10
+ 'fade-in': 'fadeIn 0.5s ease-in-out',
11
+ 'slide-up': 'slideUp 0.3s ease-out',
12
+ 'float': 'float 6s ease-in-out infinite',
13
+ },
14
+ keyframes: {
15
+ fadeIn: {
16
+ '0%': { opacity: '0', transform: 'translateY(-20px)' },
17
+ '100%': { opacity: '1', transform: 'translateY(0)' },
18
+ },
19
+ slideUp: {
20
+ '0%': { transform: 'translateY(10px)', opacity: '0' },
21
+ '100%': { transform: 'translateY(0)', opacity: '1' },
22
+ },
23
+ float: {
24
+ '0%, 100%': { transform: 'translateY(0px)' },
25
+ '50%': { transform: 'translateY(-20px)' },
26
+ },
27
+ },
28
+ },
29
+ },
30
+ plugins: [],
31
+ }
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ server: {
7
+ port: 5173,
8
+ proxy: {
9
+ '/api': {
10
+ target: 'http://localhost:5000',
11
+ changeOrigin: true,
12
+ }
13
+ }
14
+ }
15
+ })
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "krispdev-business",
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "description": "A full-stack business management application with Express backend and React frontend",
6
+ "main": "Dev-Business/backend-project/server.js",
7
+ "scripts": {
8
+ "start": "node Dev-Business/backend-project/server.js",
9
+ "dev": "nodemon Dev-Business/backend-project/server.js",
10
+ "install:all": "cd Dev-Business/backend-project && npm install && cd ../frontend-project && npm install",
11
+ "build:frontend": "cd Dev-Business/frontend-project && npm run build",
12
+ "test": "echo \"Error: no test specified\" && exit 1"
13
+ },
14
+ "keywords": ["business-management", "express", "react", "full-stack", "mysql"],
15
+ "author": "isezeranochrispin",
16
+ "license": "ISC"
17
+ }