249 lines
8.3 KiB
Vue
249 lines
8.3 KiB
Vue
<template>
|
|
<div class="page">
|
|
<section class="card hero glass">
|
|
<div>
|
|
<p class="eyebrow">Atlas</p>
|
|
<h1>Request Access</h1>
|
|
<p class="lede">
|
|
Request access to Atlas. Approved accounts are provisioned from this form only.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="card module">
|
|
<div class="module-head">
|
|
<h2>Request form</h2>
|
|
<span class="pill mono" :class="submitted ? 'pill-ok' : 'pill-warn'">
|
|
{{ submitted ? "submitted" : "pending" }}
|
|
</span>
|
|
</div>
|
|
|
|
<p class="muted">
|
|
Requests require a verified external email so Keycloak can support account recovery. After verification, an admin can approve your account.
|
|
Your lab username becomes your Atlas identity (including your @{{ mailDomain }} mailbox).
|
|
</p>
|
|
|
|
<form class="form" @submit.prevent="submit" v-if="!submitted">
|
|
<label class="field">
|
|
<span class="label mono">Lab Name (username)</span>
|
|
<input
|
|
v-model="form.username"
|
|
class="input mono"
|
|
type="text"
|
|
autocomplete="username"
|
|
placeholder="e.g. alice"
|
|
:disabled="submitting"
|
|
required
|
|
/>
|
|
<div v-if="availability.label" class="availability">
|
|
<span class="pill mono" :class="availability.pillClass">{{ availability.label }}</span>
|
|
<span v-if="availability.detail" class="hint mono">{{ availability.detail }}</span>
|
|
</div>
|
|
</label>
|
|
|
|
<label class="field">
|
|
<span class="label mono">Last name</span>
|
|
<input
|
|
v-model="form.last_name"
|
|
class="input"
|
|
type="text"
|
|
autocomplete="family-name"
|
|
placeholder="e.g. Stein"
|
|
:disabled="submitting"
|
|
required
|
|
/>
|
|
<span class="hint mono">Required for account provisioning.</span>
|
|
</label>
|
|
|
|
<label class="field">
|
|
<span class="label mono">First name (optional)</span>
|
|
<input
|
|
v-model="form.first_name"
|
|
class="input"
|
|
type="text"
|
|
autocomplete="given-name"
|
|
placeholder="e.g. Brad"
|
|
:disabled="submitting"
|
|
/>
|
|
</label>
|
|
|
|
<label class="field">
|
|
<span class="label mono">Email</span>
|
|
<input
|
|
v-model="form.email"
|
|
class="input mono"
|
|
type="email"
|
|
autocomplete="email"
|
|
placeholder="you@example.com"
|
|
:disabled="submitting"
|
|
required
|
|
/>
|
|
<span class="hint mono">Must be an external address (not @{{ mailDomain }})</span>
|
|
</label>
|
|
|
|
<label class="field">
|
|
<span class="label mono">Note (optional)</span>
|
|
<textarea
|
|
v-model="form.note"
|
|
class="textarea"
|
|
rows="4"
|
|
placeholder="What do you want access to?"
|
|
:disabled="submitting"
|
|
/>
|
|
</label>
|
|
|
|
<div class="actions">
|
|
<button
|
|
class="primary"
|
|
type="submit"
|
|
:disabled="submitting || !form.username.trim() || !form.last_name.trim() || availability.blockSubmit"
|
|
>
|
|
{{ submitting ? "Submitting..." : "Submit request" }}
|
|
</button>
|
|
<span class="hint mono">Requests are rate-limited.</span>
|
|
</div>
|
|
</form>
|
|
|
|
<div v-else class="success-box">
|
|
<div class="mono">Request submitted.</div>
|
|
<div class="muted">
|
|
Save this request code. Check your email for a verification link, then use the code to track status. Once approved,
|
|
your status will provide an onboarding link to finish account setup.
|
|
</div>
|
|
<div class="request-code-row">
|
|
<span class="label mono">Request Code</span>
|
|
<button class="copy mono" type="button" @click="copyRequestCode">
|
|
{{ requestCode }}
|
|
<span v-if="copied" class="copied">copied</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card module status-module">
|
|
<div class="module-head">
|
|
<h2>Check status</h2>
|
|
<span class="pill mono" :class="statusPillClass(status)">
|
|
{{ statusLabel(status) }}
|
|
</span>
|
|
</div>
|
|
|
|
<p class="muted">
|
|
Enter your request code to see whether it is awaiting approval, building accounts, awaiting onboarding, ready, or rejected.
|
|
</p>
|
|
|
|
<div class="status-form">
|
|
<input
|
|
v-model="statusForm.request_code"
|
|
class="input mono"
|
|
type="text"
|
|
placeholder="username~XXXXXXXXXX"
|
|
:disabled="checking"
|
|
/>
|
|
<button class="primary" type="button" @click="checkStatus" :disabled="checking || !statusForm.request_code.trim()">
|
|
{{ checking ? "Checking..." : "Check" }}
|
|
</button>
|
|
</div>
|
|
|
|
<div v-if="verifying" class="muted" style="margin-top: 10px;">
|
|
Verifying email…
|
|
</div>
|
|
|
|
<div v-if="verifyBanner" class="verify-box">
|
|
<div class="verify-title mono">{{ verifyBanner.title }}</div>
|
|
<div class="verify-body">{{ verifyBanner.body }}</div>
|
|
</div>
|
|
|
|
<div v-if="status === 'pending_email_verification'" class="actions" style="margin-top: 10px;">
|
|
<button class="pill mono" type="button" :disabled="resending" @click="resendVerification">
|
|
{{ resending ? "Resending..." : "Resend verification email" }}
|
|
</button>
|
|
<span v-if="resendMessage" class="hint mono">{{ resendMessage }}</span>
|
|
</div>
|
|
|
|
<div v-if="tasks.length" class="task-box">
|
|
<div class="module-head" style="margin-bottom: 10px;">
|
|
<h2>Automation</h2>
|
|
<span class="pill mono" :class="blocked ? 'pill-bad' : 'pill-ok'">
|
|
{{ blocked ? "blocked" : "running" }}
|
|
</span>
|
|
</div>
|
|
<ul class="task-list">
|
|
<li v-for="item in tasks" :key="item.task" class="task-row">
|
|
<span class="mono task-name">{{ item.task }}</span>
|
|
<span class="pill mono" :class="taskPillClass(item.status)">{{ item.status }}</span>
|
|
<span v-if="item.detail" class="mono task-detail">{{ item.detail }}</span>
|
|
</li>
|
|
</ul>
|
|
<p v-if="blocked" class="muted" style="margin-top: 10px;">
|
|
One or more automation steps failed. Fix the error above, then check again.
|
|
</p>
|
|
<div v-if="blocked" class="actions" style="margin-top: 10px;">
|
|
<button class="pill mono" type="button" :disabled="retrying" @click="retryProvisioning">
|
|
{{ retrying ? "Retrying..." : "Retry failed steps" }}
|
|
</button>
|
|
<span v-if="retryMessage" class="hint mono">{{ retryMessage }}</span>
|
|
</div>
|
|
<p v-if="blocked" class="muted" style="margin-top: 8px;">
|
|
If the error mentions rate limiting or a temporary outage, wait a few minutes and retry. If it keeps failing,
|
|
contact an admin.
|
|
</p>
|
|
</div>
|
|
|
|
<div
|
|
v-if="onboardingUrl && (status === 'awaiting_onboarding' || status === 'ready')"
|
|
class="actions onboarding-actions"
|
|
>
|
|
<div class="onboarding-copy">
|
|
<p class="muted" style="margin: 0;">
|
|
Your accounts are ready. Continue onboarding to finish setup.
|
|
</p>
|
|
</div>
|
|
<a class="primary onboarding-cta" :href="onboardingUrl">Continue onboarding</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="error" class="error-box">
|
|
<div class="mono">{{ error }}</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { useRoute } from "vue-router";
|
|
import { useRequestAccessFlow } from "../request-access/useRequestAccessFlow";
|
|
|
|
const {
|
|
statusLabel,
|
|
statusPillClass,
|
|
form,
|
|
submitting,
|
|
submitted,
|
|
error,
|
|
requestCode,
|
|
copied,
|
|
verifying,
|
|
mailDomain,
|
|
availability,
|
|
statusForm,
|
|
checking,
|
|
status,
|
|
onboardingUrl,
|
|
tasks,
|
|
blocked,
|
|
retrying,
|
|
retryMessage,
|
|
resending,
|
|
resendMessage,
|
|
verifyBanner,
|
|
taskPillClass,
|
|
submit,
|
|
copyRequestCode,
|
|
checkStatus,
|
|
retryProvisioning,
|
|
resendVerification,
|
|
} = useRequestAccessFlow(useRoute());
|
|
</script>
|
|
|
|
<style scoped src="../styles/request-access.css"></style>
|