mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-06-21 03:41:55 +03:00
UI tweaks (#23346)
Some checks failed
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
Some checks failed
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
* remove redundant per-view toasters in settings * add variants to standardize dialog footer button layouts * remove text-md this class name compiles to nothing in tailwind. we used to add it to prevent iOS from zooming when focusing on an input, but that is now solved via the viewport meta in index.html * make wizard footers consistent with dialog footers * consistent destructive button style remove text-white from individual buttons and add it to the variant
This commit is contained in:
parent
4b6fa49449
commit
6fdd65ddb5
@ -107,7 +107,7 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
|
|||||||
<FormLabel>{t("form.user")}</FormLabel>
|
<FormLabel>{t("form.user")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
autoFocus
|
autoFocus
|
||||||
autoCapitalize="off"
|
autoCapitalize="off"
|
||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
@ -125,7 +125,7 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
|
|||||||
<FormLabel>{t("form.password")}</FormLabel>
|
<FormLabel>{t("form.password")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
type="password"
|
type="password"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -257,7 +257,7 @@ export function ExportCard({
|
|||||||
{editName && (
|
{editName && (
|
||||||
<>
|
<>
|
||||||
<Input
|
<Input
|
||||||
className="text-md mt-3"
|
className="mt-3"
|
||||||
type="search"
|
type="search"
|
||||||
placeholder={editName?.original}
|
placeholder={editName?.original}
|
||||||
value={
|
value={
|
||||||
@ -275,7 +275,6 @@ export function ExportCard({
|
|||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button
|
<Button
|
||||||
aria-label={t("editExport.saveExport")}
|
aria-label={t("editExport.saveExport")}
|
||||||
size="sm"
|
|
||||||
variant="select"
|
variant="select"
|
||||||
disabled={(editName?.update?.length ?? 0) == 0}
|
disabled={(editName?.update?.length ?? 0) == 0}
|
||||||
onClick={() => submitRename()}
|
onClick={() => submitRename()}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ type SettingsGroupCardProps = {
|
|||||||
export function SettingsGroupCard({ title, children }: SettingsGroupCardProps) {
|
export function SettingsGroupCard({ title, children }: SettingsGroupCardProps) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 rounded-lg border border-border/70 bg-card/30 p-4">
|
<div className="space-y-4 rounded-lg border border-border/70 bg-card/30 p-4">
|
||||||
<div className="text-md border-b border-border/60 pb-4 font-semibold text-primary-variant">
|
<div className="border-b border-border/60 pb-4 font-semibold text-primary-variant">
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -48,7 +48,7 @@ export default function ChatSettings({
|
|||||||
<div className="my-3 space-y-5 py-3 md:mt-0 md:py-0">
|
<div className="my-3 space-y-5 py-3 md:mt-0 md:py-0">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="text-md">{t("settings.show_stats.title")}</div>
|
<div>{t("settings.show_stats.title")}</div>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
{t("settings.show_stats.desc")}
|
{t("settings.show_stats.desc")}
|
||||||
</div>
|
</div>
|
||||||
@ -77,7 +77,7 @@ export default function ChatSettings({
|
|||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<div className="flex items-center justify-between gap-3">
|
<div className="flex items-center justify-between gap-3">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<Label htmlFor="auto-scroll" className="text-md cursor-pointer">
|
<Label htmlFor="auto-scroll" className="cursor-pointer">
|
||||||
{t("settings.auto_scroll.title")}
|
{t("settings.auto_scroll.title")}
|
||||||
</Label>
|
</Label>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
|
|||||||
@ -485,7 +485,7 @@ export default function ClassificationModelEditDialog({
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Input
|
<Input
|
||||||
className="text-md h-8"
|
className="h-8"
|
||||||
placeholder={t(
|
placeholder={t(
|
||||||
"wizard.step1.classPlaceholder",
|
"wizard.step1.classPlaceholder",
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -214,7 +214,7 @@ export default function Step1NameAndDefine({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md h-8"
|
className="h-8"
|
||||||
placeholder={t("wizard.step1.namePlaceholder")}
|
placeholder={t("wizard.step1.namePlaceholder")}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
@ -457,7 +457,7 @@ export default function Step1NameAndDefine({
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Input
|
<Input
|
||||||
className="text-md h-8"
|
className="h-8"
|
||||||
placeholder={t("wizard.step1.classPlaceholder")}
|
placeholder={t("wizard.step1.classPlaceholder")}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
@ -489,7 +489,7 @@ export default function Step1NameAndDefine({
|
|||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
<div className="flex flex-col gap-3 pt-3 sm:flex-row sm:justify-end sm:gap-4">
|
<div className="flex flex-col-reverse gap-2 pt-3 sm:flex-row sm:justify-end">
|
||||||
<Button type="button" onClick={onCancel} className="sm:flex-1">
|
<Button type="button" onClick={onCancel} className="sm:flex-1">
|
||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -458,7 +458,7 @@ export default function Step2StateArea({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-3 pt-3 sm:flex-row sm:justify-end sm:gap-4">
|
<div className="flex flex-col-reverse gap-2 pt-3 sm:flex-row sm:justify-end">
|
||||||
<Button type="button" onClick={onBack} className="sm:flex-1">
|
<Button type="button" onClick={onBack} className="sm:flex-1">
|
||||||
{t("button.back", { ns: "common" })}
|
{t("button.back", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button, buttonVariants } from "@/components/ui/button";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useState, useEffect, useCallback, useMemo } from "react";
|
import { useState, useEffect, useCallback, useMemo } from "react";
|
||||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||||
@ -540,7 +540,7 @@ export default function Step3ChooseExamples({
|
|||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
onClick={doRefresh}
|
onClick={doRefresh}
|
||||||
className="bg-destructive text-white hover:bg-destructive/90"
|
className={cn(buttonVariants({ variant: "destructive" }))}
|
||||||
>
|
>
|
||||||
{t("button.continue", { ns: "common" })}
|
{t("button.continue", { ns: "common" })}
|
||||||
</AlertDialogAction>
|
</AlertDialogAction>
|
||||||
@ -693,7 +693,7 @@ export default function Step3ChooseExamples({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{!isTraining && (
|
{!isTraining && (
|
||||||
<div className="flex flex-col gap-3 pt-3 sm:flex-row sm:justify-end sm:gap-4">
|
<div className="flex flex-col-reverse gap-2 pt-3 sm:flex-row sm:justify-end">
|
||||||
<Button type="button" onClick={handleBack} className="sm:flex-1">
|
<Button type="button" onClick={handleBack} className="sm:flex-1">
|
||||||
{t("button.back", { ns: "common" })}
|
{t("button.back", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import {
|
|||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
|
||||||
import { StatusBarMessagesContext } from "@/context/statusbar-provider";
|
import { StatusBarMessagesContext } from "@/context/statusbar-provider";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
@ -491,7 +490,6 @@ export default function NotificationsSettingsExtras({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full flex-col md:flex-row">
|
<div className="flex size-full flex-col md:flex-row">
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto px-2 md:order-none">
|
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto px-2 md:order-none">
|
||||||
<div className={cn("w-full max-w-5xl space-y-6")}>
|
<div className={cn("w-full max-w-5xl space-y-6")}>
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
@ -521,7 +519,7 @@ export default function NotificationsSettingsExtras({
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
id="notification-email"
|
id="notification-email"
|
||||||
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark] md:w-72"
|
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark] md:w-72"
|
||||||
placeholder={t(
|
placeholder={t(
|
||||||
"notification.email.placeholder",
|
"notification.email.placeholder",
|
||||||
)}
|
)}
|
||||||
@ -788,7 +786,7 @@ export function CameraNotificationSwitch({
|
|||||||
)}
|
)}
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<CameraNameLabel
|
<CameraNameLabel
|
||||||
className="text-md cursor-pointer text-primary smart-capitalize"
|
className="cursor-pointer text-primary smart-capitalize"
|
||||||
htmlFor="camera"
|
htmlFor="camera"
|
||||||
camera={camera}
|
camera={camera}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -32,7 +32,7 @@ import { ProfileOverridesBadge } from "./ProfileOverridesBadge";
|
|||||||
import { useSectionSchema } from "@/hooks/use-config-schema";
|
import { useSectionSchema } from "@/hooks/use-config-schema";
|
||||||
import type { FrigateConfig } from "@/types/frigateConfig";
|
import type { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button, buttonVariants } from "@/components/ui/button";
|
||||||
import { LuChevronDown, LuChevronRight } from "react-icons/lu";
|
import { LuChevronDown, LuChevronRight } from "react-icons/lu";
|
||||||
import Heading from "@/components/ui/heading";
|
import Heading from "@/components/ui/heading";
|
||||||
import get from "lodash/get";
|
import get from "lodash/get";
|
||||||
@ -1236,7 +1236,7 @@ export function ConfigSection({
|
|||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
className="bg-destructive text-white hover:bg-destructive/90"
|
className={cn(buttonVariants({ variant: "destructive" }))}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onDeleteProfileSection?.();
|
onDeleteProfileSection?.();
|
||||||
setIsDeleteProfileDialogOpen(false);
|
setIsDeleteProfileDialogOpen(false);
|
||||||
|
|||||||
@ -371,7 +371,7 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
|
|||||||
key={group.groupKey}
|
key={group.groupKey}
|
||||||
className="space-y-4 rounded-lg border border-border/70 bg-card/30 p-4"
|
className="space-y-4 rounded-lg border border-border/70 bg-card/30 p-4"
|
||||||
>
|
>
|
||||||
<div className="text-md border-b border-border/60 pb-4 font-semibold text-primary-variant">
|
<div className="border-b border-border/60 pb-4 font-semibold text-primary-variant">
|
||||||
{group.label}
|
{group.label}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|||||||
@ -79,7 +79,7 @@ export function ArrayAsTextWidget(props: WidgetProps) {
|
|||||||
return (
|
return (
|
||||||
<Textarea
|
<Textarea
|
||||||
id={id}
|
id={id}
|
||||||
className={cn("text-md", fieldClassName)}
|
className={cn(fieldClassName)}
|
||||||
value={text}
|
value={text}
|
||||||
disabled={disabled || readonly}
|
disabled={disabled || readonly}
|
||||||
rows={(options.rows as number) || 3}
|
rows={(options.rows as number) || 3}
|
||||||
|
|||||||
@ -124,7 +124,7 @@ export function CameraPathWidget(props: WidgetProps) {
|
|||||||
<div className={cn("relative", fieldClassName)}>
|
<div className={cn("relative", fieldClassName)}>
|
||||||
<Input
|
<Input
|
||||||
id={id}
|
id={id}
|
||||||
className={cn("text-md", canToggle ? "pr-10" : undefined)}
|
className={cn(canToggle ? "pr-10" : undefined)}
|
||||||
type="text"
|
type="text"
|
||||||
value={displayValue}
|
value={displayValue}
|
||||||
disabled={disabled || readonly}
|
disabled={disabled || readonly}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export function TextWidget(props: WidgetProps) {
|
|||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
id={id}
|
id={id}
|
||||||
className={cn("text-md", fieldClassName)}
|
className={cn(fieldClassName)}
|
||||||
type="text"
|
type="text"
|
||||||
value={value ?? ""}
|
value={value ?? ""}
|
||||||
disabled={disabled || readonly}
|
disabled={disabled || readonly}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export function TextareaWidget(props: WidgetProps) {
|
|||||||
return (
|
return (
|
||||||
<Textarea
|
<Textarea
|
||||||
id={id}
|
id={id}
|
||||||
className={cn("text-md", fieldClassName)}
|
className={cn(fieldClassName)}
|
||||||
value={value ?? ""}
|
value={value ?? ""}
|
||||||
disabled={disabled || readonly}
|
disabled={disabled || readonly}
|
||||||
placeholder={placeholder || (options.placeholder as string) || ""}
|
placeholder={placeholder || (options.placeholder as string) || ""}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import {
|
|||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "../ui/dialog";
|
} from "../ui/dialog";
|
||||||
@ -847,7 +848,7 @@ export function CameraGroupEdit({
|
|||||||
<FormLabel>{t("group.name.label")}</FormLabel>
|
<FormLabel>{t("group.name.label")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
placeholder={t("group.name.placeholder")}
|
placeholder={t("group.name.placeholder")}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
@ -973,10 +974,9 @@ export function CameraGroupEdit({
|
|||||||
|
|
||||||
<Separator className="my-2 flex bg-secondary" />
|
<Separator className="my-2 flex bg-secondary" />
|
||||||
|
|
||||||
<div className="flex flex-row gap-2 py-5 md:pb-0">
|
<DialogFooter className="py-5 md:pb-0">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex flex-1"
|
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
onClick={onCancel}
|
onClick={onCancel}
|
||||||
>
|
>
|
||||||
@ -985,7 +985,6 @@ export function CameraGroupEdit({
|
|||||||
<Button
|
<Button
|
||||||
variant="select"
|
variant="select"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
className="flex flex-1"
|
|
||||||
aria-label={t("button.save", { ns: "common" })}
|
aria-label={t("button.save", { ns: "common" })}
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
@ -998,7 +997,7 @@ export function CameraGroupEdit({
|
|||||||
t("button.save", { ns: "common" })
|
t("button.save", { ns: "common" })
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</DialogFooter>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -40,7 +40,7 @@ export function LogSettingsButton({
|
|||||||
<div className={cn("my-3 space-y-3 py-3 md:mt-0 md:py-0")}>
|
<div className={cn("my-3 space-y-3 py-3 md:mt-0 md:py-0")}>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="text-md">{t("filter")}</div>
|
<div>{t("filter")}</div>
|
||||||
<div className="space-y-1 text-xs text-muted-foreground">
|
<div className="space-y-1 text-xs text-muted-foreground">
|
||||||
{t("logSettings.filterBySeverity")}
|
{t("logSettings.filterBySeverity")}
|
||||||
</div>
|
</div>
|
||||||
@ -53,7 +53,7 @@ export function LogSettingsButton({
|
|||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="text-md">{t("logSettings.loading.title")}</div>
|
<div>{t("logSettings.loading.title")}</div>
|
||||||
<div className="mt-2.5 flex flex-col gap-2.5">
|
<div className="mt-2.5 flex flex-col gap-2.5">
|
||||||
<div className="space-y-1 text-xs text-muted-foreground">
|
<div className="space-y-1 text-xs text-muted-foreground">
|
||||||
{t("logSettings.loading.desc")}
|
{t("logSettings.loading.desc")}
|
||||||
|
|||||||
@ -119,7 +119,7 @@ export default function IconPicker({
|
|||||||
placeholder={t("iconPicker.search.placeholder", {
|
placeholder={t("iconPicker.search.placeholder", {
|
||||||
ns: "components/icons",
|
ns: "components/icons",
|
||||||
})}
|
})}
|
||||||
className="text-md mb-3 md:text-sm"
|
className="mb-3 md:text-sm"
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -696,7 +696,7 @@ export default function InputWithTags({
|
|||||||
onFocus={handleInputFocus}
|
onFocus={handleInputFocus}
|
||||||
onBlur={handleInputBlur}
|
onBlur={handleInputBlur}
|
||||||
onKeyDown={handleInputKeyDown}
|
onKeyDown={handleInputKeyDown}
|
||||||
className="text-md h-9 pr-32"
|
className="h-9 pr-32"
|
||||||
placeholder={t("placeholder.search")}
|
placeholder={t("placeholder.search")}
|
||||||
/>
|
/>
|
||||||
<div className="absolute right-3 top-0 flex h-full flex-row items-center justify-center gap-5">
|
<div className="absolute right-3 top-0 flex h-full flex-row items-center justify-center gap-5">
|
||||||
|
|||||||
@ -112,11 +112,7 @@ export default function NameAndIdFields<T extends FieldValues = FieldValues>({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input placeholder={placeholderName} {...field} />
|
||||||
className="text-md"
|
|
||||||
placeholder={placeholderName}
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
{nameDescription && (
|
{nameDescription && (
|
||||||
<FormDescription>{nameDescription}</FormDescription>
|
<FormDescription>{nameDescription}</FormDescription>
|
||||||
@ -134,7 +130,6 @@ export default function NameAndIdFields<T extends FieldValues = FieldValues>({
|
|||||||
<FormLabel>{idLabel ?? t("label.ID")}</FormLabel>
|
<FormLabel>{idLabel ?? t("label.ID")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md"
|
|
||||||
placeholder={placeholderId}
|
placeholder={placeholderId}
|
||||||
disabled={idDisabled}
|
disabled={idDisabled}
|
||||||
{...field}
|
{...field}
|
||||||
|
|||||||
@ -69,7 +69,6 @@ export function SaveSearchDialog({
|
|||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<Input
|
<Input
|
||||||
value={searchName}
|
value={searchName}
|
||||||
className="text-md"
|
|
||||||
onChange={(e) => setSearchName(e.target.value)}
|
onChange={(e) => setSearchName(e.target.value)}
|
||||||
placeholder={t("search.saveSearch.placeholder")}
|
placeholder={t("search.saveSearch.placeholder")}
|
||||||
/>
|
/>
|
||||||
@ -88,7 +87,6 @@ export function SaveSearchDialog({
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
variant="select"
|
variant="select"
|
||||||
className="mb-2 md:mb-0"
|
|
||||||
aria-label={t("search.saveSearch.button.save.label")}
|
aria-label={t("search.saveSearch.button.save.label")}
|
||||||
>
|
>
|
||||||
{t("button.save", { ns: "common" })}
|
{t("button.save", { ns: "common" })}
|
||||||
|
|||||||
@ -77,7 +77,7 @@ export default function TextEntry({
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
className="text-md w-full"
|
className="w-full"
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -276,7 +276,7 @@ export default function LiveContextMenu({
|
|||||||
<ContextMenuTrigger>{children}</ContextMenuTrigger>
|
<ContextMenuTrigger>{children}</ContextMenuTrigger>
|
||||||
<ContextMenuContent>
|
<ContextMenuContent>
|
||||||
<div className="flex flex-col items-start gap-1 py-1 pl-2">
|
<div className="flex flex-col items-start gap-1 py-1 pl-2">
|
||||||
<div className="text-md text-primary-variant smart-capitalize">
|
<div className="text-primary-variant smart-capitalize">
|
||||||
<CameraNameLabel camera={camera} />
|
<CameraNameLabel camera={camera} />
|
||||||
</div>
|
</div>
|
||||||
{preferredLiveMode == "jsmpeg" && isRestreamed && (
|
{preferredLiveMode == "jsmpeg" && isRestreamed && (
|
||||||
|
|||||||
@ -213,36 +213,30 @@ export default function CreateRoleDialog({
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter className="flex gap-2 pt-2 sm:justify-end">
|
<DialogFooter className="pt-2">
|
||||||
<div className="flex flex-1 flex-col justify-end">
|
<Button
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
<Button
|
disabled={isLoading}
|
||||||
className="flex flex-1"
|
onClick={handleCancel}
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
type="button"
|
||||||
disabled={isLoading}
|
>
|
||||||
onClick={handleCancel}
|
{t("button.cancel", { ns: "common" })}
|
||||||
type="button"
|
</Button>
|
||||||
>
|
<Button
|
||||||
{t("button.cancel", { ns: "common" })}
|
variant="select"
|
||||||
</Button>
|
aria-label={t("button.save", { ns: "common" })}
|
||||||
<Button
|
disabled={isLoading || !form.formState.isValid}
|
||||||
variant="select"
|
type="submit"
|
||||||
aria-label={t("button.save", { ns: "common" })}
|
>
|
||||||
disabled={isLoading || !form.formState.isValid}
|
{isLoading ? (
|
||||||
className="flex flex-1"
|
<div className="flex flex-row items-center gap-2">
|
||||||
type="submit"
|
<ActivityIndicator className="size-4" />
|
||||||
>
|
<span>{t("button.saving", { ns: "common" })}</span>
|
||||||
{isLoading ? (
|
</div>
|
||||||
<div className="flex flex-row items-center gap-2">
|
) : (
|
||||||
<ActivityIndicator className="size-4" />
|
t("button.save", { ns: "common" })
|
||||||
<span>{t("button.saving", { ns: "common" })}</span>
|
)}
|
||||||
</div>
|
</Button>
|
||||||
) : (
|
|
||||||
t("button.save", { ns: "common" })
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@ -433,36 +433,30 @@ export default function CreateTriggerDialog({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DialogFooter className="flex gap-2 pt-2 sm:justify-end">
|
<DialogFooter className="pt-2">
|
||||||
<div className="flex flex-1 flex-col justify-end">
|
<Button
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
<Button
|
disabled={isLoading}
|
||||||
className="flex flex-1"
|
onClick={handleCancel}
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
type="button"
|
||||||
disabled={isLoading}
|
>
|
||||||
onClick={handleCancel}
|
{t("button.cancel", { ns: "common" })}
|
||||||
type="button"
|
</Button>
|
||||||
>
|
<Button
|
||||||
{t("button.cancel", { ns: "common" })}
|
variant="select"
|
||||||
</Button>
|
aria-label={t("button.save", { ns: "common" })}
|
||||||
<Button
|
disabled={isLoading || !form.formState.isValid}
|
||||||
variant="select"
|
type="submit"
|
||||||
aria-label={t("button.save", { ns: "common" })}
|
>
|
||||||
disabled={isLoading || !form.formState.isValid}
|
{isLoading ? (
|
||||||
className="flex flex-1"
|
<div className="flex flex-row items-center gap-2">
|
||||||
type="submit"
|
<ActivityIndicator className="size-4" />
|
||||||
>
|
<span>{t("button.saving", { ns: "common" })}</span>
|
||||||
{isLoading ? (
|
</div>
|
||||||
<div className="flex flex-row items-center gap-2">
|
) : (
|
||||||
<ActivityIndicator className="size-4" />
|
t("button.save", { ns: "common" })
|
||||||
<span>{t("button.saving", { ns: "common" })}</span>
|
)}
|
||||||
</div>
|
</Button>
|
||||||
) : (
|
|
||||||
t("button.save", { ns: "common" })
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@ -411,36 +411,30 @@ export default function CreateUserDialog({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DialogFooter className="flex gap-2 pt-2 sm:justify-end">
|
<DialogFooter className="pt-2">
|
||||||
<div className="flex flex-1 flex-col justify-end">
|
<Button
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
<Button
|
disabled={isLoading}
|
||||||
className="flex flex-1"
|
onClick={handleCancel}
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
type="button"
|
||||||
disabled={isLoading}
|
>
|
||||||
onClick={handleCancel}
|
{t("button.cancel", { ns: "common" })}
|
||||||
type="button"
|
</Button>
|
||||||
>
|
<Button
|
||||||
{t("button.cancel", { ns: "common" })}
|
variant="select"
|
||||||
</Button>
|
aria-label={t("button.save", { ns: "common" })}
|
||||||
<Button
|
disabled={isLoading || !form.formState.isValid}
|
||||||
variant="select"
|
type="submit"
|
||||||
aria-label={t("button.save", { ns: "common" })}
|
>
|
||||||
disabled={isLoading || !form.formState.isValid}
|
{isLoading ? (
|
||||||
className="flex flex-1"
|
<div className="flex flex-row items-center gap-2">
|
||||||
type="submit"
|
<ActivityIndicator className="size-4" />
|
||||||
>
|
<span>{t("button.saving", { ns: "common" })}</span>
|
||||||
{isLoading ? (
|
</div>
|
||||||
<div className="flex flex-row items-center gap-2">
|
) : (
|
||||||
<ActivityIndicator className="size-4" />
|
t("button.save", { ns: "common" })
|
||||||
<span>{t("button.saving", { ns: "common" })}</span>
|
)}
|
||||||
</div>
|
</Button>
|
||||||
) : (
|
|
||||||
t("button.save", { ns: "common" })
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@ -144,7 +144,7 @@ export function CustomTimeSelector({
|
|||||||
/>
|
/>
|
||||||
<SelectSeparator className="bg-secondary" />
|
<SelectSeparator className="bg-secondary" />
|
||||||
<input
|
<input
|
||||||
className="text-md mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
id="startTime"
|
id="startTime"
|
||||||
type="time"
|
type="time"
|
||||||
value={startClock}
|
value={startClock}
|
||||||
@ -210,7 +210,7 @@ export function CustomTimeSelector({
|
|||||||
/>
|
/>
|
||||||
<SelectSeparator className="bg-secondary" />
|
<SelectSeparator className="bg-secondary" />
|
||||||
<input
|
<input
|
||||||
className="text-md mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
id="endTime"
|
id="endTime"
|
||||||
type="time"
|
type="time"
|
||||||
value={endClock}
|
value={endClock}
|
||||||
|
|||||||
@ -113,19 +113,14 @@ export function DebugReplayContent({
|
|||||||
|
|
||||||
{isDesktop && <SelectSeparator className="my-4 bg-secondary" />}
|
{isDesktop && <SelectSeparator className="my-4 bg-secondary" />}
|
||||||
|
|
||||||
<DialogFooter
|
<DialogFooter className="mt-3 sm:mt-0">
|
||||||
className={isDesktop ? "" : "mt-3 flex flex-col-reverse gap-2"}
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
className={isDesktop ? "" : "w-full"}
|
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
variant="outline"
|
|
||||||
onClick={onCancel}
|
onClick={onCancel}
|
||||||
>
|
>
|
||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className={isDesktop ? "" : "w-full"}
|
|
||||||
variant="select"
|
variant="select"
|
||||||
disabled={isStarting}
|
disabled={isStarting}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@ -70,38 +70,31 @@ export default function DeleteRoleDialog({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter className="flex gap-2 pt-2 sm:justify-end">
|
<DialogFooter>
|
||||||
<div className="flex flex-1 flex-col justify-end">
|
<Button
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
<Button
|
disabled={isLoading}
|
||||||
className="flex flex-1"
|
onClick={onCancel}
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
type="button"
|
||||||
variant="outline"
|
>
|
||||||
disabled={isLoading}
|
{t("button.cancel", { ns: "common" })}
|
||||||
onClick={onCancel}
|
</Button>
|
||||||
type="button"
|
<Button
|
||||||
>
|
aria-label={t("button.delete", { ns: "common" })}
|
||||||
{t("button.cancel", { ns: "common" })}
|
variant="destructive"
|
||||||
</Button>
|
disabled={isLoading}
|
||||||
<Button
|
onClick={handleDelete}
|
||||||
className="flex flex-1"
|
type="button"
|
||||||
aria-label={t("button.delete", { ns: "common" })}
|
>
|
||||||
variant="destructive"
|
{isLoading ? (
|
||||||
disabled={isLoading}
|
<div className="flex flex-row items-center gap-2">
|
||||||
onClick={handleDelete}
|
<ActivityIndicator />
|
||||||
type="button"
|
<span>{t("roles.dialog.deleteRole.deleting")}</span>
|
||||||
>
|
</div>
|
||||||
{isLoading ? (
|
) : (
|
||||||
<div className="flex flex-row items-center gap-2">
|
t("button.delete", { ns: "common" })
|
||||||
<ActivityIndicator />
|
)}
|
||||||
<span>{t("roles.dialog.deleteRole.deleting")}</span>
|
</Button>
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
t("button.delete", { ns: "common" })
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@ -43,36 +43,30 @@ export default function DeleteTriggerDialog({
|
|||||||
</Trans>
|
</Trans>
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<DialogFooter className="flex gap-3 sm:justify-end">
|
<DialogFooter>
|
||||||
<div className="flex flex-1 flex-col justify-end">
|
<Button
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
<Button
|
onClick={onCancel}
|
||||||
className="flex flex-1"
|
type="button"
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
disabled={isLoading}
|
||||||
onClick={onCancel}
|
>
|
||||||
type="button"
|
{t("button.cancel", { ns: "common" })}
|
||||||
disabled={isLoading}
|
</Button>
|
||||||
>
|
<Button
|
||||||
{t("button.cancel", { ns: "common" })}
|
variant="destructive"
|
||||||
</Button>
|
aria-label={t("button.delete", { ns: "common" })}
|
||||||
<Button
|
onClick={onDelete}
|
||||||
variant="destructive"
|
disabled={isLoading}
|
||||||
aria-label={t("button.delete", { ns: "common" })}
|
>
|
||||||
className="flex flex-1 text-white"
|
{isLoading ? (
|
||||||
onClick={onDelete}
|
<div className="flex flex-row items-center gap-2">
|
||||||
disabled={isLoading}
|
<ActivityIndicator />
|
||||||
>
|
<span>{t("button.delete", { ns: "common" })}</span>
|
||||||
{isLoading ? (
|
</div>
|
||||||
<div className="flex flex-row items-center gap-2">
|
) : (
|
||||||
<ActivityIndicator />
|
t("button.delete", { ns: "common" })
|
||||||
<span>{t("button.delete", { ns: "common" })}</span>
|
)}
|
||||||
</div>
|
</Button>
|
||||||
) : (
|
|
||||||
t("button.delete", { ns: "common" })
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@ -46,27 +46,21 @@ export default function DeleteUserDialog({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
<DialogFooter>
|
||||||
<div className="flex flex-1 flex-col justify-end">
|
<Button
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
<Button
|
onClick={onCancel}
|
||||||
className="flex flex-1"
|
type="button"
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
>
|
||||||
onClick={onCancel}
|
{t("button.cancel", { ns: "common" })}
|
||||||
type="button"
|
</Button>
|
||||||
>
|
<Button
|
||||||
{t("button.cancel", { ns: "common" })}
|
variant="destructive"
|
||||||
</Button>
|
aria-label={t("button.delete", { ns: "common" })}
|
||||||
<Button
|
onClick={onDelete}
|
||||||
variant="destructive"
|
>
|
||||||
aria-label={t("button.delete", { ns: "common" })}
|
{t("button.delete", { ns: "common" })}
|
||||||
className="flex flex-1 text-white"
|
</Button>
|
||||||
onClick={onDelete}
|
|
||||||
>
|
|
||||||
{t("button.delete", { ns: "common" })}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@ -105,13 +105,15 @@ export default function EditRoleCamerasDialog({
|
|||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="space-y-5 pt-4"
|
className="space-y-5 pt-2"
|
||||||
>
|
>
|
||||||
<div className="space-y-2">
|
<div className="space-y-3">
|
||||||
<FormLabel>{t("roles.dialog.form.cameras.title")}</FormLabel>
|
<div>
|
||||||
<FormDescription className="text-xs text-muted-foreground">
|
<FormLabel>{t("roles.dialog.form.cameras.title")}</FormLabel>
|
||||||
{t("roles.dialog.form.cameras.desc")}
|
<FormDescription className="text-xs text-muted-foreground">
|
||||||
</FormDescription>
|
{t("roles.dialog.form.cameras.desc")}
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
<div className="scrollbar-container max-h-[40dvh] space-y-2 overflow-y-auto">
|
<div className="scrollbar-container max-h-[40dvh] space-y-2 overflow-y-auto">
|
||||||
{cameras.map((camera) => (
|
{cameras.map((camera) => (
|
||||||
<FormField
|
<FormField
|
||||||
@ -159,36 +161,30 @@ export default function EditRoleCamerasDialog({
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter className="flex gap-2 pt-2 sm:justify-end">
|
<DialogFooter>
|
||||||
<div className="flex flex-1 flex-col justify-end">
|
<Button
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
<Button
|
disabled={isLoading}
|
||||||
className="flex flex-1"
|
onClick={handleCancel}
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
type="button"
|
||||||
disabled={isLoading}
|
>
|
||||||
onClick={handleCancel}
|
{t("button.cancel", { ns: "common" })}
|
||||||
type="button"
|
</Button>
|
||||||
>
|
<Button
|
||||||
{t("button.cancel", { ns: "common" })}
|
variant="select"
|
||||||
</Button>
|
aria-label={t("button.save", { ns: "common" })}
|
||||||
<Button
|
disabled={isLoading || !form.formState.isValid}
|
||||||
variant="select"
|
type="submit"
|
||||||
aria-label={t("button.save", { ns: "common" })}
|
>
|
||||||
disabled={isLoading || !form.formState.isValid}
|
{isLoading ? (
|
||||||
className="flex flex-1"
|
<div className="flex flex-row items-center gap-2">
|
||||||
type="submit"
|
<ActivityIndicator className="size-4" />
|
||||||
>
|
<span>{t("button.saving", { ns: "common" })}</span>
|
||||||
{isLoading ? (
|
</div>
|
||||||
<div className="flex flex-row items-center gap-2">
|
) : (
|
||||||
<ActivityIndicator className="size-4" />
|
t("button.save", { ns: "common" })
|
||||||
<span>{t("button.saving", { ns: "common" })}</span>
|
)}
|
||||||
</div>
|
</Button>
|
||||||
) : (
|
|
||||||
t("button.save", { ns: "common" })
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@ -794,7 +794,6 @@ export function ExportContent({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
className="text-md"
|
|
||||||
type="search"
|
type="search"
|
||||||
placeholder={t("export.name.placeholder")}
|
placeholder={t("export.name.placeholder")}
|
||||||
value={name}
|
value={name}
|
||||||
@ -835,13 +834,11 @@ export function ExportContent({
|
|||||||
{selectedCaseId === "new" && (
|
{selectedCaseId === "new" && (
|
||||||
<div className="space-y-2 pt-1">
|
<div className="space-y-2 pt-1">
|
||||||
<Input
|
<Input
|
||||||
className="text-md"
|
|
||||||
placeholder={t("export.case.newCaseNamePlaceholder")}
|
placeholder={t("export.case.newCaseNamePlaceholder")}
|
||||||
value={singleNewCaseName}
|
value={singleNewCaseName}
|
||||||
onChange={(e) => setSingleNewCaseName(e.target.value)}
|
onChange={(e) => setSingleNewCaseName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="text-md"
|
|
||||||
placeholder={t("export.case.newCaseDescriptionPlaceholder")}
|
placeholder={t("export.case.newCaseDescriptionPlaceholder")}
|
||||||
value={singleNewCaseDescription}
|
value={singleNewCaseDescription}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@ -988,7 +985,6 @@ export function ExportContent({
|
|||||||
{t("export.multiCamera.nameLabel")}
|
{t("export.multiCamera.nameLabel")}
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
className="text-md"
|
|
||||||
type="search"
|
type="search"
|
||||||
placeholder={t("export.multiCamera.namePlaceholder")}
|
placeholder={t("export.multiCamera.namePlaceholder")}
|
||||||
value={name}
|
value={name}
|
||||||
@ -1028,13 +1024,11 @@ export function ExportContent({
|
|||||||
{batchCaseSelection === "new" && (
|
{batchCaseSelection === "new" && (
|
||||||
<div className="space-y-2 pt-1">
|
<div className="space-y-2 pt-1">
|
||||||
<Input
|
<Input
|
||||||
className="text-md"
|
|
||||||
placeholder={t("export.case.newCaseNamePlaceholder")}
|
placeholder={t("export.case.newCaseNamePlaceholder")}
|
||||||
value={newCaseName}
|
value={newCaseName}
|
||||||
onChange={(event) => setNewCaseName(event.target.value)}
|
onChange={(event) => setNewCaseName(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="text-md"
|
|
||||||
placeholder={t("export.case.newCaseDescriptionPlaceholder")}
|
placeholder={t("export.case.newCaseDescriptionPlaceholder")}
|
||||||
value={newCaseDescription}
|
value={newCaseDescription}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
@ -1049,20 +1043,15 @@ export function ExportContent({
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
{isDesktop && <SelectSeparator className="my-4 bg-secondary" />}
|
{isDesktop && <SelectSeparator className="my-4 bg-secondary" />}
|
||||||
<DialogFooter
|
<DialogFooter className="mt-3 sm:mt-0">
|
||||||
className={isDesktop ? "" : "mt-3 flex flex-col-reverse gap-2"}
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
className={isDesktop ? "" : "w-full"}
|
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
variant="outline"
|
|
||||||
onClick={onCancel}
|
onClick={onCancel}
|
||||||
>
|
>
|
||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
{activeTab === "export" ? (
|
{activeTab === "export" ? (
|
||||||
<Button
|
<Button
|
||||||
className={isDesktop ? "" : "w-full"}
|
|
||||||
aria-label={t("export.selectOrExport")}
|
aria-label={t("export.selectOrExport")}
|
||||||
variant="select"
|
variant="select"
|
||||||
disabled={isStartingExport}
|
disabled={isStartingExport}
|
||||||
@ -1086,12 +1075,10 @@ export function ExportContent({
|
|||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
className={isDesktop ? "" : "w-full"}
|
|
||||||
aria-label={t("export.multiCamera.exportButton", {
|
aria-label={t("export.multiCamera.exportButton", {
|
||||||
count: selectedCameraCount,
|
count: selectedCameraCount,
|
||||||
})}
|
})}
|
||||||
variant="select"
|
variant="select"
|
||||||
size="sm"
|
|
||||||
disabled={!canStartBatchExport}
|
disabled={!canStartBatchExport}
|
||||||
onClick={() => void startBatchExport()}
|
onClick={() => void startBatchExport()}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -85,7 +85,7 @@ export default function ImagePicker({
|
|||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={t("imagePicker.search.placeholder")}
|
placeholder={t("imagePicker.search.placeholder")}
|
||||||
className="text-md mb-3 md:text-sm"
|
className="mb-3 md:text-sm"
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setSearchTerm(e.target.value);
|
setSearchTerm(e.target.value);
|
||||||
|
|||||||
@ -290,7 +290,6 @@ export default function MultiExportDialog({
|
|||||||
const newCaseInputs = (
|
const newCaseInputs = (
|
||||||
<div className="space-y-2 pt-1">
|
<div className="space-y-2 pt-1">
|
||||||
<Input
|
<Input
|
||||||
className="text-md"
|
|
||||||
placeholder={t("export.case.newCaseNamePlaceholder")}
|
placeholder={t("export.case.newCaseNamePlaceholder")}
|
||||||
value={newCaseName}
|
value={newCaseName}
|
||||||
onChange={(event) => setNewCaseName(event.target.value)}
|
onChange={(event) => setNewCaseName(event.target.value)}
|
||||||
@ -298,7 +297,6 @@ export default function MultiExportDialog({
|
|||||||
autoFocus={isDesktop}
|
autoFocus={isDesktop}
|
||||||
/>
|
/>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="text-md"
|
|
||||||
placeholder={t("export.case.newCaseDescriptionPlaceholder")}
|
placeholder={t("export.case.newCaseDescriptionPlaceholder")}
|
||||||
value={newCaseDescription}
|
value={newCaseDescription}
|
||||||
onChange={(event) => setNewCaseDescription(event.target.value)}
|
onChange={(event) => setNewCaseDescription(event.target.value)}
|
||||||
@ -344,11 +342,7 @@ export default function MultiExportDialog({
|
|||||||
|
|
||||||
const footer = (
|
const footer = (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button onClick={() => handleOpenChange(false)} disabled={isExporting}>
|
||||||
variant="outline"
|
|
||||||
onClick={() => handleOpenChange(false)}
|
|
||||||
disabled={isExporting}
|
|
||||||
>
|
|
||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@ -380,7 +374,7 @@ export default function MultiExportDialog({
|
|||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{body}
|
{body}
|
||||||
<DialogFooter className="gap-2">{footer}</DialogFooter>
|
<DialogFooter>{footer}</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
@ -399,7 +393,7 @@ export default function MultiExportDialog({
|
|||||||
</DrawerDescription>
|
</DrawerDescription>
|
||||||
</DrawerHeader>
|
</DrawerHeader>
|
||||||
{body}
|
{body}
|
||||||
<div className="mt-4 flex flex-col-reverse gap-2">{footer}</div>
|
<DialogFooter className="mt-4">{footer}</DialogFooter>
|
||||||
</DrawerContent>
|
</DrawerContent>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -117,30 +117,23 @@ export default function RoleChangeDialog({
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter className="flex gap-3 sm:justify-end">
|
<DialogFooter>
|
||||||
<div className="flex flex-1 flex-col justify-end">
|
<Button
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
<Button
|
onClick={onCancel}
|
||||||
className="flex flex-1"
|
type="button"
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
>
|
||||||
variant="outline"
|
{t("button.cancel", { ns: "common" })}
|
||||||
onClick={onCancel}
|
</Button>
|
||||||
type="button"
|
<Button
|
||||||
>
|
variant="select"
|
||||||
{t("button.cancel", { ns: "common" })}
|
aria-label={t("button.save", { ns: "common" })}
|
||||||
</Button>
|
onClick={() => onSave(selectedRole)}
|
||||||
<Button
|
type="button"
|
||||||
variant="select"
|
disabled={selectedRole === currentRole}
|
||||||
aria-label={t("button.save", { ns: "common" })}
|
>
|
||||||
className="flex flex-1"
|
{t("button.save", { ns: "common" })}
|
||||||
onClick={() => onSave(selectedRole)}
|
</Button>
|
||||||
type="button"
|
|
||||||
disabled={selectedRole === currentRole}
|
|
||||||
>
|
|
||||||
{t("button.save", { ns: "common" })}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@ -450,36 +450,30 @@ export default function SetPasswordDialog({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<DialogFooter className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
<DialogFooter>
|
||||||
<div className="flex flex-1 flex-col justify-end">
|
<Button
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
<Button
|
onClick={onCancel}
|
||||||
className="flex flex-1"
|
type="button"
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
disabled={isLoading}
|
||||||
onClick={onCancel}
|
>
|
||||||
type="button"
|
{t("button.cancel", { ns: "common" })}
|
||||||
disabled={isLoading}
|
</Button>
|
||||||
>
|
<Button
|
||||||
{t("button.cancel", { ns: "common" })}
|
variant="select"
|
||||||
</Button>
|
aria-label={t("button.save", { ns: "common" })}
|
||||||
<Button
|
type="submit"
|
||||||
variant="select"
|
disabled={isLoading || !form.formState.isValid}
|
||||||
aria-label={t("button.save", { ns: "common" })}
|
>
|
||||||
className="flex flex-1"
|
{isLoading ? (
|
||||||
type="submit"
|
<div className="flex flex-row items-center gap-2">
|
||||||
disabled={isLoading || !form.formState.isValid}
|
<ActivityIndicator className="size-4" />
|
||||||
>
|
<span>{t("button.saving", { ns: "common" })}</span>
|
||||||
{isLoading ? (
|
</div>
|
||||||
<div className="flex flex-row items-center gap-2">
|
) : (
|
||||||
<ActivityIndicator className="size-4" />
|
t("button.save", { ns: "common" })
|
||||||
<span>{t("button.saving", { ns: "common" })}</span>
|
)}
|
||||||
</div>
|
</Button>
|
||||||
) : (
|
|
||||||
t("button.save", { ns: "common" })
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@ -196,21 +196,16 @@ export function ShareTimestampContent({
|
|||||||
|
|
||||||
{isDesktop && <Separator className="my-4 bg-secondary" />}
|
{isDesktop && <Separator className="my-4 bg-secondary" />}
|
||||||
|
|
||||||
<DialogFooter
|
<DialogFooter className="mt-3 sm:mt-0">
|
||||||
className={cn("mt-4", !isDesktop && "flex flex-col-reverse gap-2")}
|
|
||||||
>
|
|
||||||
{onCancel && (
|
{onCancel && (
|
||||||
<Button
|
<Button
|
||||||
className={cn(!isDesktop && "w-full")}
|
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
variant="outline"
|
|
||||||
onClick={onCancel}
|
onClick={onCancel}
|
||||||
>
|
>
|
||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
className={cn(!isDesktop && "w-full")}
|
|
||||||
variant="select"
|
variant="select"
|
||||||
onClick={() => onShareTimestamp(Math.floor(selectedTimestamp))}
|
onClick={() => onShareTimestamp(Math.floor(selectedTimestamp))}
|
||||||
>
|
>
|
||||||
@ -338,7 +333,7 @@ function CustomTimestampSelector({
|
|||||||
/>
|
/>
|
||||||
<div className="my-3 h-px w-full bg-secondary" />
|
<div className="my-3 h-px w-full bg-secondary" />
|
||||||
<input
|
<input
|
||||||
className="text-md mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
id="shareTimestamp"
|
id="shareTimestamp"
|
||||||
type="time"
|
type="time"
|
||||||
value={clock}
|
value={clock}
|
||||||
|
|||||||
@ -145,7 +145,7 @@ export function AnnotationSettingsPane({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="text-md mb-2">
|
<div className="mb-2">
|
||||||
{t("trackingDetails.annotationSettings.title")}
|
{t("trackingDetails.annotationSettings.title")}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -131,7 +131,7 @@ export default function CreateFaceWizardDialog({
|
|||||||
forbiddenPattern={/#/}
|
forbiddenPattern={/#/}
|
||||||
forbiddenErrorMessage={t("description.nameCannotContainHash")}
|
forbiddenErrorMessage={t("description.nameCannotContainHash")}
|
||||||
>
|
>
|
||||||
<div className="flex justify-end py-2">
|
<div className="flex flex-col-reverse gap-2 py-2 sm:flex-row sm:justify-end">
|
||||||
<Button variant="select" type="submit">
|
<Button variant="select" type="submit">
|
||||||
{t("button.next", { ns: "common" })}
|
{t("button.next", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
@ -144,7 +144,7 @@ export default function CreateFaceWizardDialog({
|
|||||||
{t("steps.description.uploadFace", { name })}
|
{t("steps.description.uploadFace", { name })}
|
||||||
</div>
|
</div>
|
||||||
<ImageEntry onSave={onUploadImage}>
|
<ImageEntry onSave={onUploadImage}>
|
||||||
<div className="flex justify-end py-2">
|
<div className="flex flex-col-reverse gap-2 py-2 sm:flex-row sm:justify-end">
|
||||||
<Button variant="select" type="submit">
|
<Button variant="select" type="submit">
|
||||||
{t("button.next", { ns: "common" })}
|
{t("button.next", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
@ -173,7 +173,7 @@ export default function CreateFaceWizardDialog({
|
|||||||
<LuExternalLink className="ml-2 inline-flex size-3" />
|
<LuExternalLink className="ml-2 inline-flex size-3" />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end">
|
<div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
||||||
<Button
|
<Button
|
||||||
variant="select"
|
variant="select"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@ -1569,7 +1569,7 @@ function ObjectDetailsTab({
|
|||||||
{t("button.yes", { ns: "common" })}
|
{t("button.yes", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className="flex-1 text-white"
|
className="flex-1"
|
||||||
aria-label={t("button.no", { ns: "common" })}
|
aria-label={t("button.no", { ns: "common" })}
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -1706,7 +1706,7 @@ function ObjectDetailsTab({
|
|||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Textarea
|
<Textarea
|
||||||
className="text-md h-32 md:text-sm"
|
className="h-32 md:text-sm"
|
||||||
placeholder={t("details.description.placeholder")}
|
placeholder={t("details.description.placeholder")}
|
||||||
value={desc}
|
value={desc}
|
||||||
onChange={(e) => setDesc(e.target.value)}
|
onChange={(e) => setDesc(e.target.value)}
|
||||||
|
|||||||
@ -821,7 +821,7 @@ export function TrackingDetails({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="capitalize">{label}</span>
|
<span className="capitalize">{label}</span>
|
||||||
<div className="md:text-md flex items-center text-xs text-secondary-foreground">
|
<div className="flex items-center text-xs text-secondary-foreground">
|
||||||
{formattedStart ?? ""}
|
{formattedStart ?? ""}
|
||||||
{event.end_time != null ? (
|
{event.end_time != null ? (
|
||||||
<> - {formattedEnd}</>
|
<> - {formattedEnd}</>
|
||||||
@ -1072,7 +1072,7 @@ function LifecycleIconRow({
|
|||||||
|
|
||||||
<div className="ml-2 flex w-full min-w-0 flex-1">
|
<div className="ml-2 flex w-full min-w-0 flex-1">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="text-md flex items-start break-words text-left">
|
<div className="flex items-start break-words text-left">
|
||||||
{getLifecycleItemDescription(item)}
|
{getLifecycleItemDescription(item)}
|
||||||
</div>
|
</div>
|
||||||
{/* Only show Score/Ratio/Area for object events, not for audio (heard) or manual API (external) events */}
|
{/* Only show Score/Ratio/Area for object events, not for audio (heard) or manual API (external) events */}
|
||||||
|
|||||||
@ -121,28 +121,22 @@ export default function DeleteCameraDialog({
|
|||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<DialogFooter className="flex gap-3 sm:justify-end">
|
<DialogFooter>
|
||||||
<div className="flex flex-1 flex-col justify-end">
|
<Button
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
aria-label={t("button.cancel", { ns: "common" })}
|
||||||
<Button
|
onClick={handleClose}
|
||||||
className="flex flex-1"
|
type="button"
|
||||||
aria-label={t("button.cancel", { ns: "common" })}
|
>
|
||||||
onClick={handleClose}
|
{t("button.cancel", { ns: "common" })}
|
||||||
type="button"
|
</Button>
|
||||||
>
|
<Button
|
||||||
{t("button.cancel", { ns: "common" })}
|
variant="destructive"
|
||||||
</Button>
|
aria-label={t("button.delete", { ns: "common" })}
|
||||||
<Button
|
onClick={handleDelete}
|
||||||
variant="destructive"
|
disabled={!selectedCamera}
|
||||||
aria-label={t("button.delete", { ns: "common" })}
|
>
|
||||||
className="flex flex-1 text-white"
|
{t("button.delete", { ns: "common" })}
|
||||||
onClick={handleDelete}
|
</Button>
|
||||||
disabled={!selectedCamera}
|
|
||||||
>
|
|
||||||
{t("button.delete", { ns: "common" })}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@ -173,39 +167,31 @@ export default function DeleteCameraDialog({
|
|||||||
{t("cameraManagement.deleteCameraDialog.deleteExports")}
|
{t("cameraManagement.deleteCameraDialog.deleteExports")}
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter className="flex gap-3 sm:justify-end">
|
<DialogFooter>
|
||||||
<div className="flex flex-1 flex-col justify-end">
|
<Button
|
||||||
<div className="flex flex-row gap-2 pt-5">
|
aria-label={t("button.back", { ns: "common" })}
|
||||||
<Button
|
onClick={handleBack}
|
||||||
className="flex flex-1"
|
type="button"
|
||||||
aria-label={t("button.back", { ns: "common" })}
|
disabled={isDeleting}
|
||||||
onClick={handleBack}
|
>
|
||||||
type="button"
|
{t("button.back", { ns: "common" })}
|
||||||
disabled={isDeleting}
|
</Button>
|
||||||
>
|
<Button
|
||||||
{t("button.back", { ns: "common" })}
|
variant="destructive"
|
||||||
</Button>
|
onClick={handleConfirmDelete}
|
||||||
<Button
|
disabled={isDeleting}
|
||||||
variant="destructive"
|
>
|
||||||
className="flex flex-1 text-white"
|
{isDeleting ? (
|
||||||
onClick={handleConfirmDelete}
|
<div className="flex flex-row items-center gap-2">
|
||||||
disabled={isDeleting}
|
<ActivityIndicator />
|
||||||
>
|
<span>
|
||||||
{isDeleting ? (
|
{t("cameraManagement.deleteCameraDialog.confirmButton")}
|
||||||
<div className="flex flex-row items-center gap-2">
|
</span>
|
||||||
<ActivityIndicator />
|
</div>
|
||||||
<span>
|
) : (
|
||||||
{t(
|
t("cameraManagement.deleteCameraDialog.confirmButton")
|
||||||
"cameraManagement.deleteCameraDialog.confirmButton",
|
)}
|
||||||
)}
|
</Button>
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
t("cameraManagement.deleteCameraDialog.confirmButton")
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -173,7 +173,7 @@ export function FrigatePlusDialog({
|
|||||||
{t("button.yes", { ns: "common" })}
|
{t("button.yes", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className="flex-1 text-white"
|
className="flex-1"
|
||||||
aria-label={t("button.no", { ns: "common" })}
|
aria-label={t("button.no", { ns: "common" })}
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@ -7,9 +7,7 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { isMobile } from "react-device-detect";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import FilterSwitch from "@/components/filter/FilterSwitch";
|
import FilterSwitch from "@/components/filter/FilterSwitch";
|
||||||
|
|
||||||
@ -77,7 +75,7 @@ export default function MultiSelectDialog({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter className={cn("pt-4", isMobile && "gap-2")}>
|
<DialogFooter className="pt-4">
|
||||||
<Button type="button" onClick={() => setOpen(false)}>
|
<Button type="button" onClick={() => setOpen(false)}>
|
||||||
{t("button.cancel")}
|
{t("button.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -144,18 +144,13 @@ export default function OptionAndInputDialog({
|
|||||||
<label className="text-sm font-medium text-secondary-foreground">
|
<label className="text-sm font-medium text-secondary-foreground">
|
||||||
{nameLabel}
|
{nameLabel}
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input value={name} onChange={(e) => setName(e.target.value)} />
|
||||||
className="text-md"
|
|
||||||
value={name}
|
|
||||||
onChange={(e) => setName(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm font-medium text-secondary-foreground">
|
<label className="text-sm font-medium text-secondary-foreground">
|
||||||
{descriptionLabel}
|
{descriptionLabel}
|
||||||
</label>
|
</label>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="text-md"
|
|
||||||
value={descriptionValue}
|
value={descriptionValue}
|
||||||
onChange={(e) => setDescriptionValue(e.target.value)}
|
onChange={(e) => setDescriptionValue(e.target.value)}
|
||||||
rows={2}
|
rows={2}
|
||||||
@ -164,10 +159,9 @@ export default function OptionAndInputDialog({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<DialogFooter className={cn("pt-2", isMobile && "gap-2")}>
|
<DialogFooter>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
|||||||
@ -349,7 +349,7 @@ function TimeRangeFilterContent({
|
|||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="flex flex-row items-center justify-center">
|
<PopoverContent className="flex flex-row items-center justify-center">
|
||||||
<input
|
<input
|
||||||
className="text-md mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
id="startTime"
|
id="startTime"
|
||||||
type="time"
|
type="time"
|
||||||
value={selectedAfterHour}
|
value={selectedAfterHour}
|
||||||
@ -389,7 +389,7 @@ function TimeRangeFilterContent({
|
|||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="flex flex-col items-center">
|
<PopoverContent className="flex flex-col items-center">
|
||||||
<input
|
<input
|
||||||
className="text-md mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
id="startTime"
|
id="startTime"
|
||||||
type="time"
|
type="time"
|
||||||
value={
|
value={
|
||||||
|
|||||||
@ -9,8 +9,6 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { isMobile } from "react-device-detect";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
type TextEntryDialogProps = {
|
type TextEntryDialogProps = {
|
||||||
@ -63,7 +61,7 @@ export default function TextEntryDialog({
|
|||||||
forbiddenPattern={forbiddenPattern}
|
forbiddenPattern={forbiddenPattern}
|
||||||
forbiddenErrorMessage={forbiddenErrorMessage}
|
forbiddenErrorMessage={forbiddenErrorMessage}
|
||||||
>
|
>
|
||||||
<DialogFooter className={cn("pt-4", isMobile && "gap-2")}>
|
<DialogFooter>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={isSaving}
|
disabled={isSaving}
|
||||||
|
|||||||
@ -443,7 +443,7 @@ export default function LivePlayer({
|
|||||||
<div className="absolute inset-0 rounded-lg bg-black/50 md:rounded-2xl" />
|
<div className="absolute inset-0 rounded-lg bg-black/50 md:rounded-2xl" />
|
||||||
<div className="absolute inset-0 left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center">
|
<div className="absolute inset-0 left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center">
|
||||||
<div className="flex flex-col items-center justify-center gap-2 rounded-lg bg-background/50 p-3 text-center">
|
<div className="flex flex-col items-center justify-center gap-2 rounded-lg bg-background/50 p-3 text-center">
|
||||||
<div className="text-md">{t("streamOffline.title")}</div>
|
<div>{t("streamOffline.title")}</div>
|
||||||
<TbExclamationCircle className="size-6" />
|
<TbExclamationCircle className="size-6" />
|
||||||
{!isCompact && (
|
{!isCompact && (
|
||||||
<p className="text-center text-sm">
|
<p className="text-center text-sm">
|
||||||
|
|||||||
@ -977,7 +977,7 @@ export default function CloneCameraDialog({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter className="flex flex-col items-stretch gap-3 sm:flex-row sm:items-center sm:justify-between sm:space-x-0">
|
<DialogFooter variant="split">
|
||||||
<div className="flex flex-col gap-1 text-sm text-muted-foreground">
|
<div className="flex flex-col gap-1 text-sm text-muted-foreground">
|
||||||
{changeCount > 0 && (
|
{changeCount > 0 && (
|
||||||
<>
|
<>
|
||||||
@ -1005,7 +1005,7 @@ export default function CloneCameraDialog({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row">
|
<div className="flex w-full flex-col-reverse gap-2 sm:w-auto sm:flex-row">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
|
|||||||
@ -22,7 +22,6 @@ import { FrigateConfig } from "@/types/frigateConfig";
|
|||||||
import { flattenPoints, interpolatePoints } from "@/utils/canvasUtil";
|
import { flattenPoints, interpolatePoints } from "@/utils/canvasUtil";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Toaster } from "../ui/sonner";
|
|
||||||
import ActivityIndicator from "../indicators/activity-indicator";
|
import ActivityIndicator from "../indicators/activity-indicator";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { LuExternalLink } from "react-icons/lu";
|
import { LuExternalLink } from "react-icons/lu";
|
||||||
@ -327,7 +326,6 @@ export default function MotionMaskEditPane({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
<Heading as="h3" className="my-2">
|
<Heading as="h3" className="my-2">
|
||||||
{polygon.name.length
|
{polygon.name.length
|
||||||
? t("masksAndZones.motionMasks.edit")
|
? t("masksAndZones.motionMasks.edit")
|
||||||
|
|||||||
@ -31,7 +31,6 @@ import { FaCheckCircle } from "react-icons/fa";
|
|||||||
import { flattenPoints, interpolatePoints } from "@/utils/canvasUtil";
|
import { flattenPoints, interpolatePoints } from "@/utils/canvasUtil";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Toaster } from "../ui/sonner";
|
|
||||||
import ActivityIndicator from "../indicators/activity-indicator";
|
import ActivityIndicator from "../indicators/activity-indicator";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { getTranslatedLabel } from "@/utils/i18n";
|
import { getTranslatedLabel } from "@/utils/i18n";
|
||||||
@ -335,7 +334,6 @@ export default function ObjectMaskEditPane({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
<Heading as="h3" className="my-2">
|
<Heading as="h3" className="my-2">
|
||||||
{polygon.name.length
|
{polygon.name.length
|
||||||
? t("masksAndZones.objectMasks.edit")
|
? t("masksAndZones.objectMasks.edit")
|
||||||
|
|||||||
@ -24,12 +24,12 @@ import { toRGBColorString } from "@/utils/canvasUtil";
|
|||||||
import { Polygon, PolygonType } from "@/types/canvas";
|
import { Polygon, PolygonType } from "@/types/canvas";
|
||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import { reviewQueries } from "@/utils/zoneEdutUtil";
|
import { reviewQueries } from "@/utils/zoneEdutUtil";
|
||||||
import IconWrapper from "../ui/icon-wrapper";
|
import IconWrapper from "../ui/icon-wrapper";
|
||||||
|
import { buttonVariants } from "@/components/ui/button";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import ActivityIndicator from "../indicators/activity-indicator";
|
import ActivityIndicator from "../indicators/activity-indicator";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
@ -368,8 +368,6 @@ export default function PolygonItem({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="transition-background relative my-1.5 flex flex-row items-center justify-between rounded-lg p-1 duration-100"
|
className="transition-background relative my-1.5 flex flex-row items-center justify-between rounded-lg p-1 duration-100"
|
||||||
@ -511,7 +509,7 @@ export default function PolygonItem({
|
|||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
className="bg-destructive text-white hover:bg-destructive/90"
|
className={cn(buttonVariants({ variant: "destructive" }))}
|
||||||
onClick={handleDelete}
|
onClick={handleDelete}
|
||||||
>
|
>
|
||||||
{polygon.polygonSource === "override"
|
{polygon.polygonSource === "override"
|
||||||
|
|||||||
@ -59,9 +59,7 @@ export default function ExploreSettings({
|
|||||||
<div className={cn(className, "my-3 space-y-5 py-3 md:mt-0 md:py-0")}>
|
<div className={cn(className, "my-3 space-y-5 py-3 md:mt-0 md:py-0")}>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="text-md">
|
<div>{t("explore.settings.defaultView.title")}</div>
|
||||||
{t("explore.settings.defaultView.title")}
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1 text-xs text-muted-foreground">
|
<div className="space-y-1 text-xs text-muted-foreground">
|
||||||
{t("explore.settings.defaultView.desc")}
|
{t("explore.settings.defaultView.desc")}
|
||||||
</div>
|
</div>
|
||||||
@ -97,9 +95,7 @@ export default function ExploreSettings({
|
|||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<div className="flex w-full flex-col space-y-4">
|
<div className="flex w-full flex-col space-y-4">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="text-md">
|
<div>{t("explore.settings.gridColumns.title")}</div>
|
||||||
{t("explore.settings.gridColumns.title")}
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1 text-xs text-muted-foreground">
|
<div className="space-y-1 text-xs text-muted-foreground">
|
||||||
{t("explore.settings.gridColumns.desc")}
|
{t("explore.settings.gridColumns.desc")}
|
||||||
</div>
|
</div>
|
||||||
@ -162,9 +158,7 @@ export function SearchTypeContent({
|
|||||||
<div className="overflow-x-hidden">
|
<div className="overflow-x-hidden">
|
||||||
<DropdownMenuSeparator className="mb-3" />
|
<DropdownMenuSeparator className="mb-3" />
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="text-md">
|
<div>{t("explore.settings.searchSource.label")}</div>
|
||||||
{t("explore.settings.searchSource.label")}
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1 text-xs text-muted-foreground">
|
<div className="space-y-1 text-xs text-muted-foreground">
|
||||||
{t("explore.settings.searchSource.desc")}
|
{t("explore.settings.searchSource.desc")}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import { Label } from "../ui/label";
|
|||||||
import PolygonEditControls from "./PolygonEditControls";
|
import PolygonEditControls from "./PolygonEditControls";
|
||||||
import { FaCheckCircle } from "react-icons/fa";
|
import { FaCheckCircle } from "react-icons/fa";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { flattenPoints, interpolatePoints } from "@/utils/canvasUtil";
|
import { flattenPoints, interpolatePoints } from "@/utils/canvasUtil";
|
||||||
import ActivityIndicator from "../indicators/activity-indicator";
|
import ActivityIndicator from "../indicators/activity-indicator";
|
||||||
@ -628,7 +627,6 @@ export default function ZoneEditPane({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
<Heading as="h3" className="my-2">
|
<Heading as="h3" className="my-2">
|
||||||
{polygon.name.length
|
{polygon.name.length
|
||||||
? t("masksAndZones.zones.edit")
|
? t("masksAndZones.zones.edit")
|
||||||
@ -709,7 +707,7 @@ export default function ZoneEditPane({
|
|||||||
<FormLabel>{t("masksAndZones.zones.inertia.title")}</FormLabel>
|
<FormLabel>{t("masksAndZones.zones.inertia.title")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
placeholder="3"
|
placeholder="3"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
@ -734,7 +732,7 @@ export default function ZoneEditPane({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
@ -864,7 +862,7 @@ export default function ZoneEditPane({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
{...field}
|
{...field}
|
||||||
onFocus={() => setActiveLine(1)}
|
onFocus={() => setActiveLine(1)}
|
||||||
onBlur={() => setActiveLine(undefined)}
|
onBlur={() => setActiveLine(undefined)}
|
||||||
@ -891,7 +889,7 @@ export default function ZoneEditPane({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
{...field}
|
{...field}
|
||||||
onFocus={() => setActiveLine(2)}
|
onFocus={() => setActiveLine(2)}
|
||||||
onBlur={() => setActiveLine(undefined)}
|
onBlur={() => setActiveLine(undefined)}
|
||||||
@ -918,7 +916,7 @@ export default function ZoneEditPane({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
{...field}
|
{...field}
|
||||||
onFocus={() => setActiveLine(3)}
|
onFocus={() => setActiveLine(3)}
|
||||||
onBlur={() => setActiveLine(undefined)}
|
onBlur={() => setActiveLine(undefined)}
|
||||||
@ -945,7 +943,7 @@ export default function ZoneEditPane({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
{...field}
|
{...field}
|
||||||
onFocus={() => setActiveLine(4)}
|
onFocus={() => setActiveLine(4)}
|
||||||
onBlur={() => setActiveLine(undefined)}
|
onBlur={() => setActiveLine(undefined)}
|
||||||
@ -972,7 +970,7 @@ export default function ZoneEditPane({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|||||||
@ -171,7 +171,7 @@ export default function Step1NameCamera({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md h-8"
|
className="h-8"
|
||||||
placeholder={t("cameraWizard.step1.cameraNamePlaceholder")}
|
placeholder={t("cameraWizard.step1.cameraNamePlaceholder")}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
@ -192,7 +192,7 @@ export default function Step1NameCamera({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md h-8"
|
className="h-8"
|
||||||
placeholder="192.168.1.100"
|
placeholder="192.168.1.100"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
@ -212,7 +212,7 @@ export default function Step1NameCamera({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md h-8"
|
className="h-8"
|
||||||
placeholder={t("cameraWizard.step1.usernamePlaceholder")}
|
placeholder={t("cameraWizard.step1.usernamePlaceholder")}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
@ -233,7 +233,7 @@ export default function Step1NameCamera({
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Input
|
<Input
|
||||||
className="text-md h-8 pr-10"
|
className="h-8 pr-10"
|
||||||
type={showPassword ? "text" : "password"}
|
type={showPassword ? "text" : "password"}
|
||||||
placeholder={t(
|
placeholder={t(
|
||||||
"cameraWizard.step1.passwordPlaceholder",
|
"cameraWizard.step1.passwordPlaceholder",
|
||||||
@ -316,7 +316,7 @@ export default function Step1NameCamera({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md h-8"
|
className="h-8"
|
||||||
type="text"
|
type="text"
|
||||||
{...field}
|
{...field}
|
||||||
placeholder="80"
|
placeholder="80"
|
||||||
@ -440,7 +440,7 @@ export default function Step1NameCamera({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
className="text-md h-8"
|
className="h-8"
|
||||||
placeholder="rtsp://username:password@host:port/path"
|
placeholder="rtsp://username:password@host:port/path"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
@ -455,7 +455,7 @@ export default function Step1NameCamera({
|
|||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
<div className="flex flex-col gap-3 pt-3 sm:flex-row sm:justify-end sm:gap-4">
|
<div className="flex flex-col-reverse gap-2 pt-3 sm:flex-row sm:justify-end">
|
||||||
<Button type="button" onClick={onCancel} className="sm:flex-1">
|
<Button type="button" onClick={onCancel} className="sm:flex-1">
|
||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -626,7 +626,7 @@ function ProbeFooterButtons({
|
|||||||
<ActivityIndicator className="size-4" />
|
<ActivityIndicator className="size-4" />
|
||||||
{t("cameraWizard.step2.probing")}
|
{t("cameraWizard.step2.probing")}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-3 pt-3 sm:flex-row sm:justify-end sm:gap-4">
|
<div className="flex flex-col-reverse gap-2 pt-3 sm:flex-row sm:justify-end">
|
||||||
<Button type="button" onClick={onBack} disabled className="sm:flex-1">
|
<Button type="button" onClick={onBack} disabled className="sm:flex-1">
|
||||||
{t("button.back", { ns: "common" })}
|
{t("button.back", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
@ -649,7 +649,7 @@ function ProbeFooterButtons({
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="text-sm text-destructive">{probeError}</div>
|
<div className="text-sm text-destructive">{probeError}</div>
|
||||||
<div className="flex flex-col gap-3 pt-3 sm:flex-row sm:justify-end sm:gap-4">
|
<div className="flex flex-col-reverse gap-2 pt-3 sm:flex-row sm:justify-end">
|
||||||
<Button type="button" onClick={onBack} className="sm:flex-1">
|
<Button type="button" onClick={onBack} className="sm:flex-1">
|
||||||
{t("button.back", { ns: "common" })}
|
{t("button.back", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
@ -670,7 +670,7 @@ function ProbeFooterButtons({
|
|||||||
// If manual mode, show Continue when test succeeded, otherwise show Test (calls onManualTest)
|
// If manual mode, show Continue when test succeeded, otherwise show Test (calls onManualTest)
|
||||||
if (mode === "manual") {
|
if (mode === "manual") {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-3 pt-3 sm:flex-row sm:justify-end sm:gap-4">
|
<div className="flex flex-col-reverse gap-2 pt-3 sm:flex-row sm:justify-end">
|
||||||
<Button type="button" onClick={onBack} className="sm:flex-1">
|
<Button type="button" onClick={onBack} className="sm:flex-1">
|
||||||
{t("button.back", { ns: "common" })}
|
{t("button.back", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
@ -707,7 +707,7 @@ function ProbeFooterButtons({
|
|||||||
|
|
||||||
// Default probe footer
|
// Default probe footer
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-3 pt-3 sm:flex-row sm:justify-end sm:gap-4">
|
<div className="flex flex-col-reverse gap-2 pt-3 sm:flex-row sm:justify-end">
|
||||||
<Button type="button" onClick={onBack} className="sm:flex-1">
|
<Button type="button" onClick={onBack} className="sm:flex-1">
|
||||||
{t("button.back", { ns: "common" })}
|
{t("button.back", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -731,7 +731,7 @@ export default function Step3StreamConfig({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col gap-3 pt-6 sm:flex-row sm:justify-end sm:gap-4">
|
<div className="flex flex-col-reverse gap-2 pt-6 sm:flex-row sm:justify-end">
|
||||||
{onBack && (
|
{onBack && (
|
||||||
<Button type="button" onClick={onBack} className="sm:flex-1">
|
<Button type="button" onClick={onBack} className="sm:flex-1">
|
||||||
{t("button.back", { ns: "common" })}
|
{t("button.back", { ns: "common" })}
|
||||||
|
|||||||
@ -490,7 +490,7 @@ export default function Step4Validation({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-3 pt-6 sm:flex-row sm:justify-end sm:gap-4">
|
<div className="flex flex-col-reverse gap-2 pt-6 sm:flex-row sm:justify-end">
|
||||||
{onBack && (
|
{onBack && (
|
||||||
<Button type="button" onClick={onBack} className="sm:flex-1">
|
<Button type="button" onClick={onBack} className="sm:flex-1">
|
||||||
{t("button.back", { ns: "common" })}
|
{t("button.back", { ns: "common" })}
|
||||||
|
|||||||
@ -176,20 +176,15 @@ export default function Step1NameAndType({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex gap-2 pt-4">
|
<div className="flex flex-col-reverse gap-2 pt-4 sm:flex-row sm:justify-end">
|
||||||
<Button
|
<Button type="button" onClick={onCancel} className="sm:flex-1">
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
onClick={onCancel}
|
|
||||||
className="flex-1"
|
|
||||||
>
|
|
||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="select"
|
variant="select"
|
||||||
disabled={!form.formState.isValid}
|
disabled={!form.formState.isValid}
|
||||||
className="flex-1"
|
className="sm:flex-1"
|
||||||
>
|
>
|
||||||
{t("button.next", { ns: "common" })}
|
{t("button.next", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -109,20 +109,15 @@ export default function Step2ConfigureData({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex gap-2 pt-4">
|
<div className="flex flex-col-reverse gap-2 pt-4 sm:flex-row sm:justify-end">
|
||||||
<Button
|
<Button type="button" onClick={onBack} className="sm:flex-1">
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
onClick={onBack}
|
|
||||||
className="flex-1"
|
|
||||||
>
|
|
||||||
{t("button.back", { ns: "common" })}
|
{t("button.back", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="select"
|
variant="select"
|
||||||
disabled={!form.formState.isValid}
|
disabled={!form.formState.isValid}
|
||||||
className="flex-1"
|
className="sm:flex-1"
|
||||||
>
|
>
|
||||||
{t("button.next", { ns: "common" })}
|
{t("button.next", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -181,20 +181,15 @@ export default function Step3ThresholdAndActions({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex gap-2 pt-4">
|
<div className="flex flex-col-reverse gap-2 pt-4 sm:flex-row sm:justify-end">
|
||||||
<Button
|
<Button type="button" onClick={onBack} className="sm:flex-1">
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
onClick={onBack}
|
|
||||||
className="flex-1"
|
|
||||||
>
|
|
||||||
{t("button.back", { ns: "common" })}
|
{t("button.back", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
className="flex-1"
|
className="sm:flex-1"
|
||||||
variant="select"
|
variant="select"
|
||||||
>
|
>
|
||||||
{isLoading && <ActivityIndicator className="mr-2 size-5" />}
|
{isLoading && <ActivityIndicator className="mr-2 size-5" />}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
|
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
|
||||||
|
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { buttonVariants } from "@/components/ui/button";
|
import { buttonVariants } from "@/components/ui/button";
|
||||||
|
|
||||||
@ -59,15 +61,35 @@ const AlertDialogHeader = ({
|
|||||||
);
|
);
|
||||||
AlertDialogHeader.displayName = "AlertDialogHeader";
|
AlertDialogHeader.displayName = "AlertDialogHeader";
|
||||||
|
|
||||||
|
const alertDialogFooterVariants = cva(
|
||||||
|
"flex flex-col-reverse gap-2 sm:flex-row",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
// 1-2 action buttons: full-width stacked on mobile, right-aligned auto on desktop.
|
||||||
|
// [&>button] only targets real button children, so non-button siblings are untouched.
|
||||||
|
actions: "sm:justify-end [&>button]:w-full sm:[&>button]:w-auto",
|
||||||
|
// context content (text/popover) alongside actions: space-between on desktop.
|
||||||
|
// flex-col (not -reverse) keeps the context above the buttons when stacked on mobile.
|
||||||
|
split: "flex-col sm:items-center sm:justify-between",
|
||||||
|
// alignment only; never touches children. Escape hatch for unusual content.
|
||||||
|
plain: "sm:justify-end",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "actions",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const AlertDialogFooter = ({
|
const AlertDialogFooter = ({
|
||||||
className,
|
className,
|
||||||
|
variant,
|
||||||
...props
|
...props
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
}: React.HTMLAttributes<HTMLDivElement> &
|
||||||
|
VariantProps<typeof alertDialogFooterVariants>) => (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(alertDialogFooterVariants({ variant }), className)}
|
||||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -11,8 +11,7 @@ const buttonVariants = cva(
|
|||||||
variant: {
|
variant: {
|
||||||
default: "bg-secondary text-primary hover:bg-secondary/80",
|
default: "bg-secondary text-primary hover:bg-secondary/80",
|
||||||
select: "bg-selected text-selected-foreground hover:bg-opacity-90",
|
select: "bg-selected text-selected-foreground hover:bg-opacity-90",
|
||||||
destructive:
|
destructive: "bg-destructive text-white hover:bg-destructive/90",
|
||||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
||||||
outline:
|
outline:
|
||||||
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||||
secondary: "bg-primary text-primary-foreground hover:bg-primary/90",
|
secondary: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useHistoryBack } from "@/hooks/use-history-back";
|
import { useHistoryBack } from "@/hooks/use-history-back";
|
||||||
@ -107,15 +108,32 @@ const DialogHeader = ({
|
|||||||
);
|
);
|
||||||
DialogHeader.displayName = "DialogHeader";
|
DialogHeader.displayName = "DialogHeader";
|
||||||
|
|
||||||
|
const dialogFooterVariants = cva("flex flex-col-reverse gap-2 sm:flex-row", {
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
// 1-2 action buttons: full-width stacked on mobile, right-aligned auto on desktop.
|
||||||
|
// [&>button] only targets real button children, so non-button siblings are untouched.
|
||||||
|
actions: "sm:justify-end [&>button]:w-full sm:[&>button]:w-auto",
|
||||||
|
// context content (text/popover) alongside actions: space-between on desktop.
|
||||||
|
// flex-col (not -reverse) keeps the context above the buttons when stacked on mobile.
|
||||||
|
split: "flex-col sm:items-center sm:justify-between",
|
||||||
|
// alignment only; never touches children. Escape hatch for unusual content.
|
||||||
|
plain: "sm:justify-end",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "actions",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const DialogFooter = ({
|
const DialogFooter = ({
|
||||||
className,
|
className,
|
||||||
|
variant,
|
||||||
...props
|
...props
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
}: React.HTMLAttributes<HTMLDivElement> &
|
||||||
|
VariantProps<typeof dialogFooterVariants>) => (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(dialogFooterVariants({ variant }), className)}
|
||||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -608,7 +608,6 @@ function Exports() {
|
|||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<Button
|
<Button
|
||||||
className="text-white"
|
|
||||||
aria-label="Delete Export"
|
aria-label="Delete Export"
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={() => onHandleDelete()}
|
onClick={() => onHandleDelete()}
|
||||||
@ -658,7 +657,6 @@ function Exports() {
|
|||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<Button
|
<Button
|
||||||
className="text-white"
|
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={() => void handleDeleteCase()}
|
onClick={() => void handleDeleteCase()}
|
||||||
>
|
>
|
||||||
@ -744,7 +742,7 @@ function Exports() {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Input
|
<Input
|
||||||
className="text-md w-full bg-muted md:w-1/2"
|
className="w-full bg-muted md:w-1/2"
|
||||||
placeholder={t("search")}
|
placeholder={t("search")}
|
||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
@ -1277,8 +1275,8 @@ function CaseEditorDialog({
|
|||||||
value={description}
|
value={description}
|
||||||
onChange={(event) => setDescription(event.target.value)}
|
onChange={(event) => setDescription(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className="flex justify-end gap-2">
|
<DialogFooter>
|
||||||
<Button variant="outline" onClick={onClose}>
|
<Button onClick={onClose}>
|
||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@ -1295,7 +1293,7 @@ function CaseEditorDialog({
|
|||||||
? t("button.save", { ns: "common" })
|
? t("button.save", { ns: "common" })
|
||||||
: t("toolbar.newCase")}
|
: t("toolbar.newCase")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</DialogFooter>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@ -1427,13 +1425,12 @@ function CaseAddExportDialog({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter className="flex-row justify-end gap-2">
|
<DialogFooter>
|
||||||
<Button variant="outline" size="sm" onClick={onClose}>
|
<Button onClick={onClose}>
|
||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="select"
|
variant="select"
|
||||||
size="sm"
|
|
||||||
disabled={selectedIds.length === 0 || isAdding}
|
disabled={selectedIds.length === 0 || isAdding}
|
||||||
onClick={() => void handleAdd()}
|
onClick={() => void handleAdd()}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -567,7 +567,6 @@ function LibrarySelector({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
className="text-white"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (confirmDelete) {
|
if (confirmDelete) {
|
||||||
handleDeleteFace(confirmDelete);
|
handleDeleteFace(confirmDelete);
|
||||||
|
|||||||
@ -332,7 +332,7 @@ export default function Replay() {
|
|||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="flex items-center gap-2 text-white"
|
className="flex items-center gap-2"
|
||||||
disabled={isStopping}
|
disabled={isStopping}
|
||||||
>
|
>
|
||||||
{isStopping && <ActivityIndicator className="size-4" />}
|
{isStopping && <ActivityIndicator className="size-4" />}
|
||||||
@ -355,10 +355,7 @@ export default function Replay() {
|
|||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
onClick={handleStop}
|
onClick={handleStop}
|
||||||
className={cn(
|
className={cn(buttonVariants({ variant: "destructive" }))}
|
||||||
buttonVariants({ variant: "destructive" }),
|
|
||||||
"text-white",
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{t("page.confirmStop.confirm")}
|
{t("page.confirmStop.confirm")}
|
||||||
</AlertDialogAction>
|
</AlertDialogAction>
|
||||||
@ -687,7 +684,7 @@ function ObjectList({ cameraConfig, objects, config }: ObjectListProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-8/12 flex-row items-center justify-end">
|
<div className="flex w-8/12 flex-row items-center justify-end">
|
||||||
<div className="text-md mr-2 w-1/3">
|
<div className="mr-2 w-1/3">
|
||||||
<div className="flex flex-col items-end justify-end">
|
<div className="flex flex-col items-end justify-end">
|
||||||
<p className="mb-1.5 text-sm text-primary-variant">
|
<p className="mb-1.5 text-sm text-primary-variant">
|
||||||
{t("debug.objectShapeFilterDrawing.score", {
|
{t("debug.objectShapeFilterDrawing.score", {
|
||||||
@ -697,7 +694,7 @@ function ObjectList({ cameraConfig, objects, config }: ObjectListProps) {
|
|||||||
{obj.score ? (obj.score * 100).toFixed(1).toString() : "-"}%
|
{obj.score ? (obj.score * 100).toFixed(1).toString() : "-"}%
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-md mr-2 w-1/3">
|
<div className="mr-2 w-1/3">
|
||||||
<div className="flex flex-col items-end justify-end">
|
<div className="flex flex-col items-end justify-end">
|
||||||
<p className="mb-1.5 text-sm text-primary-variant">
|
<p className="mb-1.5 text-sm text-primary-variant">
|
||||||
{t("debug.objectShapeFilterDrawing.ratio", {
|
{t("debug.objectShapeFilterDrawing.ratio", {
|
||||||
@ -707,7 +704,7 @@ function ObjectList({ cameraConfig, objects, config }: ObjectListProps) {
|
|||||||
{obj.ratio ? obj.ratio.toFixed(2).toString() : "-"}
|
{obj.ratio ? obj.ratio.toFixed(2).toString() : "-"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-md mr-2 w-1/3">
|
<div className="mr-2 w-1/3">
|
||||||
<div className="flex flex-col items-end justify-end">
|
<div className="flex flex-col items-end justify-end">
|
||||||
<p className="mb-1.5 text-sm text-primary-variant">
|
<p className="mb-1.5 text-sm text-primary-variant">
|
||||||
{t("debug.objectShapeFilterDrawing.area", {
|
{t("debug.objectShapeFilterDrawing.area", {
|
||||||
|
|||||||
@ -1616,7 +1616,7 @@ export default function Settings() {
|
|||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Toaster position="top-center" />
|
<Toaster position="top-center" closeButton={true} />
|
||||||
{!contentMobileOpen && (
|
{!contentMobileOpen && (
|
||||||
<div
|
<div
|
||||||
key={`mobile-menu-${selectedCamera}`}
|
key={`mobile-menu-${selectedCamera}`}
|
||||||
@ -1872,7 +1872,7 @@ export default function Settings() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col">
|
<div className="flex h-full flex-col">
|
||||||
<Toaster position="top-center" />
|
<Toaster position="top-center" closeButton={true} />
|
||||||
<div className="flex min-h-16 items-center justify-between border-b border-secondary p-3">
|
<div className="flex min-h-16 items-center justify-between border-b border-secondary p-3">
|
||||||
<div className="mr-2 flex w-full items-center justify-between gap-3">
|
<div className="mr-2 flex w-full items-center justify-between gap-3">
|
||||||
<Heading as="h3" className="mb-0">
|
<Heading as="h3" className="mb-0">
|
||||||
|
|||||||
@ -668,7 +668,6 @@ function LibrarySelector({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
className="text-white"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (confirmDelete) {
|
if (confirmDelete) {
|
||||||
handleDeleteCategory(confirmDelete);
|
handleDeleteCategory(confirmDelete);
|
||||||
|
|||||||
@ -1389,9 +1389,8 @@ function MotionReview({
|
|||||||
selectedCells={pendingFilterCells}
|
selectedCells={pendingFilterCells}
|
||||||
onCellsChange={setPendingFilterCells}
|
onCellsChange={setPendingFilterCells}
|
||||||
/>
|
/>
|
||||||
<DialogFooter className="justify-end gap-1">
|
<DialogFooter>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
|
||||||
disabled={pendingFilterCells.size === 0}
|
disabled={pendingFilterCells.size === 0}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPendingFilterCells(new Set());
|
setPendingFilterCells(new Set());
|
||||||
@ -1432,9 +1431,7 @@ function MotionReview({
|
|||||||
<div className="space-y-4 py-2">
|
<div className="space-y-4 py-2">
|
||||||
{!isDesktop && (
|
{!isDesktop && (
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="text-md">
|
<div>{t("motionPreviews.mobileSettingsTitle")}</div>
|
||||||
{t("motionPreviews.mobileSettingsTitle")}
|
|
||||||
</div>
|
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
{t("motionPreviews.mobileSettingsDesc")}
|
{t("motionPreviews.mobileSettingsDesc")}
|
||||||
</div>
|
</div>
|
||||||
@ -1443,9 +1440,7 @@ function MotionReview({
|
|||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="text-md">
|
<div>{t("motionPreviews.speed")}</div>
|
||||||
{t("motionPreviews.speed")}
|
|
||||||
</div>
|
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
{t("motionPreviews.speedDesc")}
|
{t("motionPreviews.speedDesc")}
|
||||||
</div>
|
</div>
|
||||||
@ -1474,7 +1469,7 @@ function MotionReview({
|
|||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="text-md">{t("motionPreviews.dim")}</div>
|
<div>{t("motionPreviews.dim")}</div>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
{t("motionPreviews.dimDesc")}
|
{t("motionPreviews.dimDesc")}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -784,7 +784,7 @@ export default function LiveCameraView({
|
|||||||
transcription != null && (
|
transcription != null && (
|
||||||
<div
|
<div
|
||||||
ref={transcriptionRef}
|
ref={transcriptionRef}
|
||||||
className="text-md scrollbar-container absolute bottom-4 left-1/2 max-h-[15vh] w-[75%] -translate-x-1/2 overflow-y-auto rounded-lg bg-black/70 p-2 text-white md:w-[50%]"
|
className="scrollbar-container absolute bottom-4 left-1/2 max-h-[15vh] w-[75%] -translate-x-1/2 overflow-y-auto rounded-lg bg-black/70 p-2 text-white md:w-[50%]"
|
||||||
>
|
>
|
||||||
{transcription}
|
{transcription}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -630,7 +630,7 @@ function SearchRangeSelector({
|
|||||||
/>
|
/>
|
||||||
<SelectSeparator className="bg-secondary" />
|
<SelectSeparator className="bg-secondary" />
|
||||||
<input
|
<input
|
||||||
className="text-md mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
id="startTime"
|
id="startTime"
|
||||||
type="time"
|
type="time"
|
||||||
value={startClock}
|
value={startClock}
|
||||||
@ -696,7 +696,7 @@ function SearchRangeSelector({
|
|||||||
/>
|
/>
|
||||||
<SelectSeparator className="bg-secondary" />
|
<SelectSeparator className="bg-secondary" />
|
||||||
<input
|
<input
|
||||||
className="text-md mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
className="mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||||
id="endTime"
|
id="endTime"
|
||||||
type="time"
|
type="time"
|
||||||
value={endClock}
|
value={endClock}
|
||||||
|
|||||||
@ -1052,7 +1052,6 @@ export default function MotionSearchView({
|
|||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
className="text-white"
|
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
void cancelMotionSearchJob(jobId, jobCamera);
|
void cancelMotionSearchJob(jobId, jobCamera);
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import Heading from "@/components/ui/heading";
|
import Heading from "@/components/ui/heading";
|
||||||
import { User } from "@/types/user";
|
import { User } from "@/types/user";
|
||||||
@ -790,7 +789,6 @@ export default function AuthenticationView({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full flex-col">
|
<div className="flex size-full flex-col">
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto pb-2 md:order-none md:mr-3 md:mt-0">
|
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto pb-2 md:order-none md:mr-3 md:mt-0">
|
||||||
{section === "users" && UsersSection}
|
{section === "users" && UsersSection}
|
||||||
{section === "roles" && RolesSection}
|
{section === "roles" && RolesSection}
|
||||||
|
|||||||
@ -250,7 +250,7 @@ export default function CameraManagementView({
|
|||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={() => setShowDeleteDialog(true)}
|
onClick={() => setShowDeleteDialog(true)}
|
||||||
className="mb-2 flex max-w-48 items-center gap-2 text-white"
|
className="mb-2 flex max-w-48 items-center gap-2"
|
||||||
>
|
>
|
||||||
<LuTrash2 className="h-4 w-4" />
|
<LuTrash2 className="h-4 w-4" />
|
||||||
{t("cameraManagement.deleteCamera")}
|
{t("cameraManagement.deleteCamera")}
|
||||||
@ -261,7 +261,7 @@ export default function CameraManagementView({
|
|||||||
{enabledCameras.length + disabledCameras.length > 0 && (
|
{enabledCameras.length + disabledCameras.length > 0 && (
|
||||||
<div className="mb-5 space-y-3">
|
<div className="mb-5 space-y-3">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="text-md font-medium">
|
<div className="font-medium">
|
||||||
{t("cameraManagement.clone.sectionTitle")}
|
{t("cameraManagement.clone.sectionTitle")}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
|
|||||||
@ -22,7 +22,6 @@ import ActivityIndicator from "@/components/indicators/activity-indicator";
|
|||||||
import Heading from "@/components/ui/heading";
|
import Heading from "@/components/ui/heading";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import {
|
import {
|
||||||
@ -598,7 +597,6 @@ export default function DetectorsAndModelSettingsView({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full flex-col md:pr-2">
|
<div className="flex size-full flex-col md:pr-2">
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
<div className="mb-1 flex items-center justify-between gap-4 pt-2">
|
<div className="mb-1 flex items-center justify-between gap-4 pt-2">
|
||||||
<div className="flex max-w-5xl flex-col">
|
<div className="flex max-w-5xl flex-col">
|
||||||
<Heading as="h4">{t("detectorsAndModel.title")}</Heading>
|
<Heading as="h4">{t("detectorsAndModel.title")}</Heading>
|
||||||
|
|||||||
@ -306,9 +306,7 @@ export default function EnrichmentsSettingsView({
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-2 flex flex-col space-y-6">
|
<div className="mt-2 flex flex-col space-y-6">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="text-md">
|
<div>{t("enrichments.semanticSearch.modelSize.label")}</div>
|
||||||
{t("enrichments.semanticSearch.modelSize.label")}
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1 text-sm text-muted-foreground">
|
<div className="space-y-1 text-sm text-muted-foreground">
|
||||||
<p>
|
<p>
|
||||||
<Trans ns="views/settings">
|
<Trans ns="views/settings">
|
||||||
@ -436,9 +434,7 @@ export default function EnrichmentsSettingsView({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="text-md">
|
<div>{t("enrichments.faceRecognition.modelSize.label")}</div>
|
||||||
{t("enrichments.faceRecognition.modelSize.label")}
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1 text-sm text-muted-foreground">
|
<div className="space-y-1 text-sm text-muted-foreground">
|
||||||
<p>
|
<p>
|
||||||
<Trans ns="views/settings">
|
<Trans ns="views/settings">
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { Link, useNavigate } from "react-router-dom";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { CheckCircle2, XCircle } from "lucide-react";
|
import { CheckCircle2, XCircle } from "lucide-react";
|
||||||
import { LuExternalLink } from "react-icons/lu";
|
import { LuExternalLink } from "react-icons/lu";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
|
||||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import Heading from "@/components/ui/heading";
|
import Heading from "@/components/ui/heading";
|
||||||
@ -35,7 +34,6 @@ export default function FrigatePlusSettingsView(_props: SettingsPageProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full flex-col md:pr-2">
|
<div className="flex size-full flex-col md:pr-2">
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
<div className="w-full max-w-5xl space-y-6 pt-2">
|
<div className="w-full max-w-5xl space-y-6 pt-2">
|
||||||
<div className="flex flex-col gap-0">
|
<div className="flex flex-col gap-0">
|
||||||
<Heading as="h4" className="mb-2">
|
<Heading as="h4" className="mb-2">
|
||||||
|
|||||||
@ -446,10 +446,7 @@ export default function Go2RtcStreamsSettingsView({
|
|||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
className={cn(
|
className={cn(buttonVariants({ variant: "destructive" }))}
|
||||||
buttonVariants({ variant: "destructive" }),
|
|
||||||
"text-white",
|
|
||||||
)}
|
|
||||||
onClick={() => deleteDialog && deleteStream(deleteDialog)}
|
onClick={() => deleteDialog && deleteStream(deleteDialog)}
|
||||||
>
|
>
|
||||||
{t("go2rtcStreams.deleteStream")}
|
{t("go2rtcStreams.deleteStream")}
|
||||||
@ -533,7 +530,6 @@ function RenameStreamDialog({
|
|||||||
<div className="space-y-2 py-2">
|
<div className="space-y-2 py-2">
|
||||||
<Label>{t("go2rtcStreams.newStreamName")}</Label>
|
<Label>{t("go2rtcStreams.newStreamName")}</Label>
|
||||||
<Input
|
<Input
|
||||||
className="text-md"
|
|
||||||
value={newName}
|
value={newName}
|
||||||
onChange={(e) => setNewName(e.target.value)}
|
onChange={(e) => setNewName(e.target.value)}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
@ -547,7 +543,7 @@ function RenameStreamDialog({
|
|||||||
<p className="text-xs text-destructive">{nameError}</p>
|
<p className="text-xs text-destructive">{nameError}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter className="gap-2 sm:justify-end md:gap-0">
|
<DialogFooter>
|
||||||
<DialogClose asChild>
|
<DialogClose asChild>
|
||||||
<Button>{t("button.cancel", { ns: "common" })}</Button>
|
<Button>{t("button.cancel", { ns: "common" })}</Button>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
@ -614,7 +610,6 @@ function AddStreamDialog({
|
|||||||
<Label>{t("go2rtcStreams.streamName")}</Label>
|
<Label>{t("go2rtcStreams.streamName")}</Label>
|
||||||
<Input
|
<Input
|
||||||
value={name}
|
value={name}
|
||||||
className="text-md"
|
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === "Enter" && canSubmit) {
|
if (e.key === "Enter" && canSubmit) {
|
||||||
@ -628,7 +623,7 @@ function AddStreamDialog({
|
|||||||
<p className="text-xs text-destructive">{nameError}</p>
|
<p className="text-xs text-destructive">{nameError}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter className="gap-2 sm:justify-end md:gap-0">
|
<DialogFooter>
|
||||||
<DialogClose asChild>
|
<DialogClose asChild>
|
||||||
<Button>{t("button.cancel", { ns: "common" })}</Button>
|
<Button>{t("button.cancel", { ns: "common" })}</Button>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
@ -924,7 +919,7 @@ function StreamUrlEntry({
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="relative flex-1">
|
<div className="relative flex-1">
|
||||||
<Input
|
<Input
|
||||||
className="text-md h-8 pr-10"
|
className="h-8 pr-10"
|
||||||
value={baseUrlForDisplay}
|
value={baseUrlForDisplay}
|
||||||
onChange={(e) => handleBaseUrlChange(e.target.value)}
|
onChange={(e) => handleBaseUrlChange(e.target.value)}
|
||||||
onFocus={() => setIsFocused(true)}
|
onFocus={() => setIsFocused(true)}
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import {
|
|||||||
} from "@/components/ui/hover-card";
|
} from "@/components/ui/hover-card";
|
||||||
import copy from "copy-to-clipboard";
|
import copy from "copy-to-clipboard";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@ -730,7 +729,6 @@ export default function MasksAndZonesView({
|
|||||||
<>
|
<>
|
||||||
{cameraConfig && editingPolygons && (
|
{cameraConfig && editingPolygons && (
|
||||||
<div className="flex size-full flex-col md:flex-row">
|
<div className="flex size-full flex-col md:flex-row">
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mr-3 md:mt-0 md:w-3/12 md:min-w-0 md:shrink-0">
|
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mr-3 md:mt-0 md:w-3/12 md:min-w-0 md:shrink-0">
|
||||||
{editPane == "zone" && (
|
{editPane == "zone" && (
|
||||||
<ZoneEditPane
|
<ZoneEditPane
|
||||||
@ -793,7 +791,7 @@ export default function MasksAndZonesView({
|
|||||||
<div className="my-3 flex flex-row items-center justify-between">
|
<div className="my-3 flex flex-row items-center justify-between">
|
||||||
<HoverCard>
|
<HoverCard>
|
||||||
<HoverCardTrigger asChild>
|
<HoverCardTrigger asChild>
|
||||||
<div className="text-md cursor-default">
|
<div className="cursor-default">
|
||||||
{t("masksAndZones.zones.label")}
|
{t("masksAndZones.zones.label")}
|
||||||
</div>
|
</div>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
@ -871,7 +869,7 @@ export default function MasksAndZonesView({
|
|||||||
<div className="my-3 flex flex-row items-center justify-between">
|
<div className="my-3 flex flex-row items-center justify-between">
|
||||||
<HoverCard>
|
<HoverCard>
|
||||||
<HoverCardTrigger asChild>
|
<HoverCardTrigger asChild>
|
||||||
<div className="text-md cursor-default">
|
<div className="cursor-default">
|
||||||
{t("masksAndZones.motionMasks.label")}
|
{t("masksAndZones.motionMasks.label")}
|
||||||
</div>
|
</div>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
@ -953,7 +951,7 @@ export default function MasksAndZonesView({
|
|||||||
<div className="my-3 flex flex-row items-center justify-between">
|
<div className="my-3 flex flex-row items-center justify-between">
|
||||||
<HoverCard>
|
<HoverCard>
|
||||||
<HoverCardTrigger asChild>
|
<HoverCardTrigger asChild>
|
||||||
<div className="text-md cursor-default">
|
<div className="cursor-default">
|
||||||
{t("masksAndZones.objectMasks.label")}
|
{t("masksAndZones.objectMasks.label")}
|
||||||
</div>
|
</div>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import Heading from "@/components/ui/heading";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
|
||||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@ -105,7 +104,6 @@ export default function MediaSyncSettingsView() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex size-full flex-col md:flex-row">
|
<div className="flex size-full flex-col md:flex-row">
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto px-2 md:order-none">
|
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto px-2 md:order-none">
|
||||||
<div className="grid w-full grid-cols-1 gap-4 md:grid-cols-2">
|
<div className="grid w-full grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
<div className="col-span-1">
|
<div className="col-span-1">
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import {
|
|||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
@ -184,7 +183,6 @@ export default function MotionTunerView({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full flex-col md:flex-row">
|
<div className="flex size-full flex-col md:flex-row">
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mr-3 md:mt-0 md:w-3/12">
|
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mr-3 md:mt-0 md:w-3/12">
|
||||||
<Heading as="h4" className="mb-2">
|
<Heading as="h4" className="mb-2">
|
||||||
{t("motionDetectionTuner.title")}
|
{t("motionDetectionTuner.title")}
|
||||||
@ -208,7 +206,7 @@ export default function MotionTunerView({
|
|||||||
<div className="flex w-full flex-col space-y-6">
|
<div className="flex w-full flex-col space-y-6">
|
||||||
<div className="mt-2 space-y-6">
|
<div className="mt-2 space-y-6">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<Label htmlFor="motion-threshold" className="text-md">
|
<Label htmlFor="motion-threshold">
|
||||||
{t("motionDetectionTuner.Threshold.title")}
|
{t("motionDetectionTuner.Threshold.title")}
|
||||||
</Label>
|
</Label>
|
||||||
<div className="my-2 text-sm text-muted-foreground">
|
<div className="my-2 text-sm text-muted-foreground">
|
||||||
@ -237,7 +235,7 @@ export default function MotionTunerView({
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-2 space-y-6">
|
<div className="mt-2 space-y-6">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<Label htmlFor="motion-threshold" className="text-md">
|
<Label htmlFor="motion-threshold">
|
||||||
{t("motionDetectionTuner.contourArea.title")}
|
{t("motionDetectionTuner.contourArea.title")}
|
||||||
</Label>
|
</Label>
|
||||||
<div className="my-2 text-sm text-muted-foreground">
|
<div className="my-2 text-sm text-muted-foreground">
|
||||||
|
|||||||
@ -415,7 +415,7 @@ function ObjectList({ cameraConfig, objects }: ObjectListProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-8/12 flex-row items-center justify-end">
|
<div className="flex w-8/12 flex-row items-center justify-end">
|
||||||
<div className="text-md mr-2 w-1/3">
|
<div className="mr-2 w-1/3">
|
||||||
<div className="flex flex-col items-end justify-end">
|
<div className="flex flex-col items-end justify-end">
|
||||||
<p className="mb-1.5 text-sm text-primary-variant">
|
<p className="mb-1.5 text-sm text-primary-variant">
|
||||||
{t("debug.objectShapeFilterDrawing.score")}
|
{t("debug.objectShapeFilterDrawing.score")}
|
||||||
@ -426,7 +426,7 @@ function ObjectList({ cameraConfig, objects }: ObjectListProps) {
|
|||||||
%
|
%
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-md mr-2 w-1/3">
|
<div className="mr-2 w-1/3">
|
||||||
<div className="flex flex-col items-end justify-end">
|
<div className="flex flex-col items-end justify-end">
|
||||||
<p className="mb-1.5 text-sm text-primary-variant">
|
<p className="mb-1.5 text-sm text-primary-variant">
|
||||||
{t("debug.objectShapeFilterDrawing.ratio")}
|
{t("debug.objectShapeFilterDrawing.ratio")}
|
||||||
@ -434,7 +434,7 @@ function ObjectList({ cameraConfig, objects }: ObjectListProps) {
|
|||||||
{obj.ratio ? obj.ratio.toFixed(2).toString() : "-"}
|
{obj.ratio ? obj.ratio.toFixed(2).toString() : "-"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-md mr-2 w-1/3">
|
<div className="mr-2 w-1/3">
|
||||||
<div className="flex flex-col items-end justify-end">
|
<div className="flex flex-col items-end justify-end">
|
||||||
<p className="mb-1.5 text-sm text-primary-variant">
|
<p className="mb-1.5 text-sm text-primary-variant">
|
||||||
{t("debug.objectShapeFilterDrawing.area")}
|
{t("debug.objectShapeFilterDrawing.area")}
|
||||||
@ -505,7 +505,7 @@ function AudioList({ cameraConfig, audioDetections }: AudioListProps) {
|
|||||||
<div className="ml-3 text-lg">{getTranslatedLabel(key)}</div>
|
<div className="ml-3 text-lg">{getTranslatedLabel(key)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-8/12 flex-row items-center justify-end">
|
<div className="flex w-8/12 flex-row items-center justify-end">
|
||||||
<div className="text-md mr-2 w-1/3">
|
<div className="mr-2 w-1/3">
|
||||||
<div className="flex flex-col items-end justify-end">
|
<div className="flex flex-col items-end justify-end">
|
||||||
<p className="mb-1.5 text-sm text-primary-variant">
|
<p className="mb-1.5 text-sm text-primary-variant">
|
||||||
{t("debug.audio.score")}
|
{t("debug.audio.score")}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import { resolveCameraName } from "@/hooks/use-camera-friendly-name";
|
|||||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import Heading from "@/components/ui/heading";
|
import Heading from "@/components/ui/heading";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button, buttonVariants } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import NameAndIdFields from "@/components/input/NameAndIdFields";
|
import NameAndIdFields from "@/components/input/NameAndIdFields";
|
||||||
@ -654,10 +654,9 @@ export default function ProfilesView({
|
|||||||
ns: "views/settings",
|
ns: "views/settings",
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<DialogFooter className="gap-2 md:gap-0">
|
<DialogFooter>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
|
||||||
onClick={() => setAddDialogOpen(false)}
|
onClick={() => setAddDialogOpen(false)}
|
||||||
disabled={addingProfile}
|
disabled={addingProfile}
|
||||||
>
|
>
|
||||||
@ -709,7 +708,7 @@ export default function ProfilesView({
|
|||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
className="bg-destructive text-white hover:bg-destructive/90"
|
className={cn(buttonVariants({ variant: "destructive" }))}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleDeleteProfile();
|
handleDeleteProfile();
|
||||||
@ -746,7 +745,6 @@ export default function ProfilesView({
|
|||||||
/>
|
/>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
|
||||||
onClick={() => setRenameProfile(null)}
|
onClick={() => setRenameProfile(null)}
|
||||||
disabled={renaming}
|
disabled={renaming}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import {
|
|||||||
AlertDialogHeader,
|
AlertDialogHeader,
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
} from "@/components/ui/alert-dialog";
|
} from "@/components/ui/alert-dialog";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
|
||||||
import { useCallback, useContext, useState } from "react";
|
import { useCallback, useContext, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
@ -59,7 +58,6 @@ export default function RegionGridSettingsView({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex size-full flex-col md:flex-row">
|
<div className="flex size-full flex-col md:flex-row">
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto px-2 md:order-none">
|
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto px-2 md:order-none">
|
||||||
<Heading as="h4" className="mb-2 hidden md:block">
|
<Heading as="h4" className="mb-2 hidden md:block">
|
||||||
{t("maintenance.regionGrid.title")}
|
{t("maintenance.regionGrid.title")}
|
||||||
@ -85,7 +83,7 @@ export default function RegionGridSettingsView({
|
|||||||
onClick={() => setIsConfirmOpen(true)}
|
onClick={() => setIsConfirmOpen(true)}
|
||||||
disabled={isClearing}
|
disabled={isClearing}
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
className="flex flex-1 text-white md:max-w-sm"
|
className="flex flex-1 md:max-w-sm"
|
||||||
>
|
>
|
||||||
{t("maintenance.regionGrid.clear")}
|
{t("maintenance.regionGrid.clear")}
|
||||||
</Button>
|
</Button>
|
||||||
@ -108,10 +106,7 @@ export default function RegionGridSettingsView({
|
|||||||
{t("button.cancel", { ns: "common" })}
|
{t("button.cancel", { ns: "common" })}
|
||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
className={cn(
|
className={cn(buttonVariants({ variant: "destructive" }))}
|
||||||
buttonVariants({ variant: "destructive" }),
|
|
||||||
"text-white",
|
|
||||||
)}
|
|
||||||
onClick={handleClear}
|
onClick={handleClear}
|
||||||
>
|
>
|
||||||
{t("maintenance.regionGrid.clear")}
|
{t("maintenance.regionGrid.clear")}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@ -443,7 +442,6 @@ export default function TriggerView({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full flex-col md:flex-row">
|
<div className="flex size-full flex-col md:flex-row">
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto pb-2",
|
"scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto pb-2",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { ReactNode, useCallback, useContext, useEffect } from "react";
|
import { ReactNode, useCallback, useContext, useEffect } from "react";
|
||||||
import { Toaster, toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
@ -211,7 +211,6 @@ export default function UiSettingsView() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full flex-col">
|
<div className="flex size-full flex-col">
|
||||||
<Toaster position="top-center" closeButton={true} />
|
|
||||||
<div className="scrollbar-container mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto pb-2">
|
<div className="scrollbar-container mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto pb-2">
|
||||||
<Heading as="h4" className="mb-3">
|
<Heading as="h4" className="mb-3">
|
||||||
{t("general.title")}
|
{t("general.title")}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user