Camera wizard tweaks (#20643)
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions

* dialog size tweaks

* move icon and make links in popvers clickable

* colors and clickable links
This commit is contained in:
Josh Hawkins 2025-10-23 14:58:22 -05:00 committed by GitHub
parent f5a57edcc9
commit e2da8aa04c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 66 additions and 45 deletions

View File

@ -20,6 +20,8 @@ import type {
ConfigSetBody, ConfigSetBody,
} from "@/types/cameraWizard"; } from "@/types/cameraWizard";
import { processCameraName } from "@/utils/cameraUtil"; import { processCameraName } from "@/utils/cameraUtil";
import { isDesktop } from "react-device-detect";
import { cn } from "@/lib/utils";
type WizardState = { type WizardState = {
wizardData: Partial<WizardFormData>; wizardData: Partial<WizardFormData>;
@ -335,7 +337,15 @@ export default function CameraWizardDialog({
return ( return (
<Dialog open={open} onOpenChange={handleClose}> <Dialog open={open} onOpenChange={handleClose}>
<DialogContent <DialogContent
className="max-h-[90dvh] max-w-4xl overflow-y-auto" className={cn(
"max-h-[90dvh] max-w-xl overflow-y-auto",
isDesktop &&
currentStep == 0 &&
state.wizardData?.streams?.[0]?.testResult?.snapshot &&
"max-w-4xl",
isDesktop && currentStep == 1 && "max-w-2xl",
isDesktop && currentStep > 1 && "max-w-4xl",
)}
onInteractOutside={(e) => { onInteractOutside={(e) => {
e.preventDefault(); e.preventDefault();
}} }}

View File

@ -6,7 +6,6 @@ import {
FormItem, FormItem,
FormLabel, FormLabel,
FormMessage, FormMessage,
FormDescription,
} from "@/components/ui/form"; } from "@/components/ui/form";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { import {
@ -298,6 +297,7 @@ export default function Step1NameCamera({
}; };
setTestResult(testResult); setTestResult(testResult);
onUpdate({ streams: [{ id: "", url: "", roles: [], testResult }] });
toast.success(t("cameraWizard.step1.testSuccess")); toast.success(t("cameraWizard.step1.testSuccess"));
} else { } else {
const error = const error =
@ -337,7 +337,7 @@ export default function Step1NameCamera({
setIsTesting(false); setIsTesting(false);
setTestStatus(""); setTestStatus("");
} }
}, [form, generateStreamUrl, t]); }, [form, generateStreamUrl, t, onUpdate]);
const onSubmit = (data: z.infer<typeof step1FormData>) => { const onSubmit = (data: z.infer<typeof step1FormData>) => {
onUpdate(data); onUpdate(data);
@ -380,7 +380,9 @@ export default function Step1NameCamera({
name="cameraName" name="cameraName"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("cameraWizard.step1.cameraName")}</FormLabel> <FormLabel className="text-primary-variant">
{t("cameraWizard.step1.cameraName")}
</FormLabel>
<FormControl> <FormControl>
<Input <Input
className="h-8" className="h-8"
@ -400,7 +402,43 @@ export default function Step1NameCamera({
name="brandTemplate" name="brandTemplate"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("cameraWizard.step1.cameraBrand")}</FormLabel> <div className="flex items-center gap-1 pb-1">
<FormLabel className="text-primary-variant">
{t("cameraWizard.step1.cameraBrand")}
</FormLabel>
{field.value &&
(() => {
const selectedBrand = CAMERA_BRANDS.find(
(brand) => brand.value === field.value,
);
return selectedBrand &&
selectedBrand.value != "other" ? (
<Popover>
<PopoverTrigger asChild>
<Button
variant="ghost"
size="sm"
className="h-4 w-4 p-0"
>
<LuInfo className="size-3" />
</Button>
</PopoverTrigger>
<PopoverContent className="pointer-events-auto w-80 text-primary-variant">
<div className="space-y-2">
<h4 className="font-medium">
{selectedBrand.label}
</h4>
<p className="break-all text-sm text-muted-foreground">
{t("cameraWizard.step1.brandUrlFormat", {
exampleUrl: selectedBrand.exampleUrl,
})}
</p>
</div>
</PopoverContent>
</Popover>
) : null;
})()}
</div>
<Select <Select
onValueChange={field.onChange} onValueChange={field.onChange}
defaultValue={field.value} defaultValue={field.value}
@ -421,37 +459,6 @@ export default function Step1NameCamera({
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage /> <FormMessage />
{field.value &&
(() => {
const selectedBrand = CAMERA_BRANDS.find(
(brand) => brand.value === field.value,
);
return selectedBrand &&
selectedBrand.value != "other" ? (
<FormDescription className="mt-1 pt-0.5 text-xs text-muted-foreground">
<Popover>
<PopoverTrigger>
<div className="flex flex-row items-center gap-0.5 text-xs text-muted-foreground hover:text-primary">
<LuInfo className="mr-1 size-3" />
{t("cameraWizard.step1.brandInformation")}
</div>
</PopoverTrigger>
<PopoverContent className="w-80">
<div className="space-y-2">
<h4 className="font-medium">
{selectedBrand.label}
</h4>
<p className="break-all text-sm text-muted-foreground">
{t("cameraWizard.step1.brandUrlFormat", {
exampleUrl: selectedBrand.exampleUrl,
})}
</p>
</div>
</PopoverContent>
</Popover>
</FormDescription>
) : null;
})()}
</FormItem> </FormItem>
)} )}
/> />
@ -463,7 +470,9 @@ export default function Step1NameCamera({
name="host" name="host"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("cameraWizard.step1.host")}</FormLabel> <FormLabel className="text-primary-variant">
{t("cameraWizard.step1.host")}
</FormLabel>
<FormControl> <FormControl>
<Input <Input
className="h-8" className="h-8"
@ -481,7 +490,7 @@ export default function Step1NameCamera({
name="username" name="username"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel> <FormLabel className="text-primary-variant">
{t("cameraWizard.step1.username")} {t("cameraWizard.step1.username")}
</FormLabel> </FormLabel>
<FormControl> <FormControl>
@ -503,7 +512,7 @@ export default function Step1NameCamera({
name="password" name="password"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel> <FormLabel className="text-primary-variant">
{t("cameraWizard.step1.password")} {t("cameraWizard.step1.password")}
</FormLabel> </FormLabel>
<FormControl> <FormControl>
@ -544,7 +553,9 @@ export default function Step1NameCamera({
name="customUrl" name="customUrl"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("cameraWizard.step1.customUrl")}</FormLabel> <FormLabel className="text-primary-variant">
{t("cameraWizard.step1.customUrl")}
</FormLabel>
<FormControl> <FormControl>
<Input <Input
className="h-8" className="h-8"

View File

@ -277,7 +277,7 @@ export default function Step2StreamConfig({
<div className="grid grid-cols-1 gap-4"> <div className="grid grid-cols-1 gap-4">
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium"> <label className="text-sm font-medium text-primary-variant">
{t("cameraWizard.step2.url")} {t("cameraWizard.step2.url")}
</label> </label>
<div className="flex flex-row items-center gap-2"> <div className="flex flex-row items-center gap-2">
@ -325,7 +325,7 @@ export default function Step2StreamConfig({
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Label className="text-sm font-medium"> <Label className="text-sm font-medium text-primary-variant">
{t("cameraWizard.step2.roles")} {t("cameraWizard.step2.roles")}
</Label> </Label>
<Popover> <Popover>
@ -334,7 +334,7 @@ export default function Step2StreamConfig({
<LuInfo className="size-3" /> <LuInfo className="size-3" />
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-80 text-xs"> <PopoverContent className="pointer-events-auto w-80 text-xs">
<div className="space-y-2"> <div className="space-y-2">
<div className="font-medium"> <div className="font-medium">
{t("cameraWizard.step2.rolesPopover.title")} {t("cameraWizard.step2.rolesPopover.title")}
@ -395,7 +395,7 @@ export default function Step2StreamConfig({
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Label className="text-sm font-medium"> <Label className="text-sm font-medium text-primary-variant">
{t("cameraWizard.step2.featuresTitle")} {t("cameraWizard.step2.featuresTitle")}
</Label> </Label>
<Popover> <Popover>
@ -404,7 +404,7 @@ export default function Step2StreamConfig({
<LuInfo className="size-3" /> <LuInfo className="size-3" />
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-80 text-xs"> <PopoverContent className="pointer-events-auto w-80 text-xs">
<div className="space-y-2"> <div className="space-y-2">
<div className="font-medium"> <div className="font-medium">
{t("cameraWizard.step2.featuresPopover.title")} {t("cameraWizard.step2.featuresPopover.title")}