reorganize components

This commit is contained in:
Josh Hawkins 2024-02-20 09:32:53 -06:00
parent 920f76c97c
commit 24291a2bc2
3 changed files with 143 additions and 89 deletions

View File

@ -11,7 +11,7 @@ import EventSegment from "./EventSegment";
import { useEventUtils } from "@/hooks/use-event-utils"; import { useEventUtils } from "@/hooks/use-event-utils";
import { Event } from "@/types/event"; import { Event } from "@/types/event";
export type ReviewTimelineProps = { export type EventReviewTimelineProps = {
segmentDuration: number; segmentDuration: number;
timestampSpread: number; timestampSpread: number;
timelineStart: number; timelineStart: number;
@ -26,7 +26,7 @@ export type ReviewTimelineProps = {
contentRef: RefObject<HTMLDivElement>; contentRef: RefObject<HTMLDivElement>;
}; };
export function ReviewTimeline({ export function EventReviewTimeline({
segmentDuration, segmentDuration,
timestampSpread, timestampSpread,
timelineStart, timelineStart,
@ -39,7 +39,7 @@ export function ReviewTimeline({
events, events,
severityType, severityType,
contentRef, contentRef,
}: ReviewTimelineProps) { }: EventReviewTimelineProps) {
const [isDragging, setIsDragging] = useState(false); const [isDragging, setIsDragging] = useState(false);
const [currentTimeSegment, setCurrentTimeSegment] = useState<number>(0); const [currentTimeSegment, setCurrentTimeSegment] = useState<number>(0);
const scrollTimeRef = useRef<HTMLDivElement>(null); const scrollTimeRef = useRef<HTMLDivElement>(null);
@ -203,7 +203,7 @@ export function ReviewTimeline({
return ( return (
<div <div
ref={timelineRef} ref={timelineRef}
className={`relative w-[100px] h-screen overflow-y-scroll no-scrollbar ${ className={`relative md:w-[100px] h-screen overflow-y-scroll no-scrollbar ${
isDragging && showHandlebar ? "cursor-grabbing" : "cursor-auto" isDragging && showHandlebar ? "cursor-grabbing" : "cursor-auto"
}`} }`}
> >
@ -237,4 +237,4 @@ export function ReviewTimeline({
); );
} }
export default ReviewTimeline; export default EventReviewTimeline;

View File

@ -14,6 +14,109 @@ type EventSegmentProps = {
severityType: string; severityType: string;
}; };
type MinimapSegmentProps = {
isFirstSegmentInMinimap: boolean;
isLastSegmentInMinimap: boolean;
alignedMinimapStartTime: number;
alignedMinimapEndTime: number;
};
type TickSegmentProps = {
isFirstSegmentInMinimap: boolean;
isLastSegmentInMinimap: boolean;
timestamp: Date;
timestampSpread: number;
};
type TimestampSegmentProps = {
isFirstSegmentInMinimap: boolean;
isLastSegmentInMinimap: boolean;
timestamp: Date;
timestampSpread: number;
segmentKey: number;
};
function MinimapBounds({
isFirstSegmentInMinimap,
isLastSegmentInMinimap,
alignedMinimapStartTime,
alignedMinimapEndTime,
}: MinimapSegmentProps) {
return (
<>
{isFirstSegmentInMinimap && (
<div className="absolute inset-0 -bottom-5 w-full flex items-center justify-center text-xs text-primary font-medium z-20 text-center text-[9px]">
{new Date(alignedMinimapStartTime).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
month: "short",
day: "2-digit",
})}
</div>
)}
{isLastSegmentInMinimap && (
<div className="absolute inset-0 -top-1 w-full flex items-center justify-center text-xs text-primary font-medium z-20 text-center text-[9px]">
{new Date(alignedMinimapEndTime).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
month: "short",
day: "2-digit",
})}
</div>
)}
</>
);
}
function Tick({
isFirstSegmentInMinimap,
isLastSegmentInMinimap,
timestamp,
timestampSpread,
}: TickSegmentProps) {
return (
<div className="w-5 h-2 flex justify-left items-end">
{!isFirstSegmentInMinimap && !isLastSegmentInMinimap && (
<div
className={`h-0.5 ${
timestamp.getMinutes() % timestampSpread === 0 &&
timestamp.getSeconds() === 0
? "w-4 bg-gray-400"
: "w-2 bg-gray-600"
}`}
></div>
)}
</div>
);
}
function Timestamp({
isFirstSegmentInMinimap,
isLastSegmentInMinimap,
timestamp,
timestampSpread,
segmentKey,
}: TimestampSegmentProps) {
return (
<div className="w-10 h-2 flex justify-left items-top z-10">
{!isFirstSegmentInMinimap && !isLastSegmentInMinimap && (
<div
key={`${segmentKey}_timestamp`}
className="text-[8px] text-gray-400"
>
{timestamp.getMinutes() % timestampSpread === 0 &&
timestamp.getSeconds() === 0 &&
timestamp.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}
</div>
)}
</div>
);
}
export function EventSegment({ export function EventSegment({
events, events,
segmentTime, segmentTime,
@ -97,56 +200,39 @@ export function EventSegment({
: "" : ""
}`; }`;
const severityColors: { [key: number]: string } = {
1: reviewed
? "from-yellow-200/30 to-yellow-400/30"
: "from-yellow-200 to-yellow-400",
2: reviewed
? "from-orange-400/30 to-orange-600/30"
: "from-orange-400 to-orange-600",
3: reviewed ? "from-red-500/30 to-red-800/30" : "from-red-500 to-red-800",
};
return ( return (
<div key={segmentKey} className={segmentClasses}> <div key={segmentKey} className={segmentClasses}>
{isFirstSegmentInMinimap && ( <MinimapBounds
<div className="absolute inset-0 -bottom-5 w-full flex items-center justify-center text-xs text-primary font-medium z-20 text-center text-[9px]"> isFirstSegmentInMinimap={isFirstSegmentInMinimap}
{new Date(alignedMinimapStartTime).toLocaleTimeString([], { isLastSegmentInMinimap={isLastSegmentInMinimap}
hour: "2-digit", alignedMinimapStartTime={alignedMinimapStartTime}
minute: "2-digit", alignedMinimapEndTime={alignedMinimapEndTime}
month: "short", />
day: "2-digit",
})}
</div>
)}
{isLastSegmentInMinimap && ( <Tick
<div className="absolute inset-0 -top-1 w-full flex items-center justify-center text-xs text-primary font-medium z-20 text-center text-[9px]"> isFirstSegmentInMinimap={isFirstSegmentInMinimap}
{new Date(alignedMinimapEndTime).toLocaleTimeString([], { isLastSegmentInMinimap={isLastSegmentInMinimap}
hour: "2-digit", timestamp={timestamp}
minute: "2-digit", timestampSpread={timestampSpread}
month: "short", />
day: "2-digit",
})} <Timestamp
</div> isFirstSegmentInMinimap={isFirstSegmentInMinimap}
)} isLastSegmentInMinimap={isLastSegmentInMinimap}
<div className="w-5 h-2 flex justify-left items-end"> timestamp={timestamp}
{!isFirstSegmentInMinimap && !isLastSegmentInMinimap && ( timestampSpread={timestampSpread}
<div segmentKey={segmentKey}
className={`h-0.5 ${ />
timestamp.getMinutes() % timestampSpread === 0 &&
timestamp.getSeconds() === 0
? "w-4 bg-gray-400"
: "w-2 bg-gray-600"
}`}
></div>
)}
</div>
<div className="w-10 h-2 flex justify-left items-top z-10">
{!isFirstSegmentInMinimap && !isLastSegmentInMinimap && (
<div
key={`${segmentKey}_timestamp`}
className="text-[8px] text-gray-400"
>
{timestamp.getMinutes() % timestampSpread === 0 &&
timestamp.getSeconds() === 0 &&
timestamp.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}
</div>
)}
</div>
{severity == displaySeverityType && ( {severity == displaySeverityType && (
<div className="mr-3 w-2 h-2 flex justify-left items-end"> <div className="mr-3 w-2 h-2 flex justify-left items-end">
@ -164,23 +250,7 @@ export function EventSegment({
? "rounded-tl-full rounded-tr-full" ? "rounded-tl-full rounded-tr-full"
: "" : ""
} }
${ ${severityColors[severity]}
reviewed
? severity === 1
? "from-yellow-200/30 to-yellow-400/30"
: severity === 2
? "from-orange-400/30 to-orange-600/30"
: severity === 3
? "from-red-500/30 to-red-800/30"
: ""
: severity === 1
? "from-yellow-200 to-yellow-400"
: severity === 2
? "from-orange-400 to-orange-600"
: severity === 3
? "from-red-500 to-red-800"
: ""
}
`} `}
></div> ></div>
@ -203,23 +273,7 @@ export function EventSegment({
? "rounded-tl-full rounded-tr-full" ? "rounded-tl-full rounded-tr-full"
: "" : ""
} }
${ ${severityColors[severity]}
reviewed
? severity === 1
? "from-yellow-200/30 to-yellow-400/30"
: severity === 2
? "from-orange-400/30 to-orange-600/30"
: severity === 3
? "from-red-500/30 to-red-800/30"
: ""
: severity === 1
? "from-yellow-200 to-yellow-400"
: severity === 2
? "from-orange-400 to-orange-600"
: severity === 3
? "from-red-500 to-red-800"
: ""
}
`} `}
></div> ></div>

View File

@ -9,7 +9,7 @@ import { Event } from "@/types/event";
import ActivityIndicator from "@/components/ui/activity-indicator"; import ActivityIndicator from "@/components/ui/activity-indicator";
import { useApiHost } from "@/api"; import { useApiHost } from "@/api";
import TimelineScrubber from "@/components/playground/TimelineScrubber"; import TimelineScrubber from "@/components/playground/TimelineScrubber";
import { ReviewTimeline } from "@/components/timeline/ReviewTimeline"; import EventReviewTimeline from "@/components/timeline/EventReviewTimeline";
// Color data // Color data
const colors = [ const colors = [
@ -60,7 +60,7 @@ function eventsToScrubberItems(events: Event[]): ScrubberItem[] {
const generateRandomEvent = (): Event => { const generateRandomEvent = (): Event => {
const start_time = Date.now() - Math.random() * 3600000 * 3; const start_time = Date.now() - Math.random() * 3600000 * 3;
const end_time = start_time + Math.random() * 36000; const end_time = start_time + Math.random() * 360000;
const severities = ["motion", "detection", "alert"]; const severities = ["motion", "detection", "alert"];
const severity = severities[Math.floor(Math.random() * severities.length)]; const severity = severities[Math.floor(Math.random() * severities.length)];
const has_been_reviewed = Math.random() < 0.2; const has_been_reviewed = Math.random() < 0.2;
@ -158,7 +158,7 @@ function UIPlayground() {
</div> </div>
</div> </div>
<div className="flex-none"> <div className="flex-none">
<ReviewTimeline <EventReviewTimeline
segmentDuration={60} // seconds per segment segmentDuration={60} // seconds per segment
timestampSpread={15} // minutes between each major timestamp timestampSpread={15} // minutes between each major timestamp
timelineStart={Date.now()} // start of the timeline - all times are numeric, not Date objects timelineStart={Date.now()} // start of the timeline - all times are numeric, not Date objects