-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* upgrade drizzle and drizzle-kit * add location to measurements for mobile devices * drop last migration * add locations for mobile devices * workaround to handle postgis geometry in query * remove migrations * add locations * disable esLint for now * get rid of `button can not be a descendant of button` error * make tags optional * mobile box overview * add dynamic popup to mobile trajectories * enhance mobile boxes * add build target * fix `top level await` error * add trips switch * remove colors when trips are deactivated * allow to select two sensors on mobile device * enable switching overlaying mobile sensor * add sensor unit to dependencies in MobileBoxLayer effect * refactor(routes): update route configuration and dependencies * refactor(donut-chart-cluster): simplify destructuring and formatting in component * refactor(device): comment out time column in getDevice function * refactor(graph): rename LineWithZoom to GraphWithZoom and update zoom state management * refactor(mobile): update MobileBoxView structure and clean up MobileOverviewLayer formatting * refactor(device): add time column to extras in getDevice function * refactor(package): remove chartjs-scale-timestack dependency from package.json * update package-lock.json * refactor(color): update color palette and introduce dynamic color calculation * refactor(device-detail): improve image handling and update log entry styles * delete relation to location on device deletion * init new device without sensorsWiki * refactor: enhance layout * refactor(sensor-selection): group sensors by type and update selection logic * feat(location-info): integrate map component for location selection * feat(auth): add user authentication check and enhance login page layout * feat(join): add profile existence check during user registration * feat(device): update device model enum and enhance form validation * feat(location-info): sync marker state with form values on mount * feat(device): enhance sensor selection with dynamic device model handling and add images for sensor groups * feat(device): add summary info component and update device stepper with new summary step * feat(device): add custom device configuration component and enforce sensor selection validation * feat(device): update location info handling and enhance stepper with tooltips for better user guidance * feat(device): implement breadcrumb navigation * fix(device): update device selection handling to reset values to null * feat(device): get rid of render errors * feat(device): define device models and update model column type * refactor(device): only allow to go back in breadcrumbs (for now) * feat(device): implement advanced form handling and validation for MQTT and TTN settings * feat(device): add device to database (still need to post to old db) * feat(device): update device creation to include model and tags * fix(device): reset selected sensors on device change and clean up unused code * feat(device): add temporary expiration date for devices and implement automatic deletion procedure * fix(device): remove debug logs and ensure expiration date is correctly formatted during device creation * redirect after device registration from completed * feat(device): modify temporary expiration date handling in device creation * feat(device): disable edit and data upload options in device dropdown menu --------- Co-authored-by: Matthias Pfeil <[email protected]>
- Loading branch information
Showing
30 changed files
with
5,365 additions
and
682 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
import { useFormContext } from "react-hook-form"; | ||
import { | ||
Card, | ||
CardContent, | ||
CardDescription, | ||
CardHeader, | ||
CardTitle, | ||
} from "~/components/ui/card"; | ||
import { Input } from "~/components/ui/input"; | ||
import { Label } from "~/components/ui/label"; | ||
import { | ||
Select, | ||
SelectContent, | ||
SelectItem, | ||
SelectTrigger, | ||
SelectValue, | ||
} from "~/components/ui/select"; | ||
import { Switch } from "~/components/ui/switch"; | ||
import { Textarea } from "~/components/ui/textarea"; | ||
|
||
export function AdvancedStep() { | ||
const { register, setValue, watch, resetField } = useFormContext(); | ||
|
||
// Watch field states | ||
const isMqttEnabled = watch("mqttEnabled") || false; | ||
const isTtnEnabled = watch("ttnEnabled") || false; | ||
|
||
// Clear corresponding fields when disabling | ||
const handleMqttToggle = (checked: boolean) => { | ||
setValue("mqttEnabled", checked); | ||
if (!checked) { | ||
resetField("url"); | ||
resetField("topic"); | ||
resetField("messageFormat"); | ||
resetField("decodeOptions"); | ||
resetField("connectionOptions"); | ||
} | ||
}; | ||
|
||
const handleTtnToggle = (checked: boolean) => { | ||
setValue("ttnEnabled", checked); | ||
if (!checked) { | ||
resetField("dev_id"); | ||
resetField("app_id"); | ||
resetField("profile"); | ||
resetField("decodeOptions"); | ||
resetField("port"); | ||
} | ||
}; | ||
|
||
const handleInputChange = ( | ||
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, | ||
) => { | ||
const { name, value } = event.target; | ||
setValue(name, value); | ||
}; | ||
|
||
const handleSelectChange = (field: string, value: string) => { | ||
setValue(field, value); | ||
}; | ||
|
||
return ( | ||
<> | ||
{/* MQTT Configuration */} | ||
<Card className="w-full"> | ||
<CardHeader> | ||
<CardTitle>MQTT Configuration</CardTitle> | ||
<CardDescription> | ||
Configure your MQTT settings for data streaming | ||
</CardDescription> | ||
</CardHeader> | ||
<CardContent> | ||
<div className="flex items-center justify-between space-x-2"> | ||
<Label htmlFor="mqttEnabled" className="text-base font-semibold"> | ||
Enable MQTT | ||
</Label> | ||
<Switch | ||
disabled | ||
id="mqttEnabled" | ||
checked={isMqttEnabled} | ||
onCheckedChange={handleMqttToggle} | ||
/> | ||
</div> | ||
|
||
{isMqttEnabled && ( | ||
<div className="space-y-4"> | ||
<div className="space-y-2"> | ||
<Label htmlFor="mqtt-url">MQTT URL</Label> | ||
<Input | ||
id="mqtt-url" | ||
placeholder="mqtt://example.com:1883" | ||
{...register("url")} | ||
onChange={handleInputChange} | ||
/> | ||
</div> | ||
|
||
<div className="space-y-2"> | ||
<Label htmlFor="mqtt-topic">MQTT Topic</Label> | ||
<Input | ||
id="mqtt-topic" | ||
placeholder="my/mqtt/topic" | ||
{...register("topic")} | ||
onChange={handleInputChange} | ||
/> | ||
</div> | ||
|
||
<div className="space-y-2"> | ||
<Label htmlFor="mqtt-message-format">Message Format</Label> | ||
<Select | ||
onValueChange={(value) => | ||
handleSelectChange("messageFormat", value) | ||
} | ||
defaultValue={watch("messageFormat")} | ||
> | ||
<SelectTrigger id="mqtt-message-format"> | ||
<SelectValue placeholder="Select a message format" /> | ||
</SelectTrigger> | ||
<SelectContent> | ||
<SelectItem value="json">JSON</SelectItem> | ||
<SelectItem value="csv">CSV</SelectItem> | ||
</SelectContent> | ||
</Select> | ||
</div> | ||
|
||
<div className="space-y-2"> | ||
<Label htmlFor="mqtt-decode-options">Decode Options</Label> | ||
<Textarea | ||
id="mqtt-decode-options" | ||
placeholder="Enter decode options as JSON" | ||
className="resize-none" | ||
{...register("decodeOptions")} | ||
onChange={handleInputChange} | ||
/> | ||
</div> | ||
|
||
<div className="space-y-2"> | ||
<Label htmlFor="mqtt-connection-options"> | ||
Connection Options | ||
</Label> | ||
<Textarea | ||
id="mqtt-connection-options" | ||
placeholder="Enter connection options as JSON" | ||
className="resize-none" | ||
{...register("connectionOptions")} | ||
onChange={handleInputChange} | ||
/> | ||
</div> | ||
</div> | ||
)} | ||
</CardContent> | ||
</Card> | ||
|
||
{/* TTN Configuration */} | ||
<Card className="w-full mt-6"> | ||
<CardHeader> | ||
<CardTitle>TTN Configuration</CardTitle> | ||
<CardDescription> | ||
Configure your TTN (The Things Network) settings | ||
</CardDescription> | ||
</CardHeader> | ||
<CardContent> | ||
<div className="flex items-center justify-between space-x-2"> | ||
<Label htmlFor="ttnEnabled" className="text-base font-semibold"> | ||
Enable TTN | ||
</Label> | ||
<Switch | ||
disabled | ||
id="ttnEnabled" | ||
checked={isTtnEnabled} | ||
onCheckedChange={handleTtnToggle} | ||
/> | ||
</div> | ||
|
||
{isTtnEnabled && ( | ||
<div className="space-y-4"> | ||
<div className="space-y-2"> | ||
<Label htmlFor="ttn-dev-id">Device ID</Label> | ||
<Input | ||
id="ttn-dev-id" | ||
placeholder="Enter TTN Device ID" | ||
{...register("dev_id")} | ||
onChange={handleInputChange} | ||
/> | ||
</div> | ||
|
||
<div className="space-y-2"> | ||
<Label htmlFor="ttn-app-id">Application ID</Label> | ||
<Input | ||
id="ttn-app-id" | ||
placeholder="Enter TTN Application ID" | ||
{...register("app_id")} | ||
onChange={handleInputChange} | ||
/> | ||
</div> | ||
|
||
<div className="space-y-2"> | ||
<Label htmlFor="ttn-profile">Profile</Label> | ||
<Select | ||
onValueChange={(value) => | ||
handleSelectChange("profile", value) | ||
} | ||
defaultValue={watch("profile")} | ||
> | ||
<SelectTrigger id="ttn-profile"> | ||
<SelectValue placeholder="Select a profile" /> | ||
</SelectTrigger> | ||
<SelectContent> | ||
<SelectItem value="lora-serialization"> | ||
Lora Serialization | ||
</SelectItem> | ||
<SelectItem value="sensebox/home">Sensebox/Home</SelectItem> | ||
<SelectItem value="json">JSON</SelectItem> | ||
<SelectItem value="debug">Debug</SelectItem> | ||
<SelectItem value="cayenne-lpp">Cayenne LPP</SelectItem> | ||
</SelectContent> | ||
</Select> | ||
</div> | ||
|
||
<div className="space-y-2"> | ||
<Label htmlFor="ttn-decode-options">Decode Options</Label> | ||
<Textarea | ||
id="ttn-decode-options" | ||
placeholder="Enter decode options as JSON" | ||
className="resize-none" | ||
{...register("decodeOptions")} | ||
onChange={handleInputChange} | ||
/> | ||
</div> | ||
|
||
<div className="space-y-2"> | ||
<Label htmlFor="ttn-port">Port</Label> | ||
<Input | ||
id="ttn-port" | ||
placeholder="Enter TTN Port" | ||
type="number" | ||
{...register("port", { valueAsNumber: true })} | ||
onChange={handleInputChange} | ||
/> | ||
</div> | ||
</div> | ||
)} | ||
</CardContent> | ||
</Card> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { useState, useEffect } from "react"; | ||
import { useFormContext } from "react-hook-form"; | ||
import { Button } from "@/components/ui/button"; | ||
import { Input } from "@/components/ui/input"; | ||
import { Label } from "@/components/ui/label"; | ||
import { Card, CardContent } from "@/components/ui/card"; | ||
import type { Sensor } from "./sensors-info"; | ||
import { X } from "lucide-react"; | ||
import { Separator } from "~/components/ui/separator"; | ||
|
||
export function CustomDeviceConfig() { | ||
const { setValue, watch } = useFormContext(); | ||
|
||
// Initialize state from form context | ||
const [sensors, setSensors] = useState<Sensor[]>( | ||
() => watch("selectedSensors") || [], | ||
); | ||
const [newSensor, setNewSensor] = useState<Sensor>({ | ||
title: "", | ||
unit: "", | ||
sensorType: "", | ||
}); | ||
|
||
// Sync state with form context on mount | ||
useEffect(() => { | ||
const savedSensors = watch("selectedSensors") || []; | ||
if (savedSensors.length > 0) { | ||
setSensors(savedSensors); | ||
} | ||
}, [watch]); | ||
|
||
const updateNewSensor = (field: keyof Sensor, value: string) => { | ||
setNewSensor((prev) => ({ ...prev, [field]: value })); | ||
}; | ||
|
||
const addSensor = () => { | ||
if (newSensor.title && newSensor.unit && newSensor.sensorType) { | ||
const updatedSensors = [...sensors, newSensor]; | ||
setSensors(updatedSensors); | ||
setValue("selectedSensors", updatedSensors); // Sync with form | ||
setNewSensor({ title: "", unit: "", sensorType: "" }); | ||
} | ||
}; | ||
|
||
const removeSensor = (index: number) => { | ||
const updatedSensors = sensors.filter((_, i) => i !== index); | ||
setSensors(updatedSensors); | ||
setValue("selectedSensors", updatedSensors); // Sync with form | ||
}; | ||
|
||
return ( | ||
<div className="space-y-4 p-2"> | ||
<div> | ||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4"> | ||
<div> | ||
<Label htmlFor="phenomenon">Phenomenon</Label> | ||
<Input | ||
id="phenomenon" | ||
value={newSensor.title} | ||
onChange={(e) => updateNewSensor("title", e.target.value)} | ||
placeholder="e.g., Temperature" | ||
/> | ||
</div> | ||
<div> | ||
<Label htmlFor="unit">Unit</Label> | ||
<Input | ||
id="unit" | ||
value={newSensor.unit} | ||
onChange={(e) => updateNewSensor("unit", e.target.value)} | ||
placeholder="e.g., °C" | ||
/> | ||
</div> | ||
<div> | ||
<Label htmlFor="type">Type</Label> | ||
<Input | ||
id="type" | ||
value={newSensor.sensorType} | ||
onChange={(e) => updateNewSensor("sensorType", e.target.value)} | ||
placeholder="e.g., HDC1080" | ||
/> | ||
</div> | ||
</div> | ||
<Button | ||
onClick={addSensor} | ||
disabled={ | ||
!newSensor.title || !newSensor.unit || !newSensor.sensorType | ||
} | ||
> | ||
Add Sensor | ||
</Button> | ||
</div> | ||
|
||
{sensors.length > 0 && <Separator />} | ||
{sensors.map((sensor, index) => ( | ||
<Card key={index} className="mb-2"> | ||
<CardContent className="p-4 flex justify-between items-center"> | ||
<div> | ||
<span className="font-medium">{sensor.title}</span> ({sensor.unit} | ||
) - {sensor.sensorType} | ||
</div> | ||
<Button | ||
variant="ghost" | ||
size="icon" | ||
onClick={(e) => { | ||
e.preventDefault(); | ||
removeSensor(index); | ||
}} | ||
> | ||
<X className="h-4 w-4" /> | ||
</Button> | ||
</CardContent> | ||
</Card> | ||
))} | ||
</div> | ||
); | ||
} |
Oops, something went wrong.