diff --git a/WebApp/src/lib/sim/ui/AppChrome.svelte b/WebApp/src/lib/sim/ui/AppChrome.svelte index 72a2f41..a4e3397 100644 --- a/WebApp/src/lib/sim/ui/AppChrome.svelte +++ b/WebApp/src/lib/sim/ui/AppChrome.svelte @@ -1,20 +1,42 @@
-
+
+ {#each $appState.toasts as toast (toast.id)} +
+ {toast.message} + +
+ {/each} +
@@ -118,20 +144,30 @@ diff --git a/WebApp/src/routes/+layout.svelte b/WebApp/src/routes/+layout.svelte index fe2f029..227ad59 100644 --- a/WebApp/src/routes/+layout.svelte +++ b/WebApp/src/routes/+layout.svelte @@ -2,6 +2,7 @@ import './layout.css'; import { onMount } from 'svelte'; import favicon from '$lib/assets/favicon.svg'; + import { appController } from '$lib/app/controller'; let { children } = $props(); @@ -9,13 +10,10 @@ const bodyClasses = ['h-screen', 'bg-[#0a0a0c]', 'text-gray-200', 'overflow-hidden', 'flex']; document.body.classList.add(...bodyClasses); document.documentElement.dataset.theme = 'black'; - const simWindow = window as Window & { __mobileSimLoaded?: boolean }; - if (!simWindow.__mobileSimLoaded) { - simWindow.__mobileSimLoaded = true; - void import('$lib/sim/mobile-sim.js'); - } + void appController.init(); return () => { + void appController.destroy(); document.body.classList.remove(...bodyClasses); }; }); diff --git a/WebApp/src/routes/+page.svelte b/WebApp/src/routes/+page.svelte index fc2fc0e..217475d 100644 --- a/WebApp/src/routes/+page.svelte +++ b/WebApp/src/routes/+page.svelte @@ -1,8 +1,77 @@ - +
+
+
+ + + +
+

SecureCam Web

+

Sign in to manage visual security from your browser.

+
+ +
+
+ + appController.setAuthField('email', (event.currentTarget as HTMLInputElement).value)} + /> +
+
+ appController.setAuthField('password', (event.currentTarget as HTMLInputElement).value)} + /> +
+ {#if $appState.isRegistering} +
+ appController.setAuthField('name', (event.currentTarget as HTMLInputElement).value)} + /> +
+ {/if} + +
+ +
OR
+ +
+
+
diff --git a/WebApp/src/routes/activity/+page.svelte b/WebApp/src/routes/activity/+page.svelte index f115ef8..91b5186 100644 --- a/WebApp/src/routes/activity/+page.svelte +++ b/WebApp/src/routes/activity/+page.svelte @@ -1,8 +1,50 @@ - +
+
+

Activity History

+ +
+ +
+
+ {#if $appState.motionNotifications.length === 0} +
+ + + +

All quiet. No notifications yet.

+
+ {:else} + {#each $appState.motionNotifications as notification (notification.id)} + + {/each} + {/if} +
+
+
diff --git a/WebApp/src/routes/camera/+page.svelte b/WebApp/src/routes/camera/+page.svelte index ca18c40..5052873 100644 --- a/WebApp/src/routes/camera/+page.svelte +++ b/WebApp/src/routes/camera/+page.svelte @@ -1,8 +1,107 @@ - +
+
+
+
+ +
+ + REC +
+
+ +
+ + + +

Camera Offline

+ +
+
+ +
+
+

Logs

+
+ {#if $appState.activityLog.length === 0} +
Awaiting connection...
+ {:else} + {#each $appState.activityLog as log (log.id)} +
[{new Date(log.createdAt).toLocaleTimeString()}] {log.type}: {log.message}
+ {/each} + {/if} +
+
+ +
+

Actions / Options

+
+ {#if !$appState.isMotionActive} + + {:else} + + {/if} +
+
+
+
+
diff --git a/WebApp/src/routes/client/+page.svelte b/WebApp/src/routes/client/+page.svelte index 9518b8c..e0be89d 100644 --- a/WebApp/src/routes/client/+page.svelte +++ b/WebApp/src/routes/client/+page.svelte @@ -1,8 +1,243 @@ + appController.closeLinkedCameraMenu()} /> + - +
+
+

Client Dashboard

+
+ + +
+
+ +
+

Your Cameras

+
+ {#if $appState.linkedCameras.length === 0} +
+

No cameras linked yet

+
+ {:else} + {#each $appState.linkedCameras as link (link.id)} +
appController.selectCamera(link.cameraDeviceId)} + onkeydown={(event) => { + if (event.key === 'Enter' || event.key === ' ') appController.selectCamera(link.cameraDeviceId); + }} + > +
+ {#if $appState.activeCameraDeviceId === link.cameraDeviceId} +
+ + + + +

Viewing

+
+ {:else} +
+ + + +

{isCameraLive(link.cameraDeviceId) ? 'Live Stream Active' : 'Click to view'}

+
+ {/if} +
+
+
+
+
+
+

+ {link.cameraName || link.cameraDeviceId} +

+

{link.cameraStatus || 'offline'}

+
+
+
+ +
+ + +
+
+
+
+
+ {/each} + {/if} +
+
+ +
+
+
+
+ +

Live Feed: {activeCameraLabel()}

+
+ +
+ +
+ + {#if $appState.clientFallbackFrame} + Live stream frame + {/if} + +
+ + + +

{$appState.clientPlaceholderText}

+
+
+
+ +
+
+

Recent Recordings

+
+ {#if $appState.recordings.length === 0} +
+

No recordings found

+
+ {:else} + {#each $appState.recordings.slice(0, 5) as recording (recording.id)} +
+
+ {new Date(recording.createdAt).toLocaleString()} + + {recording.durationSeconds != null ? `${recording.durationSeconds}s duration` : 'Duration pending'} ยท {recording.status ?? 'unknown'} + +
+ +
+ {/each} + {/if} +
+
+
+
+
diff --git a/WebApp/src/routes/onboarding/+page.svelte b/WebApp/src/routes/onboarding/+page.svelte index 2d715c1..e4c8ea6 100644 --- a/WebApp/src/routes/onboarding/+page.svelte +++ b/WebApp/src/routes/onboarding/+page.svelte @@ -1,8 +1,76 @@ - +
+
+

Configure Device

+

Set up this browser dashboard role

+
+ +
+
+ + appController.setOnboardingField('name', (event.currentTarget as HTMLInputElement).value)} + /> +
+ +
+ +
+ + +
+
+ +
+ + appController.setOnboardingField('pushToken', (event.currentTarget as HTMLInputElement).value)} + /> +
+ +
+ + +
+
+
diff --git a/WebApp/src/routes/settings/+page.svelte b/WebApp/src/routes/settings/+page.svelte index 2590904..abe1976 100644 --- a/WebApp/src/routes/settings/+page.svelte +++ b/WebApp/src/routes/settings/+page.svelte @@ -1,8 +1,89 @@ - +
+

Settings

+ +
+
+ {profileInitial()} +
+
+

{profileName()}

+

{profileEmail()}

+
+
+ +
+ + +
+ + +