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>
<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>
@ -176,6 +177,7 @@
</div>
<p class="muted" v-if="step.description">{{ step.description }}</p>
<p class="muted step-note" v-if="stepNote(step)">{{ stepNote(step) }}</p>
<div
v-if="step.id === 'vaultwarden_master_password' && vaultwardenGrandfathered"
@ -186,20 +188,16 @@
<span class="mono">{{ vaultwardenRecoveryEmail || "your recovery email" }}</span>.
This skips the invite flow and keeps your existing vault.
</p>
<button
class="secondary"
type="button"
@click="claimVaultwarden"
:disabled="
loading ||
vaultwardenClaiming ||
!auth.authenticated ||
isStepDone(step.id) ||
isStepBlocked(step.id)
"
>
{{ vaultwardenClaiming ? "Claiming..." : "Claim existing account" }}
</button>
<span class="tooltip-wrap" :title="vaultwardenClaimHint">
<button
class="secondary"
type="button"
@click="claimVaultwarden"
:disabled="vaultwardenClaimDisabled"
>
{{ vaultwardenClaiming ? "Claiming..." : "Claim existing account" }}
</button>
</span>
</div>
<ul v-if="step.bullets && step.bullets.length" class="step-bullets">
@ -289,8 +287,8 @@
<div v-if="status === 'ready'" class="ready-box">
<h3>You're ready</h3>
<p class="muted">
Your Atlas account is provisioned and onboarding is complete. You can log in at
<a href="https://cloud.bstein.dev" target="_blank" rel="noreferrer">cloud.bstein.dev</a>.
Your Atlas account is provisioned and onboarding is complete. Visit the Apps page to access the full suite.
<a href="/apps">Atlas Apps</a>.
</p>
</div>
</div>
@ -354,6 +352,27 @@ const passwordRevealHint = computed(() =>
);
const vaultwardenGrandfathered = computed(() => Boolean(onboarding.value?.vaultwarden?.grandfathered));
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 = {
vaultwarden_master_password: [],
@ -381,7 +400,8 @@ const SECTION_DEFS = [
{
id: "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: [
{
id: "vaultwarden_master_password",
@ -427,7 +447,8 @@ const SECTION_DEFS = [
{
id: "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: [
{
id: "keycloak_password_rotated",
@ -463,7 +484,8 @@ const SECTION_DEFS = [
{
id: "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: [
{
id: "mail_client_setup",
@ -479,7 +501,8 @@ const SECTION_DEFS = [
{
id: "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: [
{
id: "nextcloud_web_access",
@ -519,7 +542,8 @@ const SECTION_DEFS = [
{
id: "budget",
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: [
{
id: "budget_encryption_ack",
@ -542,7 +566,8 @@ const SECTION_DEFS = [
{
id: "firefly",
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: [
{
id: "firefly_password_rotated",
@ -573,7 +598,8 @@ const SECTION_DEFS = [
{
id: "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: [
{
id: "wger_password_rotated",
@ -604,7 +630,8 @@ const SECTION_DEFS = [
{
id: "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: [
{
id: "jellyfin_web_access",
@ -715,6 +742,19 @@ function isStepBlocked(stepId) {
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) {
if (isStepDone(step.id)) return "done";
if (isStepBlocked(step.id)) return "blocked";
@ -1462,6 +1502,10 @@ button.copy:disabled {
flex-direction: column;
}
.step-note {
margin-top: 6px;
}
.step-card.blocked {
opacity: 0.55;
pointer-events: none;