diff --git a/web/src/components/overlay/detail/ObjectLifecycle.tsx b/web/src/components/overlay/detail/ObjectLifecycle.tsx index d335e35c9..c07fe5a95 100644 --- a/web/src/components/overlay/detail/ObjectLifecycle.tsx +++ b/web/src/components/overlay/detail/ObjectLifecycle.tsx @@ -45,8 +45,9 @@ import { useNavigate } from "react-router-dom"; import { ObjectPath } from "./ObjectPath"; import { getLifecycleItemDescription } from "@/utils/lifecycleUtil"; import { IoPlayCircleOutline } from "react-icons/io5"; -import { useTranslation } from "react-i18next"; +import { Trans, useTranslation } from "react-i18next"; import { getTranslatedLabel } from "@/utils/i18n"; +import { resolveZoneName } from "@/hooks/use-zone-friendly-name"; type ObjectLifecycleProps = { className?: string; @@ -62,7 +63,7 @@ export default function ObjectLifecycle({ setPane, }: ObjectLifecycleProps) { const { t } = useTranslation(["views/explore"]); - + const { data: config } = useSWR("config"); const { data: eventSequence } = useSWR([ "timeline", { @@ -70,7 +71,12 @@ export default function ObjectLifecycle({ }, ]); - const { data: config } = useSWR("config"); + eventSequence?.map((event) => { + event.data.zones_friendly_names = event.data?.zones?.map((zone) => { + return resolveZoneName(config, zone); + }); + }); + const apiHost = useApiHost(); const navigate = useNavigate(); @@ -673,7 +679,9 @@ export default function ObjectLifecycle({ />
-
{getLifecycleItemDescription(item)}
+ +
{getLifecycleItemDescription(item)}
+
{formattedEventTimestamp}
@@ -731,7 +739,9 @@ export default function ObjectLifecycle({ }} /> - {zone.replaceAll("_", " ")} + {item.data?.zones_friendly_names?.[ + zidx + ] ?? zone.replaceAll("_", " ")}
))} diff --git a/web/src/types/timeline.ts b/web/src/types/timeline.ts index 561e64f4f..bc4271c2e 100644 --- a/web/src/types/timeline.ts +++ b/web/src/types/timeline.ts @@ -22,6 +22,7 @@ export type ObjectLifecycleSequence = { attribute: string; attribute_box?: [number, number, number, number]; zones: string[]; + zones_friendly_names?: string[]; }; class_type: LifecycleClassType; source_id: string; diff --git a/web/src/utils/lifecycleUtil.ts b/web/src/utils/lifecycleUtil.ts index 0a716d9fc..4c96d4643 100644 --- a/web/src/utils/lifecycleUtil.ts +++ b/web/src/utils/lifecycleUtil.ts @@ -1,6 +1,6 @@ import { ObjectLifecycleSequence } from "@/types/timeline"; import { t } from "i18next"; -import { getTranslatedLabel } from "./i18n"; +import i18n, { getTranslatedLabel } from "./i18n"; export function getLifecycleItemDescription( lifecycleItem: ObjectLifecycleSequence, @@ -11,6 +11,17 @@ export function getLifecycleItemDescription( const label = getTranslatedLabel(rawLabel); + let supportedListFormat = false; + let zonesFormatter: Intl.ListFormat | null = null; + + if (typeof Intl !== "undefined" && Intl.ListFormat) { + supportedListFormat = true; + zonesFormatter = new Intl.ListFormat(i18n.language, { + style: "long", + type: "conjunction", + }); + } + switch (lifecycleItem.class_type) { case "visible": return t("objectLifecycle.lifecycleItemDesc.visible", { @@ -21,7 +32,18 @@ export function getLifecycleItemDescription( return t("objectLifecycle.lifecycleItemDesc.entered_zone", { ns: "views/explore", label, - zones: lifecycleItem.data.zones.join(" and ").replaceAll("_", " "), + zones: + supportedListFormat && zonesFormatter + ? zonesFormatter.format( + lifecycleItem.data.zones_friendly_names?.map( + (x) => `${x}`, + ) ?? + lifecycleItem.data.zones.map((x) => `${x}`), + ) + : ( + lifecycleItem.data.zones_friendly_names ?? + lifecycleItem.data.zones + ).join(" and "), }); case "active": return t("objectLifecycle.lifecycleItemDesc.active", { diff --git a/web/tsconfig.json b/web/tsconfig.json index 0874f4123..cead074fb 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "target": "ES2020", + "target": "ES2021", "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable", "ES2021.String"], + "lib": ["ES2021", "DOM", "DOM.Iterable", "ES2021.String"], "module": "ESNext", "skipLibCheck": true, "baseUrl": ".",