setting design

This commit is contained in:
Yan Zhou 2025-06-11 19:59:33 +02:00
parent 1c7be2a776
commit a44284b9b1

View file

@ -3,20 +3,223 @@
import Button from "$lib/components/ui/button/button.svelte"; import Button from "$lib/components/ui/button/button.svelte";
import Input from "$lib/components/ui/input/input.svelte"; import Input from "$lib/components/ui/input/input.svelte";
import Label from "$lib/components/ui/label/label.svelte"; import Label from "$lib/components/ui/label/label.svelte";
import Card from "$lib/components/ui/card/card.svelte";
import CardContent from "$lib/components/ui/card/card-content.svelte";
import CardDescription from "$lib/components/ui/card/card-description.svelte";
import CardHeader from "$lib/components/ui/card/card-header.svelte";
import CardTitle from "$lib/components/ui/card/card-title.svelte";
import { Thermometer, Droplets, Gauge, Mountain, Upload, Cpu } from "@lucide/svelte";
const { form } = $props(); const { form } = $props();
let floorPlanImage = $state(null);
let availableDevices = $state([
{ id: "esp8266-1", name: "ESP8266 #1", type: "esp8266", status: "online" },
{ id: "esp8266-2", name: "ESP8266 #2", type: "esp8266", status: "online" },
{ id: "esp8266-3", name: "ESP8266 #3", type: "esp8266", status: "offline" },
{ id: "esp8266-4", name: "ESP8266 #4", type: "esp8266", status: "online" },
]);
let placedDevices = $state([]);
let draggedDevice = $state(null);
let floorPlanRef = $state(null);
function handleFileUpload(event) {
const file = event.target.files?.[0];
if (file && file.type.startsWith("image/")) {
const reader = new FileReader();
reader.onload = (e) => {
floorPlanImage = e.target.result;
};
reader.readAsDataURL(file);
}
}
function handleDragStart(device) {
draggedDevice = device;
}
function handleDragOver(event) {
event.preventDefault();
}
function handleDrop(event) {
event.preventDefault();
if (!draggedDevice || !floorPlanRef) return;
const rect = floorPlanRef.getBoundingClientRect();
const x = ((event.clientX - rect.left) / rect.width) * 100;
const y = ((event.clientY - rect.top) / rect.height) * 100;
// Check if device is already placed
const existingIndex = placedDevices.findIndex((d) => d.id === draggedDevice.id);
if (existingIndex >= 0) {
// Update position
placedDevices[existingIndex] = { ...placedDevices[existingIndex], x, y };
} else {
// Add new device
placedDevices = [...placedDevices, { ...draggedDevice, x, y }];
}
draggedDevice = null;
}
function removeDevice(deviceId) {
placedDevices = placedDevices.filter((d) => d.id !== deviceId);
}
</script> </script>
<div class="container mx-auto space-y-6 p-6">
<Card>
<CardHeader>
<CardTitle>Floor Management</CardTitle>
<CardDescription>Add or remove floors from your monitoring system</CardDescription>
</CardHeader>
<CardContent>
<form method="post" action="?/newfloor" use:enhance> <form method="post" action="?/newfloor" use:enhance>
<div class="form-group"> <div class="form-group mb-4">
<Label for="number">Floor number</Label> <Label for="number">Floor number</Label>
<Input id="number" name="number" /> <Input id="number" name="number" class="mt-2" />
</div> </div>
<div class="button-group"> <div class="button-group flex gap-4">
<Button type="submit">Add floor</Button> <Button type="submit">Add floor</Button>
<Button type="submit" formaction="?/rmfloor" variant="secondary">Delete floor</Button> <Button type="submit" formaction="?/rmfloor" variant="secondary">Delete floor</Button>
</div> </div>
</form> </form>
{#if form?.message} {#if form?.message}
<p class="error-message">{form.message}</p> <p class="error-message mt-4 text-red-600">{form.message}</p>
{/if} {/if}
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Floor Plan & Devices</CardTitle>
<CardDescription>Upload a floor plan image and view available devices</CardDescription>
</CardHeader>
<CardContent>
<div class="space-y-4">
<div>
<Label for="floorplan" class="cursor-pointer">
<div
class="rounded-lg border-2 border-dashed border-gray-300 p-6 text-center transition-colors hover:border-gray-400"
>
<Upload class="mx-auto mb-2 h-8 w-8 text-gray-400" />
<span class="text-sm text-gray-600">Click to upload floor plan image</span>
</div>
</Label>
<input
id="floorplan"
type="file"
accept="image/*"
class="hidden"
onchange={handleFileUpload}
/>
</div>
{#if floorPlanImage}
<div
class="relative overflow-hidden rounded-lg border bg-gray-50"
bind:this={floorPlanRef}
ondragover={handleDragOver}
ondrop={handleDrop}
>
<img src={floorPlanImage} alt="Floor plan" class="pointer-events-none h-auto w-full" />
{#each placedDevices as device}
<div
class="absolute -translate-x-1/2 -translate-y-1/2 transform cursor-move rounded-lg border-2 border-blue-500 bg-white p-3 shadow-lg transition-shadow hover:shadow-xl"
style="left: {device.x}%; top: {device.y}%;"
draggable="true"
ondragstart={() => handleDragStart(device)}
>
<div class="flex items-center gap-2">
<Cpu class="h-5 w-5 text-blue-600" />
<span class="text-xs font-medium">{device.name}</span>
<button
onclick={() => removeDevice(device.id)}
class="ml-2 text-red-500 hover:text-red-700"
>
×
</button>
</div>
<div class="mt-2 grid grid-cols-2 gap-1">
<div class="flex items-center gap-1">
<Thermometer class="h-3 w-3 text-orange-500" />
<span class="text-xs">Temp</span>
</div>
<div class="flex items-center gap-1">
<Droplets class="h-3 w-3 text-blue-500" />
<span class="text-xs">Humid</span>
</div>
<div class="flex items-center gap-1">
<Gauge class="h-3 w-3 text-green-500" />
<span class="text-xs">Press</span>
</div>
<div class="flex items-center gap-1">
<Mountain class="h-3 w-3 text-purple-500" />
<span class="text-xs">Alt</span>
</div>
</div>
</div>
{/each}
</div>
<p class="mt-2 text-sm text-gray-600">
Drag ESP8266 devices from below onto the floor plan to place them in specific rooms.
</p>
{/if}
<div class="pt-4">
<h3 class="mb-3 font-semibold">Available ESP8266 Devices</h3>
<div class="grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-4">
{#each availableDevices as device}
<div
class="cursor-move rounded-lg border-2 bg-white p-4 transition-shadow hover:shadow-md {device.status ===
'online'
? 'border-green-200'
: 'border-gray-200'}"
draggable="true"
ondragstart={() => handleDragStart(device)}
>
<div class="mb-2 flex items-center justify-between">
<div class="flex items-center gap-2">
<Cpu class="h-5 w-5 text-blue-600" />
<span class="text-sm font-medium">{device.name}</span>
</div>
<div class="flex items-center gap-1">
<div
class="h-2 w-2 rounded-full {device.status === 'online'
? 'bg-green-500'
: 'bg-gray-400'}"
></div>
<span class="text-xs text-gray-600">{device.status}</span>
</div>
</div>
<div class="mb-2 text-xs text-gray-500">Sensors:</div>
<div class="grid grid-cols-2 gap-1">
<div class="flex items-center gap-1 text-xs">
<Thermometer class="h-3 w-3 text-orange-500" />
<span>Temperature</span>
</div>
<div class="flex items-center gap-1 text-xs">
<Droplets class="h-3 w-3 text-blue-500" />
<span>Humidity</span>
</div>
<div class="flex items-center gap-1 text-xs">
<Gauge class="h-3 w-3 text-green-500" />
<span>Pressure</span>
</div>
<div class="flex items-center gap-1 text-xs">
<Mountain class="h-3 w-3 text-purple-500" />
<span>Altitude</span>
</div>
</div>
</div>
{/each}
</div>
</div>
</div>
</CardContent>
</Card>
</div>