diff --git a/admin/inertia/components/maps/MapComponent.tsx b/admin/inertia/components/maps/MapComponent.tsx index 0580e3c..b906932 100644 --- a/admin/inertia/components/maps/MapComponent.tsx +++ b/admin/inertia/components/maps/MapComponent.tsx @@ -137,9 +137,11 @@ export default function MapComponent() { { await addMarker(name, placingMarker.lng, placingMarker.lat, color, notes || undefined) setPlacingMarker(null) + setHasUnsavedMarkerChanges(false) }} onCancel={() => { if (!confirmDiscardMarkerChanges()) return diff --git a/admin/inertia/components/maps/MapMarkerFormPopup.tsx b/admin/inertia/components/maps/MapMarkerFormPopup.tsx index 190e6e7..94ff34b 100644 --- a/admin/inertia/components/maps/MapMarkerFormPopup.tsx +++ b/admin/inertia/components/maps/MapMarkerFormPopup.tsx @@ -1,4 +1,4 @@ -import {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react' +import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' import { Popup } from 'react-map-gl/maplibre' import { PIN_COLORS } from '~/hooks/useMapMarkers' @@ -18,7 +18,7 @@ type MapMarkerFormPopupProps = { name: string notes: string color: PinColorId - }) => void + }) => Promise | void onCancel: () => void onDirtyChange?: (dirty: boolean) => void } @@ -34,6 +34,7 @@ export default function MapMarkerFormPopup({ const [name, setName] = useState(initialMarker?.name ?? '') const [notes, setNotes] = useState(initialMarker?.notes ?? '') const [color, setColor] = useState(initialMarker?.color ?? 'orange') + const [isSaving, setIsSaving] = useState(false) const textareaRef = useRef(null) @@ -49,10 +50,6 @@ export default function MapMarkerFormPopup({ resizeTextarea() }, [resizeTextarea]) - useLayoutEffect(() => { - resizeTextarea() - }, []) - const isDirty = name !== (initialMarker?.name ?? '') || notes !== (initialMarker?.notes ?? '') || @@ -62,15 +59,21 @@ export default function MapMarkerFormPopup({ onDirtyChange?.(isDirty) }, [isDirty, onDirtyChange]) - const handleSave = () => { - if (!name.trim()) return + const handleSave = async () => { + if (!name.trim() || isSaving) return - onSave({ - id: initialMarker?.id, - name: name.trim(), - notes: notes.trim(), - color, - }) + try { + setIsSaving(true) + + await onSave({ + id: initialMarker?.id, + name: name.trim(), + notes: notes.trim(), + color, + }) + } finally { + setIsSaving(false) + } } return ( @@ -136,7 +139,8 @@ export default function MapMarkerFormPopup({ @@ -144,10 +148,10 @@ export default function MapMarkerFormPopup({ diff --git a/admin/inertia/components/maps/ViewMapMarkerPopup.tsx b/admin/inertia/components/maps/ViewMapMarkerPopup.tsx index d7762f0..227b73b 100644 --- a/admin/inertia/components/maps/ViewMapMarkerPopup.tsx +++ b/admin/inertia/components/maps/ViewMapMarkerPopup.tsx @@ -29,6 +29,8 @@ export default function ViewMapMarkerPopup({ {marker.notes && (
+ {/* react-markdown is intentionally used without rehypeRaw. + Do not enable raw HTML rendering unless notes are sanitized first. */} { - const response = await this.client.get< - Array<{ id: number; name: string; longitude: number; latitude: number; color: string; created_at: string }> - >('/maps/markers') + const response = await this.client.get('/maps/markers') return response.data })() } - async createMapMarker(data: { name: string; longitude: number; latitude: number; color?: string }) { + async createMapMarker(data: { + name: string + notes?: string | null + longitude: number + latitude: number + color?: string + }) { return catchInternal(async () => { - const response = await this.client.post< - { id: number; name: string; longitude: number; latitude: number; color: string; created_at: string } - >('/maps/markers', data) + const response = await this.client.post('/maps/markers', data) return response.data })() } - async updateMapMarker(id: number, data: { name?: string; color?: string }) { + async updateMapMarker( + id: number, + data: { name?: string; notes?: string | null; color?: string } + ) { return catchInternal(async () => { - const response = await this.client.patch< - { id: number; name: string; longitude: number; latitude: number; color: string } - >(`/maps/markers/${id}`, data) + const response = await this.client.patch(`/maps/markers/${id}`, data) return response.data })() } diff --git a/admin/types/maps.ts b/admin/types/maps.ts index f907126..ff3072d 100644 --- a/admin/types/maps.ts +++ b/admin/types/maps.ts @@ -21,3 +21,17 @@ export type MapLayer = { 'source-layer'?: string [key: string]: any } + +export type MapMarkerResponse = { + id: number + name: string + longitude: number + latitude: number + color: string + notes?: string | null + marker_type?: string + route_id?: string | null + route_order?: number | null + created_at: string + updated_at?: string +}