A new user could sign in and see an empty page with no way forward. Setup walks them through it: create a farm (reusing their organisation if they already have one); get the gateway settings with a freshly minted token, shown once and removed from the page when dismissed, with a confirmation first if a gateway is already connected, since minting replaces the old token; add a Bambu printer by address, serial and access code (a printer the gateway discovered fills the form in); remove a printer behind a named confirmation that says it also deletes the stored credential. The access code field is cleared after every submit, success or failure, and the page says plainly that the code goes to the gateway and nowhere else. Add is disabled while no gateway is connected, because the code has nowhere to go. The fleet page now lists open alerts above the printers (polled every 5s) with a Dismiss button, and points an empty account at Setup. Verified in real Chrome by scripts/e2e-setup-browser.sh in continuum-backend: nothing -> farm -> gateway -> printer -> failed-print alert -> dismissed -> removed, with no page errors.
109 lines
2.2 KiB
Vue
109 lines
2.2 KiB
Vue
<script setup lang="ts">
|
|
import { useTelemetryStore } from "~/stores/telemetry";
|
|
|
|
const telemetry = useTelemetryStore();
|
|
const user = useSupabaseUser();
|
|
const session = useSupabaseSession();
|
|
|
|
// Driven by the session rather than a one-shot onMounted: the session can
|
|
// arrive after mount, and sign-out should drop the socket instead of leaving
|
|
// it running on a token that is about to stop being valid.
|
|
onMounted(() => {
|
|
watch(
|
|
() => session.value?.access_token,
|
|
(token) => (token ? telemetry.connect() : telemetry.disconnect()),
|
|
{ immediate: true },
|
|
);
|
|
});
|
|
|
|
onBeforeUnmount(() => {
|
|
telemetry.disconnect();
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="app-shell">
|
|
<header class="app-shell__header">
|
|
<span class="app-shell__brand">Continuum</span>
|
|
<nav class="app-shell__nav">
|
|
<NuxtLink to="/">Fleet</NuxtLink>
|
|
<NuxtLink to="/jobs">Jobs</NuxtLink>
|
|
<NuxtLink to="/setup">Setup</NuxtLink>
|
|
</nav>
|
|
<span class="app-shell__status" :class="telemetry.connected ? 'is-online' : 'is-offline'">
|
|
{{
|
|
telemetry.link === "connected"
|
|
? "Live"
|
|
: telemetry.link === "connecting"
|
|
? "Connecting…"
|
|
: "Offline"
|
|
}}
|
|
</span>
|
|
<span v-if="user" class="app-shell__user">{{ user.email }}</span>
|
|
</header>
|
|
<main class="app-shell__main">
|
|
<NuxtPage />
|
|
</main>
|
|
</div>
|
|
</template>
|
|
|
|
<style>
|
|
:root {
|
|
color-scheme: dark;
|
|
font-family: system-ui, -apple-system, sans-serif;
|
|
}
|
|
|
|
body {
|
|
margin: 0;
|
|
background: #0b0d12;
|
|
color: #e6e8ec;
|
|
}
|
|
|
|
.app-shell__header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
padding: 0.75rem 1.25rem;
|
|
border-bottom: 1px solid #1f232c;
|
|
}
|
|
|
|
.app-shell__brand {
|
|
font-weight: 700;
|
|
letter-spacing: 0.02em;
|
|
}
|
|
|
|
.app-shell__status.is-online {
|
|
color: #35d07f;
|
|
}
|
|
|
|
.app-shell__status.is-offline {
|
|
color: #f2a33c;
|
|
}
|
|
|
|
.app-shell__user {
|
|
margin-left: auto;
|
|
opacity: 0.7;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.app-shell__main {
|
|
padding: 1.25rem;
|
|
}
|
|
|
|
.app-shell__nav {
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.app-shell__nav a {
|
|
color: inherit;
|
|
opacity: 0.7;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.app-shell__nav a.router-link-exact-active {
|
|
opacity: 1;
|
|
text-decoration: underline;
|
|
}
|
|
</style>
|