src/pages/CitySelect.jsx   A
last analyzed

Complexity

Total Complexity 15
Complexity/F 3.75

Size

Lines of Code 586
Function Count 4

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 483
dl 0
loc 586
rs 10
c 0
b 0
f 0
wmc 15
mnd 11
bc 11
fnc 4
bpm 2.75
cpm 3.75
noi 0

1 Function

Rating   Name   Duplication   Size   Complexity  
A CitySelect.jsx ➔ fetchData 0 5 2
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