Skip to content

Commit 0cc0e63

Browse files
feat: make dashboard panel component collapsible (#21)
1 parent 6404513 commit 0cc0e63

File tree

1 file changed

+159
-156
lines changed

1 file changed

+159
-156
lines changed

components/map-dashboard.tsx

Lines changed: 159 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -168,173 +168,176 @@ export default function MapDashboard({ defaultSelected }: Props) {
168168
>
169169
<ResizablePanel
170170
defaultSize={25}
171-
minSize={10}
172-
className="h-full p-4 flex flex-col items-center gap-4"
171+
minSize={16}
172+
collapsible
173+
className="h-full"
173174
style={{
174175
overflowY: 'auto',
175176
}}
176177
>
177-
<ComboboxArea
178-
area="provinces"
179-
selected={selected?.province}
180-
query={provinceQuery}
181-
onSelect={(province) => {
182-
setSelected((current) => ({ ...current, province }))
183-
if (province.code === query?.regencies?.parentCode) return
184-
setQuery((current) => ({
185-
...current,
186-
regencies: { parentCode: province.code, limit: MAX_PAGE_SIZE },
187-
}))
188-
}}
189-
/>
190-
191-
<ComboboxArea
192-
area="regencies"
193-
selected={selected?.regency}
194-
query={query?.regencies}
195-
onSelect={(regency) => {
196-
setSelected((current) => ({ ...current, regency }))
197-
if (regency.code === query?.districts?.parentCode) return
198-
setQuery((current) => ({
199-
...current,
200-
islands: { parentCode: regency.code, limit: MAX_PAGE_SIZE },
201-
districts: { parentCode: regency.code, limit: MAX_PAGE_SIZE },
202-
}))
203-
}}
204-
inputProps={{
205-
onValueChange: debounce((name) => {
206-
if (!selected?.province) {
207-
setQuery((current) => ({
208-
...current,
209-
regencies: { ...current?.regencies, name },
210-
}))
211-
}
212-
}, 500),
213-
}}
214-
disabled={isLoading?.provinces}
215-
/>
216-
217-
<ComboboxArea
218-
area="districts"
219-
selected={selected?.district}
220-
query={query?.districts}
221-
onSelect={(district) => {
222-
setSelected((current) => ({ ...current, district }))
223-
if (district.code === query?.villages?.parentCode) return
224-
setQuery((current) => ({
225-
...current,
226-
villages: { parentCode: district.code, limit: MAX_PAGE_SIZE },
227-
}))
228-
}}
229-
inputProps={{
230-
onValueChange: debounce((name) => {
231-
if (!selected?.regency) {
232-
setQuery((current) => ({
233-
...current,
234-
districts: { ...current?.districts, name },
235-
}))
236-
}
237-
}, 500),
238-
}}
239-
disabled={isLoading?.regencies}
240-
/>
241-
242-
<ComboboxArea
243-
area="villages"
244-
selected={selected?.village}
245-
query={query?.villages}
246-
onSelect={(village) => {
247-
setSelected((current) => ({ ...current, village }))
248-
}}
249-
inputProps={{
250-
onValueChange: debounce((name) => {
251-
if (!selected?.district) {
252-
setQuery((current) => ({
253-
...current,
254-
villages: { ...current?.villages, name },
255-
}))
256-
}
257-
}, 500),
258-
}}
259-
disabled={isLoading?.districts}
260-
/>
261-
262-
{/* Islands info */}
263-
<div className="w-full p-2 border rounded flex gap-2 justify-center items-center">
264-
{query?.islands?.parentCode ? (
265-
<>
266-
{isLoading?.islands && (
267-
<ReloadIcon className="h-4 w-4 animate-spin" />
268-
)}
269-
<span className="text-sm w-fit">
270-
{islands.length} islands found
271-
</span>
272-
</>
273-
) : (
274-
<span className="text-sm w-fit text-gray-500">
275-
Select a regency to see islands
276-
</span>
277-
)}
278-
</div>
279-
280-
{/* Boundary settings */}
281-
<div className="w-full p-2 flex flex-col gap-2">
282-
<h3 className="font-semibold">Show Boundary</h3>
283-
{Object.entries(featureConfig).map(([area, config]) => (
284-
<div key={area} className="flex items-center space-x-2">
285-
<Switch
286-
id={`${area}Boundary`}
287-
disabled={
288-
!selected?.[
289-
singletonArea[area as FeatureAreas] as keyof Selected
290-
]
178+
<div className="p-4 flex flex-col items-center gap-4">
179+
<ComboboxArea
180+
area="provinces"
181+
selected={selected?.province}
182+
query={provinceQuery}
183+
onSelect={(province) => {
184+
setSelected((current) => ({ ...current, province }))
185+
if (province.code === query?.regencies?.parentCode) return
186+
setQuery((current) => ({
187+
...current,
188+
regencies: { parentCode: province.code, limit: MAX_PAGE_SIZE },
189+
}))
190+
}}
191+
/>
192+
193+
<ComboboxArea
194+
area="regencies"
195+
selected={selected?.regency}
196+
query={query?.regencies}
197+
onSelect={(regency) => {
198+
setSelected((current) => ({ ...current, regency }))
199+
if (regency.code === query?.districts?.parentCode) return
200+
setQuery((current) => ({
201+
...current,
202+
islands: { parentCode: regency.code, limit: MAX_PAGE_SIZE },
203+
districts: { parentCode: regency.code, limit: MAX_PAGE_SIZE },
204+
}))
205+
}}
206+
inputProps={{
207+
onValueChange: debounce((name) => {
208+
if (!selected?.province) {
209+
setQuery((current) => ({
210+
...current,
211+
regencies: { ...current?.regencies, name },
212+
}))
291213
}
292-
defaultChecked
293-
onCheckedChange={(checked) => {
294-
setHideBoundary((current) => ({
214+
}, 500),
215+
}}
216+
disabled={isLoading?.provinces}
217+
/>
218+
219+
<ComboboxArea
220+
area="districts"
221+
selected={selected?.district}
222+
query={query?.districts}
223+
onSelect={(district) => {
224+
setSelected((current) => ({ ...current, district }))
225+
if (district.code === query?.villages?.parentCode) return
226+
setQuery((current) => ({
227+
...current,
228+
villages: { parentCode: district.code, limit: MAX_PAGE_SIZE },
229+
}))
230+
}}
231+
inputProps={{
232+
onValueChange: debounce((name) => {
233+
if (!selected?.regency) {
234+
setQuery((current) => ({
295235
...current,
296-
[area]: !checked,
236+
districts: { ...current?.districts, name },
297237
}))
298-
}}
299-
/>
300-
<label
301-
htmlFor={`${area}Boundary`}
302-
className={cn(
303-
'flex items-center gap-2',
304-
!selected?.[
305-
singletonArea[area as FeatureAreas] as keyof Selected
306-
] && 'text-gray-400',
238+
}
239+
}, 500),
240+
}}
241+
disabled={isLoading?.regencies}
242+
/>
243+
244+
<ComboboxArea
245+
area="villages"
246+
selected={selected?.village}
247+
query={query?.villages}
248+
onSelect={(village) => {
249+
setSelected((current) => ({ ...current, village }))
250+
}}
251+
inputProps={{
252+
onValueChange: debounce((name) => {
253+
if (!selected?.district) {
254+
setQuery((current) => ({
255+
...current,
256+
villages: { ...current?.villages, name },
257+
}))
258+
}
259+
}, 500),
260+
}}
261+
disabled={isLoading?.districts}
262+
/>
263+
264+
{/* Islands info */}
265+
<div className="w-full p-2 border rounded flex gap-2 justify-center items-center">
266+
{query?.islands?.parentCode ? (
267+
<>
268+
{isLoading?.islands && (
269+
<ReloadIcon className="h-4 w-4 animate-spin" />
307270
)}
308-
>
309-
<div
310-
className="w-4 h-4 rounded"
311-
style={{
312-
backgroundColor: config.color,
271+
<span className="text-sm w-fit">
272+
{islands.length} islands found
273+
</span>
274+
</>
275+
) : (
276+
<span className="text-sm w-fit text-gray-500">
277+
Select a regency to see islands
278+
</span>
279+
)}
280+
</div>
281+
282+
{/* Boundary settings */}
283+
<div className="w-full p-2 flex flex-col gap-2">
284+
<h3 className="font-semibold">Show Boundary</h3>
285+
{Object.entries(featureConfig).map(([area, config]) => (
286+
<div key={area} className="flex items-center space-x-2 truncate">
287+
<Switch
288+
id={`${area}Boundary`}
289+
disabled={
290+
!selected?.[
291+
singletonArea[area as FeatureAreas] as keyof Selected
292+
]
293+
}
294+
defaultChecked
295+
onCheckedChange={(checked) => {
296+
setHideBoundary((current) => ({
297+
...current,
298+
[area]: !checked,
299+
}))
313300
}}
314301
/>
315-
316-
{ucFirstStr(singletonArea[area as FeatureAreas])}
317-
318-
{isLoading?.[area as FeatureAreas] && (
319-
<ReloadIcon className="h-4 w-4 animate-spin" />
320-
)}
321-
</label>
322-
</div>
323-
))}
302+
<label
303+
htmlFor={`${area}Boundary`}
304+
className={cn(
305+
'flex items-center gap-2',
306+
!selected?.[
307+
singletonArea[area as FeatureAreas] as keyof Selected
308+
] && 'text-gray-400',
309+
)}
310+
>
311+
<div
312+
className="w-4 h-4 rounded"
313+
style={{
314+
backgroundColor: config.color,
315+
}}
316+
/>
317+
318+
{ucFirstStr(singletonArea[area as FeatureAreas])}
319+
320+
{isLoading?.[area as FeatureAreas] && (
321+
<ReloadIcon className="h-4 w-4 animate-spin" />
322+
)}
323+
</label>
324+
</div>
325+
))}
326+
</div>
327+
328+
<Button
329+
variant="outline"
330+
className="mt-auto items-center gap-1"
331+
onClick={() => {
332+
setQuery(undefined)
333+
setSelected(undefined)
334+
setIslands([])
335+
}}
336+
>
337+
<Cross2Icon className="h-4 w-4" />
338+
Clear All Data
339+
</Button>
324340
</div>
325-
326-
<Button
327-
variant="outline"
328-
className="mt-auto items-center gap-1"
329-
onClick={() => {
330-
setQuery(undefined)
331-
setSelected(undefined)
332-
setIslands([])
333-
}}
334-
>
335-
<Cross2Icon className="h-4 w-4" />
336-
Clear All Data
337-
</Button>
338341
</ResizablePanel>
339342

340343
<ResizableHandle withHandle />

0 commit comments

Comments
 (0)