"use client"; import { useCallback } from "react"; import { ActionIcon, Alert, Anchor, Button, Fieldset, Group, Loader, NumberInput, Stack, Table, Text, TextInput, Tooltip, } from "@mantine/core"; import { IconClick, IconListSearch } from "@tabler/icons-react"; import type { RouterOutputs } from "@homarr/api"; import { clientApi } from "@homarr/api/client"; import { createModal, useModalAction } from "@homarr/modals"; import { useScopedI18n } from "@homarr/translation/client"; import type { OptionLocation } from "../options"; import type { CommonWidgetInputProps } from "./common"; import { useWidgetInputTranslation } from "./common"; import { useFormContext } from "./form"; export const WidgetLocationInput = ({ property, kind }: CommonWidgetInputProps<"location">) => { const t = useWidgetInputTranslation(kind, property); const tLocation = useScopedI18n("widget.common.location"); const form = useFormContext(); const { openModal } = useModalAction(LocationSearchModal); const inputProps = form.getInputProps(`options.${property}`); const value = inputProps.value as OptionLocation; const selectionEnabled = value.name.length > 1; const handleChange = inputProps.onChange as LocationOnChange; const unknownLocation = tLocation("unknownLocation"); const onLocationSelect = useCallback( (location: OptionLocation) => { handleChange(location); form.clearFieldError(`options.${property}.latitude`); form.clearFieldError(`options.${property}.longitude`); }, [form, handleChange, property], ); const onSearch = useCallback(() => { if (!selectionEnabled) return; openModal({ query: value.name, onLocationSelect, }); }, [selectionEnabled, value.name, onLocationSelect, openModal]); form.watch(`options.${property}.latitude`, ({ value }) => { if (typeof value !== "number") return; form.setFieldValue(`options.${property}.name`, unknownLocation); }); form.watch(`options.${property}.longitude`, ({ value }) => { if (typeof value !== "number") return; form.setFieldValue(`options.${property}.name`, unknownLocation); }); return (
); }; type LocationOnChange = ( location: Pick & { latitude: OptionLocation["latitude"] | ""; longitude: OptionLocation["longitude"] | ""; }, ) => void; interface LocationSearchInnerProps { query: string; onLocationSelect: (location: OptionLocation) => void; } const LocationSearchModal = createModal(({ actions, innerProps }) => { const t = useScopedI18n("widget.common.location.table"); const tCommon = useScopedI18n("common"); const { data, isPending, error } = clientApi.location.searchCity.useQuery({ query: innerProps.query, }); if (error) { return ( {error.message} ); } return ( {t("header.city")} {t("header.country")} {t("header.coordinates")} {t("header.population")} {isPending && ( )} {data?.results.map((city) => ( ))}
); }).withOptions({ defaultTitle(t) { return t("widget.common.location.search"); }, size: "xl", }); interface LocationSearchTableRowProps { city: RouterOutputs["location"]["searchCity"]["results"][number]; onLocationSelect: (location: OptionLocation) => void; closeModal: () => void; } const LocationSelectTableRow = ({ city, onLocationSelect, closeModal }: LocationSearchTableRowProps) => { const t = useScopedI18n("widget.common.location.table"); const onSelect = useCallback(() => { onLocationSelect({ name: city.name, latitude: city.latitude, longitude: city.longitude, }); closeModal(); }, [city, onLocationSelect, closeModal]); const formatter = Intl.NumberFormat("en", { notation: "compact" }); return ( {city.name} {city.country} {city.latitude}, {city.longitude} {city.population ? ( {formatter.format(city.population)} ) : ( {t("population.fallback")} )} ); };