diff --git a/app/components/device/new/advanced-info.tsx b/app/components/device/new/advanced-info.tsx index 240c8f20..160a5e7c 100644 --- a/app/components/device/new/advanced-info.tsx +++ b/app/components/device/new/advanced-info.tsx @@ -1,4 +1,5 @@ import { useFormContext } from 'react-hook-form' +import { useTranslation } from 'react-i18next' import { Card, CardContent, @@ -20,6 +21,7 @@ import { Textarea } from '~/components/ui/textarea' export function AdvancedStep() { const { register, setValue, watch, resetField } = useFormContext() + const { t } = useTranslation('newdevice') // Watch field states const isMqttEnabled = watch('mqttEnabled') || false @@ -64,15 +66,15 @@ export function AdvancedStep() { {/* MQTT Configuration */} - MQTT Configuration + {t('MQTT Configuration')} - Configure your MQTT settings for data streaming + {t('mqtt_config_text')}
- TTN Configuration + {t('TTN Configuration')} - Configure your TTN (The Things Network) settings + {t('ttn_config_text')}
{ @@ -53,7 +55,7 @@ export function CustomDeviceConfig() {
- +
- +
- + - Add Sensor + {t('Add Sensor')}
diff --git a/app/components/device/new/general-info.tsx b/app/components/device/new/general-info.tsx index 3af5395d..65f52fc8 100644 --- a/app/components/device/new/general-info.tsx +++ b/app/components/device/new/general-info.tsx @@ -1,6 +1,7 @@ import { Plus, Cloud, Home, HelpCircle, Bike, X, Info } from 'lucide-react' import React, { useState } from 'react' import { useFormContext, useFieldArray } from 'react-hook-form' +import { useTranslation } from 'react-i18next' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Badge } from '~/components/ui/badge' @@ -13,10 +14,12 @@ import { TooltipTrigger, } from '~/components/ui/tooltip' + type ExposureOption = 'outdoor' | 'indoor' | 'mobile' | 'unknown' export function GeneralInfoStep() { const { register, control, setValue, getValues, watch } = useFormContext() + const {t} = useTranslation('newdevice') const { fields, append, remove } = useFieldArray({ control, name: 'tags', // Tags array @@ -88,7 +91,7 @@ export function GeneralInfoStep() { />
- +
{exposureOptions.map((option) => ( ))}
@@ -117,7 +120,7 @@ export function GeneralInfoStep() { onCheckedChange={handleTemporaryChange} /> @@ -133,8 +136,7 @@ export function GeneralInfoStep() { {

- Temporary devices will be automatically deleted after a - maximum of one month. + {t('temporary_info_text')}

}
@@ -147,7 +149,7 @@ export function GeneralInfoStep() { htmlFor="temporaryExpirationDate" className="whitespace-nowrap text-sm font-medium" > - Expiration Date: + {t('expiration_date')} { if (e.key === 'Enter') { e.preventDefault() diff --git a/app/components/device/new/location-info.tsx b/app/components/device/new/location-info.tsx index dc1681e7..6d2ce582 100644 --- a/app/components/device/new/location-info.tsx +++ b/app/components/device/new/location-info.tsx @@ -1,5 +1,6 @@ import React, { useRef, useState, useEffect, useCallback } from 'react' import { useFormContext } from 'react-hook-form' +import { useTranslation } from 'react-i18next' import { Map, Marker, @@ -12,10 +13,11 @@ import { Input } from '@/components/ui/input' import { Label } from '~/components/ui/label' import 'mapbox-gl/dist/mapbox-gl.css' + export function LocationStep() { const mapRef = useRef(null) const { register, setValue, watch } = useFormContext() - + const { t } = useTranslation('newdevice') const savedLatitude = watch('latitude') const savedLongitude = watch('longitude') @@ -129,7 +131,7 @@ export function LocationStep() {
- +
- +
diff --git a/app/components/device/new/new-device-stepper.tsx b/app/components/device/new/new-device-stepper.tsx index af5f2201..058c0fa9 100644 --- a/app/components/device/new/new-device-stepper.tsx +++ b/app/components/device/new/new-device-stepper.tsx @@ -3,6 +3,7 @@ import { defineStepper } from '@stepperize/react' 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, useSubmit } from 'react-router' import { z } from 'zod' import { AdvancedStep } from './advanced-info' @@ -173,42 +174,42 @@ export const Stepper = defineStepper( { id: 'general-info', label: 'General Info', - info: 'Provide a unique name for your device, select its operating environment (outdoor, indoor, mobile, or unknown), and add relevant tags (optional).', + infoKey: 'general_information_text', schema: generalInfoSchema, index: 0, }, { id: 'location', label: 'Location', - info: "Select the device's location by clicking on the map or entering latitude and longitude coordinates manually. Drag the marker on the map to adjust the location if needed.", + infoKey: 'location_info_text', schema: locationSchema, index: 1, }, { id: 'device-selection', label: 'Device Selection', - info: 'Select a device model from the available options', + infoKey: 'device_selection_info_text', schema: deviceSchema, index: 2, }, { id: 'sensor-selection', label: 'Sensor Selection', - info: 'Select sensors for your device by choosing from predefined groups or individual sensors based on your device model. If using a custom device, configure sensors manually.', + infoKey: 'sensor_selection_info_text', schema: sensorsSchema, index: 3, }, { id: 'advanced', label: 'Advanced', - info: null, + infoKey: null, schema: advancedSchema, index: 4, }, { id: 'summary', label: 'Summary', - info: null, + infoKey: null, schema: z.object({}), index: 5, }, @@ -237,6 +238,7 @@ export default function NewDeviceStepper() { resolver: zodResolver(stepper.current.schema), }) const { toast } = useToast() + const { t } = useTranslation('newdevice') const [isFirst, setIsFirst] = useState(false) useEffect(() => { @@ -302,7 +304,7 @@ export default function NewDeviceStepper() { : 'cursor-pointer text-gray-500 hover:text-black' } `} > - {step.label} + {t(step.label)} @@ -320,10 +322,10 @@ export default function NewDeviceStepper() { {/* Step Header with Info */}

- Step {stepper.current.index + 1} of {Stepper.steps.length}:{' '} - {stepper.current.label} + {t('step')} {stepper.current.index + 1} {t('of')} {Stepper.steps.length}:{' '} + {t(stepper.current.label)}

- {stepper.current.info && ( + {stepper.current.infoKey && ( - {stepper.current.info} + {t(stepper.current.infoKey)} )} @@ -362,10 +364,10 @@ export default function NewDeviceStepper() { onClick={stepper.prev} disabled={isFirst} > - Back + {t('back')}
diff --git a/app/components/device/new/sensors-info.tsx b/app/components/device/new/sensors-info.tsx index 1c6b77ed..f97103a5 100644 --- a/app/components/device/new/sensors-info.tsx +++ b/app/components/device/new/sensors-info.tsx @@ -1,11 +1,21 @@ import { useState, useEffect } from 'react' import { useFormContext } from 'react-hook-form' +import { useTranslation } from 'react-i18next' import { z } from 'zod' import { CustomDeviceConfig } from './custom-device-config' -import { Card, CardContent } from '~/components/ui/card' +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '~/components/ui/accordion' +import { Badge } from '~/components/ui/badge' +import { Checkbox } from '~/components/ui/checkbox' +import { Label } from '~/components/ui/label' import { cn } from '~/lib/utils' import { getSensorsForModel } from '~/utils/model-definitions' + export const sensorSchema = z.object({ title: z.string(), unit: z.string(), @@ -25,6 +35,7 @@ type SensorGroup = { export function SensorSelectionStep() { const { watch, setValue } = useFormContext() const selectedDevice = watch('model') + const { t } = useTranslation('newdevice'); const [selectedDeviceModel, setSelectedDeviceModel] = useState( null, ) @@ -38,9 +49,10 @@ export function SensorSelectionStep() { : selectedDevice setSelectedDeviceModel(deviceModel) + // Add safety check for custom devices if (deviceModel !== 'custom') { const fetchedSensors = getSensorsForModel(deviceModel) - setSensors(fetchedSensors) + setSensors(fetchedSensors ?? []) // Also handle null return } else { setSensors([]) } @@ -53,6 +65,9 @@ export function SensorSelectionStep() { }, [watch]) const groupSensorsByType = (sensors: Sensor[]): SensorGroup[] => { + // Add safety check in case sensors is null/undefined + if (!sensors || sensors.length === 0) return [] + const grouped = sensors.reduce( (acc, sensor) => { if (!acc[sensor.sensorType]) { @@ -73,144 +88,206 @@ export function SensorSelectionStep() { const sensorGroups = groupSensorsByType(sensors) - const handleGroupToggle = (group: SensorGroup) => { - const isGroupSelected = group.sensors.every((sensor) => - selectedSensors.some( - (s) => s.title === sensor.title && s.sensorType === sensor.sensorType, - ), + const isSensorSelected = (sensor: Sensor) => + selectedSensors.some( + (s) => s.title === sensor.title && s.sensorType === sensor.sensorType, ) - const updatedSensors = isGroupSelected + const isGroupFullySelected = (group: SensorGroup) => + group.sensors.every((sensor) => isSensorSelected(sensor)) + + const isGroupPartiallySelected = (group: SensorGroup) => + group.sensors.some((sensor) => isSensorSelected(sensor)) && + !isGroupFullySelected(group) + + const getSelectedCountForGroup = (group: SensorGroup) => + group.sensors.filter((sensor) => isSensorSelected(sensor)).length + + const handleSensorToggle = (sensor: Sensor) => { + const isAlreadySelected = isSensorSelected(sensor) + + const updatedSensors = isAlreadySelected ? selectedSensors.filter( (s) => - !group.sensors.some( - (sensor) => - s.title === sensor.title && s.sensorType === sensor.sensorType, - ), + !(s.title === sensor.title && s.sensorType === sensor.sensorType), ) - : [ - ...selectedSensors, - ...group.sensors.filter( - (sensor) => - !selectedSensors.some( - (s) => - s.title === sensor.title && - s.sensorType === sensor.sensorType, - ), - ), - ] + : [...selectedSensors, sensor] setSelectedSensors(updatedSensors) setValue('selectedSensors', updatedSensors) } - const handleSensorToggle = (sensor: Sensor) => { - const isAlreadySelected = selectedSensors.some( - (s) => s.title === sensor.title && s.sensorType === sensor.sensorType, - ) + const handleGroupToggle = (group: SensorGroup) => { + const isFullySelected = isGroupFullySelected(group) - const updatedSensors = isAlreadySelected + const updatedSensors = isFullySelected ? selectedSensors.filter( (s) => - !(s.title === sensor.title && s.sensorType === sensor.sensorType), + !group.sensors.some( + (sensor) => + s.title === sensor.title && s.sensorType === sensor.sensorType, + ), ) - : [...selectedSensors, sensor] + : [ + ...selectedSensors, + ...group.sensors.filter((sensor) => !isSensorSelected(sensor)), + ] setSelectedSensors(updatedSensors) setValue('selectedSensors', updatedSensors) } if (!selectedDevice) { - return

Please select a device first.

+ return

{t('device_not_selected')}

} if (selectedDevice === 'custom') { return } + const isSenseBoxHomeV2 = selectedDeviceModel === 'senseBoxHomeV2' + return ( -
-
-
- {sensorGroups.map((group) => { - const isGroupSelected = group.sensors.every((sensor) => - selectedSensors.some( - (s) => - s.title === sensor.title && - s.sensorType === sensor.sensorType, - ), - ) - - return ( - handleGroupToggle(group) - : undefined - } - > - -

- {group.sensorType} -

- -
    - {group.sensors.map((sensor) => { - const isSelected = selectedSensors.some( - (s) => - s.title === sensor.title && - s.sensorType === sensor.sensorType, - ) - - return ( -
  • { - e.stopPropagation() - handleSensorToggle(sensor) - } - : undefined - } - > - {sensor.title} ({sensor.unit}) -
  • - ) - })} -
-
+
+ {/* Selected count summary */} +
+

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

+ {selectedSensors.length > 0 && ( + + )} +
+ + + {sensorGroups.map((group) => { + const isFullySelected = isGroupFullySelected(group) + const isPartiallySelected = isGroupPartiallySelected(group) + const selectedCount = getSelectedCountForGroup(group) + + return ( + + +
+
{group.image && ( {`${group.sensorType} )} +
+

{group.sensorType}

+

+ {group.sensors.length} {t('phenomenon')} + {group.sensors.length !== 1 ? 's' : ''} +

+
- - - ) - })} -
-
+ {selectedCount > 0 && ( + + {selectedCount} {t('selected')} + + )} +
+ + +
+ {/* Select All option */} +
e.stopPropagation()} + > + handleGroupToggle(group)} + /> + +
+ + {/* Individual sensors - only show for non-senseBoxHomeV2 */} + {!isSenseBoxHomeV2 && ( +
+ {group.sensors.map((sensor) => { + const isSelected = isSensorSelected(sensor) + const sensorId = `sensor-${group.sensorType}-${sensor.title}` + + return ( +
e.stopPropagation()} + > + handleSensorToggle(sensor)} + /> + +
+ ) + })} +
+ )} + + {/* For senseBoxHomeV2, just show the parameters as info */} + {isSenseBoxHomeV2 && ( +
+

{t('Includes')}:

+ {group.sensors.map((sensor) => ( +

+ • {sensor.title} ({sensor.unit}) +

+ ))} +
+ )} +
+
+ + ) + })} +
) -} +} \ No newline at end of file diff --git a/app/components/device/new/summary-info.tsx b/app/components/device/new/summary-info.tsx index 245c9c10..bd2d1651 100644 --- a/app/components/device/new/summary-info.tsx +++ b/app/components/device/new/summary-info.tsx @@ -1,11 +1,14 @@ import { MapPin, Tag, Smartphone, Cpu, Cog } from 'lucide-react' import { useFormContext } from 'react-hook-form' +import { useTranslation } from 'react-i18next' import { Badge } from '@/components/ui/badge' import { Card, CardContent } from '@/components/ui/card' + export function SummaryInfo() { const { getValues } = useFormContext() const formData = getValues() + const { t } = useTranslation('newdevice') const sections = [ { @@ -13,7 +16,7 @@ export function SummaryInfo() { icon: , data: [ { label: 'Name', value: formData.name }, - { label: 'Exposure', value: formData.exposure }, + { label: 'exposure', value: formData.exposure }, { label: 'Tags', value: @@ -25,9 +28,9 @@ export function SummaryInfo() { title: 'Location', icon: , data: [ - { label: 'Latitude', value: parseFloat(formData.latitude).toFixed(4) }, + { label: 'latitude', value: parseFloat(formData.latitude).toFixed(4) }, { - label: 'Longitude', + label: 'longitude', value: parseFloat(formData.longitude).toFixed(4), }, ], @@ -65,15 +68,15 @@ export function SummaryInfo() {
{section.icon}

- {section.title} + {t(section.title)}

{section.data.map((item: any, idx: any) => (
- {item.label}: + {t(item.label)}: - {item.value} + {t(item.value)}
))} diff --git a/app/components/nav-bar.tsx b/app/components/nav-bar.tsx index 5f02e348..3278b3e1 100644 --- a/app/components/nav-bar.tsx +++ b/app/components/nav-bar.tsx @@ -1,8 +1,8 @@ import { LogIn, Mailbox, Plus } from 'lucide-react' +import { useTranslation } from 'react-i18next' import { Link, useLocation } from 'react-router' import Menu from './header/menu' import { Button } from './ui/button' - import { DropdownMenu, DropdownMenuGroup, @@ -11,7 +11,7 @@ import { DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { useOptionalUser } from '~/utils' -import { useTranslation } from 'react-i18next' + export function NavBar() { const { t } = useTranslation('navbar') @@ -61,13 +61,13 @@ export function NavBar() { - New device + {t("new Device")} - Transfer device + {t("transfer Device")} diff --git a/app/models/device.server.ts b/app/models/device.server.ts index 36aff181..01a270cf 100644 --- a/app/models/device.server.ts +++ b/app/models/device.server.ts @@ -242,7 +242,7 @@ export async function updateDevice( const result = await drizzleClient.transaction(async (tx) => { if (args.location) { - const { lat, lng, height } = args.location + const { lat, lng } = args.location const pointWKT = `POINT(${lng} ${lat})` @@ -723,15 +723,27 @@ export async function createDevice(deviceData: any, userId: string) { } if ( - Array.isArray(deviceData.sensorTemplates) && - deviceData.sensorTemplates.length > 0 + Array.isArray(deviceData.selectedSensorDetails) && + deviceData.selectedSensorDetails.length > 0 ) { sensorsToAdd = modelSensors.filter((sensor) => - deviceData.sensorTemplates.includes( - sensor.sensorType.toLowerCase(), + deviceData.selectedSensorDetails.some( + (selected: { title: string; sensorType: string }) => + selected.title === sensor.title && + selected.sensorType === sensor.sensorType, ), ) - } else { + } + else if (Array.isArray(deviceData.sensorTemplates) && + deviceData.sensorTemplates.length > 0){ + sensorsToAdd = modelSensors.filter((sensor) => + deviceData.sensorTemplates.includes( + sensor.sensorType.toLowerCase(), + ), + ) + } + + else { sensorsToAdd = modelSensors } } diff --git a/app/routes/device.new.tsx b/app/routes/device.new.tsx index 0d294659..7638faa4 100644 --- a/app/routes/device.new.tsx +++ b/app/routes/device.new.tsx @@ -43,10 +43,11 @@ export async function action({ request }: ActionFunctionArgs) { ...(data['device-selection'].model !== 'custom' && { model: data['device-selection'].model, - - sensorTemplates: selectedSensors.map((sensor: any) => - sensor.sensorType.toLowerCase(), - ), + // Send full sensor details for precise filtering + selectedSensorDetails: selectedSensors.map((sensor: any) => ({ + title: sensor.title, + sensorType: sensor.sensorType, + })), }), ...(data['device-selection'].model === 'custom' && { @@ -62,7 +63,6 @@ export async function action({ request }: ActionFunctionArgs) { ttnEnabled: data.advanced.ttnEnabled ?? false, } - // Call server function const newDevice = await createDevice(devicePayload, userId) console.log('🚀 ~ New Device Created:', newDevice) @@ -72,7 +72,6 @@ export async function action({ request }: ActionFunctionArgs) { return redirect('/profile/me') } } - export default function NewDevice() { return (
diff --git a/public/locales/de/navbar.json b/public/locales/de/navbar.json index e8dff9a2..402eee4a 100644 --- a/public/locales/de/navbar.json +++ b/public/locales/de/navbar.json @@ -1,5 +1,7 @@ { "date_picker_label": "Wähle einen Zeitpunkt", + "new Device": "Neues Gerät", + "transfer Device": "Gerät übertragen", "date_range_picker_label": "Wähle einen Zeitraum", "search_label": "Suche", "temperature_label": "Temperatur", diff --git a/public/locales/de/newdevice.json b/public/locales/de/newdevice.json index 96918dc1..4aa0f911 100644 --- a/public/locales/de/newdevice.json +++ b/public/locales/de/newdevice.json @@ -3,36 +3,72 @@ "prev": "Zurück", "next": "Weiter", "submit": "Abschicken", + "step": "Schritt", + "of": "von", + "back": "Zurück", + "Complete": "Abschließen", "select_device": "Gerät auswählen", + "Device": "Gerät", "select_device_text": "Welche Hardware benutzt du?", + "device_not_selected": "Bitte wähle zuerst ein Gerät aus.", + "device_selection_info_text": "Wähle ein Gerätemodell aus den verfügbaren Optionen aus", + "Device Selection": "Geräteauswahl", "own_device": "Eigenes Gerät", "select_device_info_text": "Dein Gerät is nicht in der Liste? Füge es im <0>sensorWiki hinzu, um es auf der openSenseMap zu benutzen.", "general": "Allgemein", + "General Info": "Allgemeine Informationen", + "Location": "Standort", + "Advanced": "Erweitert", + "Summary": "Zusammenfassung", "general_text": "Bitte trage zusätzliche Informationen zu deinem Gerät ein.", + "general_information_text": "Gib einen eindeutigen Namen für dein Gerät an, wähle die Umgebung aus, in der es betrieben wird (draußen, drinnen, mobil oder unbekannt) und füge relevante Tags hinzu (optional).", "general_info_text": "Diese Informationen werden veröffentlicht, also sei vorsichtig beim Teilen. Du kannst die Informationen auch nach der Registrierung noch ändern.", "station_name": "Name deiner Station", "exposure": "Standort", + "selected": "Ausgewählt", + "Select all phenomenons": "Alle Phänomene auswählen", "exposure_explaination": "Der Standort deiner Station.", - "indoor": "drinnen", - "outdoor": "draußen", - "mobile": "mobil", + "Indoor": "drinnen", + "Outdoor": "draußen", + "Mobile": "mobil", + "Unknown": "unbekannt", + "temporary": "temporär Gerät", + "temporary_info_text": "temporäre Geräte werden automatisch nach maximal einem Monat gelöscht.", + "expiration_date": "Ablaufdatum:", + "add_tag": "Tag hinzufügen", "optional": "optional", "select_sensors": "Sensoren auswählen", + "Sensor Selection": "Sensorauswahl", + "Add Sensor": "Sensor hinzufügen", + "sensor_selection_info_text": "Wähle die Sensoren für dein Gerät aus, indem du vordefinierte Gruppen oder einzelne Sensoren basierend auf deinem Gerätemodell auswählst. Wenn du ein benutzerdefiniertes Gerät verwendest, konfiguriere die Sensoren manuell.", "select_sensors_text": "Wähle die Sensoren aus die du benutzen willst. Du kannst einen Sensor mehrfach hinzufügen.", "select_sensors_info_text": "Dein Sensor is nicht in der Liste? Füge ihn im <0>sensorWiki hinzu, um ihn auf der openSenseMap zu benutzen.", "your_added": "Deine hinzugefügten", - "sensors": "Sensoren", + "Sensors": "Sensoren", + "Model": "Modell", "sensor": "Sensor", "phenomenon": "Phänomen", + "MQTT Enabled": "MQTT aktiviert", + "TTN Enabled": "TTN aktiviert", + "Yes": "Ja", + "No": "Nein", "unit": "Einheit", + "type": "Typ", + "Includes": "Enthält", "delete": "Löschen", "title": "Titel", "advanced": "Advanced", "advanced_text": "Erweiterte Konnektivitätseinstellungen für dein Gerät. Du kannst nur eine Option auswählen.", + "MQTT Configuration": "MQTT Konfiguration", + "mqtt_config_text": "Konfiguriere deine MQTT Einstellungen für den Datenstream", + "Enable MQTT": "MQTT aktivieren", + "Enable TTN": "TTN aktivieren", + "TTN Configuration": "TTN Konfiguration", + "ttn_config_text": "Konfiguriere deine TTN (The Things Network) Einstellungen", "ttn_info_text": "Die openSenseMap bietet eine Integration mit <0>TheThingsNetwork an. Eine Dokumentation für die Parameter findest du auf <1>GitHub", "ttn_app_id_info": "Die von TTN erhaltene Application-ID", "ttn_dev_id_info": "Die von TTN erhaltene Device-ID", @@ -48,10 +84,13 @@ "mqtt_connect_options_info": "Eine json-kodierte Zeichenkette mit Optionen, die an den MQTT-Client übergeben werden", "location": "Standort", + "location_info_text": "Wähle den Standort des Geräts aus, indem du auf die Karte klickst oder die Breiten- und Längengradkoordinaten manuell eingibst. Ziehe den Marker auf der Karte, um den Standort bei Bedarf anzupassen.", "location_text": "Klicke in die Karte, um einen Standort für dein Gerät auszuwählen. Du kannst auch Koordinaten manuell eingeben oder die Geosuche benutzen.", "search_placeholder": "Suche", "latitude": "Breitengrad", "longitude": "Längengrad", + "enter latitude": "Breitengrad eingeben (-90 bis 90)", + "enter longitude": "Längengrad eingeben (-180 bis 180)", "height": "Höhe", "height_info_text": "Höhe über dem Meeresspiegel des von Dir gewählten Standorts in Metern. Wenn Du dein Gerät deutlich über der Höhe des Erdbodens aufgestellt hast (z.B. hohes Gebäude), sollten Du diese Höhe zu der abgeleiteten Höhe hinzuaddieren. Die Höhe ist vor allem dann wichtig, wenn Du einen Sensor angeschlossen hast, der den Luftdruck misst, um diese Messungen vergleichbar zu machen.", diff --git a/public/locales/en/navbar.json b/public/locales/en/navbar.json index 083aeae6..8098bfd5 100644 --- a/public/locales/en/navbar.json +++ b/public/locales/en/navbar.json @@ -1,5 +1,7 @@ { "date_picker_label": "Select a date", + "new Device":"New Device", + "transfer Device": "Transfer Device", "date_range_picker_label": "Select a time period", "search_label": "Search", "temperature_label": "Temperature", diff --git a/public/locales/en/newdevice.json b/public/locales/en/newdevice.json index a451a6f9..652eacfa 100644 --- a/public/locales/en/newdevice.json +++ b/public/locales/en/newdevice.json @@ -3,37 +3,67 @@ "prev": "Previous", "next": "Next", "submit": "Submit", + "step": "Step", + "of": "of", + "back": "Back", + "Complete": "Complete", "select_device": "Select Device", + "Device": "Device", "select_device_text": "Which hardware do you use?", + "device_not_selected": "Please select a device first.", + "device_selection_info_text": "Select a device model from the available options", "own_device": "Custom Device", "select_device_info_text": "Your device is not in the list? Add it to the <0>sensorWiki to use it on the openSenseMap.", "general": "General", "general_text": "Please submit additional information about your device.", + "general_information_text": "Provide a unique name for your device, select its operating environment (outdoor, indoor, mobile, or unknown), and add relevant tags (optional).", "general_info_text": "This information will be displayed publicly so be careful what you share. You can change this information later on.", "station_name": "Name of your station", "exposure": "Exposure", + "selected": "Selected", + "Select all phenomenons": "Select all phenomenons", "exposure_explaination": "This is how your device is exposed/placed.", - "indoor": "Indoor", - "outdoor": "Outdoor", - "mobile": "Mobile", + "Indoor": "Indoor", + "Outdoor": "Outdoor", + "Mobile": "Mobile", + "Unknown": "Unknown", + "temporary": "Temporary Device", + "temporary_info_text": "Temporary devices will be automatically deleted after a maximum of one month.", + "expiration_date": "Expiration Date:", + "add_tag": "Add a tag", "optional": "optional", "select_sensors": "Select sensors", + "Add Sensor": "Add Sensor", + "sensor_selection_info_text": "Select sensors for your device by choosing from predefined groups or individual sensors based on your device model. If using a custom device, configure sensors manually.", "select_sensors_text": "Select the sensors you want to use by clicking on the cards. You can add the same sensor multiple times.", "select_sensors_info_text": "Your sensor is not in the list? Add it to the <0>sensorWiki to use it on the openSenseMap.", "your_added": "Your added", - "sensors": "Sensors", + "Sensors": "Sensors", + "Model": "Model", "sensor": "Sensor", "phenomenon": "Phenomenon", + "MQTT Enabled": "MQTT Enabled", + "TTN Enabled": "TTN Enabled", + "Yes": "Yes", + "No": "No", "unit": "Unit", + "type": "Type", + "Includes": "Includes", "delete": "Delete", "title": "Title", "advanced": "Advanced", "advanced_text": "Advanced connectivity settings for your device. You can only choose one option.", "ttn_info_text": "The openSenseMap offers an integration with <0>TheThingsNetwork. Documentation for the parameters is provided on <1>GitHub", + "MQTT Configuration": "MQTT Configuration", + "mqtt_config_text": "Configure your MQTT settings for data streaming", + "Enable MQTT": "Enable MQTT", + "Enable TTN": "Enable TTN", + "TTN Configuration": "TTN Configuration", + "ttn_config_text": "Configure your TTN (The Things Network) settings", "ttn_app_id_info": "The application ID recieved from TTN", "ttn_dev_id_info": "The device ID recieved from TTN", "ttn_decode_profile_info": "A decoding profile matching the payload format. For details and configuration see <0>here.", @@ -48,10 +78,13 @@ "mqtt_connect_options_info": "A json encoded string with options to supply to the MQTT client", "location": "Location", + "location_info_text": "Select the device's location by clicking on the map or entering latitude and longitude coordinates manually. Drag the marker on the map to adjust the location if needed.", "location_text": "Click on the map to choose a location for your device. You can also enter your coordinates manually or use the geosearch.", "search_placeholder": "Search", "latitude": "Latitude", "longitude": "Longitude", + "enter latitude": "Enter latitude (-90 to 90)", + "enter longitude": "Enter longitude (-180 to 180)", "height": "Height", "height_info_text": "Height above sea level of your selected location in meters. If you have set up your device above ground level (e.g. high building), you should add this to the derived height. The height is espacially important if you have connected a sensor that meassures air pressure to make this meassurements compareable.",