setting design
This commit is contained in:
parent
1c7be2a776
commit
a44284b9b1
1 changed files with 216 additions and 13 deletions
|
@ -3,20 +3,223 @@
|
|||
import Button from "$lib/components/ui/button/button.svelte";
|
||||
import Input from "$lib/components/ui/input/input.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();
|
||||
|
||||
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>
|
||||
|
||||
<form method="post" action="?/newfloor" use:enhance>
|
||||
<div class="form-group">
|
||||
<Label for="number">Floor number</Label>
|
||||
<Input id="number" name="number" />
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<Button type="submit">Add floor</Button>
|
||||
<Button type="submit" formaction="?/rmfloor" variant="secondary">Delete floor</Button>
|
||||
</div>
|
||||
</form>
|
||||
{#if form?.message}
|
||||
<p class="error-message">{form.message}</p>
|
||||
{/if}
|
||||
<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>
|
||||
<div class="form-group mb-4">
|
||||
<Label for="number">Floor number</Label>
|
||||
<Input id="number" name="number" class="mt-2" />
|
||||
</div>
|
||||
<div class="button-group flex gap-4">
|
||||
<Button type="submit">Add floor</Button>
|
||||
<Button type="submit" formaction="?/rmfloor" variant="secondary">Delete floor</Button>
|
||||
</div>
|
||||
</form>
|
||||
{#if form?.message}
|
||||
<p class="error-message mt-4 text-red-600">{form.message}</p>
|
||||
{/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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue