* Catch error and show toast when failing to delete review items

* i18n keys

* add link to speed estimation docs in zone edit pane

* Implement reset of tracked object update for each camera

* Cleanup

* register mqtt callbacks for toggling alerts and detections

* clarify snapshots docs

* clarify semantic search reindexing

* add ukrainian

* adjust date granularity for last recording time

The api endpoint only returns granularity down to the day

* Add amd hardware

* fix crash in face library on initial start after enabling

* Fix recordings view for mobile landscape

The events view incorrectly was displaying two columns on landscape view and it only took up 20% of the screen width. Additionally, in landscape view the timeline was too wide (especially on iPads of various screen sizes) and would overlap the main video

* face rec overfitting instructions

* Clarify

* face docs

* clarify

* clarify

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
This commit is contained in:
Josh Hawkins
2025-05-11 12:03:53 -06:00
committed by GitHub
co-authored by Nicolas Mowen
parent 8094dd4075
commit f39ddbc00d
21 changed files with 130 additions and 25 deletions
@@ -17,6 +17,7 @@ import {
} from "../ui/alert-dialog";
import useKeyboardListener from "@/hooks/use-keyboard-listener";
import { Trans, useTranslation } from "react-i18next";
import { toast } from "sonner";
type ReviewActionGroupProps = {
selectedReviews: string[];
@@ -41,11 +42,33 @@ export default function ReviewActionGroup({
pullLatestData();
}, [selectedReviews, setSelectedReviews, pullLatestData]);
const onDelete = useCallback(async () => {
await axios.post(`reviews/delete`, { ids: selectedReviews });
setSelectedReviews([]);
pullLatestData();
}, [selectedReviews, setSelectedReviews, pullLatestData]);
const onDelete = useCallback(() => {
axios
.post(`reviews/delete`, { ids: selectedReviews })
.then((resp) => {
if (resp.status === 200) {
toast.success(t("recording.confirmDelete.toast.success"), {
position: "top-center",
});
setSelectedReviews([]);
pullLatestData();
}
})
.catch((error) => {
const errorMessage =
error.response?.data?.message ||
error.response?.data?.detail ||
"Unknown error";
toast.error(
t("recording.confirmDelete.toast.error", {
error: errorMessage,
}),
{
position: "top-center",
},
);
});
}, [selectedReviews, setSelectedReviews, pullLatestData, t]);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [bypassDialog, setBypassDialog] = useState(false);
@@ -30,6 +30,8 @@ import { flattenPoints, interpolatePoints } from "@/utils/canvasUtil";
import ActivityIndicator from "../indicators/activity-indicator";
import { getAttributeLabels } from "@/utils/iconUtil";
import { Trans, useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { LuExternalLink } from "react-icons/lu";
type ZoneEditPaneProps = {
polygons?: Polygon[];
@@ -669,6 +671,17 @@ export default function ZoneEditPane({
</div>
<FormDescription>
{t("masksAndZones.zones.speedEstimation.desc")}
<div className="mt-2 flex items-center text-primary">
<Link
to="https://docs.frigate.video/configuration/zones#speed-estimation"
target="_blank"
rel="noopener noreferrer"
className="inline"
>
{t("masksAndZones.zones.speedEstimation.docs")}
<LuExternalLink className="ml-2 inline-flex size-3" />
</Link>
</div>
</FormDescription>
<FormMessage />
</FormItem>
+1
View File
@@ -11,4 +11,5 @@ export const supportedLanguageKeys = [
"zh-CN",
"yue-Hant",
"ru",
"uk",
];
+2 -2
View File
@@ -390,10 +390,10 @@ export default function FaceLibrary() {
</div>
)}
</div>
{pageToggle && faceImages.length === 0 && pageToggle !== "train" ? (
{pageToggle && faceImages?.length === 0 && pageToggle !== "train" ? (
<div className="absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 flex-col items-center justify-center text-center">
<LuFolderCheck className="size-16" />
No faces available
{t("nofaces")}
</div>
) : (
pageToggle &&
+2 -2
View File
@@ -819,7 +819,7 @@ function Timeline({
className={`${
isDesktop
? `${timelineType == "timeline" ? "w-[100px]" : "w-60"} no-scrollbar overflow-y-auto`
: "overflow-hidden portrait:flex-grow landscape:w-[20%]"
: `overflow-hidden portrait:flex-grow ${timelineType == "timeline" ? "landscape:w-[100px]" : "landscape:w-[175px]"} `
} relative`}
>
<div className="pointer-events-none absolute inset-x-0 top-0 z-20 h-[30px] w-full bg-gradient-to-b from-secondary to-transparent"></div>
@@ -855,7 +855,7 @@ function Timeline({
<div
className={cn(
"scrollbar-container grid h-auto grid-cols-1 gap-4 overflow-auto p-4",
isMobile && "sm:grid-cols-2",
isMobile && "sm:portrait:grid-cols-2",
)}
>
{mainCameraReviewItems.length === 0 ? (
+1 -1
View File
@@ -71,7 +71,7 @@ export default function StorageMetrics({
const timeFormat = config?.ui.time_format === "24hour" ? "24hour" : "12hour";
const format = useMemo(() => {
return t(`time.formattedTimestampMonthDayYearHourMinute.${timeFormat}`, {
return t(`time.formattedTimestampMonthDayYear.${timeFormat}`, {
ns: "common",
});
}, [t, timeFormat]);