create-myexam-app 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/index.js +65 -0
- package/package.json +22 -0
- package/projects/parking managementt system/Backend/.env +3 -0
- package/projects/parking managementt system/Backend/controllers/CarController.js +43 -0
- package/projects/parking managementt system/Backend/controllers/ParkingRecordController.js +42 -0
- package/projects/parking managementt system/Backend/controllers/ParkingSlotController.js +42 -0
- package/projects/parking managementt system/Backend/controllers/PaymentController.js +42 -0
- package/projects/parking managementt system/Backend/controllers/UserController.js +71 -0
- package/projects/parking managementt system/Backend/db/connectDB.js +11 -0
- package/projects/parking managementt system/Backend/models/CarModels.js +9 -0
- package/projects/parking managementt system/Backend/models/ParkingRecordModelss.js +10 -0
- package/projects/parking managementt system/Backend/models/ParkingSlotModels.js +7 -0
- package/projects/parking managementt system/Backend/models/PaymentModels.js +9 -0
- package/projects/parking managementt system/Backend/models/UserModels.js +9 -0
- package/projects/parking managementt system/Backend/package-lock.json +1526 -0
- package/projects/parking managementt system/Backend/package.json +23 -0
- package/projects/parking managementt system/Backend/routes/CarRoutes.js +11 -0
- package/projects/parking managementt system/Backend/routes/ParkingRecordRoutes.js +9 -0
- package/projects/parking managementt system/Backend/routes/ParkingSlotRoutes.js +11 -0
- package/projects/parking managementt system/Backend/routes/PaymentRoutes.js +10 -0
- package/projects/parking managementt system/Backend/routes/UserRoutes.js +17 -0
- package/projects/parking managementt system/Backend/server.js +27 -0
- package/projects/parking managementt system/frontend/README.md +16 -0
- package/projects/parking managementt system/frontend/eslint.config.js +21 -0
- package/projects/parking managementt system/frontend/index.html +13 -0
- package/projects/parking managementt system/frontend/package-lock.json +3033 -0
- package/projects/parking managementt system/frontend/package.json +31 -0
- package/projects/parking managementt system/frontend/public/favicon.svg +1 -0
- package/projects/parking managementt system/frontend/public/icons.svg +24 -0
- package/projects/parking managementt system/frontend/src/App.css +184 -0
- package/projects/parking managementt system/frontend/src/App.jsx +34 -0
- package/projects/parking managementt system/frontend/src/assets/hero.png +0 -0
- package/projects/parking managementt system/frontend/src/assets/react.svg +1 -0
- package/projects/parking managementt system/frontend/src/assets/vite.svg +1 -0
- package/projects/parking managementt system/frontend/src/components/Navbar.jsx +27 -0
- package/projects/parking managementt system/frontend/src/index.css +1 -0
- package/projects/parking managementt system/frontend/src/main.jsx +10 -0
- package/projects/parking managementt system/frontend/src/pages/Cars.jsx +116 -0
- package/projects/parking managementt system/frontend/src/pages/Dashboard.jsx +45 -0
- package/projects/parking managementt system/frontend/src/pages/Login.jsx +63 -0
- package/projects/parking managementt system/frontend/src/pages/ParkingRecord.jsx +168 -0
- package/projects/parking managementt system/frontend/src/pages/ParkingSlot.jsx +102 -0
- package/projects/parking managementt system/frontend/src/pages/Payment.jsx +101 -0
- package/projects/parking managementt system/frontend/src/pages/Register.jsx +65 -0
- package/projects/parking managementt system/frontend/src/pages/Report.jsx +98 -0
- package/projects/parking managementt system/frontend/vite.config.js +10 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { useState,useEffect } from "react";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
|
|
4
|
+
function ParkingRecord(){
|
|
5
|
+
|
|
6
|
+
const [car, setCar] = useState('')
|
|
7
|
+
const [slot, setSlot] = useState('')
|
|
8
|
+
const [entryTime, setEntryTime] = useState('')
|
|
9
|
+
const [exitTime, setExitTime] = useState('')
|
|
10
|
+
const [duration, setDuration] = useState('')
|
|
11
|
+
const [record, setRecord] = useState([])
|
|
12
|
+
const [editId, setEditId] = useState(null)
|
|
13
|
+
|
|
14
|
+
const getAllRecords = async () => {
|
|
15
|
+
try{
|
|
16
|
+
const res = await axios.get('http://localhost:5000/api/parkingRecords')
|
|
17
|
+
setRecord(res.data)
|
|
18
|
+
}catch(error){
|
|
19
|
+
console.log(error)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
getAllRecords()
|
|
24
|
+
}, [])
|
|
25
|
+
|
|
26
|
+
const handleSubmit = async (e) => {
|
|
27
|
+
e.preventDefault()
|
|
28
|
+
try{
|
|
29
|
+
if (editId) {
|
|
30
|
+
await axios.put(`http://localhost:5000/api/parkingRecords/${editId}`, {
|
|
31
|
+
car,
|
|
32
|
+
slot,
|
|
33
|
+
entryTime,
|
|
34
|
+
exitTime,
|
|
35
|
+
duration,
|
|
36
|
+
|
|
37
|
+
})
|
|
38
|
+
alert ('record succussefully updated')
|
|
39
|
+
setEditId(null)
|
|
40
|
+
} else {
|
|
41
|
+
await axios.post('http://localhost:5000/api/parkingRecords', {
|
|
42
|
+
car,
|
|
43
|
+
slot,
|
|
44
|
+
entryTime,
|
|
45
|
+
exitTime,
|
|
46
|
+
duration,
|
|
47
|
+
})
|
|
48
|
+
alert ('Record added succussfully')
|
|
49
|
+
}
|
|
50
|
+
setCar('')
|
|
51
|
+
setDuration('')
|
|
52
|
+
setEntryTime('')
|
|
53
|
+
setExitTime('')
|
|
54
|
+
setSlot('')
|
|
55
|
+
getAllRecords()
|
|
56
|
+
|
|
57
|
+
}catch(error){
|
|
58
|
+
alert('failed to record')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
const handleEdit = (record) => {
|
|
63
|
+
setEditId(record._id)
|
|
64
|
+
setCar(record.car)
|
|
65
|
+
setSlot(record.slot)
|
|
66
|
+
setEntryTime(record.entryTime)
|
|
67
|
+
setDuration(record.duration)
|
|
68
|
+
setExitTime(record.exitTime)
|
|
69
|
+
}
|
|
70
|
+
const handleDelete = async (id) => {
|
|
71
|
+
try{
|
|
72
|
+
await axios.delete(`http://localhost:5000/api/parkingRecords/${id}`)
|
|
73
|
+
alert('deleted successfully')
|
|
74
|
+
getAllRecords()
|
|
75
|
+
}catch(error){
|
|
76
|
+
alert('failed to delete')
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return (
|
|
80
|
+
<div className="bg-amber-50 p-6 min-h-screen">
|
|
81
|
+
<h2 className="text-2xl font-bold text-gray-800 mb-6">ParkingRecord</h2>
|
|
82
|
+
<form onSubmit={handleSubmit} className="bg-white rounded-x1 border border-gray-200
|
|
83
|
+
p-6 mb-8 flex flex-col gap-4 max-w-md m-7">
|
|
84
|
+
|
|
85
|
+
<label className="text-sm font-medium text-gray-600"> Car Id</label>
|
|
86
|
+
<input type="text" value={car}
|
|
87
|
+
onChange={(e) => setCar(e.target.value)} required
|
|
88
|
+
className="border border-gray-300 rounded-lg px-3 py-2 yext-sm focus:outline-none
|
|
89
|
+
focus:border-orange-400"
|
|
90
|
+
/>
|
|
91
|
+
<label className="text-sm font-medium text-gray-500">Slot ID</label>
|
|
92
|
+
<input
|
|
93
|
+
type="text"
|
|
94
|
+
value={slot}
|
|
95
|
+
onChange={(e) => setSlot(e.target.value)}
|
|
96
|
+
required
|
|
97
|
+
className="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none
|
|
98
|
+
focus:border-orange-400 "
|
|
99
|
+
/>
|
|
100
|
+
<label className="text-sm font-medium text-gray-300">Entry Time</label>
|
|
101
|
+
<input
|
|
102
|
+
type="datetime-local"
|
|
103
|
+
value={entryTime}
|
|
104
|
+
onChange={(e) => setEntryTime(e.target.value)}
|
|
105
|
+
required
|
|
106
|
+
className="border border-gray-500 rounded-lg px-4 py-2 text-sm focus:outline-none
|
|
107
|
+
focus:border-orange-800"
|
|
108
|
+
/>
|
|
109
|
+
<label className="text-sm font-medium text-gray-400">Exit Time</label>
|
|
110
|
+
<input
|
|
111
|
+
type="datetime-local"
|
|
112
|
+
value={exitTime}
|
|
113
|
+
onChange={(e) => setExitTime(e.target.value)}
|
|
114
|
+
className="border border-gray-500 text-sm rounded-lg px-4 py-2 focus:outline-none
|
|
115
|
+
focus:border-orange-800"
|
|
116
|
+
/>
|
|
117
|
+
<label className="text-sm font-medium border-gray-400">Duration (hours)</label>
|
|
118
|
+
<input
|
|
119
|
+
type="number"
|
|
120
|
+
value={duration}
|
|
121
|
+
onChange={(e) => setDuration(e.target.value)}
|
|
122
|
+
className="border border-gray-400 rounded-lg text-sm px-4 py-2
|
|
123
|
+
focus:outline-none focus:border-orange-800"
|
|
124
|
+
/>
|
|
125
|
+
<button type="submit" className="bg-orange-300 text-white font-medium
|
|
126
|
+
py-2 rounded-lg hover:bg-orange-800 transition-colors">
|
|
127
|
+
{editId? 'update Record': 'Add record'}
|
|
128
|
+
</button>
|
|
129
|
+
</form>
|
|
130
|
+
|
|
131
|
+
<h3 className="font-medium text-sm text-gray-500 mb-2">Record list</h3>
|
|
132
|
+
<div className="shadow-sm bg-white rounded-xl border border-gray-400 overflow-hidden">
|
|
133
|
+
<table className="w-full text-sm">
|
|
134
|
+
<thead className="bg-gray-400 text-gray-400 text-left">
|
|
135
|
+
<tr >
|
|
136
|
+
<th className="px-4 py-2 font-medium">Car</th>
|
|
137
|
+
<th className="px-4 py-2 font-medium">Slot</th>
|
|
138
|
+
<th className="px-4 py-2 font-medium">Entry Time</th>
|
|
139
|
+
<th className="px-4 py-2 font-medium">Exit Time</th>
|
|
140
|
+
<th className="px-4 py-2 font-medium">Duration</th>
|
|
141
|
+
<th className="px-4 py-2 font-medium">Actions</th>
|
|
142
|
+
</tr>
|
|
143
|
+
</thead>
|
|
144
|
+
<tbody className="divide-y divide-gray-100">
|
|
145
|
+
{record.map((record) => (
|
|
146
|
+
<tr key={record._id} className="hover:bg-gray-50">
|
|
147
|
+
<td className="px-4 py-2 text-gray-800">{record.car}</td>
|
|
148
|
+
<td className="px-4 py-2 text-gray-800">{record.slot}</td>
|
|
149
|
+
<td className="px-4 py-2 text-gray-800">{record.entryTime}</td>
|
|
150
|
+
<td className="px-4 py-2 text-gray-800">{record.exitTime}</td>
|
|
151
|
+
<td className="px-4 py-2 text-gray-800">{record.duration}</td>
|
|
152
|
+
<td className="px-4 py-2">
|
|
153
|
+
<button onClick={() => handleEdit(record)} className="px-3 py-2 bg-orange-400 text-orange-700
|
|
154
|
+
rounded-lg hover:bg-orange-600 transition-colors"
|
|
155
|
+
>Edit</button>
|
|
156
|
+
<button onClick={() => handleDelete(record._id)} className="px-3 py-2 bg-orange-400 text-orange-500
|
|
157
|
+
rounded-lg hover:bg-orange-500 transition-colors">Delete</button>
|
|
158
|
+
</td>
|
|
159
|
+
</tr>
|
|
160
|
+
))}
|
|
161
|
+
</tbody>
|
|
162
|
+
</table>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export default ParkingRecord
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
|
|
4
|
+
function ParkingSlot() {
|
|
5
|
+
const [slotNumber, setSlotNumber] = useState('');
|
|
6
|
+
// Fix 1: Set default to 'available' so it matches the dropdown
|
|
7
|
+
const [slotStatus, setSlotStatus] = useState('available');
|
|
8
|
+
const [slots, setSlots] = useState([]);
|
|
9
|
+
|
|
10
|
+
const getAllSlots = async () => {
|
|
11
|
+
try {
|
|
12
|
+
const res = await axios.get('http://localhost:5000/api/parkingSlots');
|
|
13
|
+
// Fix 2: Better "Shield" logic. Checks for array OR nested array.
|
|
14
|
+
const data = Array.isArray(res.data) ? res.data : (res.data.slots || []);
|
|
15
|
+
setSlots(data);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
console.log("Fetch error:", error);
|
|
18
|
+
setSlots([]);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
getAllSlots();
|
|
24
|
+
}, []);
|
|
25
|
+
|
|
26
|
+
const handleSubmit = async (e) => {
|
|
27
|
+
e.preventDefault();
|
|
28
|
+
try {
|
|
29
|
+
await axios.post('http://localhost:5000/api/parkingSlots', {
|
|
30
|
+
slotNumber,
|
|
31
|
+
slotStatus,
|
|
32
|
+
});
|
|
33
|
+
alert('Slot added successfully');
|
|
34
|
+
setSlotNumber('');
|
|
35
|
+
setSlotStatus('available'); // Reset to default
|
|
36
|
+
getAllSlots();
|
|
37
|
+
} catch (error) {
|
|
38
|
+
alert('Failed to add');
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div className="min-h-screen bg-gray-100 p-6">
|
|
44
|
+
<h2 className="text-2xl font-bold text-gray-600 mb-6">Parking Slot</h2>
|
|
45
|
+
|
|
46
|
+
<form onSubmit={handleSubmit} className="bg-white rounded-xl border border-gray-200 p-6 mb-8 flex flex-col gap-4 max-w-md">
|
|
47
|
+
<div className="flex flex-col gap-2">
|
|
48
|
+
<label className="text-sm font-medium text-gray-600">Slot Number</label>
|
|
49
|
+
<input
|
|
50
|
+
type="text"
|
|
51
|
+
value={slotNumber}
|
|
52
|
+
onChange={(e) => setSlotNumber(e.target.value)}
|
|
53
|
+
className="border border-gray-300 rounded-lg px-4 py-2 text-sm focus:outline-none focus:border-orange-400"
|
|
54
|
+
required
|
|
55
|
+
/>
|
|
56
|
+
|
|
57
|
+
<label className="text-sm font-medium text-gray-600">Slot Status</label>
|
|
58
|
+
<select
|
|
59
|
+
value={slotStatus}
|
|
60
|
+
onChange={(e) => setSlotStatus(e.target.value)}
|
|
61
|
+
className="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-orange-400"
|
|
62
|
+
>
|
|
63
|
+
<option value="available">Available</option>
|
|
64
|
+
<option value="occupied">Occupied</option>
|
|
65
|
+
</select>
|
|
66
|
+
</div>
|
|
67
|
+
<button
|
|
68
|
+
type="submit"
|
|
69
|
+
className="bg-orange-500 text-white font-medium py-2 rounded-lg hover:bg-orange-600 transition-colors"
|
|
70
|
+
>
|
|
71
|
+
Add Slot
|
|
72
|
+
</button>
|
|
73
|
+
</form>
|
|
74
|
+
|
|
75
|
+
<h2 className="text-lg font-semibold text-gray-700 mb-3">Slots List</h2>
|
|
76
|
+
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden shadow-sm">
|
|
77
|
+
<table className="w-full text-sm">
|
|
78
|
+
<thead className="bg-gray-50 text-gray-600 text-left">
|
|
79
|
+
<tr>
|
|
80
|
+
<th className="px-4 py-3 font-medium">Slot Number</th>
|
|
81
|
+
<th className="px-4 py-3 font-medium">Status</th>
|
|
82
|
+
</tr>
|
|
83
|
+
</thead>
|
|
84
|
+
<tbody className="divide-y divide-gray-100">
|
|
85
|
+
{slots.map((slot) => (
|
|
86
|
+
<tr key={slot._id} className="hover:bg-gray-50 transition-colors">
|
|
87
|
+
<td className="px-4 py-3 text-gray-800">{slot.slotNumber}</td>
|
|
88
|
+
<td className="px-4 py-3">
|
|
89
|
+
<span className={`px-2 py-1 rounded-full text-xs ${slot.slotStatus === 'available' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>
|
|
90
|
+
{slot.slotStatus}
|
|
91
|
+
</span>
|
|
92
|
+
</td>
|
|
93
|
+
</tr>
|
|
94
|
+
))}
|
|
95
|
+
</tbody>
|
|
96
|
+
</table>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export default ParkingSlot;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { useState,useEffect } from "react";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
|
|
4
|
+
function Payment(){
|
|
5
|
+
const [record, setRecord] = useState('')
|
|
6
|
+
const [amountPaid, setAmountPaid] = useState('')
|
|
7
|
+
const [paymentDate, setPaymentDate] = useState('')
|
|
8
|
+
const [payments, setPayments] = useState([])
|
|
9
|
+
|
|
10
|
+
const getAllPayments = async () => {
|
|
11
|
+
try{
|
|
12
|
+
const res = await axios.get('http://localhost:5000/api/payments')
|
|
13
|
+
setPayments(res.data)
|
|
14
|
+
}catch(error){
|
|
15
|
+
console.log('error')
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
getAllPayments()
|
|
21
|
+
}, [])
|
|
22
|
+
|
|
23
|
+
const handleSubmit = async (e) => {
|
|
24
|
+
e.preventDefault()
|
|
25
|
+
try{
|
|
26
|
+
await axios.post('http://localhost:5000/api/payments', {
|
|
27
|
+
record,
|
|
28
|
+
amountPaid,
|
|
29
|
+
paymentDate,
|
|
30
|
+
})
|
|
31
|
+
alert ('Payment added successfully')
|
|
32
|
+
setRecord('')
|
|
33
|
+
setPaymentDate('')
|
|
34
|
+
setAmountPaid('')
|
|
35
|
+
getAllPayments()
|
|
36
|
+
}catch(error){
|
|
37
|
+
alert ('failed to add payment')
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return(
|
|
42
|
+
<div className="min-h-screen bg-gray-400">
|
|
43
|
+
<h2 className="text-2xl font-bold text-gray-800 mb-6"> Paymnet</h2>
|
|
44
|
+
<form onSubmit={handleSubmit} className="bg-white rounded-xl border border-gray-200 p-6
|
|
45
|
+
mb-6 flex flex-col gap-4 max-w-md">
|
|
46
|
+
<div className="flex flex-col gap-1">
|
|
47
|
+
<label className="text-sm font-medium text-gray-600">Record</label>
|
|
48
|
+
<input type="text" value={record}
|
|
49
|
+
onChange={(e) => setRecord(e.target.value)}
|
|
50
|
+
required
|
|
51
|
+
className="border border-gray-400 rounded-lg px-4 py-2 text-sm focus:outline-none
|
|
52
|
+
focus:border-orange-500"
|
|
53
|
+
/>
|
|
54
|
+
|
|
55
|
+
<label className="text-sm font-medium text-gray-600">Amount paid </label>
|
|
56
|
+
<input type="number" value={amountPaid}
|
|
57
|
+
onChange={(e) => setAmountPaid(e.target.value)}
|
|
58
|
+
required
|
|
59
|
+
className="border border-gray-400 rounded-lg px-4 py-2 text-sm
|
|
60
|
+
focus:outline-none focus:border-orange-400"
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
<label className="text-sm font-medium text-gray-600">Payment Date</label>
|
|
64
|
+
<input type="date" value={paymentDate}
|
|
65
|
+
onChange={(e) => setPaymentDate(e.target.value)}
|
|
66
|
+
className="border border-gray-600 rounded-lg px-4 py-2 text-sm
|
|
67
|
+
focus:outline-none focus:border-orange-500"
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
<button type="submit" className="bg-orange-500 text-white font-medium
|
|
71
|
+
py-2 rounded-lg hover:bg-orange-400 transition-colors">Add Payment</button>
|
|
72
|
+
</form>
|
|
73
|
+
<h2 className="text-lg font-semibold text-gray-700 mb-3">Payment List</h2>
|
|
74
|
+
<div className=" bg-white rounded-xl border border-gray-400 overflow-hidden">
|
|
75
|
+
<table className="w-full text-sm">
|
|
76
|
+
<thead className="bg-gray-50 text-gray-400 text-left ">
|
|
77
|
+
<tr>
|
|
78
|
+
<th className="px-4 py-3 font-medium">Record</th>
|
|
79
|
+
<th className="px-4 py-3 font-medium">Amount Paid</th>
|
|
80
|
+
<th className="px-4 py-3 font-medium">Payment Date</th>
|
|
81
|
+
</tr>
|
|
82
|
+
</thead>
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
<tbody className="divide-y divide-gray-100">
|
|
86
|
+
{payments.map((payment) =>(
|
|
87
|
+
<tr key={payment._id} className="hover:bg-gray-50">
|
|
88
|
+
<td className="px-4 py-2 text-gray-800">{payment.record}</td>
|
|
89
|
+
<td className="px-4 py-2 text-gray-800">{payment.amountPaid}</td>
|
|
90
|
+
<td className="px-4 py-2 text-gray-800">{payment.paymentDate}</td>
|
|
91
|
+
</tr>
|
|
92
|
+
))}
|
|
93
|
+
|
|
94
|
+
</tbody>
|
|
95
|
+
</table>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default Payment
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { useNavigate, Link } from 'react-router-dom'
|
|
3
|
+
import axios from 'axios'
|
|
4
|
+
|
|
5
|
+
function Register(){
|
|
6
|
+
const navigate = useNavigate()
|
|
7
|
+
const [email, setEmail] = useState('')
|
|
8
|
+
const [userName, setUserName] = useState('')
|
|
9
|
+
const [password, setPassword] = useState('')
|
|
10
|
+
const [error, setError] = useState('')
|
|
11
|
+
|
|
12
|
+
const handleRegister = async (e) => {
|
|
13
|
+
e.preventDefault()
|
|
14
|
+
try{
|
|
15
|
+
await axios.post('http://localhost:5000/api/users/register',{
|
|
16
|
+
email,
|
|
17
|
+
userName,
|
|
18
|
+
password
|
|
19
|
+
});
|
|
20
|
+
navigate('/')
|
|
21
|
+
}catch(error){
|
|
22
|
+
setError('Registration failled. try again')
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return(
|
|
26
|
+
<div className=' min-h-screen bg-gray-100 p-6 '>
|
|
27
|
+
|
|
28
|
+
<h2 className='text-2xl font-bold text-gray-800 mb-1'>Register</h2>
|
|
29
|
+
{error && <p>{error}</p>}
|
|
30
|
+
<form onSubmit={handleRegister}
|
|
31
|
+
className='bg-white rounded-2xl border border-gray-500 p-6 mb-8
|
|
32
|
+
flex flex-col gap-2 w-150 '
|
|
33
|
+
>
|
|
34
|
+
<label className='text-sm font-medium text-gray-600'>Email</label>
|
|
35
|
+
<input type="email" placeholder="enter ur email" onChange={(e) => setEmail(e.target.value)}
|
|
36
|
+
required
|
|
37
|
+
className='border border-black rounded-1g px-20 py-1 text-sm
|
|
38
|
+
focus:outline-none focus:border-orange-400'
|
|
39
|
+
/>
|
|
40
|
+
<label className='text-sm font-medium text-gray-600'>Name</label>
|
|
41
|
+
<input type="text" placeholder="enter ur name" onChange={(e) => setUserName(e.target.value)}
|
|
42
|
+
required
|
|
43
|
+
className='text-sm border border-black rounded-1g px-20 py-1
|
|
44
|
+
focus:outline-none focus:border-orange-400'
|
|
45
|
+
/>
|
|
46
|
+
<label className='text-sm font-medium text-gray-600'>Password</label>
|
|
47
|
+
<input type="password" placeholder="enter ur password" onChange={(e) => setPassword(e.target.value)}
|
|
48
|
+
required
|
|
49
|
+
className='border border-black rounded-1g px-20 py-1 text-sm
|
|
50
|
+
focus:outline-none focus:border-orange-400'
|
|
51
|
+
/>
|
|
52
|
+
|
|
53
|
+
<button type='submit' className='bg-orange-500 text-white font-medium
|
|
54
|
+
py-2 rounded-1g hover:bg-orange-600 transition-colors'>Register</button>
|
|
55
|
+
|
|
56
|
+
</form>
|
|
57
|
+
<p className='text-gray-5 text-sm mt-4 w-50 '>Arleady have an account? <Link to="/"
|
|
58
|
+
className='text-orange-500 font-medium hover:underline'
|
|
59
|
+
>Login</Link> </p>
|
|
60
|
+
</div>
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export default Register
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useState,useEffect } from "react";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
|
|
4
|
+
function Report (){
|
|
5
|
+
const [records, setRecords] = useState([])
|
|
6
|
+
const [payments, setPayments] = useState([])
|
|
7
|
+
|
|
8
|
+
const getAllRecords = async () => {
|
|
9
|
+
try{
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const res = await axios.get('http://localhost:5000/api/parkingRecords')
|
|
13
|
+
setRecords(res.data)
|
|
14
|
+
}catch(error){
|
|
15
|
+
console.log('error')
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const getAllPayments = async () => {
|
|
20
|
+
try{
|
|
21
|
+
const res = await axios.get('http://localhost:5000/api/payments')
|
|
22
|
+
setPayments(res.data)
|
|
23
|
+
}catch(error){
|
|
24
|
+
console.log('erro')
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
getAllPayments()
|
|
29
|
+
getAllRecords()
|
|
30
|
+
}, [])
|
|
31
|
+
|
|
32
|
+
const calculateFee = (duration) => {
|
|
33
|
+
if (!duration || duration <= 0) return 500
|
|
34
|
+
return Math.ceil(duration) * 500
|
|
35
|
+
}
|
|
36
|
+
const today = new Date(). toDateString()
|
|
37
|
+
|
|
38
|
+
const todayPayment = payments.filter((payment) => {
|
|
39
|
+
return new Date(payment.paymentDate). toDateString() === today
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
return(
|
|
43
|
+
<div className="min-h-screen bg-gray-400">
|
|
44
|
+
<h2 className="text-2xl font-bold text-gray-800 mb-6">Payment</h2>
|
|
45
|
+
<h3 className="text-lg font-semibold text-black-500 mb-3">Bill</h3>
|
|
46
|
+
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden mb-8">
|
|
47
|
+
<table className="w-full text-sm flex">
|
|
48
|
+
<thead className="bg-gray-50 text-gray-500 text-left">
|
|
49
|
+
<tr>
|
|
50
|
+
<th className="px-4 py-2">Car</th>
|
|
51
|
+
<th className="px-4 py-2">Entry Time</th>
|
|
52
|
+
<th className="px-4 py-2">Exit Time</th>
|
|
53
|
+
<th className="px-4 py-2">Duration (hrs)</th>
|
|
54
|
+
<th className="px-4 py-2">Amount (Rwf)</th>
|
|
55
|
+
</tr>
|
|
56
|
+
</thead>
|
|
57
|
+
<tbody>
|
|
58
|
+
{records.map((record) => (
|
|
59
|
+
<tr key={record._id} className="border-t">
|
|
60
|
+
<td className="px-4 py-2">{record.car}</td>
|
|
61
|
+
<td className="px-4 py-2">{record.entryTime}</td>
|
|
62
|
+
<td className="px-4 py-2">{record.exitTime}</td>
|
|
63
|
+
<td className="px-4 py-2">{record.duration}</td>
|
|
64
|
+
<td className="px-4 py-2 text-orange-600 font-semibold" >{calculateFee(record.duration)}</td>
|
|
65
|
+
</tr>
|
|
66
|
+
))}
|
|
67
|
+
</tbody>
|
|
68
|
+
</table>
|
|
69
|
+
</div>
|
|
70
|
+
<h3 className="text-lg font-semibold text-black-100 mb-2">Daily Parking Payment Record</h3>
|
|
71
|
+
<p className="mb-2 text-gray-700">Date:{today}</p>
|
|
72
|
+
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
<table className="w-full text-sm">
|
|
76
|
+
<thead className="bg-gray-50 text-gray-500 text-left">
|
|
77
|
+
<tr>
|
|
78
|
+
<td className="px-4 py-2">Record ID</td>
|
|
79
|
+
<td className="px-4 py-2">Amount Paid</td>
|
|
80
|
+
<td className="px-4 py-2">Payment Date</td>
|
|
81
|
+
</tr>
|
|
82
|
+
</thead>
|
|
83
|
+
<tbody>
|
|
84
|
+
{todayPayment.map((payment) => (
|
|
85
|
+
<tr key={payment._id} className="border-t">
|
|
86
|
+
<td className="px-3 py-2">{payment.record}</td>
|
|
87
|
+
<td className="px-3 py-2 text-orange-300 font-semibold">{payment.amountPaid}</td>
|
|
88
|
+
<td className="px-3 py-2">{new Date(payment.paymentDate).toLocaleDateString()}</td>
|
|
89
|
+
</tr>
|
|
90
|
+
))}
|
|
91
|
+
</tbody>
|
|
92
|
+
</table>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export default Report
|