Skip to content

Commit bcdf152

Browse files
committed
idk
1 parent 9ced416 commit bcdf152

8 files changed

Lines changed: 69 additions & 15 deletions

File tree

src/App.tsx

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useCallback, useMemo, useEffect } from "react"
1+
import { useState, useCallback, useMemo, useEffect, useRef } from "react"
22
import { MapContainer } from "./components/Map/MapContainer"
33
import { useMapData } from "./hooks/useMapData"
44
import { useCameras } from "./hooks/useCameras"
@@ -21,6 +21,8 @@ function App() {
2121
useState<FreeSpotFilterValue>("all")
2222
const [selectedCameraId, setSelectedCameraId] = useState<number | null>(null)
2323
const [filtersVisible, setFiltersVisible] = useState(false)
24+
const filtersRef = useRef<HTMLDivElement>(null)
25+
const toggleButtonRef = useRef<HTMLButtonElement>(null)
2426

2527
const { zones, loading, error, total, refetch } = useMapData({
2628
autoFetch: true,
@@ -113,6 +115,28 @@ function App() {
113115
return () => clearInterval(interval)
114116
}, [refetch])
115117

118+
useEffect(() => {
119+
const handleClickOutside = (event: MouseEvent) => {
120+
if (
121+
filtersVisible &&
122+
filtersRef.current &&
123+
toggleButtonRef.current &&
124+
!filtersRef.current.contains(event.target as Node) &&
125+
!toggleButtonRef.current.contains(event.target as Node)
126+
) {
127+
setFiltersVisible(false)
128+
}
129+
}
130+
131+
if (filtersVisible) {
132+
document.addEventListener("mousedown", handleClickOutside)
133+
}
134+
135+
return () => {
136+
document.removeEventListener("mousedown", handleClickOutside)
137+
}
138+
}, [filtersVisible])
139+
116140
return (
117141
<div className="min-h-screen bg-gray-50">
118142
<main className="mx-auto">
@@ -126,26 +150,45 @@ function App() {
126150
/>
127151

128152
<button
153+
ref={toggleButtonRef}
129154
onClick={() => setFiltersVisible(!filtersVisible)}
130155
className="absolute top-20 left-2 sm:top-20 sm:left-4 z-[1001] bg-white/90 backdrop-blur-sm rounded-lg shadow-lg border border-gray-200 p-2 hover:bg-white transition-colors"
131156
aria-label="Toggle filters"
132157
>
133-
<svg
134-
xmlns="http://www.w3.org/2000/svg"
135-
className="h-5 w-5 text-gray-700"
136-
viewBox="0 0 20 20"
137-
fill="currentColor"
138-
>
139-
<path
140-
fillRule="evenodd"
141-
d="M3 3a1 1 0 011-1h12a1 1 0 011 1v3a1 1 0 01-.293.707L12 11.414V15a1 1 0 01-.293.707l-2 2A1 1 0 018 17v-5.586L3.293 6.707A1 1 0 013 6V3z"
142-
clipRule="evenodd"
143-
/>
144-
</svg>
158+
{filtersVisible ? (
159+
<svg
160+
xmlns="http://www.w3.org/2000/svg"
161+
className="h-5 w-5 text-gray-700"
162+
viewBox="0 0 20 20"
163+
fill="currentColor"
164+
>
165+
<path
166+
fillRule="evenodd"
167+
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
168+
clipRule="evenodd"
169+
/>
170+
</svg>
171+
) : (
172+
<svg
173+
xmlns="http://www.w3.org/2000/svg"
174+
className="h-5 w-5 text-gray-700"
175+
viewBox="0 0 20 20"
176+
fill="currentColor"
177+
>
178+
<path
179+
fillRule="evenodd"
180+
d="M3 3a1 1 0 011-1h12a1 1 0 011 1v3a1 1 0 01-.293.707L12 11.414V15a1 1 0 01-.293.707l-2 2A1 1 0 018 17v-5.586L3.293 6.707A1 1 0 013 6V3z"
181+
clipRule="evenodd"
182+
/>
183+
</svg>
184+
)}
145185
</button>
146186

147187
{filtersVisible && (
148-
<div className="absolute top-2 left-16 right-2 sm:top-4 sm:left-16 sm:right-auto sm:max-w-md z-[1000] bg-white/90 backdrop-blur-sm rounded-lg shadow-lg border border-gray-200">
188+
<div
189+
ref={filtersRef}
190+
className="absolute top-2 left-16 right-2 sm:top-4 sm:left-16 sm:right-auto sm:max-w-md z-[1000] bg-white/90 backdrop-blur-sm rounded-lg shadow-lg border border-gray-200"
191+
>
149192
<div className="p-3 sm:p-4">
150193
<div className="flex flex-col gap-3">
151194
<div>

src/components/Filters/CameraSelector.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,4 @@ export const CameraSelector: React.FC<CameraSelectorProps> = ({
4848
</div>
4949
)
5050
}
51+

src/components/Filters/FreeSpotsFilter.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ export const FreeSpotsFilter: React.FC<FreeSpotsFilterProps> = ({
3434
</div>
3535
)
3636
}
37+

src/components/Filters/ZoneSelector.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,4 @@ export const ZoneSelector: React.FC<ZoneSelectorProps> = ({
4848
</div>
4949
)
5050
}
51+

src/components/Filters/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export { FreeSpotsFilter } from "./FreeSpotsFilter"
22
export type { FreeSpotFilterValue } from "./FreeSpotsFilter"
33
export { ZoneSelector } from "./ZoneSelector"
44
export { CameraSelector } from "./CameraSelector"
5+

src/components/Map/MapPoints.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,12 @@ const isValidPoint = (point: Point): boolean => {
9797
}
9898

9999
const validateZone = (zone: Zone): boolean => {
100-
if (!zone.points || !Array.isArray(zone.points) || zone.points.length !== 4) {
100+
if (
101+
!zone.points ||
102+
!Array.isArray(zone.points) ||
103+
zone.points.length !== 4 ||
104+
zone.occupied == null
105+
) {
101106
return false
102107
}
103108

src/hooks/useCameras.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,4 @@ export const useCameras = (
6060
refetch,
6161
}
6262
}
63+

src/services/camerasApi.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ export const camerasApi = {
1212
return response.data
1313
},
1414
}
15+

0 commit comments

Comments
 (0)