Add members/invitations and code-based gateway setup to the Setup page
Gateway setup shows a `continuum-proxy enrol` command with a one-time code; the manual settings stay behind "Set it up by hand". New members section for inviting by email, plus an /invite page to accept, and the login page honours a safe `next` redirect.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
<script setup lang="ts">
|
||||
// Reached from an invitation link. Excluded from the sign-in redirect (see
|
||||
// nuxt.config.ts) so the token in the URL survives: a visitor who is not
|
||||
// signed in is sent to /login and brought back here afterwards.
|
||||
const route = useRoute();
|
||||
const user = useSupabaseUser();
|
||||
const apiFetch = useApiFetch();
|
||||
|
||||
const token = computed(() => (typeof route.query.token === "string" ? route.query.token : ""));
|
||||
const state = ref<"waiting" | "working" | "done" | "failed">("waiting");
|
||||
const message = ref("");
|
||||
const joined = ref<{ orgName: string; role: string } | null>(null);
|
||||
|
||||
async function accept() {
|
||||
state.value = "working";
|
||||
try {
|
||||
const result = await apiFetch<{ orgName: string; role: string }>("/invitations/accept", {
|
||||
method: "POST",
|
||||
body: { token: token.value },
|
||||
});
|
||||
joined.value = result;
|
||||
state.value = "done";
|
||||
} catch (err) {
|
||||
message.value = apiErrorMessage(err, "the invitation could not be accepted");
|
||||
state.value = "failed";
|
||||
}
|
||||
}
|
||||
|
||||
const next = computed(() => `/invite?token=${encodeURIComponent(token.value)}`);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="invite">
|
||||
<h1>Join an organisation</h1>
|
||||
<p v-if="!token">This link has no invitation in it.</p>
|
||||
<template v-else-if="!user">
|
||||
<p>Sign in with the email address the invitation was sent to, then you will come back here.</p>
|
||||
<NuxtLink :to="{ path: '/login', query: { next } }">Sign in</NuxtLink>
|
||||
</template>
|
||||
<template v-else>
|
||||
<p>You are signed in as {{ user.email }}.</p>
|
||||
<button v-if="state === 'waiting' || state === 'working'" type="button" :disabled="state === 'working'" @click="accept">
|
||||
Accept the invitation
|
||||
</button>
|
||||
<p v-if="state === 'done'" role="status">
|
||||
You have joined {{ joined?.orgName }} as {{ joined?.role }}. <NuxtLink to="/">Go to the dashboard</NuxtLink>
|
||||
</p>
|
||||
<p v-if="state === 'failed'" role="alert">{{ message }}</p>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.invite {
|
||||
max-width: 28rem;
|
||||
margin: 4rem auto;
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user