1
|
|
|
import React, { useEffect, useState } from "react"; |
2
|
|
|
import { useLocation, Link, useNavigate } from "react-router-dom"; |
3
|
|
|
import { Map, ScooterSelectList } from "../components"; |
4
|
|
|
import { cityScooterOverview, cityZoneOverview } from "../data/data"; |
5
|
|
|
import { TbZoomQuestion } from "react-icons/tb"; |
6
|
|
|
|
7
|
|
|
import Tippy from "@tippyjs/react"; |
8
|
|
|
import "tippy.js/dist/tippy.css"; |
9
|
|
|
|
10
|
|
|
import cities from "../models/cities"; |
11
|
|
|
import scooter from "../models/scooters"; |
12
|
|
|
import "../Map.css"; |
13
|
|
|
import utils from "../utils/utils"; |
14
|
|
|
import getCoordinates from "../models/nominatim"; |
15
|
|
|
|
16
|
|
|
const zoom = 14; |
17
|
|
|
|
18
|
|
|
const CitySelect = () => { |
19
|
|
|
const [selected, setSelected] = useState(); |
20
|
|
|
const [cityCoords, setCityCoords] = useState(); |
21
|
|
|
const [scooters, setScooters] = useState(); |
22
|
|
|
const [isSelected, setIsSelected] = useState([]); |
23
|
|
|
const [selectedOverView, setSelectedOverView] = useState(); |
24
|
|
|
const [deleteProcess, setDeleteProcess] = useState(false); |
25
|
|
|
const [deletePhrase, setDeletePhrase] = useState(""); |
26
|
|
|
const [taxes, setTaxes] = useState({}); |
27
|
|
|
const [zonesCount, setZonesCount] = useState({}); |
28
|
|
|
const [deleteStatus, setDeleteStatus] = useState( |
29
|
|
|
"Enter city name to confirm delete" |
30
|
|
|
); |
31
|
|
|
const location = useLocation(); |
32
|
|
|
const navigate = useNavigate(); |
33
|
|
|
const { id } = location.state; |
34
|
|
|
const [isSaved, setIsSaved] = useState(true); |
35
|
|
|
useEffect(() => { |
36
|
|
|
async function fetchData() { |
37
|
|
|
const res = await cities.getCityById(id); |
38
|
|
|
setSelected(res.city); |
39
|
|
|
setTaxes(res.city.taxRates); |
40
|
|
|
setZonesCount(utils.zoneCount(res.city.zones)); |
41
|
|
|
} |
42
|
|
|
fetchData(); |
43
|
|
|
}, []); |
44
|
|
|
|
45
|
|
|
useEffect(() => { |
46
|
|
|
async function fetchData() { |
47
|
|
|
const res = await cities.getCitiesOverview(); |
48
|
|
|
const data = res.arrayOverview; |
49
|
|
|
const select = data.find((x) => { |
50
|
|
|
return x._id === id; |
51
|
|
|
}); |
52
|
|
|
setSelectedOverView(select); |
53
|
|
|
} |
54
|
|
|
fetchData(); |
55
|
|
|
}, []); |
56
|
|
|
|
57
|
|
|
useEffect(() => { |
58
|
|
|
async function fetchData() { |
59
|
|
|
const res = await scooter.getScootersByCity(selected.name); |
60
|
|
|
setScooters(res.cityScooters); |
61
|
|
|
} |
62
|
|
|
if (selected) { |
63
|
|
|
fetchData(); |
64
|
|
|
} |
65
|
|
|
}, [selected]); |
66
|
|
|
|
67
|
|
|
useEffect(() => { |
68
|
|
|
async function fetchData() { |
69
|
|
|
const result = await getCoordinates(selected.name); |
70
|
|
|
const coordArr = [result.latitude, result.longitude]; |
71
|
|
|
setCityCoords(coordArr); |
72
|
|
|
} |
73
|
|
|
if (selected) { |
74
|
|
|
fetchData(); |
75
|
|
|
} |
76
|
|
|
}, [selected]); |
77
|
|
|
|
78
|
|
|
const handleSaveEdit = () => { |
79
|
|
|
const editedCity = { |
80
|
|
|
_id: selected._id, |
81
|
|
|
name: selected.name, |
82
|
|
|
taxRates: taxes, |
83
|
|
|
}; |
84
|
|
|
cities.editCity(editedCity); |
85
|
|
|
}; |
86
|
|
|
|
87
|
|
|
const handleChange = (e) => { |
88
|
|
|
let value = e.target.value; |
89
|
|
|
setTaxes({ ...taxes, [e.target.name]: value }); |
90
|
|
|
}; |
91
|
|
|
|
92
|
|
|
const GetCityScooterDetails = () => { |
93
|
|
|
return cityScooterOverview.map((item, index) => { |
94
|
|
|
return ( |
95
|
|
|
<div |
96
|
|
|
key={index} |
97
|
|
|
className="flex flex-row w-80 justify-between border-b" |
98
|
|
|
> |
99
|
|
|
<p>{item.label}</p> |
100
|
|
|
<p>{selectedOverView[item.data]}</p> |
101
|
|
|
</div> |
102
|
|
|
); |
103
|
|
|
}); |
104
|
|
|
}; |
105
|
|
|
|
106
|
|
|
const GetCityZoneDetails = () => { |
107
|
|
|
return cityZoneOverview.map((item, index) => { |
108
|
|
|
return ( |
109
|
|
|
<div |
110
|
|
|
key={index} |
111
|
|
|
className="flex flex-row w-80 justify-between border-b" |
112
|
|
|
> |
113
|
|
|
<p>{item.label}</p> |
114
|
|
|
<p>{zonesCount[item.data]}</p> |
115
|
|
|
</div> |
116
|
|
|
); |
117
|
|
|
}); |
118
|
|
|
}; |
119
|
|
|
|
120
|
|
|
//For deleting scooter |
121
|
|
|
const handleDelete = () => { |
122
|
|
|
let updatedScooterList = scooters; |
123
|
|
|
|
124
|
|
|
for (const scooterId of isSelected) { |
125
|
|
|
scooter.deleteScooter(scooterId); |
126
|
|
|
updatedScooterList = updatedScooterList.filter((sctr) => { |
127
|
|
|
return scooterId !== sctr._id; |
128
|
|
|
}); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
setScooters(updatedScooterList); |
132
|
|
|
}; |
133
|
|
|
|
134
|
|
|
const handleCityDelete = async () => { |
135
|
|
|
if (deletePhrase.includes(selected.name.trim())) { |
136
|
|
|
await cities.deleteCity(id); |
137
|
|
|
setDeletePhrase(""); |
138
|
|
|
navigate("/cities"); |
139
|
|
|
} else { |
140
|
|
|
setDeleteStatus("ERROR: Please type the correct city title to delete"); |
141
|
|
|
} |
142
|
|
|
}; |
143
|
|
|
|
144
|
|
|
if (!selected) { |
145
|
|
|
return <div>loading...</div>; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
return ( |
149
|
|
|
<div className="w-full p-4 flex flex-col"> |
150
|
|
|
<div className="bg-white p-7 w-full shadow-md mb-4 rounded-xl"> |
151
|
|
|
<h1 className="text-3xl mr-2">{selected.name}</h1> |
152
|
|
|
</div> |
153
|
|
|
|
154
|
|
|
<div className="flex flex-row max-xl:flex-col"> |
155
|
|
|
<div className="p-4 mr-4 w-2/3 rounded-xl shadow-md bg-white max-xl:w-full max-xl:mr-0"> |
156
|
|
|
{cityCoords ? ( |
157
|
|
|
<div className="h-125 overflow-hidden"> |
158
|
|
|
<Map |
159
|
|
|
center={cityCoords} |
160
|
|
|
zoom={zoom} |
161
|
|
|
scooters={scooters} |
162
|
|
|
cities={[selected]} |
163
|
|
|
/> |
164
|
|
|
</div> |
165
|
|
|
) : ( |
166
|
|
|
<div>Loading map data</div> |
167
|
|
|
)} |
168
|
|
|
<div> |
169
|
|
|
<h1 className="font-semibold text-xl mt-3 mb-1">Overview</h1> |
170
|
|
|
<div className="flex flex-row"> |
171
|
|
|
{selectedOverView ? ( |
172
|
|
|
<div className="mr-7">{GetCityScooterDetails()}</div> |
173
|
|
|
) : ( |
174
|
|
|
<div>No Data...</div> |
175
|
|
|
)} |
176
|
|
|
|
177
|
|
|
<div>{GetCityZoneDetails()}</div> |
178
|
|
|
</div> |
179
|
|
|
</div> |
180
|
|
|
</div> |
181
|
|
|
|
182
|
|
|
<div |
183
|
|
|
className="p-7 ml-4 rounded-xl w-1/3 bg-white |
184
|
|
|
shadow-md flex flex-col justify-between h-full |
185
|
|
|
max-xl:mt-4 max-xl:ml-0 max-xl:w-full " |
186
|
|
|
> |
187
|
|
|
<h1 className="text-center font-semibold text-2xl ">Settings</h1> |
188
|
|
|
<div> |
189
|
|
|
<h1 className="text-xl font-semibold">Rates</h1> |
190
|
|
|
</div> |
191
|
|
|
<div className="flex flex-col max-xl:flex-row max-xl:justify-between"> |
192
|
|
|
<div> |
193
|
|
|
<div className="flex flex-row justify-between py-3"> |
194
|
|
|
<div> |
195
|
|
|
<label className="flex flex-row"> |
196
|
|
|
<span>Fixed Rate</span> |
197
|
|
|
<Tippy |
198
|
|
|
placement="top" |
199
|
|
|
arrow={true} |
200
|
|
|
className="w-44" |
201
|
|
|
content={ |
202
|
|
|
<span> |
203
|
|
|
<h1 className="text-xl">Fixed Rate</h1> |
204
|
|
|
<p> |
205
|
|
|
Fixed rates are is the initial fee for starting a |
206
|
|
|
ride. |
207
|
|
|
</p> |
208
|
|
|
</span> |
209
|
|
|
} |
210
|
|
|
> |
211
|
|
|
<span className="text-lg text-slate-800"> |
212
|
|
|
<TbZoomQuestion /> |
213
|
|
|
</span> |
214
|
|
|
</Tippy> |
215
|
|
|
</label> |
216
|
|
|
|
217
|
|
|
<input |
218
|
|
|
onChange={(e) => { |
219
|
|
|
setIsSaved(false); |
220
|
|
|
handleChange(e); |
221
|
|
|
}} |
222
|
|
|
name="fixedRate" |
223
|
|
|
type="number" |
224
|
|
|
placeholder="Fixed rate" |
225
|
|
|
value={taxes.fixedRate} |
226
|
|
|
className="border-b border-gray-800 mr-2" |
227
|
|
|
/> |
228
|
|
|
</div> |
229
|
|
|
<div> |
230
|
|
|
<label className="flex flex-row"> |
231
|
|
|
<span>Rate per minute</span> |
232
|
|
|
<Tippy |
233
|
|
|
placement="top" |
234
|
|
|
arrow={true} |
235
|
|
|
className="w-44" |
236
|
|
|
content={ |
237
|
|
|
<span> |
238
|
|
|
<h1 className="text-xl">Rate per minute</h1> |
239
|
|
|
<p> |
240
|
|
|
Rate per minutes is what will be charged each minute |
241
|
|
|
the ride is active |
242
|
|
|
</p> |
243
|
|
|
</span> |
244
|
|
|
} |
245
|
|
|
> |
246
|
|
|
<span className="text-lg text-slate-800"> |
247
|
|
|
<TbZoomQuestion /> |
248
|
|
|
</span> |
249
|
|
|
</Tippy> |
250
|
|
|
</label> |
251
|
|
|
<input |
252
|
|
|
onChange={(e) => { |
253
|
|
|
setIsSaved(false); |
254
|
|
|
handleChange(e); |
255
|
|
|
}} |
256
|
|
|
type="number" |
257
|
|
|
name="timeRate" |
258
|
|
|
placeholder="Rate per minute" |
259
|
|
|
value={taxes.timeRate} |
260
|
|
|
className="border-b border-gray-800 mr-2" |
261
|
|
|
/> |
262
|
|
|
</div> |
263
|
|
|
</div> |
264
|
|
|
|
265
|
|
|
<div className="flex flex-row justify-between py-3"> |
266
|
|
|
<div> |
267
|
|
|
<label className="flex flex-row"> |
268
|
|
|
<span>Parking Rate</span> |
269
|
|
|
<Tippy |
270
|
|
|
placement="top" |
271
|
|
|
arrow={true} |
272
|
|
|
className="w-44" |
273
|
|
|
content={ |
274
|
|
|
<span> |
275
|
|
|
<h1 className="text-xl">Parking Rate</h1> |
276
|
|
|
<p> |
277
|
|
|
Parking rate is the the fee for stopping your ride |
278
|
|
|
within a default parking zone |
279
|
|
|
</p> |
280
|
|
|
</span> |
281
|
|
|
} |
282
|
|
|
> |
283
|
|
|
<span className="text-lg text-slate-800"> |
284
|
|
|
<TbZoomQuestion /> |
285
|
|
|
</span> |
286
|
|
|
</Tippy> |
287
|
|
|
</label> |
288
|
|
|
<input |
289
|
|
|
onChange={(e) => { |
290
|
|
|
setIsSaved(false); |
291
|
|
|
handleChange(e); |
292
|
|
|
}} |
293
|
|
|
type="number" |
294
|
|
|
name="parkingZoneRate" |
295
|
|
|
placeholder="Parking Rate" |
296
|
|
|
value={taxes.parkingZoneRate} |
297
|
|
|
className="border-b border-gray-800 mr-2" |
298
|
|
|
/> |
299
|
|
|
</div> |
300
|
|
|
<div> |
301
|
|
|
<label className="flex flex-row"> |
302
|
|
|
<span>Discount Rate</span> |
303
|
|
|
<Tippy |
304
|
|
|
placement="top" |
305
|
|
|
arrow={true} |
306
|
|
|
className="w-44" |
307
|
|
|
content={ |
308
|
|
|
<span> |
309
|
|
|
<h1 className="text-xl">Discount Rate</h1> |
310
|
|
|
<p> |
311
|
|
|
Discount rate is a discount granted to the user for |
312
|
|
|
stopping their ride within a "bonus zone" |
313
|
|
|
</p> |
314
|
|
|
</span> |
315
|
|
|
} |
316
|
|
|
> |
317
|
|
|
<span className="text-lg text-slate-800"> |
318
|
|
|
<TbZoomQuestion /> |
319
|
|
|
</span> |
320
|
|
|
</Tippy> |
321
|
|
|
</label> |
322
|
|
|
<input |
323
|
|
|
onChange={(e) => { |
324
|
|
|
setIsSaved(false); |
325
|
|
|
handleChange(e); |
326
|
|
|
}} |
327
|
|
|
type="number" |
328
|
|
|
name="bonusParkingZoneRate" |
329
|
|
|
placeholder="Discount Parking Rate" |
330
|
|
|
value={taxes.bonusParkingZoneRate} |
331
|
|
|
className="border-b border-gray-800 mr-2" |
332
|
|
|
/> |
333
|
|
|
</div> |
334
|
|
|
</div> |
335
|
|
|
<div className="flex flex-row justify-between py-3"> |
336
|
|
|
<div> |
337
|
|
|
<label className="flex flex-row"> |
338
|
|
|
<span>Invalid Parking Fee</span> |
339
|
|
|
<Tippy |
340
|
|
|
placement="top" |
341
|
|
|
arrow={true} |
342
|
|
|
className="w-44" |
343
|
|
|
content={ |
344
|
|
|
<span> |
345
|
|
|
<h1 className="text-xl">Invalid Parking Fee</h1> |
346
|
|
|
<p> |
347
|
|
|
Invalid parking fee is a fee the user has to pay if |
348
|
|
|
they decide to stop their ride outiside of a parking |
349
|
|
|
zone |
350
|
|
|
</p> |
351
|
|
|
</span> |
352
|
|
|
} |
353
|
|
|
> |
354
|
|
|
<span className="text-lg text-slate-800"> |
355
|
|
|
<TbZoomQuestion /> |
356
|
|
|
</span> |
357
|
|
|
</Tippy> |
358
|
|
|
</label> |
359
|
|
|
<input |
360
|
|
|
onChange={(e) => { |
361
|
|
|
setIsSaved(false); |
362
|
|
|
handleChange(e); |
363
|
|
|
}} |
364
|
|
|
type="number" |
365
|
|
|
name="noParkingZoneRate" |
366
|
|
|
placeholder="Invalid parking fee" |
367
|
|
|
value={taxes.noParkingZoneRate} |
368
|
|
|
className="border-b border-gray-800 mr-2" |
369
|
|
|
/> |
370
|
|
|
</div> |
371
|
|
|
<div> |
372
|
|
|
<div> |
373
|
|
|
<label className="flex flex-row"> |
374
|
|
|
<span>Invalid To Valid Parking Fee</span> |
375
|
|
|
<Tippy |
376
|
|
|
placement="top" |
377
|
|
|
arrow={true} |
378
|
|
|
className="w-44" |
379
|
|
|
content={ |
380
|
|
|
<span> |
381
|
|
|
<h1 className="text-xl"> |
382
|
|
|
Invalid To Valid Parking Fee |
383
|
|
|
</h1> |
384
|
|
|
<p> |
385
|
|
|
Invalid to valid parking fee is a discount granted |
386
|
|
|
to the user if they start a ride with a scooter |
387
|
|
|
parked outside a parking zone and afterwards park |
388
|
|
|
it within a valid parking zone |
389
|
|
|
</p> |
390
|
|
|
</span> |
391
|
|
|
} |
392
|
|
|
> |
393
|
|
|
<span className="text-lg text-slate-800"> |
394
|
|
|
<TbZoomQuestion /> |
395
|
|
|
</span> |
396
|
|
|
</Tippy> |
397
|
|
|
</label> |
398
|
|
|
<input |
399
|
|
|
onChange={(e) => { |
400
|
|
|
setIsSaved(false); |
401
|
|
|
handleChange(e); |
402
|
|
|
}} |
403
|
|
|
type="number" |
404
|
|
|
name="noParkingToValidParking" |
405
|
|
|
placeholder="Invalid to valid parking" |
406
|
|
|
value={taxes.noParkingToValidParking} |
407
|
|
|
className="border-b border-gray-800 mr-2" |
408
|
|
|
/> |
409
|
|
|
</div> |
410
|
|
|
</div> |
411
|
|
|
</div> |
412
|
|
|
<div className="flex flex-row justify-start py-3"> |
413
|
|
|
<div> |
414
|
|
|
<div className="flex flex-col"> |
415
|
|
|
<label className="flex flex-row"> |
416
|
|
|
<span>Charging Zone</span> |
417
|
|
|
<Tippy |
418
|
|
|
placement="top" |
419
|
|
|
arrow={true} |
420
|
|
|
className="w-44" |
421
|
|
|
content={ |
422
|
|
|
<span> |
423
|
|
|
<h1 className="text-xl">Charging Zone</h1> |
424
|
|
|
<p> |
425
|
|
|
Charging Zone is a discount granted to the user if |
426
|
|
|
the stop their ride within a charging zone |
427
|
|
|
</p> |
428
|
|
|
</span> |
429
|
|
|
} |
430
|
|
|
> |
431
|
|
|
<span className="text-lg text-slate-800"> |
432
|
|
|
<TbZoomQuestion /> |
433
|
|
|
</span> |
434
|
|
|
</Tippy> |
435
|
|
|
</label> |
436
|
|
|
<input |
437
|
|
|
onChange={(e) => { |
438
|
|
|
setIsSaved(false); |
439
|
|
|
handleChange(e); |
440
|
|
|
}} |
441
|
|
|
type="number" |
442
|
|
|
name="chargingZoneRate" |
443
|
|
|
placeholder="Invalid to valid parking" |
444
|
|
|
value={taxes.chargingZoneRate} |
445
|
|
|
className="border-b border-gray-800 mr-2" |
446
|
|
|
/> |
447
|
|
|
</div> |
448
|
|
|
</div> |
449
|
|
|
</div> |
450
|
|
|
|
451
|
|
|
<div className="mb-12 text-center"> |
452
|
|
|
{!isSaved ? ( |
453
|
|
|
<p className="text-red-700 py-4">Unsaved Changes!</p> |
454
|
|
|
) : null} |
455
|
|
|
<button |
456
|
|
|
onClick={() => { |
457
|
|
|
setIsSaved(true); |
458
|
|
|
handleSaveEdit(); |
459
|
|
|
}} |
460
|
|
|
className="py-2 px-7 transition-colors mt-6 w-48 |
461
|
|
|
bg-sidebarHover hover:bg-sidebarBlue text-white rounded-xl" |
462
|
|
|
> |
463
|
|
|
Save |
464
|
|
|
</button> |
465
|
|
|
</div> |
466
|
|
|
</div> |
467
|
|
|
|
468
|
|
|
<div> |
469
|
|
|
<h1 className="text-xl font-semibold text-center">Tools</h1> |
470
|
|
|
<div className="flex flex-row max-xl:flex-col justify-center xl:first-line:mb-20"> |
471
|
|
|
{cityCoords ? ( |
472
|
|
|
<Link |
473
|
|
|
to={"/cities/select/zones"} |
474
|
|
|
state={{ id: selected._id, coords: cityCoords }} |
475
|
|
|
className="py-2 transition-colors mt-6 xl:mr-3 w-48 text-center |
476
|
|
|
bg-sidebarHover hover:bg-sidebarBlue text-white rounded-xl" |
477
|
|
|
> |
478
|
|
|
Manage Zones |
479
|
|
|
</Link> |
480
|
|
|
) : ( |
481
|
|
|
<button |
482
|
|
|
className="py-2 transition-colors mt-6 xl:mr-3 w-48 text-center |
483
|
|
|
bg-sidebarHover hover:bg-sidebarBlue text-white rounded-xl" |
484
|
|
|
> |
485
|
|
|
Manage Zones |
486
|
|
|
</button> |
487
|
|
|
)} |
488
|
|
|
<button |
489
|
|
|
onClick={() => { |
490
|
|
|
navigate("/scooters"); |
491
|
|
|
}} |
492
|
|
|
className="py-2 transition-colors mt-6 xl:mr-3 w-48 |
493
|
|
|
bg-sidebarHover hover:bg-sidebarBlue text-white rounded-xl" |
494
|
|
|
> |
495
|
|
|
Register Scooter |
496
|
|
|
</button> |
497
|
|
|
</div> |
498
|
|
|
<div className="xl:mt-6 w-full"> |
499
|
|
|
{deleteProcess ? ( |
500
|
|
|
<div className="flex flex-col w-full"> |
501
|
|
|
<label className="text-xl text-center py-3"> |
502
|
|
|
{deleteStatus} |
503
|
|
|
</label> |
504
|
|
|
<input |
505
|
|
|
onChange={(e) => setDeletePhrase(e.target.value)} |
506
|
|
|
value={deletePhrase} |
507
|
|
|
className="bg-slate-100 p-4 reounded rounded-xl |
508
|
|
|
text-garay-400 border border-slate-400" |
509
|
|
|
placeholder={`Type: ${selected.name}`} |
510
|
|
|
/> |
511
|
|
|
<div className="w-full text-center"> |
512
|
|
|
<button |
513
|
|
|
onClick={() => { |
514
|
|
|
handleCityDelete(); |
515
|
|
|
}} |
516
|
|
|
className="py-2 transition-colors mt-6 ml-3 w-36 |
517
|
|
|
bg-sidebarHover hover:bg-sidebarBlue text-white rounded-xl" |
518
|
|
|
> |
519
|
|
|
Confirm |
520
|
|
|
</button> |
521
|
|
|
<button |
522
|
|
|
onClick={() => { |
523
|
|
|
setDeleteProcess(false); |
524
|
|
|
setDeletePhrase(""); |
525
|
|
|
}} |
526
|
|
|
className="py-2 transition-colors mt-6 ml-3 w-36 |
527
|
|
|
bg-sidebarHover hover:bg-sidebarBlue text-white rounded-xl" |
528
|
|
|
> |
529
|
|
|
Cancel |
530
|
|
|
</button> |
531
|
|
|
</div> |
532
|
|
|
</div> |
533
|
|
|
) : ( |
534
|
|
|
<div |
535
|
|
|
onClick={() => { |
536
|
|
|
setDeleteProcess(true); |
537
|
|
|
}} |
538
|
|
|
className="flex flex-row justify-center" |
539
|
|
|
> |
540
|
|
|
<button |
541
|
|
|
className="py-2 transition-colors mt-6 xl:ml-3 w-48 |
542
|
|
|
bg-red-500 hover:bg-red-600 text-white rounded-xl" |
543
|
|
|
> |
544
|
|
|
Delete City |
545
|
|
|
</button> |
546
|
|
|
</div> |
547
|
|
|
)} |
548
|
|
|
</div> |
549
|
|
|
</div> |
550
|
|
|
</div> |
551
|
|
|
</div> |
552
|
|
|
</div> |
553
|
|
|
<div |
554
|
|
|
className="h-full p-7 my-4 rounded-xl w-full bg-white |
555
|
|
|
shadow-md flex flex-col justify-between" |
556
|
|
|
> |
557
|
|
|
<div className="flex flex-row justify-between"> |
558
|
|
|
<h1 className="font-semibold text-2xl"> |
559
|
|
|
Scooters in {selected.name} |
560
|
|
|
</h1> |
561
|
|
|
<button |
562
|
|
|
onClick={handleDelete} |
563
|
|
|
className="py-2 px-3 transition-colors bg-sidebarHover |
564
|
|
|
hover:bg-sidebarBlue text-white rounded-full" |
565
|
|
|
> |
566
|
|
|
Remove Selected |
567
|
|
|
</button> |
568
|
|
|
</div> |
569
|
|
|
{scooters ? ( |
570
|
|
|
<div className="mt-7"> |
571
|
|
|
<ScooterSelectList |
572
|
|
|
scooters={scooters} |
573
|
|
|
isSelected={isSelected} |
574
|
|
|
setIsSelected={setIsSelected} |
575
|
|
|
/> |
576
|
|
|
</div> |
577
|
|
|
) : ( |
578
|
|
|
<div className="text-xl">No Scooters</div> |
579
|
|
|
)} |
580
|
|
|
</div> |
581
|
|
|
</div> |
582
|
|
|
); |
583
|
|
|
}; |
584
|
|
|
|
585
|
|
|
export default CitySelect; |
586
|
|
|
|