diff --git a/src/components/blocks/schedule/schedule-room-directions-dialog.tsx b/src/components/blocks/schedule/schedule-room-directions-dialog.tsx new file mode 100644 index 0000000..3ef7937 --- /dev/null +++ b/src/components/blocks/schedule/schedule-room-directions-dialog.tsx @@ -0,0 +1,164 @@ +"use client"; + +import { + ExternalLinkIcon, + LoaderCircleIcon, + MapPinnedIcon, +} from "lucide-react"; +import { useEffect, useMemo, useState } from "react"; + +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { useTranslations } from "@/contexts/language-context"; +import { + getScheduleRoomDirectionsUrl, + getScheduleRoomLocation, + getScheduleRoomMapEmbedUrl, +} from "@/lib/schedule-rooms"; + +type ScheduleRoomDirectionsDialogProps = { + room: string | null; + open: boolean; + onOpenChange: (open: boolean) => void; +}; + +type GeolocationState = + | { status: "idle" } + | { status: "loading" } + | { status: "ready"; lat: number; lng: number } + | { status: "denied" | "unavailable"; message: string }; + +function ScheduleRoomDirectionsDialog({ + room, + open, + onOpenChange, +}: ScheduleRoomDirectionsDialogProps) { + const { t } = useTranslations(); + const [geo, setGeo] = useState({ status: "idle" }); + + const location = useMemo( + () => (room ? getScheduleRoomLocation(room) : undefined), + [room], + ); + + useEffect(() => { + if (!open || !location) { + setGeo({ status: "idle" }); + return; + } + + if (!navigator.geolocation) { + setGeo({ + status: "unavailable", + message: t("blocks.scheduleUi.roomMap.geoUnavailable"), + }); + return; + } + + let cancelled = false; + setGeo({ status: "loading" }); + + navigator.geolocation.getCurrentPosition( + (position) => { + if (cancelled) return; + setGeo({ + status: "ready", + lat: position.coords.latitude, + lng: position.coords.longitude, + }); + }, + (error) => { + if (cancelled) return; + setGeo({ + status: + error.code === error.PERMISSION_DENIED ? "denied" : "unavailable", + message: + error.code === error.PERMISSION_DENIED + ? t("blocks.scheduleUi.roomMap.geoDenied") + : t("blocks.scheduleUi.roomMap.geoUnavailable"), + }); + }, + { + enableHighAccuracy: true, + timeout: 12_000, + maximumAge: 60_000, + }, + ); + + return () => { + cancelled = true; + }; + }, [location, open, t]); + + if (!location) { + return null; + } + + const directionsUrl = getScheduleRoomDirectionsUrl( + location, + geo.status === "ready" ? { lat: geo.lat, lng: geo.lng } : undefined, + ); + + return ( + + +
+ + + + {location.name} + + + {t("blocks.scheduleUi.roomMap.description")} + + + + {geo.status === "loading" || geo.status === "idle" ? ( +

+ + {t("blocks.scheduleUi.roomMap.locating")} +

+ ) : geo.status === "ready" ? ( +

+ {t("blocks.scheduleUi.roomMap.located")} +

+ ) : ( +

{geo.message}

+ )} +
+ +
+