portal: enrich onboarding copy

This commit is contained in:
Brad Stein 2026-01-23 23:01:44 -03:00
parent 4732334c44
commit 077736b598

View File

@ -149,7 +149,8 @@
<div class="section-header"> <div class="section-header">
<div> <div>
<h3>{{ activeSection.title }}</h3> <h3>{{ activeSection.title }}</h3>
<p class="muted">{{ activeSection.description }}</p> <p v-if="activeSection.summary" class="muted">{{ activeSection.summary }}</p>
<p v-if="activeSection.benefit" class="muted">{{ activeSection.benefit }}</p>
</div> </div>
</div> </div>
@ -176,6 +177,7 @@
</div> </div>
<p class="muted" v-if="step.description">{{ step.description }}</p> <p class="muted" v-if="step.description">{{ step.description }}</p>
<p class="muted step-note" v-if="stepNote(step)">{{ stepNote(step) }}</p>
<div <div
v-if="step.id === 'vaultwarden_master_password' && vaultwardenGrandfathered" v-if="step.id === 'vaultwarden_master_password' && vaultwardenGrandfathered"
@ -186,20 +188,16 @@
<span class="mono">{{ vaultwardenRecoveryEmail || "your recovery email" }}</span>. <span class="mono">{{ vaultwardenRecoveryEmail || "your recovery email" }}</span>.
This skips the invite flow and keeps your existing vault. This skips the invite flow and keeps your existing vault.
</p> </p>
<button <span class="tooltip-wrap" :title="vaultwardenClaimHint">
class="secondary" <button
type="button" class="secondary"
@click="claimVaultwarden" type="button"
:disabled=" @click="claimVaultwarden"
loading || :disabled="vaultwardenClaimDisabled"
vaultwardenClaiming || >
!auth.authenticated || {{ vaultwardenClaiming ? "Claiming..." : "Claim existing account" }}
isStepDone(step.id) || </button>
isStepBlocked(step.id) </span>
"
>
{{ vaultwardenClaiming ? "Claiming..." : "Claim existing account" }}
</button>
</div> </div>
<ul v-if="step.bullets && step.bullets.length" class="step-bullets"> <ul v-if="step.bullets && step.bullets.length" class="step-bullets">
@ -289,8 +287,8 @@
<div v-if="status === 'ready'" class="ready-box"> <div v-if="status === 'ready'" class="ready-box">
<h3>You're ready</h3> <h3>You're ready</h3>
<p class="muted"> <p class="muted">
Your Atlas account is provisioned and onboarding is complete. You can log in at Your Atlas account is provisioned and onboarding is complete. Visit the Apps page to access the full suite.
<a href="https://cloud.bstein.dev" target="_blank" rel="noreferrer">cloud.bstein.dev</a>. <a href="/apps">Atlas Apps</a>.
</p> </p>
</div> </div>
</div> </div>
@ -354,6 +352,27 @@ const passwordRevealHint = computed(() =>
); );
const vaultwardenGrandfathered = computed(() => Boolean(onboarding.value?.vaultwarden?.grandfathered)); const vaultwardenGrandfathered = computed(() => Boolean(onboarding.value?.vaultwarden?.grandfathered));
const vaultwardenRecoveryEmail = computed(() => onboarding.value?.vaultwarden?.recovery_email || ""); const vaultwardenRecoveryEmail = computed(() => onboarding.value?.vaultwarden?.recovery_email || "");
const vaultwardenLoginEmail = computed(() => {
if (vaultwardenGrandfathered.value) {
return vaultwardenRecoveryEmail.value || "your recovery email";
}
if (requestUsername.value) {
return `${requestUsername.value}@bstein.dev`;
}
return "your @bstein.dev address";
});
const mailAddress = computed(() => (requestUsername.value ? `${requestUsername.value}@bstein.dev` : "your @bstein.dev address"));
const vaultwardenClaimDisabled = computed(
() =>
loading.value ||
vaultwardenClaiming.value ||
!auth.authenticated ||
isStepDone("vaultwarden_master_password") ||
isStepBlocked("vaultwarden_master_password"),
);
const vaultwardenClaimHint = computed(() =>
!auth.authenticated ? "Log in to claim an existing Vaultwarden account." : "",
);
const STEP_PREREQS = { const STEP_PREREQS = {
vaultwarden_master_password: [], vaultwarden_master_password: [],
@ -381,7 +400,8 @@ const SECTION_DEFS = [
{ {
id: "vaultwarden", id: "vaultwarden",
title: "Vaultwarden", title: "Vaultwarden",
description: "Unlock your vault and install the tools that will store every other credential.", summary: "Self-hosted password manager for Atlas credentials.",
benefit: "Keeps every lab password encrypted and synced across devices.",
steps: [ steps: [
{ {
id: "vaultwarden_master_password", id: "vaultwarden_master_password",
@ -427,7 +447,8 @@ const SECTION_DEFS = [
{ {
id: "element", id: "element",
title: "Element", title: "Element",
description: "Secure chat, calls, and video for the lab.", summary: "Secure chat, calls, and video for the lab.",
benefit: "Private messaging with encryption and recovery controls you own.",
steps: [ steps: [
{ {
id: "keycloak_password_rotated", id: "keycloak_password_rotated",
@ -463,7 +484,8 @@ const SECTION_DEFS = [
{ {
id: "mail", id: "mail",
title: "Mail", title: "Mail",
description: "Add your @bstein.dev mailbox to your preferred mail apps.", summary: "Your @bstein.dev inbox for lab notifications and contact.",
benefit: "One address for every Atlas service and shared communication.",
steps: [ steps: [
{ {
id: "mail_client_setup", id: "mail_client_setup",
@ -479,7 +501,8 @@ const SECTION_DEFS = [
{ {
id: "nextcloud", id: "nextcloud",
title: "Nextcloud", title: "Nextcloud",
description: "Access files and confirm your Atlas mail integration inside Nextcloud.", summary: "File storage, calendar, and mail hub for the lab.",
benefit: "Central workspace for docs, sharing, and your mailbox.",
steps: [ steps: [
{ {
id: "nextcloud_web_access", id: "nextcloud_web_access",
@ -519,7 +542,8 @@ const SECTION_DEFS = [
{ {
id: "budget", id: "budget",
title: "Budget Encryption", title: "Budget Encryption",
description: "Encrypt financial data inside Actual Budget and store the key safely.", summary: "Actual Budget for private personal finance.",
benefit: "Encryption keeps your budget data safe and portable.",
steps: [ steps: [
{ {
id: "budget_encryption_ack", id: "budget_encryption_ack",
@ -542,7 +566,8 @@ const SECTION_DEFS = [
{ {
id: "firefly", id: "firefly",
title: "Firefly III", title: "Firefly III",
description: "Change your initial Firefly password and store it in Vaultwarden.", summary: "Personal finance tracker for transactions and reporting.",
benefit: "Detailed insights, budgets, and exports under your control.",
steps: [ steps: [
{ {
id: "firefly_password_rotated", id: "firefly_password_rotated",
@ -573,7 +598,8 @@ const SECTION_DEFS = [
{ {
id: "wger", id: "wger",
title: "Wger", title: "Wger",
description: "Change your initial Wger password and store it in Vaultwarden.", summary: "Fitness tracking for workouts and nutrition.",
benefit: "Keeps training plans and progress in one place.",
steps: [ steps: [
{ {
id: "wger_password_rotated", id: "wger_password_rotated",
@ -604,7 +630,8 @@ const SECTION_DEFS = [
{ {
id: "jellyfin", id: "jellyfin",
title: "Jellyfin", title: "Jellyfin",
description: "Optional media access across web, mobile, and TV clients.", summary: "Self-hosted media streaming for the lab.",
benefit: "Watch your media anywhere without third-party accounts.",
steps: [ steps: [
{ {
id: "jellyfin_web_access", id: "jellyfin_web_access",
@ -715,6 +742,19 @@ function isStepBlocked(stepId) {
return prereqs.some((req) => !isStepDone(req)); return prereqs.some((req) => !isStepDone(req));
} }
function stepNote(step) {
if (step.id === "vaultwarden_master_password") {
return `Vaultwarden uses an email login. Use ${vaultwardenLoginEmail.value} to sign in.`;
}
if (step.id === "firefly_password_rotated") {
return `Firefly uses an email login. Use ${mailAddress.value} to sign in.`;
}
if (step.id === "mail_client_setup") {
return `Your mailbox address is ${mailAddress.value}.`;
}
return "";
}
function stepPillLabel(step) { function stepPillLabel(step) {
if (isStepDone(step.id)) return "done"; if (isStepDone(step.id)) return "done";
if (isStepBlocked(step.id)) return "blocked"; if (isStepBlocked(step.id)) return "blocked";
@ -1462,6 +1502,10 @@ button.copy:disabled {
flex-direction: column; flex-direction: column;
} }
.step-note {
margin-top: 6px;
}
.step-card.blocked { .step-card.blocked {
opacity: 0.55; opacity: 0.55;
pointer-events: none; pointer-events: none;