* spacing tweaks and add link to explore for plate

* clear selected objects when changing cameras

* plate link and spacing in object lifecycle

* set tabindex to prevent tooltip from showing on reopen

* show month and day in object lifecycle timestamp
This commit is contained in:
Josh Hawkins 2025-10-26 07:27:07 -05:00 committed by GitHub
parent 2c480b9a89
commit 840d567d22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 55 additions and 27 deletions

View File

@ -41,7 +41,7 @@ import {
ContextMenuItem, ContextMenuItem,
ContextMenuTrigger, ContextMenuTrigger,
} from "@/components/ui/context-menu"; } from "@/components/ui/context-menu";
import { useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import { ObjectPath } from "./ObjectPath"; import { ObjectPath } from "./ObjectPath";
import { getLifecycleItemDescription } from "@/utils/lifecycleUtil"; import { getLifecycleItemDescription } from "@/utils/lifecycleUtil";
import { IoPlayCircleOutline } from "react-icons/io5"; import { IoPlayCircleOutline } from "react-icons/io5";
@ -289,10 +289,10 @@ export default function ObjectLifecycle({
timezone: config.ui.timezone, timezone: config.ui.timezone,
date_format: date_format:
config.ui.time_format == "24hour" config.ui.time_format == "24hour"
? t("time.formattedTimestampHourMinuteSecond.24hour", { ? t("time.formattedTimestamp.24hour", {
ns: "common", ns: "common",
}) })
: t("time.formattedTimestampHourMinuteSecond.12hour", { : t("time.formattedTimestamp.12hour", {
ns: "common", ns: "common",
}), }),
time_style: "medium", time_style: "medium",
@ -305,10 +305,10 @@ export default function ObjectLifecycle({
timezone: config.ui.timezone, timezone: config.ui.timezone,
date_format: date_format:
config.ui.time_format == "24hour" config.ui.time_format == "24hour"
? t("time.formattedTimestampHourMinuteSecond.24hour", { ? t("time.formattedTimestamp.24hour", {
ns: "common", ns: "common",
}) })
: t("time.formattedTimestampHourMinuteSecond.12hour", { : t("time.formattedTimestamp.12hour", {
ns: "common", ns: "common",
}), }),
time_style: "medium", time_style: "medium",
@ -412,6 +412,7 @@ export default function ObjectLifecycle({
return ( return (
<div className={className}> <div className={className}>
<span tabIndex={0} className="sr-only" />
{!fullscreen && ( {!fullscreen && (
<div className={cn("flex items-center gap-2")}> <div className={cn("flex items-center gap-2")}>
<Button <Button
@ -649,10 +650,15 @@ export default function ObjectLifecycle({
</span> </span>
{event.data?.recognized_license_plate && ( {event.data?.recognized_license_plate && (
<> <>
·{" "} <span className="text-secondary-foreground">·</span>
<span className="text-sm text-secondary-foreground"> <div className="text-sm text-secondary-foreground">
{event.data.recognized_license_plate} <Link
</span> to={`/explore?recognized_license_plate=${event.data.recognized_license_plate}`}
className="text-sm"
>
{event.data.recognized_license_plate}
</Link>
</div>
</> </>
)} )}
</div> </div>
@ -832,10 +838,12 @@ function LifecycleIconRow({
/> />
</div> </div>
<div className="flex w-full flex-row justify-between"> <div className="ml-2 flex w-full min-w-0 flex-1">
<div className="flex flex-col"> <div className="flex flex-col">
<div>{getLifecycleItemDescription(item)}</div> <div className="text-md flex items-start break-words text-left">
<div className="mt-1 flex flex-wrap items-center gap-2 text-sm text-secondary-foreground md:gap-5"> {getLifecycleItemDescription(item)}
</div>
<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"> <div className="flex items-center gap-1">
<span className="text-primary-variant"> <span className="text-primary-variant">
{t("objectLifecycle.lifecycleItemDesc.header.ratio")} {t("objectLifecycle.lifecycleItemDesc.header.ratio")}
@ -893,8 +901,9 @@ function LifecycleIconRow({
)} )}
</div> </div>
</div> </div>
</div>
<div className={cn("p-1 text-sm")}>{formattedEventTimestamp}</div> <div className="ml-3 flex-shrink-0 px-1 text-right text-xs text-primary-variant">
<div className="whitespace-nowrap">{formattedEventTimestamp}</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -22,6 +22,7 @@ import EventMenu from "@/components/timeline/EventMenu";
import { FrigatePlusDialog } from "@/components/overlay/dialog/FrigatePlusDialog"; import { FrigatePlusDialog } from "@/components/overlay/dialog/FrigatePlusDialog";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
import { Link } from "react-router-dom";
type DetailStreamProps = { type DetailStreamProps = {
reviewItems?: ReviewSegment[]; reviewItems?: ReviewSegment[];
@ -499,15 +500,22 @@ function EventList({
}} }}
role="button" role="button"
> >
<span className="capitalize">{label}</span> <div className="flex gap-2">
{event.data?.recognized_license_plate && ( <span className="capitalize">{label}</span>
<> {event.data?.recognized_license_plate && (
·{" "} <>
<span className="text-sm text-secondary-foreground"> <span className="text-secondary-foreground">·</span>
{event.data.recognized_license_plate} <div className="text-sm text-secondary-foreground">
</span> <Link
</> to={`/explore?recognized_license_plate=${event.data.recognized_license_plate}`}
)} className="text-sm"
>
{event.data.recognized_license_plate}
</Link>
</div>
</>
)}
</div>
</div> </div>
</div> </div>
<div className="mr-2 flex flex-row justify-end"> <div className="mr-2 flex flex-row justify-end">
@ -615,10 +623,11 @@ function LifecycleItem({
)} )}
/> />
</div> </div>
<div className="flex w-full flex-row justify-between">
<div className="ml-0.5 flex min-w-0 flex-1">
<Tooltip> <Tooltip>
<TooltipTrigger> <TooltipTrigger>
<div className="flex items-start text-left"> <div className="flex items-start break-words text-left">
{getLifecycleItemDescription(item)} {getLifecycleItemDescription(item)}
</div> </div>
</TooltipTrigger> </TooltipTrigger>
@ -638,7 +647,9 @@ function LifecycleItem({
</span> </span>
{areaPx !== undefined && areaPct !== undefined ? ( {areaPx !== undefined && areaPct !== undefined ? (
<span className="font-medium text-foreground"> <span className="font-medium text-foreground">
{areaPx} {t("pixels", { ns: "common" })} · {areaPct}% {areaPx} {t("pixels", { ns: "common" })}{" "}
<span className="text-secondary-foreground">·</span>{" "}
{areaPct}%
</span> </span>
) : ( ) : (
<span>N/A</span> <span>N/A</span>
@ -648,7 +659,10 @@ function LifecycleItem({
</div> </div>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
<div className={cn("p-1 text-xs")}>{formattedEventTimestamp}</div> </div>
<div className="ml-3 flex-shrink-0 px-1 text-right text-xs text-primary-variant">
<div className="whitespace-nowrap">{formattedEventTimestamp}</div>
</div> </div>
</div> </div>
); );

View File

@ -58,6 +58,11 @@ export function DetailStreamProvider({
setAnnotationOffset(cfgOffset); setAnnotationOffset(cfgOffset);
}, [config, camera]); }, [config, camera]);
// Clear selected objects when exiting detail mode or changing cameras
useEffect(() => {
setSelectedObjectIds([]);
}, [isDetailMode, camera]);
const value: DetailStreamContextType = { const value: DetailStreamContextType = {
selectedObjectIds, selectedObjectIds,
currentTime, currentTime,