mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Improve stats (#10911)
* Add overview stats for overall detection and skipped fps * Fix intel memory stats * Fix iOS image long pressing * Cleanup
This commit is contained in:
		
							parent
							
								
									15e4f5c771
								
							
						
					
					
						commit
						524732ec73
					
				@ -13,13 +13,14 @@ import { getIconForLabel } from "@/utils/iconUtil";
 | 
				
			|||||||
import TimeAgo from "../dynamic/TimeAgo";
 | 
					import TimeAgo from "../dynamic/TimeAgo";
 | 
				
			||||||
import useSWR from "swr";
 | 
					import useSWR from "swr";
 | 
				
			||||||
import { FrigateConfig } from "@/types/frigateConfig";
 | 
					import { FrigateConfig } from "@/types/frigateConfig";
 | 
				
			||||||
import { isFirefox, isMobile, isSafari } from "react-device-detect";
 | 
					import { isFirefox, isIOS, isMobile, isSafari } from "react-device-detect";
 | 
				
			||||||
import Chip from "@/components/indicators/Chip";
 | 
					import Chip from "@/components/indicators/Chip";
 | 
				
			||||||
import { useFormattedTimestamp } from "@/hooks/use-date-utils";
 | 
					import { useFormattedTimestamp } from "@/hooks/use-date-utils";
 | 
				
			||||||
import useImageLoaded from "@/hooks/use-image-loaded";
 | 
					import useImageLoaded from "@/hooks/use-image-loaded";
 | 
				
			||||||
import { useSwipeable } from "react-swipeable";
 | 
					import { useSwipeable } from "react-swipeable";
 | 
				
			||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
 | 
					import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
 | 
				
			||||||
import ImageLoadingIndicator from "../indicators/ImageLoadingIndicator";
 | 
					import ImageLoadingIndicator from "../indicators/ImageLoadingIndicator";
 | 
				
			||||||
 | 
					import useContextMenu from "@/hooks/use-contextmenu";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type PreviewPlayerProps = {
 | 
					type PreviewPlayerProps = {
 | 
				
			||||||
  review: ReviewSegment;
 | 
					  review: ReviewSegment;
 | 
				
			||||||
@ -73,6 +74,10 @@ export default function PreviewThumbnailPlayer({
 | 
				
			|||||||
    setReviewed(review);
 | 
					    setReviewed(review);
 | 
				
			||||||
  }, [review, setReviewed]);
 | 
					  }, [review, setReviewed]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useContextMenu(imgRef, () => {
 | 
				
			||||||
 | 
					    onClick(review, true);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // playback
 | 
					  // playback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const relevantPreview = useMemo(() => {
 | 
					  const relevantPreview = useMemo(() => {
 | 
				
			||||||
@ -170,10 +175,6 @@ export default function PreviewThumbnailPlayer({
 | 
				
			|||||||
      className="relative size-full cursor-pointer"
 | 
					      className="relative size-full cursor-pointer"
 | 
				
			||||||
      onMouseOver={isMobile ? undefined : () => setIsHovered(true)}
 | 
					      onMouseOver={isMobile ? undefined : () => setIsHovered(true)}
 | 
				
			||||||
      onMouseLeave={isMobile ? undefined : () => setIsHovered(false)}
 | 
					      onMouseLeave={isMobile ? undefined : () => setIsHovered(false)}
 | 
				
			||||||
      onContextMenu={(e) => {
 | 
					 | 
				
			||||||
        e.preventDefault();
 | 
					 | 
				
			||||||
        onClick(review, true);
 | 
					 | 
				
			||||||
      }}
 | 
					 | 
				
			||||||
      onClick={handleOnClick}
 | 
					      onClick={handleOnClick}
 | 
				
			||||||
      {...swipeHandlers}
 | 
					      {...swipeHandlers}
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
@ -196,9 +197,18 @@ export default function PreviewThumbnailPlayer({
 | 
				
			|||||||
      <div className={`${imgLoaded ? "visible" : "invisible"}`}>
 | 
					      <div className={`${imgLoaded ? "visible" : "invisible"}`}>
 | 
				
			||||||
        <img
 | 
					        <img
 | 
				
			||||||
          ref={imgRef}
 | 
					          ref={imgRef}
 | 
				
			||||||
          className={`size-full transition-opacity ${
 | 
					          className={`size-full transition-opacity select-none ${
 | 
				
			||||||
            playingBack ? "opacity-0" : "opacity-100"
 | 
					            playingBack ? "opacity-0" : "opacity-100"
 | 
				
			||||||
          }`}
 | 
					          }`}
 | 
				
			||||||
 | 
					          style={
 | 
				
			||||||
 | 
					            isIOS
 | 
				
			||||||
 | 
					              ? {
 | 
				
			||||||
 | 
					                  WebkitUserSelect: "none",
 | 
				
			||||||
 | 
					                  WebkitTouchCallout: "none",
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              : undefined
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          draggable={false}
 | 
				
			||||||
          src={`${apiHost}${review.thumb_path.replace("/media/frigate/", "")}`}
 | 
					          src={`${apiHost}${review.thumb_path.replace("/media/frigate/", "")}`}
 | 
				
			||||||
          loading={isSafari ? "eager" : "lazy"}
 | 
					          loading={isSafari ? "eager" : "lazy"}
 | 
				
			||||||
          onLoad={() => {
 | 
					          onLoad={() => {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										46
									
								
								web/src/hooks/use-contextmenu.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								web/src/hooks/use-contextmenu.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					import { MutableRefObject, useEffect } from "react";
 | 
				
			||||||
 | 
					import { isIOS } from "react-device-detect";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function useContextMenu(
 | 
				
			||||||
 | 
					  ref: MutableRefObject<HTMLDivElement | null>,
 | 
				
			||||||
 | 
					  callback: () => void,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    if (!ref.current) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const elem = ref.current;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isIOS) {
 | 
				
			||||||
 | 
					      let timeoutId: NodeJS.Timeout;
 | 
				
			||||||
 | 
					      const touchStart = () => {
 | 
				
			||||||
 | 
					        timeoutId = setTimeout(() => {
 | 
				
			||||||
 | 
					          callback();
 | 
				
			||||||
 | 
					        }, 610);
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      const touchClear = () => {
 | 
				
			||||||
 | 
					        clearTimeout(timeoutId);
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      elem.addEventListener("touchstart", touchStart);
 | 
				
			||||||
 | 
					      elem.addEventListener("touchmove", touchClear);
 | 
				
			||||||
 | 
					      elem.addEventListener("touchend", touchClear);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return () => {
 | 
				
			||||||
 | 
					        elem.removeEventListener("touchstart", touchStart);
 | 
				
			||||||
 | 
					        elem.removeEventListener("touchmove", touchClear);
 | 
				
			||||||
 | 
					        elem.removeEventListener("touchend", touchClear);
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      const context = (e: MouseEvent) => {
 | 
				
			||||||
 | 
					        e.preventDefault();
 | 
				
			||||||
 | 
					        callback();
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      elem.addEventListener("contextmenu", context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return () => {
 | 
				
			||||||
 | 
					        elem.removeEventListener("contextmenu", context);
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [callback, ref]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -19,7 +19,7 @@ export default function CameraMetrics({
 | 
				
			|||||||
  // stats
 | 
					  // stats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { data: initialStats } = useSWR<FrigateStats[]>(
 | 
					  const { data: initialStats } = useSWR<FrigateStats[]>(
 | 
				
			||||||
    ["stats/history", { keys: "cpu_usages,cameras,service" }],
 | 
					    ["stats/history", { keys: "cpu_usages,cameras,detection_fps,service" }],
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      revalidateOnFocus: false,
 | 
					      revalidateOnFocus: false,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -57,6 +57,44 @@ export default function CameraMetrics({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // stats data
 | 
					  // stats data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const overallFpsSeries = useMemo(() => {
 | 
				
			||||||
 | 
					    if (!statsHistory) {
 | 
				
			||||||
 | 
					      return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const series: {
 | 
				
			||||||
 | 
					      [key: string]: { name: string; data: { x: number; y: number }[] };
 | 
				
			||||||
 | 
					    } = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    series["overall_dps"] = { name: "overall detections per second", data: [] };
 | 
				
			||||||
 | 
					    series["overall_skipped_dps"] = {
 | 
				
			||||||
 | 
					      name: "overall skipped detections per second",
 | 
				
			||||||
 | 
					      data: [],
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    statsHistory.forEach((stats, statsIdx) => {
 | 
				
			||||||
 | 
					      if (!stats) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      series["overall_dps"].data.push({
 | 
				
			||||||
 | 
					        x: statsIdx,
 | 
				
			||||||
 | 
					        y: stats.detection_fps,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let skipped = 0;
 | 
				
			||||||
 | 
					      Object.values(stats.cameras).forEach(
 | 
				
			||||||
 | 
					        (camStat) => (skipped += camStat.skipped_fps),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      series["overall_skipped_dps"].data.push({
 | 
				
			||||||
 | 
					        x: statsIdx,
 | 
				
			||||||
 | 
					        y: skipped,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    return Object.values(series);
 | 
				
			||||||
 | 
					  }, [statsHistory]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const cameraCpuSeries = useMemo(() => {
 | 
					  const cameraCpuSeries = useMemo(() => {
 | 
				
			||||||
    if (!statsHistory || statsHistory.length == 0) {
 | 
					    if (!statsHistory || statsHistory.length == 0) {
 | 
				
			||||||
      return {};
 | 
					      return {};
 | 
				
			||||||
@ -147,19 +185,36 @@ export default function CameraMetrics({
 | 
				
			|||||||
  }, [statsHistory]);
 | 
					  }, [statsHistory]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className="size-full mt-4 flex flex-col overflow-y-auto">
 | 
					    <div className="size-full mt-4 flex flex-col gap-3 overflow-y-auto">
 | 
				
			||||||
 | 
					      <div className="text-muted-foreground text-sm font-medium">Overview</div>
 | 
				
			||||||
 | 
					      <div className="grid grid-cols-1 md:grid-cols-3">
 | 
				
			||||||
 | 
					        {statsHistory.length != 0 ? (
 | 
				
			||||||
 | 
					          <div className="p-2.5 bg-background_alt rounded-2xl">
 | 
				
			||||||
 | 
					            <div className="mb-5">DPS</div>
 | 
				
			||||||
 | 
					            <CameraLineGraph
 | 
				
			||||||
 | 
					              graphId="overall-stats"
 | 
				
			||||||
 | 
					              unit=" DPS"
 | 
				
			||||||
 | 
					              dataLabels={["detect", "skipped"]}
 | 
				
			||||||
 | 
					              updateTimes={updateTimes}
 | 
				
			||||||
 | 
					              data={overallFpsSeries}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        ) : (
 | 
				
			||||||
 | 
					          <Skeleton className="w-full h-32" />
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
      <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
 | 
					      <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
 | 
				
			||||||
        {config &&
 | 
					        {config &&
 | 
				
			||||||
          Object.values(config.cameras).map((camera) => {
 | 
					          Object.values(config.cameras).map((camera) => {
 | 
				
			||||||
            if (camera.enabled) {
 | 
					            if (camera.enabled) {
 | 
				
			||||||
              return (
 | 
					              return (
 | 
				
			||||||
                <div className="w-full flex flex-col">
 | 
					                <div className="w-full flex flex-col gap-3">
 | 
				
			||||||
                  <div className="mb-6 capitalize">
 | 
					                  <div className="capitalize text-muted-foreground text-sm font-medium">
 | 
				
			||||||
                    {camera.name.replaceAll("_", " ")}
 | 
					                    {camera.name.replaceAll("_", " ")}
 | 
				
			||||||
                  </div>
 | 
					                  </div>
 | 
				
			||||||
                  <div key={camera.name} className="grid sm:grid-cols-2 gap-2">
 | 
					                  <div key={camera.name} className="grid sm:grid-cols-2 gap-2">
 | 
				
			||||||
                    {Object.keys(cameraCpuSeries).includes(camera.name) ? (
 | 
					                    {Object.keys(cameraCpuSeries).includes(camera.name) ? (
 | 
				
			||||||
                      <div className="p-2.5 bg-background_alt rounded-2xl flex-col">
 | 
					                      <div className="p-2.5 bg-background_alt rounded-2xl">
 | 
				
			||||||
                        <div className="mb-5">CPU</div>
 | 
					                        <div className="mb-5">CPU</div>
 | 
				
			||||||
                        <CameraLineGraph
 | 
					                        <CameraLineGraph
 | 
				
			||||||
                          graphId={`${camera.name}-cpu`}
 | 
					                          graphId={`${camera.name}-cpu`}
 | 
				
			||||||
@ -175,7 +230,7 @@ export default function CameraMetrics({
 | 
				
			|||||||
                      <Skeleton className="size-full aspect-video" />
 | 
					                      <Skeleton className="size-full aspect-video" />
 | 
				
			||||||
                    )}
 | 
					                    )}
 | 
				
			||||||
                    {Object.keys(cameraFpsSeries).includes(camera.name) ? (
 | 
					                    {Object.keys(cameraFpsSeries).includes(camera.name) ? (
 | 
				
			||||||
                      <div className="p-2.5 bg-background_alt rounded-2xl flex-col">
 | 
					                      <div className="p-2.5 bg-background_alt rounded-2xl">
 | 
				
			||||||
                        <div className="mb-5">DPS</div>
 | 
					                        <div className="mb-5">DPS</div>
 | 
				
			||||||
                        <CameraLineGraph
 | 
					                        <CameraLineGraph
 | 
				
			||||||
                          graphId={`${camera.name}-dps`}
 | 
					                          graphId={`${camera.name}-dps`}
 | 
				
			||||||
 | 
				
			|||||||
@ -193,6 +193,14 @@ export default function GeneralMetrics({
 | 
				
			|||||||
      return [];
 | 
					      return [];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (
 | 
				
			||||||
 | 
					      Object.keys(statsHistory?.at(0)?.gpu_usages ?? {}).length == 1 &&
 | 
				
			||||||
 | 
					      Object.keys(statsHistory?.at(0)?.gpu_usages ?? {})[0].includes("intel")
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      // intel gpu stats do not support memory
 | 
				
			||||||
 | 
					      return undefined;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const series: {
 | 
					    const series: {
 | 
				
			||||||
      [key: string]: { name: string; data: { x: number; y: string }[] };
 | 
					      [key: string]: { name: string; data: { x: number; y: string }[] };
 | 
				
			||||||
    } = {};
 | 
					    } = {};
 | 
				
			||||||
@ -285,7 +293,7 @@ export default function GeneralMetrics({
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div className="w-full mt-4 grid grid-cols-1 sm:grid-cols-3 gap-2">
 | 
					        <div className="w-full mt-4 grid grid-cols-1 sm:grid-cols-3 gap-2">
 | 
				
			||||||
          {statsHistory.length != 0 ? (
 | 
					          {statsHistory.length != 0 ? (
 | 
				
			||||||
            <div className="p-2.5 bg-background_alt rounded-2xl flex-col">
 | 
					            <div className="p-2.5 bg-background_alt rounded-2xl">
 | 
				
			||||||
              <div className="mb-5">Detector Inference Speed</div>
 | 
					              <div className="mb-5">Detector Inference Speed</div>
 | 
				
			||||||
              {detInferenceTimeSeries.map((series) => (
 | 
					              {detInferenceTimeSeries.map((series) => (
 | 
				
			||||||
                <ThresholdBarGraph
 | 
					                <ThresholdBarGraph
 | 
				
			||||||
@ -303,7 +311,7 @@ export default function GeneralMetrics({
 | 
				
			|||||||
            <Skeleton className="w-full aspect-video" />
 | 
					            <Skeleton className="w-full aspect-video" />
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
          {statsHistory.length != 0 ? (
 | 
					          {statsHistory.length != 0 ? (
 | 
				
			||||||
            <div className="p-2.5 bg-background_alt rounded-2xl flex-col">
 | 
					            <div className="p-2.5 bg-background_alt rounded-2xl">
 | 
				
			||||||
              <div className="mb-5">Detector CPU Usage</div>
 | 
					              <div className="mb-5">Detector CPU Usage</div>
 | 
				
			||||||
              {detCpuSeries.map((series) => (
 | 
					              {detCpuSeries.map((series) => (
 | 
				
			||||||
                <ThresholdBarGraph
 | 
					                <ThresholdBarGraph
 | 
				
			||||||
@ -321,7 +329,7 @@ export default function GeneralMetrics({
 | 
				
			|||||||
            <Skeleton className="w-full aspect-video" />
 | 
					            <Skeleton className="w-full aspect-video" />
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
          {statsHistory.length != 0 ? (
 | 
					          {statsHistory.length != 0 ? (
 | 
				
			||||||
            <div className="p-2.5 bg-background_alt rounded-2xl flex-col">
 | 
					            <div className="p-2.5 bg-background_alt rounded-2xl">
 | 
				
			||||||
              <div className="mb-5">Detector Memory Usage</div>
 | 
					              <div className="mb-5">Detector Memory Usage</div>
 | 
				
			||||||
              {detMemSeries.map((series) => (
 | 
					              {detMemSeries.map((series) => (
 | 
				
			||||||
                <ThresholdBarGraph
 | 
					                <ThresholdBarGraph
 | 
				
			||||||
@ -358,7 +366,7 @@ export default function GeneralMetrics({
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div className=" mt-4 grid grid-cols-1 sm:grid-cols-2 gap-2">
 | 
					            <div className=" mt-4 grid grid-cols-1 sm:grid-cols-2 gap-2">
 | 
				
			||||||
              {statsHistory.length != 0 ? (
 | 
					              {statsHistory.length != 0 ? (
 | 
				
			||||||
                <div className="p-2.5 bg-background_alt rounded-2xl flex-col">
 | 
					                <div className="p-2.5 bg-background_alt rounded-2xl">
 | 
				
			||||||
                  <div className="mb-5">GPU Usage</div>
 | 
					                  <div className="mb-5">GPU Usage</div>
 | 
				
			||||||
                  {gpuSeries.map((series) => (
 | 
					                  {gpuSeries.map((series) => (
 | 
				
			||||||
                    <ThresholdBarGraph
 | 
					                    <ThresholdBarGraph
 | 
				
			||||||
@ -376,7 +384,9 @@ export default function GeneralMetrics({
 | 
				
			|||||||
                <Skeleton className="w-full aspect-video" />
 | 
					                <Skeleton className="w-full aspect-video" />
 | 
				
			||||||
              )}
 | 
					              )}
 | 
				
			||||||
              {statsHistory.length != 0 ? (
 | 
					              {statsHistory.length != 0 ? (
 | 
				
			||||||
                <div className="p-2.5 bg-background_alt rounded-2xl flex-col">
 | 
					                <>
 | 
				
			||||||
 | 
					                  {gpuMemSeries && (
 | 
				
			||||||
 | 
					                    <div className="p-2.5 bg-background_alt rounded-2xl">
 | 
				
			||||||
                      <div className="mb-5">GPU Memory</div>
 | 
					                      <div className="mb-5">GPU Memory</div>
 | 
				
			||||||
                      {gpuMemSeries.map((series) => (
 | 
					                      {gpuMemSeries.map((series) => (
 | 
				
			||||||
                        <ThresholdBarGraph
 | 
					                        <ThresholdBarGraph
 | 
				
			||||||
@ -390,6 +400,8 @@ export default function GeneralMetrics({
 | 
				
			|||||||
                        />
 | 
					                        />
 | 
				
			||||||
                      ))}
 | 
					                      ))}
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  )}
 | 
				
			||||||
 | 
					                </>
 | 
				
			||||||
              ) : (
 | 
					              ) : (
 | 
				
			||||||
                <Skeleton className="w-full aspect-video" />
 | 
					                <Skeleton className="w-full aspect-video" />
 | 
				
			||||||
              )}
 | 
					              )}
 | 
				
			||||||
@ -402,7 +414,7 @@ export default function GeneralMetrics({
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div className="mt-4 grid grid-cols-1 sm:grid-cols-2 gap-2">
 | 
					        <div className="mt-4 grid grid-cols-1 sm:grid-cols-2 gap-2">
 | 
				
			||||||
          {statsHistory.length != 0 ? (
 | 
					          {statsHistory.length != 0 ? (
 | 
				
			||||||
            <div className="p-2.5 bg-background_alt rounded-2xl flex-col">
 | 
					            <div className="p-2.5 bg-background_alt rounded-2xl">
 | 
				
			||||||
              <div className="mb-5">Process CPU Usage</div>
 | 
					              <div className="mb-5">Process CPU Usage</div>
 | 
				
			||||||
              {otherProcessCpuSeries.map((series) => (
 | 
					              {otherProcessCpuSeries.map((series) => (
 | 
				
			||||||
                <ThresholdBarGraph
 | 
					                <ThresholdBarGraph
 | 
				
			||||||
@ -420,7 +432,7 @@ export default function GeneralMetrics({
 | 
				
			|||||||
            <Skeleton className="w-full aspect-tall" />
 | 
					            <Skeleton className="w-full aspect-tall" />
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
          {statsHistory.length != 0 ? (
 | 
					          {statsHistory.length != 0 ? (
 | 
				
			||||||
            <div className="p-2.5 bg-background_alt rounded-2xl flex-col">
 | 
					            <div className="p-2.5 bg-background_alt rounded-2xl">
 | 
				
			||||||
              <div className="mb-5">Process Memory Usage</div>
 | 
					              <div className="mb-5">Process Memory Usage</div>
 | 
				
			||||||
              {otherProcessMemSeries.map((series) => (
 | 
					              {otherProcessMemSeries.map((series) => (
 | 
				
			||||||
                <ThresholdBarGraph
 | 
					                <ThresholdBarGraph
 | 
				
			||||||
 | 
				
			|||||||
@ -43,9 +43,7 @@ export default function StorageMetrics({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className="size-full mt-4 flex flex-col overflow-y-auto">
 | 
					    <div className="size-full mt-4 flex flex-col overflow-y-auto">
 | 
				
			||||||
      <div className="text-muted-foreground text-sm font-medium">
 | 
					      <div className="text-muted-foreground text-sm font-medium">Overview</div>
 | 
				
			||||||
        General Storage
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <div className="mt-4 grid grid-cols-1 sm:grid-cols-3 gap-2">
 | 
					      <div className="mt-4 grid grid-cols-1 sm:grid-cols-3 gap-2">
 | 
				
			||||||
        <div className="p-2.5 bg-background_alt rounded-2xl flex-col">
 | 
					        <div className="p-2.5 bg-background_alt rounded-2xl flex-col">
 | 
				
			||||||
          <div className="mb-5">Recordings</div>
 | 
					          <div className="mb-5">Recordings</div>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user