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.
61 lines
2.0 KiB
Vue
61 lines
2.0 KiB
Vue
<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>
|