Add Setup: first farm, gateway settings, add and remove printers; show alerts
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.
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import type { AppFarm } from "../types";
|
||||
|
||||
const props = defineProps<{
|
||||
farm: AppFarm;
|
||||
/** null until the first answer arrives. */
|
||||
gatewayOnline: boolean | null;
|
||||
/** Where the gateway connects; shown in its settings so there is one place to copy from. */
|
||||
uplinkUrl: string;
|
||||
/** Mints a new proxy token. Rejects with a readable message. */
|
||||
mint: () => Promise<string>;
|
||||
}>();
|
||||
|
||||
const token = ref("");
|
||||
const confirming = ref(false);
|
||||
const busy = ref(false);
|
||||
const failure = ref("");
|
||||
const copied = ref(false);
|
||||
|
||||
// Minting replaces the old token, which disconnects a gateway that already
|
||||
// has it at its next reconnect - so a farm that has a gateway online asks
|
||||
// first, and a farm that has never had one does not.
|
||||
async function run() {
|
||||
busy.value = true;
|
||||
failure.value = "";
|
||||
try {
|
||||
token.value = await props.mint();
|
||||
confirming.value = false;
|
||||
} catch (err) {
|
||||
failure.value = err instanceof Error ? err.message : String(err);
|
||||
} finally {
|
||||
busy.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (props.gatewayOnline) confirming.value = true;
|
||||
else void run();
|
||||
}
|
||||
|
||||
const settings = computed(() =>
|
||||
[
|
||||
`gateway_id = "gateway-1"`,
|
||||
`farm_id = "${props.farm.id}"`,
|
||||
`gateway_token = "${token.value}"`,
|
||||
`uplink_url = "${props.uplinkUrl}"`,
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
async function copy() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(settings.value);
|
||||
copied.value = true;
|
||||
} catch {
|
||||
// Clipboard access can be refused; the text is selectable on screen.
|
||||
copied.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="gateway-setup" :aria-label="`Gateway for ${farm.name}`">
|
||||
<h3>Gateway</h3>
|
||||
<p v-if="gatewayOnline" class="gateway-setup__ok">A gateway is connected.</p>
|
||||
<p v-else>
|
||||
No gateway is connected. Install the Continuum gateway on a computer on the printers' network and give it the
|
||||
settings below.
|
||||
</p>
|
||||
|
||||
<template v-if="!token">
|
||||
<button v-if="!confirming" type="button" :disabled="busy" @click="start">
|
||||
{{ gatewayOnline ? "Create a new gateway token" : "Create gateway settings" }}
|
||||
</button>
|
||||
<div v-else class="gateway-setup__confirm" role="alertdialog">
|
||||
<p>
|
||||
This replaces the current token. The connected gateway will be refused the next time it reconnects, until
|
||||
you give it the new one.
|
||||
</p>
|
||||
<button type="button" :disabled="busy" @click="run">Replace token</button>
|
||||
<button type="button" :disabled="busy" @click="confirming = false">Keep the current one</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<p>
|
||||
Put this in the gateway's <code>continuum.toml</code>. <strong>The token is shown only now</strong> - it is not
|
||||
kept anywhere you can read it back.
|
||||
</p>
|
||||
<pre class="gateway-setup__settings">{{ settings }}</pre>
|
||||
<button type="button" @click="copy">{{ copied ? "Copied" : "Copy" }}</button>
|
||||
<button type="button" @click="token = ''">Done</button>
|
||||
</template>
|
||||
|
||||
<p v-if="failure" role="alert" class="gateway-setup__error">{{ failure }}</p>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.gateway-setup__ok {
|
||||
color: #35d07f;
|
||||
}
|
||||
|
||||
.gateway-setup__settings {
|
||||
background: #12151c;
|
||||
padding: 0.75rem;
|
||||
border-radius: 6px;
|
||||
overflow-x: auto;
|
||||
user-select: all;
|
||||
}
|
||||
|
||||
.gateway-setup__error {
|
||||
color: #f2604c;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user