rename object lifecycle to tracking details

This commit is contained in:
Josh Hawkins 2025-10-26 12:35:01 -05:00
parent 8d3f43101a
commit fe5a8cae92
17 changed files with 91 additions and 91 deletions

View File

@ -36,8 +36,8 @@
"video": "video",
"object_lifecycle": "object lifecycle"
},
"objectLifecycle": {
"title": "Object Lifecycle",
"trackingDetails": {
"title": "Tracking Details",
"noImageFound": "No image found for this timestamp.",
"createObjectMask": "Create Object Mask",
"adjustAnnotationSettings": "Adjust annotation settings",
@ -168,9 +168,9 @@
"label": "Download snapshot",
"aria": "Download snapshot"
},
"viewObjectLifecycle": {
"label": "View object lifecycle",
"aria": "Show the object lifecycle"
"viewTrackingDetails": {
"label": "View tracking details",
"aria": "Show the tracking details"
},
"findSimilar": {
"label": "Find similar",
@ -205,7 +205,7 @@
"dialog": {
"confirmDelete": {
"title": "Confirm Delete",
"desc": "Deleting this tracked object removes the snapshot, any saved embeddings, and any associated object lifecycle entries. Recorded footage of this tracked object in History view will <em>NOT</em> be deleted.<br /><br />Are you sure you want to proceed?"
"desc": "Deleting this tracked object removes the snapshot, any saved embeddings, and any associated tracking details entries. Recorded footage of this tracked object in History view will <em>NOT</em> be deleted.<br /><br />Are you sure you want to proceed?"
}
},
"noTrackedObjects": "No Tracked Objects Found",

View File

@ -13,7 +13,7 @@ type SearchThumbnailProps = {
columns: number;
findSimilar: () => void;
refreshResults: () => void;
showObjectLifecycle: () => void;
showTrackingDetails: () => void;
showSnapshot: () => void;
addTrigger: () => void;
};
@ -23,7 +23,7 @@ export default function SearchThumbnailFooter({
columns,
findSimilar,
refreshResults,
showObjectLifecycle,
showTrackingDetails,
showSnapshot,
addTrigger,
}: SearchThumbnailProps) {
@ -61,7 +61,7 @@ export default function SearchThumbnailFooter({
searchResult={searchResult}
findSimilar={findSimilar}
refreshResults={refreshResults}
showObjectLifecycle={showObjectLifecycle}
showTrackingDetails={showTrackingDetails}
showSnapshot={showSnapshot}
addTrigger={addTrigger}
/>

View File

@ -47,7 +47,7 @@ type SearchResultActionsProps = {
searchResult: SearchResult;
findSimilar: () => void;
refreshResults: () => void;
showObjectLifecycle: () => void;
showTrackingDetails: () => void;
showSnapshot: () => void;
addTrigger: () => void;
isContextMenu?: boolean;
@ -58,7 +58,7 @@ export default function SearchResultActions({
searchResult,
findSimilar,
refreshResults,
showObjectLifecycle,
showTrackingDetails,
showSnapshot,
addTrigger,
isContextMenu = false,
@ -125,11 +125,11 @@ export default function SearchResultActions({
)}
{searchResult.data.type == "object" && (
<MenuItem
aria-label={t("itemMenu.viewObjectLifecycle.aria")}
onClick={showObjectLifecycle}
aria-label={t("itemMenu.viewTrackingDetails.aria")}
onClick={showTrackingDetails}
>
<FaArrowsRotate className="mr-2 size-4" />
<span>{t("itemMenu.viewObjectLifecycle.label")}</span>
<span>{t("itemMenu.viewTrackingDetails.label")}</span>
</MenuItem>
)}
{config?.semantic_search?.enabled && isContextMenu && (

View File

@ -1,5 +1,5 @@
import { useMemo, useCallback } from "react";
import { ObjectLifecycleSequence, LifecycleClassType } from "@/types/timeline";
import { TrackingDetailsSequence, LifecycleClassType } from "@/types/timeline";
import { FrigateConfig } from "@/types/frigateConfig";
import useSWR from "swr";
import { useDetailStream } from "@/context/detail-stream-context";
@ -27,7 +27,7 @@ type PathPoint = {
x: number;
y: number;
timestamp: number;
lifecycle_item?: ObjectLifecycleSequence;
lifecycle_item?: TrackingDetailsSequence;
objectId: string;
};
@ -63,7 +63,7 @@ export default function ObjectTrackOverlay({
);
// Fetch timeline data for each object ID using fixed number of hooks
const { data: timelineData } = useSWR<ObjectLifecycleSequence[]>(
const { data: timelineData } = useSWR<TrackingDetailsSequence[]>(
selectedObjectIds.length > 0
? `timeline?source_id=${selectedObjectIds.join(",")}&limit=1000`
: null,
@ -74,7 +74,7 @@ export default function ObjectTrackOverlay({
// Group timeline entries by source_id
if (!timelineData) return selectedObjectIds.map(() => []);
const grouped: Record<string, ObjectLifecycleSequence[]> = {};
const grouped: Record<string, TrackingDetailsSequence[]> = {};
for (const entry of timelineData) {
if (!grouped[entry.source_id]) {
grouped[entry.source_id] = [];
@ -152,9 +152,9 @@ export default function ObjectTrackOverlay({
const eventSequencePoints: PathPoint[] =
timelineData
?.filter(
(event: ObjectLifecycleSequence) => event.data.box !== undefined,
(event: TrackingDetailsSequence) => event.data.box !== undefined,
)
.map((event: ObjectLifecycleSequence) => {
.map((event: TrackingDetailsSequence) => {
const [left, top, width, height] = event.data.box!;
return {
x: left + width / 2, // Center x
@ -183,22 +183,22 @@ export default function ObjectTrackOverlay({
const currentZones =
timelineData
?.filter(
(event: ObjectLifecycleSequence) =>
(event: TrackingDetailsSequence) =>
event.timestamp <= effectiveCurrentTime,
)
.sort(
(a: ObjectLifecycleSequence, b: ObjectLifecycleSequence) =>
(a: TrackingDetailsSequence, b: TrackingDetailsSequence) =>
b.timestamp - a.timestamp,
)[0]?.data?.zones || [];
// Get current bounding box
const currentBox = timelineData
?.filter(
(event: ObjectLifecycleSequence) =>
(event: TrackingDetailsSequence) =>
event.timestamp <= effectiveCurrentTime && event.data.box,
)
.sort(
(a: ObjectLifecycleSequence, b: ObjectLifecycleSequence) =>
(a: TrackingDetailsSequence, b: TrackingDetailsSequence) =>
b.timestamp - a.timestamp,
)[0]?.data?.box;

View File

@ -40,7 +40,7 @@ export default function AnnotationOffsetSlider({ className }: Props) {
);
toast.success(
t("objectLifecycle.annotationSettings.offset.toast.success", {
t("trackingDetails.annotationSettings.offset.toast.success", {
camera,
}),
{ position: "top-center" },

View File

@ -79,7 +79,7 @@ export function AnnotationSettingsPane({
.then((res) => {
if (res.status === 200) {
toast.success(
t("objectLifecycle.annotationSettings.offset.toast.success", {
t("trackingDetails.annotationSettings.offset.toast.success", {
camera: event?.camera,
}),
{
@ -142,7 +142,7 @@ export function AnnotationSettingsPane({
return (
<div className="mb-3 space-y-3 rounded-lg border border-secondary-foreground bg-background_alt p-2">
<Heading as="h4" className="my-2">
{t("objectLifecycle.annotationSettings.title")}
{t("trackingDetails.annotationSettings.title")}
</Heading>
<div className="flex flex-col">
<div className="flex flex-row items-center justify-start gap-2 p-3">
@ -152,11 +152,11 @@ export function AnnotationSettingsPane({
onCheckedChange={setShowZones}
/>
<Label className="cursor-pointer" htmlFor="show-zones">
{t("objectLifecycle.annotationSettings.showAllZones.title")}
{t("trackingDetails.annotationSettings.showAllZones.title")}
</Label>
</div>
<div className="text-sm text-muted-foreground">
{t("objectLifecycle.annotationSettings.showAllZones.desc")}
{t("trackingDetails.annotationSettings.showAllZones.desc")}
</div>
</div>
<Separator className="my-2 flex bg-secondary" />
@ -171,14 +171,14 @@ export function AnnotationSettingsPane({
render={({ field }) => (
<FormItem>
<FormLabel>
{t("objectLifecycle.annotationSettings.offset.label")}
{t("trackingDetails.annotationSettings.offset.label")}
</FormLabel>
<div className="flex flex-col gap-3 md:flex-row-reverse md:gap-8">
<div className="flex flex-row items-center gap-3 rounded-lg bg-destructive/50 p-3 text-sm text-primary-variant md:my-5">
<PiWarningCircle className="size-24" />
<div>
<Trans ns="views/explore">
objectLifecycle.annotationSettings.offset.desc
trackingDetails.annotationSettings.offset.desc
</Trans>
<div className="mt-2 flex items-center text-primary">
<Link
@ -203,10 +203,10 @@ export function AnnotationSettingsPane({
</FormControl>
<FormDescription>
<Trans ns="views/explore">
objectLifecycle.annotationSettings.offset.millisecondsToOffset
trackingDetails.annotationSettings.offset.millisecondsToOffset
</Trans>
<div className="mt-2">
{t("objectLifecycle.annotationSettings.offset.tips")}
{t("trackingDetails.annotationSettings.offset.tips")}
</div>
</FormDescription>
</div>

View File

@ -105,7 +105,7 @@ export function ObjectPath({
<TooltipContent side="top" className="smart-capitalize">
{pos.lifecycle_item
? getLifecycleItemDescription(pos.lifecycle_item)
: t("objectLifecycle.trackedPoint")}
: t("trackingDetails.trackedPoint")}
</TooltipContent>
</TooltipPortal>
</Tooltip>

View File

@ -20,7 +20,7 @@ import { Event } from "@/types/event";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { FrigatePlusDialog } from "../dialog/FrigatePlusDialog";
import ObjectLifecycle from "./ObjectLifecycle";
import TrackingDetails from "./TrackingDetails";
import Chip from "@/components/indicators/Chip";
import { FaDownload, FaImages, FaShareAlt } from "react-icons/fa";
import FrigatePlusIcon from "@/components/icons/FrigatePlusIcon";
@ -411,7 +411,7 @@ export default function ReviewDetailDialog({
{pane == "details" && selectedEvent && (
<div className="mt-0 flex size-full flex-col gap-2">
<ObjectLifecycle event={selectedEvent} setPane={setPane} />
<TrackingDetails event={selectedEvent} setPane={setPane} />
</div>
)}
</Content>
@ -544,7 +544,7 @@ function EventItem({
</Chip>
</TooltipTrigger>
<TooltipContent>
{t("itemMenu.viewObjectLifecycle.label")}
{t("itemMenu.viewTrackingDetails.label")}
</TooltipContent>
</Tooltip>
)}

View File

@ -35,7 +35,7 @@ import {
FaVideo,
} from "react-icons/fa";
import { FaRotate } from "react-icons/fa6";
import ObjectLifecycle from "./ObjectLifecycle";
import TrackingDetails from "./TrackingDetails";
import {
MobilePage,
MobilePageContent,
@ -85,7 +85,7 @@ const SEARCH_TABS = [
"details",
"snapshot",
"video",
"object_lifecycle",
"tracking_details",
] as const;
export type SearchTab = (typeof SEARCH_TABS)[number];
@ -160,7 +160,7 @@ export default function SearchDetailDialog({
}
if (search.data.type != "object" || !search.has_clip) {
const index = views.indexOf("object_lifecycle");
const index = views.indexOf("tracking_details");
views.splice(index, 1);
}
@ -235,7 +235,7 @@ export default function SearchDetailDialog({
{item == "details" && <FaRegListAlt className="size-4" />}
{item == "snapshot" && <FaImage className="size-4" />}
{item == "video" && <FaVideo className="size-4" />}
{item == "object_lifecycle" && (
{item == "tracking_details" && (
<FaRotate className="size-4" />
)}
<div className="smart-capitalize">{t(`type.${item}`)}</div>
@ -268,8 +268,8 @@ export default function SearchDetailDialog({
/>
)}
{page == "video" && <VideoTab search={search} />}
{page == "object_lifecycle" && (
<ObjectLifecycle
{page == "tracking_details" && (
<TrackingDetails
className="w-full overflow-x-hidden"
event={search as unknown as Event}
fullscreen={true}

View File

@ -3,7 +3,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Event } from "@/types/event";
import ActivityIndicator from "@/components/indicators/activity-indicator";
import { Button } from "@/components/ui/button";
import { ObjectLifecycleSequence } from "@/types/timeline";
import { TrackingDetailsSequence } from "@/types/timeline";
import Heading from "@/components/ui/heading";
import { ReviewDetailPaneType } from "@/types/review";
import { FrigateConfig } from "@/types/frigateConfig";
@ -60,22 +60,22 @@ import { HiDotsHorizontal } from "react-icons/hi";
import axios from "axios";
import { toast } from "sonner";
type ObjectLifecycleProps = {
type TrackingDetailsProps = {
className?: string;
event: Event;
fullscreen?: boolean;
setPane: React.Dispatch<React.SetStateAction<ReviewDetailPaneType>>;
};
export default function ObjectLifecycle({
export default function TrackingDetails({
className,
event,
fullscreen = false,
setPane,
}: ObjectLifecycleProps) {
}: TrackingDetailsProps) {
const { t } = useTranslation(["views/explore"]);
const { data: eventSequence } = useSWR<ObjectLifecycleSequence[]>([
const { data: eventSequence } = useSWR<TrackingDetailsSequence[]>([
"timeline",
{
source_id: event.id,
@ -458,7 +458,7 @@ export default function ObjectLifecycle({
<div className="relative aspect-video">
<div className="flex flex-col items-center justify-center p-20 text-center">
<LuFolderX className="size-16" />
{t("objectLifecycle.noImageFound")}
{t("trackingDetails.noImageFound")}
</div>
</div>
)}
@ -569,7 +569,7 @@ export default function ObjectLifecycle({
}
>
<div className="text-primary">
{t("objectLifecycle.createObjectMask")}
{t("trackingDetails.createObjectMask")}
</div>
</div>
</ContextMenuItem>
@ -579,7 +579,7 @@ export default function ObjectLifecycle({
</div>
<div className="mt-3 flex flex-row items-center justify-between">
<Heading as="h4">{t("objectLifecycle.title")}</Heading>
<Heading as="h4">{t("trackingDetails.title")}</Heading>
<div className="flex flex-row gap-2">
<Tooltip>
@ -587,7 +587,7 @@ export default function ObjectLifecycle({
<Button
variant={showControls ? "select" : "default"}
className="size-7 p-1.5"
aria-label={t("objectLifecycle.adjustAnnotationSettings")}
aria-label={t("trackingDetails.adjustAnnotationSettings")}
>
<LuSettings
className="size-5"
@ -597,7 +597,7 @@ export default function ObjectLifecycle({
</TooltipTrigger>
<TooltipPortal>
<TooltipContent>
{t("objectLifecycle.adjustAnnotationSettings")}
{t("trackingDetails.adjustAnnotationSettings")}
</TooltipContent>
</TooltipPortal>
</Tooltip>
@ -605,10 +605,10 @@ export default function ObjectLifecycle({
</div>
<div className="flex flex-row items-center justify-between">
<div className="mb-2 text-sm text-muted-foreground">
{t("objectLifecycle.scrollViewTips")}
{t("trackingDetails.scrollViewTips")}
</div>
<div className="min-w-20 text-right text-sm text-muted-foreground">
{t("objectLifecycle.count", {
{t("trackingDetails.count", {
first: selectedIndex + 1,
second: eventSequence?.length ?? 0,
})}
@ -616,7 +616,7 @@ export default function ObjectLifecycle({
</div>
{config?.cameras[event.camera]?.onvif.autotracking.enabled_in_config && (
<div className="-mt-2 mb-2 text-sm text-danger">
{t("objectLifecycle.autoTrackingTips")}
{t("trackingDetails.autoTrackingTips")}
</div>
)}
{showControls && (
@ -767,7 +767,7 @@ export default function ObjectLifecycle({
}
type GetTimelineIconParams = {
lifecycleItem: ObjectLifecycleSequence;
lifecycleItem: TrackingDetailsSequence;
className?: string;
};
@ -805,7 +805,7 @@ export function LifecycleIcon({
}
type LifecycleIconRowProps = {
item: ObjectLifecycleSequence;
item: TrackingDetailsSequence;
isActive?: boolean;
formattedEventTimestamp: string;
ratio: string;
@ -859,13 +859,13 @@ function LifecycleIconRow({
<div className="mt-1 flex flex-wrap items-center gap-2 text-xs text-secondary-foreground md:gap-5">
<div className="flex items-center gap-1">
<span className="text-primary-variant">
{t("objectLifecycle.lifecycleItemDesc.header.ratio")}
{t("trackingDetails.lifecycleItemDesc.header.ratio")}
</span>
<span className="font-medium text-primary">{ratio}</span>
</div>
<div className="flex items-center gap-1">
<span className="text-primary-variant">
{t("objectLifecycle.lifecycleItemDesc.header.area")}
{t("trackingDetails.lifecycleItemDesc.header.area")}
</span>
{areaPx !== undefined && areaPct !== undefined ? (
<span className="font-medium text-primary">

View File

@ -1,7 +1,7 @@
import { Recording } from "@/types/record";
import { DynamicPlayback } from "@/types/playback";
import { PreviewController } from "../PreviewPlayer";
import { TimeRange, ObjectLifecycleSequence } from "@/types/timeline";
import { TimeRange, TrackingDetailsSequence } from "@/types/timeline";
import { calculateInpointOffset } from "@/utils/videoUtil";
type PlayerMode = "playback" | "scrubbing";
@ -12,7 +12,7 @@ export class DynamicVideoController {
private playerController: HTMLVideoElement;
private previewController: PreviewController;
private setNoRecording: (noRecs: boolean) => void;
private setFocusedItem: (timeline: ObjectLifecycleSequence) => void;
private setFocusedItem: (timeline: TrackingDetailsSequence) => void;
private playerMode: PlayerMode = "playback";
// playback
@ -29,7 +29,7 @@ export class DynamicVideoController {
annotationOffset: number,
defaultMode: PlayerMode,
setNoRecording: (noRecs: boolean) => void,
setFocusedItem: (timeline: ObjectLifecycleSequence) => void,
setFocusedItem: (timeline: TrackingDetailsSequence) => void,
) {
this.camera = camera;
this.playerController = playerController;
@ -132,7 +132,7 @@ export class DynamicVideoController {
});
}
seekToTimelineItem(timeline: ObjectLifecycleSequence) {
seekToTimelineItem(timeline: TrackingDetailsSequence) {
this.playerController.pause();
this.seekToTimestamp(timeline.timestamp + this.annotationOffset);
this.setFocusedItem(timeline);

View File

@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { ObjectLifecycleSequence } from "@/types/timeline";
import { TrackingDetailsSequence } from "@/types/timeline";
import { getLifecycleItemDescription } from "@/utils/lifecycleUtil";
import { useDetailStream } from "@/context/detail-stream-context";
import scrollIntoView from "scroll-into-view-if-needed";
@ -553,7 +553,7 @@ function EventList({
}
type LifecycleItemProps = {
item: ObjectLifecycleSequence;
item: TrackingDetailsSequence;
isActive?: boolean;
onSeek?: (timestamp: number, play?: boolean) => void;
effectiveTime?: number;
@ -650,14 +650,14 @@ function LifecycleItem({
<div className="flex flex-col gap-1">
<div className="flex items-start gap-1">
<span className="text-muted-foreground">
{t("objectLifecycle.lifecycleItemDesc.header.ratio")}
{t("trackingDetails.lifecycleItemDesc.header.ratio")}
</span>
<span className="font-medium text-foreground">{ratio}</span>
</div>
<div className="flex items-start gap-1">
<span className="text-muted-foreground">
{t("objectLifecycle.lifecycleItemDesc.header.area")}
{t("trackingDetails.lifecycleItemDesc.header.area")}
</span>
{areaPx !== undefined && areaPct !== undefined ? (
<span className="font-medium text-foreground">
@ -697,7 +697,7 @@ function ObjectTimeline({
endTime?: number;
}) {
const { t } = useTranslation("views/events");
const { data: timeline, isValidating } = useSWR<ObjectLifecycleSequence[]>([
const { data: timeline, isValidating } = useSWR<TrackingDetailsSequence[]>([
"timeline",
{
source_id: eventId,

View File

@ -212,13 +212,13 @@ const CarouselPrevious = React.forwardRef<
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
className,
)}
aria-label={t("objectLifecycle.carousel.previous")}
aria-label={t("trackingDetails.carousel.previous")}
disabled={!canScrollPrev}
onClick={scrollPrev}
{...props}
>
<ArrowLeft className="h-4 w-4" />
<span className="sr-only">{t("objectLifecycle.carousel.previous")}</span>
<span className="sr-only">{t("trackingDetails.carousel.previous")}</span>
</Button>
);
});
@ -243,13 +243,13 @@ const CarouselNext = React.forwardRef<
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
className,
)}
aria-label={t("objectLifecycle.carousel.next")}
aria-label={t("trackingDetails.carousel.next")}
disabled={!canScrollNext}
onClick={scrollNext}
{...props}
>
<ArrowRight className="h-4 w-4" />
<span className="sr-only">{t("objectLifecycle.carousel.next")}</span>
<span className="sr-only">{t("trackingDetails.carousel.next")}</span>
</Button>
);
});

View File

@ -10,7 +10,7 @@ export enum LifecycleClassType {
PATH_POINT = "path_point",
}
export type ObjectLifecycleSequence = {
export type TrackingDetailsSequence = {
camera: string;
timestamp: number;
data: {
@ -38,5 +38,5 @@ export type Position = {
x: number;
y: number;
timestamp: number;
lifecycle_item?: ObjectLifecycleSequence;
lifecycle_item?: TrackingDetailsSequence;
};

View File

@ -1,10 +1,10 @@
import { ObjectLifecycleSequence } from "@/types/timeline";
import { TrackingDetailsSequence } from "@/types/timeline";
import { t } from "i18next";
import { getTranslatedLabel } from "./i18n";
import { capitalizeFirstLetter } from "./stringUtil";
export function getLifecycleItemDescription(
lifecycleItem: ObjectLifecycleSequence,
lifecycleItem: TrackingDetailsSequence,
) {
const rawLabel = Array.isArray(lifecycleItem.data.sub_label)
? lifecycleItem.data.sub_label[0]
@ -16,23 +16,23 @@ export function getLifecycleItemDescription(
switch (lifecycleItem.class_type) {
case "visible":
return t("objectLifecycle.lifecycleItemDesc.visible", {
return t("trackingDetails.lifecycleItemDesc.visible", {
ns: "views/explore",
label,
});
case "entered_zone":
return t("objectLifecycle.lifecycleItemDesc.entered_zone", {
return t("trackingDetails.lifecycleItemDesc.entered_zone", {
ns: "views/explore",
label,
zones: lifecycleItem.data.zones.join(" and ").replaceAll("_", " "),
});
case "active":
return t("objectLifecycle.lifecycleItemDesc.active", {
return t("trackingDetails.lifecycleItemDesc.active", {
ns: "views/explore",
label,
});
case "stationary":
return t("objectLifecycle.lifecycleItemDesc.stationary", {
return t("trackingDetails.lifecycleItemDesc.stationary", {
ns: "views/explore",
label,
});
@ -43,7 +43,7 @@ export function getLifecycleItemDescription(
lifecycleItem.data.attribute == "license_plate"
) {
title = t(
"objectLifecycle.lifecycleItemDesc.attribute.faceOrLicense_plate",
"trackingDetails.lifecycleItemDesc.attribute.faceOrLicense_plate",
{
ns: "views/explore",
label,
@ -53,7 +53,7 @@ export function getLifecycleItemDescription(
},
);
} else {
title = t("objectLifecycle.lifecycleItemDesc.attribute.other", {
title = t("trackingDetails.lifecycleItemDesc.attribute.other", {
ns: "views/explore",
label: lifecycleItem.data.label,
attribute: getTranslatedLabel(
@ -64,17 +64,17 @@ export function getLifecycleItemDescription(
return title;
}
case "gone":
return t("objectLifecycle.lifecycleItemDesc.gone", {
return t("trackingDetails.lifecycleItemDesc.gone", {
ns: "views/explore",
label,
});
case "heard":
return t("objectLifecycle.lifecycleItemDesc.heard", {
return t("trackingDetails.lifecycleItemDesc.heard", {
ns: "views/explore",
label,
});
case "external":
return t("objectLifecycle.lifecycleItemDesc.external", {
return t("trackingDetails.lifecycleItemDesc.external", {
ns: "views/explore",
label,
});

View File

@ -232,8 +232,8 @@ function ExploreThumbnailImage({
}
};
const handleShowObjectLifecycle = () => {
onSelectSearch(event, false, "object_lifecycle");
const handleShowTrackingDetails = () => {
onSelectSearch(event, false, "tracking_details");
};
const handleShowSnapshot = () => {
@ -251,7 +251,7 @@ function ExploreThumbnailImage({
searchResult={event}
findSimilar={handleFindSimilar}
refreshResults={mutate}
showObjectLifecycle={handleShowObjectLifecycle}
showTrackingDetails={handleShowTrackingDetails}
showSnapshot={handleShowSnapshot}
addTrigger={handleAddTrigger}
isContextMenu={true}

View File

@ -644,8 +644,8 @@ export default function SearchView({
}
}}
refreshResults={refresh}
showObjectLifecycle={() =>
onSelectSearch(value, false, "object_lifecycle")
showTrackingDetails={() =>
onSelectSearch(value, false, "tracking_details")
}
showSnapshot={() =>
onSelectSearch(value, false, "snapshot")