form description and field change

This commit is contained in:
Josh Hawkins 2025-11-10 07:32:41 -06:00
parent 02678370db
commit a09c4fdffb
2 changed files with 35 additions and 22 deletions

View File

@ -172,7 +172,7 @@
"testFailed": "Stream test failed: {{error}}" "testFailed": "Stream test failed: {{error}}"
}, },
"step1": { "step1": {
"description": "Enter your camera details and test the connection.", "description": "Enter your camera details and choose to probe the camera or manually select the brand.",
"cameraName": "Camera Name", "cameraName": "Camera Name",
"cameraNamePlaceholder": "e.g., front_door or Back Yard Overview", "cameraNamePlaceholder": "e.g., front_door or Back Yard Overview",
"host": "Host/IP Address", "host": "Host/IP Address",
@ -201,6 +201,7 @@
"probingMetadata": "Probing camera metadata...", "probingMetadata": "Probing camera metadata...",
"fetchingSnapshot": "Fetching camera snapshot..." "fetchingSnapshot": "Fetching camera snapshot..."
}, },
"detectionMethodDescription": "Probe the camera with ONVIF to find camera stream URLs, or manually select the camera brand to use pre-defined URLs. To enter a custom RTSP URL, choose the manual method and select \"Other\".",
"probingDevice": "Probing device...", "probingDevice": "Probing device...",
"probeError": "Probe Error", "probeError": "Probe Error",
"probeNoSuccess": "Probe unsuccessful", "probeNoSuccess": "Probe unsuccessful",

View File

@ -2,6 +2,7 @@ import { Button } from "@/components/ui/button";
import { import {
Form, Form,
FormControl, FormControl,
FormDescription,
FormField, FormField,
FormItem, FormItem,
FormLabel, FormLabel,
@ -73,7 +74,6 @@ export default function Step1NameCamera({
const [testStatus, setTestStatus] = useState<string>(""); const [testStatus, setTestStatus] = useState<string>("");
const [testResult, setTestResult] = useState<TestResult | null>(null); const [testResult, setTestResult] = useState<TestResult | null>(null);
const [probeMode, setProbeMode] = useState<boolean>(true); const [probeMode, setProbeMode] = useState<boolean>(true);
const [onvifPort, setOnvifPort] = useState<number>(80);
const [isProbing, setIsProbing] = useState(false); const [isProbing, setIsProbing] = useState(false);
const [probeError, setProbeError] = useState<string | null>(null); const [probeError, setProbeError] = useState<string | null>(null);
const [probeResult, setProbeResult] = useState<OnvifProbeResponse | null>( const [probeResult, setProbeResult] = useState<OnvifProbeResponse | null>(
@ -111,6 +111,7 @@ export default function Step1NameCamera({
username: z.string().optional(), username: z.string().optional(),
password: z.string().optional(), password: z.string().optional(),
brandTemplate: z.enum(CAMERA_BRAND_VALUES).optional(), brandTemplate: z.enum(CAMERA_BRAND_VALUES).optional(),
onvifPort: z.coerce.number().int().min(1).max(65535).optional(),
customUrl: z customUrl: z
.string() .string()
.optional() .optional()
@ -147,6 +148,7 @@ export default function Step1NameCamera({
? (wizardData.brandTemplate as CameraBrand) ? (wizardData.brandTemplate as CameraBrand)
: "dahua", : "dahua",
customUrl: wizardData.customUrl || "", customUrl: wizardData.customUrl || "",
onvifPort: wizardData.onvifPort ?? 80,
}, },
mode: "onChange", mode: "onChange",
}); });
@ -246,7 +248,7 @@ export default function Step1NameCamera({
const response = await axios.get("/onvif/probe", { const response = await axios.get("/onvif/probe", {
params: { params: {
host: data.host, host: data.host,
port: onvifPort, port: data.onvifPort ?? 80,
username: data.username || "", username: data.username || "",
password: data.password || "", password: data.password || "",
test: false, test: false,
@ -276,7 +278,7 @@ export default function Step1NameCamera({
} finally { } finally {
setIsProbing(false); setIsProbing(false);
} }
}, [form, onvifPort, t]); }, [form, t]);
const handleSelectCandidate = useCallback((uri: string) => { const handleSelectCandidate = useCallback((uri: string) => {
// toggle selection: add or remove from selectedCandidateUris // toggle selection: add or remove from selectedCandidateUris
@ -737,9 +739,16 @@ export default function Step1NameCamera({
</label> </label>
</div> </div>
</RadioGroup> </RadioGroup>
<FormDescription>
{t("cameraWizard.step1.detectionMethodDescription")}
</FormDescription>
</div> </div>
{probeMode && ( {probeMode && (
<FormField
control={form.control}
name="onvifPort"
render={({ field, fieldState }) => (
<FormItem> <FormItem>
<FormLabel className="text-primary-variant"> <FormLabel className="text-primary-variant">
{t("cameraWizard.step1.onvifPort")} {t("cameraWizard.step1.onvifPort")}
@ -747,18 +756,18 @@ export default function Step1NameCamera({
<FormControl> <FormControl>
<Input <Input
className="text-md h-8" className="text-md h-8"
type="number" type="text"
min="1" {...field}
max="65535"
value={onvifPort}
onChange={(e) =>
setOnvifPort(parseInt(e.target.value, 10) || 80)
}
placeholder="80" placeholder="80"
/> />
</FormControl> </FormControl>
<FormMessage>
{fieldState.error ? fieldState.error.message : null}
</FormMessage>
</FormItem> </FormItem>
)} )}
/>
)}
{!probeMode && ( {!probeMode && (
<div className="space-y-4"> <div className="space-y-4">
@ -954,6 +963,9 @@ export default function Step1NameCamera({
variant="select" variant="select"
className="flex items-center justify-center gap-2 sm:flex-1" className="flex items-center justify-center gap-2 sm:flex-1"
> >
{(isProbing || isTesting) && (
<ActivityIndicator className="size-4" />
)}
{probeMode {probeMode
? t("cameraWizard.step1.probeMode") ? t("cameraWizard.step1.probeMode")
: t("cameraWizard.step1.testConnection")} : t("cameraWizard.step1.testConnection")}