add i18n and popover for brand url

This commit is contained in:
Josh Hawkins 2025-10-12 12:51:23 -05:00
parent 84ac8d3654
commit c2183bd850
3 changed files with 46 additions and 11 deletions

View File

@ -164,7 +164,7 @@
"step1": {
"description": "Enter your camera details and test the connection.",
"cameraName": "Camera Name",
"cameraNamePlaceholder": "e.g., front_door",
"cameraNamePlaceholder": "e.g., front_door or Back Yard Overview",
"host": "Host/IP Address",
"port": "Port",
"username": "Username",
@ -175,6 +175,8 @@
"cameraBrand": "Camera Brand",
"selectBrand": "Select camera brand for URL template",
"customUrl": "Custom Stream URL",
"brandInformation": "Brand information",
"brandUrlFormat": "For cameras with the RTSP URL format as: {{exampleUrl}}",
"customUrlPlaceholder": "rtsp://username:password@host:port/path",
"testConnection": "Test Connection",
"testSuccess": "Connection test successful!",
@ -184,7 +186,11 @@
"noSnapshot": "Unable to fetch a snapshot from the configured stream."
},
"errors": {
"brandOrCustomUrlRequired": "Either select a camera brand with host/IP or choose 'Other' with a custom URL"
"brandOrCustomUrlRequired": "Either select a camera brand with host/IP or choose 'Other' with a custom URL",
"nameRequired": "Camera name is required",
"nameLength": "Camera name must be 64 characters or less",
"invalidCharacters": "Camera name contains invalid characters",
"nameExists": "Camera name already exists"
}
},
"step2": {
@ -292,8 +298,8 @@
"description": "Configure camera settings including stream inputs and roles.",
"name": "Camera Name",
"nameRequired": "Camera name is required",
"nameLength": "Camera name must be less than 24 characters.",
"namePlaceholder": "e.g., front_door",
"nameLength": "Camera name must be less than 64 characters.",
"namePlaceholder": "e.g., front_door or Back Yard Overview",
"enabled": "Enabled",
"ffmpeg": {
"inputs": "Input Streams",

View File

@ -39,6 +39,12 @@ import {
} from "@/types/cameraWizard";
import { FaCircleCheck } from "react-icons/fa6";
import { Card, CardContent, CardTitle } from "../ui/card";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { LuInfo } from "react-icons/lu";
type Step1NameCameraProps = {
wizardData: Partial<WizardFormData>;
@ -71,12 +77,15 @@ export default function Step1NameCamera({
.object({
cameraName: z
.string()
.min(1, "Camera name is required")
.max(64, "Camera name must be 64 characters or less")
.regex(/^[a-zA-Z0-9\s_-]+$/, "Camera name contains invalid characters")
.min(1, t("cameraWizard.step1.errors.nameRequired"))
.max(64, t("cameraWizard.step1.errors.nameLength"))
.regex(
/^[a-zA-Z0-9\s_-]+$/,
t("cameraWizard.step1.errors.invalidCharacters"),
)
.refine(
(value) => !existingCameraNames.includes(value),
"Camera name already exists",
t("cameraWizard.step1.errors.nameExists"),
),
host: z.string().optional(),
username: z.string().optional(),
@ -357,9 +366,29 @@ export default function Step1NameCamera({
const selectedBrand = CAMERA_BRANDS.find(
(brand) => brand.value === field.value,
);
return selectedBrand ? (
return selectedBrand &&
selectedBrand.value != "other" ? (
<FormDescription className="mt-1 pt-0.5 text-xs text-muted-foreground">
{selectedBrand.exampleUrl}
<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="text-sm text-muted-foreground">
{t("cameraWizard.step1.brandUrlFormat", {
exampleUrl: selectedBrand.exampleUrl,
})}
</p>
</div>
</PopoverContent>
</Popover>
</FormDescription>
) : null;
})()}

View File

@ -226,7 +226,7 @@ export default function Step2StreamConfig({
{[
stream.testResult.resolution,
stream.testResult.fps
? `${stream.testResult.fps} fps`
? `${stream.testResult.fps} ${t("cameraWizard.testResultLabels.fps")}`
: null,
stream.testResult.videoCodec,
stream.testResult.audioCodec,