From c7a922a1d04f30dd8ddb97bba724b93eaf8e876d Mon Sep 17 00:00:00 2001 From: jona159 Date: Mon, 9 Mar 2026 12:43:42 +0100 Subject: [PATCH 1/2] fix: device creation translations and prevent to submit form twice --- app/components/device/new/advanced-info.tsx | 11 +++++--- app/components/device/new/device-info.tsx | 4 ++- app/components/device/new/general-info.tsx | 8 +++--- .../device/new/new-device-stepper.tsx | 10 ++++--- app/components/device/new/sensors-info.tsx | 3 +-- app/components/device/new/summary-info.tsx | 23 +++++++++------- public/locales/de/newdevice.json | 26 ++++++++++++------- public/locales/en/newdevice.json | 10 ++++++- 8 files changed, 61 insertions(+), 34 deletions(-) diff --git a/app/components/device/new/advanced-info.tsx b/app/components/device/new/advanced-info.tsx index 6a45bc6b..618d6b09 100644 --- a/app/components/device/new/advanced-info.tsx +++ b/app/components/device/new/advanced-info.tsx @@ -1,7 +1,9 @@ import Form from "@rjsf/core"; import validator from "@rjsf/validator-ajv8"; -import { useEffect, useState } from "react"; +import { T } from "node_modules/vitest/dist/chunks/traces.d.402V_yFI"; +import { useState } from "react"; import { useFormContext } from "react-hook-form"; +import { useTranslation } from "react-i18next"; import { ArrayFieldTemplate } from "~/components/rjsf/arrayFieldTemplate"; import { CheckboxWidget } from "~/components/rjsf/checkboxWidget"; import { FieldTemplate } from "~/components/rjsf/fieldTemplate"; @@ -27,6 +29,7 @@ export function AdvancedStep({ integrations }: AdvancedStepProps) { const { watch, setValue, resetField } = useFormContext(); const [schemas, setSchemas] = useState>({}); const [loading, setLoading] = useState>({}); + const { t } = useTranslation('newdevice') const loadSchema = async (slug: string) => { @@ -68,7 +71,7 @@ export function AdvancedStep({ integrations }: AdvancedStepProps) { return ( - {intg.name} Configuration + {intg.name} {t('configuration')} {intg.description && ( {intg.description} )} @@ -77,7 +80,7 @@ export function AdvancedStep({ integrations }: AdvancedStepProps) {
{isLoading && (

- Loading {intg.name} configuration… + {t('loading')} {intg.name} {t('configuration')}...

)} diff --git a/app/components/device/new/device-info.tsx b/app/components/device/new/device-info.tsx index 9a217d9c..4056939d 100644 --- a/app/components/device/new/device-info.tsx +++ b/app/components/device/new/device-info.tsx @@ -1,6 +1,7 @@ import { X } from 'lucide-react' import { useEffect, useState } from 'react' import { useFormContext } from 'react-hook-form' +import { useTranslation } from 'react-i18next' import { Button } from '~/components/ui/button' import { Card, CardContent } from '~/components/ui/card' import { Label } from '~/components/ui/label' @@ -35,6 +36,7 @@ const connectionTypes = ['Wifi', 'Lora', 'Ethernet'] export function DeviceSelectionStep() { const { setValue, watch } = useFormContext() + const { t } = useTranslation('newdevice') // Watch the existing values from the form state const model = watch('model') @@ -154,7 +156,7 @@ export function DeviceSelectionStep() {

- Connection Type: + {t('connection_type')}

, label: 'Outdoor' }, - { value: 'indoor', icon: , label: 'Indoor' }, + { value: 'outdoor', icon: , label: t('outdoor') }, + { value: 'indoor', icon: , label: t('indoor') }, { value: 'mobile', icon: , - label: 'Mobile', + label: t('mobile'), }, { value: 'unknown', icon: , - label: 'Unknown', + label: t('unknown'), }, ] diff --git a/app/components/device/new/new-device-stepper.tsx b/app/components/device/new/new-device-stepper.tsx index 5ed8b0b9..391a8d62 100644 --- a/app/components/device/new/new-device-stepper.tsx +++ b/app/components/device/new/new-device-stepper.tsx @@ -4,7 +4,7 @@ import { Info, Slash } from 'lucide-react' import { useEffect, useState } from 'react' import { type FieldErrors, FormProvider, useForm } from 'react-hook-form' import { useTranslation } from 'react-i18next' -import { Form, useLoaderData, useSubmit } from 'react-router' +import { Form, useLoaderData, useSubmit, useNavigation } from 'react-router' import { z } from 'zod' import { AdvancedStep } from './advanced-info' import { DeviceSelectionStep } from './device-info' @@ -159,6 +159,8 @@ export default function NewDeviceStepper() { const { toast } = useToast() const { t } = useTranslation('newdevice') const [isFirst, setIsFirst] = useState(false) + const navigation = useNavigation() + const isSubmitting = navigation.state === 'submitting' useEffect(() => { setIsFirst(stepper.isFirst) @@ -286,12 +288,12 @@ export default function NewDeviceStepper() { type="button" variant="secondary" onClick={stepper.prev} - disabled={isFirst} + disabled={isFirst || isSubmitting} > {t('back')} -
diff --git a/app/components/device/new/sensors-info.tsx b/app/components/device/new/sensors-info.tsx index c0e17167..478b987d 100644 --- a/app/components/device/new/sensors-info.tsx +++ b/app/components/device/new/sensors-info.tsx @@ -149,8 +149,7 @@ export function SensorSelectionStep() {

- {selectedSensors.length} {t('sensor')} - {selectedSensors.length !== 1 ? 's' : ''} {t('selected')} + {t('selectedSensors', { count: selectedSensors.length })}

{selectedSensors.length > 0 && (