portal: improve onboarding login UX

This commit is contained in:
Brad Stein 2026-01-04 08:44:25 -03:00
parent 59830e19c8
commit 72dae3e7a2
3 changed files with 60 additions and 9 deletions

View File

@ -92,10 +92,17 @@ export async function initAuth() {
return initPromise;
}
export async function login(redirectPath = window.location.pathname + window.location.search + window.location.hash) {
export async function login(
redirectPath = window.location.pathname + window.location.search + window.location.hash,
loginHint = "",
) {
if (!keycloak) return;
const redirectUri = new URL(redirectPath, window.location.origin).toString();
await keycloak.login({ redirectUri });
const options = { redirectUri };
if (typeof loginHint === "string" && loginHint.trim()) {
options.loginHint = loginHint.trim();
}
await keycloak.login(options);
}
export async function logout() {

View File

@ -26,7 +26,10 @@
<div v-if="requestUsername" class="status-meta">
<div class="meta-row">
<span class="label mono">Username</span>
<span class="value mono">{{ requestUsername }}</span>
<button class="copy mono" type="button" @click="copyUsername">
{{ requestUsername }}
<span v-if="usernameCopied" class="copied">copied</span>
</button>
</div>
</div>
@ -87,7 +90,8 @@
<div v-if="initialPassword" class="initial-password">
<h3>Temporary password</h3>
<p class="muted">
Use this password to log in for the first time. Keycloak will prompt you to change it.
Use this password to log in for the first time. Keycloak will prompt you to change it. This password is shown
once copy it now.
</p>
<div class="request-code-row">
<span class="label mono">Password</span>
@ -212,6 +216,7 @@ const error = ref("");
const onboarding = ref({ required_steps: [], completed_steps: [] });
const initialPassword = ref("");
const copied = ref(false);
const usernameCopied = ref(false);
const tasks = ref([]);
const blocked = ref(false);
@ -304,8 +309,35 @@ async function copyInitialPassword() {
}
}
async function copyUsername() {
if (!requestUsername.value) return;
try {
if (navigator?.clipboard?.writeText) {
await navigator.clipboard.writeText(requestUsername.value);
} else {
const textarea = document.createElement("textarea");
textarea.value = requestUsername.value;
textarea.setAttribute("readonly", "");
textarea.style.position = "fixed";
textarea.style.top = "-9999px";
textarea.style.left = "-9999px";
document.body.appendChild(textarea);
textarea.select();
textarea.setSelectionRange(0, textarea.value.length);
document.execCommand("copy");
document.body.removeChild(textarea);
}
usernameCopied.value = true;
setTimeout(() => (usernameCopied.value = false), 1500);
} catch (err) {
error.value = err?.message || "Failed to copy username";
}
}
async function loginToContinue() {
await login(`/onboarding?code=${encodeURIComponent(requestCode.value.trim())}`);
const trimmedCode = requestCode.value.trim();
const hint = requestUsername.value.trim() || trimmedCode.split("~", 1)[0] || "";
await login(`/onboarding?code=${encodeURIComponent(trimmedCode)}`, hint);
}
async function toggleStep(step, event) {

View File

@ -112,10 +112,6 @@
Verifying email
</div>
<div v-if="onboardingUrl" class="actions" style="margin-top: 12px;">
<a class="primary" :href="onboardingUrl">Continue onboarding</a>
</div>
<div v-if="tasks.length" class="task-box">
<div class="module-head" style="margin-bottom: 10px;">
<h2>Automation</h2>
@ -134,6 +130,13 @@
One or more automation steps failed. Fix the error above, then check again.
</p>
</div>
<div
v-if="onboardingUrl && (status === 'awaiting_onboarding' || status === 'ready')"
class="actions onboarding-actions"
>
<a class="primary onboarding-cta" :href="onboardingUrl">Continue onboarding</a>
</div>
</div>
<div v-if="error" class="error-box">
@ -429,6 +432,15 @@ h1 {
margin-top: 6px;
}
.onboarding-actions {
margin-top: 14px;
}
.onboarding-cta {
flex: 1;
text-align: center;
}
.status-form {
display: flex;
gap: 10px;