diff --git a/src/app/dashboard/devices/page.tsx b/src/app/dashboard/devices/page.tsx new file mode 100644 index 0000000..d1d6651 --- /dev/null +++ b/src/app/dashboard/devices/page.tsx @@ -0,0 +1,138 @@ +'use client'; + +import { useState } from 'react'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import Link from 'next/link'; +import { apiFetch } from '../../../lib/api'; +import { useSession } from '../../../components/session-provider'; + +interface DataSource { id: string; name: string; type: string; status: string; config: Record | null; lastSyncAt: string | null; createdAt: string; } + +const DEVICE_TYPES = ['device', 'iot', 'sensor', 'laptop', 'phone', 'tablet', 'server', 'raspberry', 'arduino']; +const DEVICE_ICONS: Record = { + laptop: '💻', phone: '📱', tablet: '📟', server: '🖥️', raspberry: '🍓', + arduino: '🔧', sensor: '📡', iot: '🌐', device: '⚙️', +}; + +export default function DevicesPage() { + const { activeTenant } = useSession(); + const tenantId = activeTenant?.tenantId ?? ''; + const qc = useQueryClient(); + const [showAdd, setShowAdd] = useState(false); + const [form, setForm] = useState({ name: '', type: 'device', location: '', notes: '' }); + + const { data: sources = [], isLoading } = useQuery({ + queryKey: ['devices', tenantId], + queryFn: () => apiFetch('/v1/data-sources', { tenantId }), + enabled: Boolean(tenantId), staleTime: 60_000, + }); + + const devices = sources.filter((s) => DEVICE_TYPES.includes(s.type.toLowerCase())); + + const createMut = useMutation({ + mutationFn: () => apiFetch('/v1/data-sources', { tenantId, method: 'POST', body: { + name: form.name, type: form.type, + config: { location: form.location || undefined, notes: form.notes || undefined }, + status: 'active', + }}), + onSuccess: () => { qc.invalidateQueries({ queryKey: ['devices', tenantId] }); setShowAdd(false); setForm({ name: '', type: 'device', location: '', notes: '' }); }, + }); + + return ( +
+
+
+

Dispozitive

+

+ {isLoading ? 'Se încarcă…' : `${devices.length} dispozitive înregistrate`} +

+
+ +
+ +
+

+ Dispozitivele pot trimite date (telemetrie, senzori) către CEO OS via webhook sau API. + Vezi Telemetrie pentru date primite. +

+
+ + {showAdd && ( +
+

Dispozitiv nou

+
+ setForm((p) => ({ ...p, name: e.target.value }))} + className="rounded-lg border bg-background px-3 py-2 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-ring sm:col-span-2" /> + + setForm((p) => ({ ...p, location: e.target.value }))} + className="rounded-lg border bg-background px-3 py-2 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-ring" /> + setForm((p) => ({ ...p, notes: e.target.value }))} + className="rounded-lg border bg-background px-3 py-2 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-ring sm:col-span-2" /> +
+
+ + +
+
+ )} + + {isLoading ? ( +
Se încarcă…
+ ) : devices.length === 0 ? ( +
+

⚙️

+

Niciun dispozitiv înregistrat.

+
+ ) : ( +
+ {devices.map((d) => { + const cfg = (d.config ?? {}) as Record; + const icon = DEVICE_ICONS[d.type.toLowerCase()] ?? '⚙️'; + return ( +
+
+ {icon} +
+

{d.name}

+

{d.type}

+
+ + {d.status} + +
+
+ {cfg.location &&

📍 {cfg.location}

} + {cfg.notes &&

{cfg.notes}

} + {d.lastSyncAt &&

Ultima activitate: {new Date(d.lastSyncAt).toLocaleDateString('ro-RO')}

} +
+
+ ); + })} +
+ )} + + + 📡 Telemetrie dispozitive → + +
+ ); +}