refactor(account): migrate member/account to PageShell vocabulary
- Replace .member-account-page + .account-authenticated flex chains with <PageShell> - Replace <SidebarLayout> with <ColumnsLayout cols=events-sidebar> - Replace .account-columns grid with nested <ColumnsLayout cols=2> + named slots - Replace all 5 .account-col-inset wrappers with <PageSection> components - Rename .account-section--danger → .danger-section - Delete ~75 lines of layout CSS (flex chains, grid, asymmetric paddings, collapse rules)
This commit is contained in:
parent
f267b35214
commit
37fceac3fd
1 changed files with 163 additions and 259 deletions
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="member-account-page">
|
||||
<PageShell>
|
||||
<!-- Unauthenticated -->
|
||||
<div v-if="!memberData" class="loading">
|
||||
<p>Please sign in to access your account settings.</p>
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<div v-else class="account-authenticated">
|
||||
<template v-else>
|
||||
<!-- PAGE HEADER -->
|
||||
<PageHeader
|
||||
title="Account Settings"
|
||||
|
|
@ -19,189 +19,179 @@
|
|||
/>
|
||||
|
||||
<!-- CONTENT AREA WITH EVENTS SIDEBAR -->
|
||||
<SidebarLayout>
|
||||
<div class="account-columns">
|
||||
<ColumnsLayout cols="events-sidebar">
|
||||
<ColumnsLayout cols="2">
|
||||
<!-- LEFT COLUMN: Membership Status & Email -->
|
||||
<div class="account-col-left">
|
||||
<section class="account-section">
|
||||
<div class="account-col-inset">
|
||||
<div class="section-label">Current Membership</div>
|
||||
<template #left>
|
||||
<PageSection>
|
||||
<div class="section-label">Current Membership</div>
|
||||
|
||||
<div class="membership-card">
|
||||
<div class="membership-row">
|
||||
<span class="membership-k">Status</span>
|
||||
<span class="membership-v status-v">
|
||||
<span
|
||||
class="status-dot"
|
||||
:class="memberData.status || 'active'"
|
||||
></span>
|
||||
<span>{{
|
||||
formatStatus(memberData.status || "active")
|
||||
}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="membership-row">
|
||||
<span class="membership-k">Circle</span>
|
||||
<div class="membership-card">
|
||||
<div class="membership-row">
|
||||
<span class="membership-k">Status</span>
|
||||
<span class="membership-v status-v">
|
||||
<span
|
||||
class="membership-v"
|
||||
:style="{
|
||||
color: `var(--c-${memberData.circle || 'community'})`,
|
||||
}"
|
||||
>
|
||||
{{
|
||||
memberData.circle
|
||||
? capitalise(memberData.circle)
|
||||
: "Community"
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="membership-row">
|
||||
<span class="membership-k">Contribution</span>
|
||||
<span class="membership-v"
|
||||
>${{ memberData.contributionTier || 0 }} / month</span
|
||||
>
|
||||
</div>
|
||||
<div class="membership-row">
|
||||
<span class="membership-k">Member since</span>
|
||||
<span class="membership-v">{{
|
||||
formatMemberSince(memberData.createdAt)
|
||||
class="status-dot"
|
||||
:class="memberData.status || 'active'"
|
||||
></span>
|
||||
<span>{{
|
||||
formatStatus(memberData.status || "active")
|
||||
}}</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="account-section">
|
||||
<div class="account-col-inset">
|
||||
<div class="section-label">Email</div>
|
||||
|
||||
<div v-if="!showEmailEdit" class="email-display">
|
||||
<span class="email-value">{{ memberData.email }}</span>
|
||||
<button class="btn btn-inline" @click="showEmailEdit = true">
|
||||
Change
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-else class="email-edit">
|
||||
<div class="field">
|
||||
<label>New email address</label>
|
||||
<input
|
||||
type="email"
|
||||
v-model="newEmail"
|
||||
placeholder="you@example.com"
|
||||
@keydown.enter="handleUpdateEmail"
|
||||
@keydown.escape="cancelEmailEdit"
|
||||
autofocus
|
||||
/>
|
||||
</div>
|
||||
<div class="email-edit-actions">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="handleUpdateEmail"
|
||||
:disabled="isUpdatingEmail || !newEmail.trim()"
|
||||
>
|
||||
{{ isUpdatingEmail ? "Saving…" : "Save" }}
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
@click="cancelEmailEdit"
|
||||
:disabled="isUpdatingEmail"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="email-hint">
|
||||
Used for login magic links and notifications
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="account-section account-section--danger">
|
||||
<div class="account-col-inset">
|
||||
<div class="section-label danger">Danger Zone</div>
|
||||
<div class="danger-zone">
|
||||
<p>
|
||||
Cancelling your membership will immediately revoke access to
|
||||
member-only resources, events, and the Slack workspace.
|
||||
<strong>This action cannot be easily undone.</strong>
|
||||
</p>
|
||||
<div v-if="showCancelConfirm" class="cancel-confirm">
|
||||
<p class="cancel-confirm-prompt">
|
||||
Are you sure? This cannot be easily undone.
|
||||
</p>
|
||||
<div class="cancel-confirm-actions">
|
||||
<button
|
||||
class="btn btn-danger"
|
||||
@click="confirmCancelMembership"
|
||||
:disabled="isCancelling"
|
||||
>
|
||||
{{ isCancelling ? "Cancelling…" : "Yes, Cancel" }}
|
||||
</button>
|
||||
<button class="btn" @click="showCancelConfirm = false">
|
||||
Nevermind
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-danger"
|
||||
@click="handleCancelMembership"
|
||||
:disabled="isCancelling"
|
||||
<div class="membership-row">
|
||||
<span class="membership-k">Circle</span>
|
||||
<span
|
||||
class="membership-v"
|
||||
:style="{
|
||||
color: `var(--c-${memberData.circle || 'community'})`,
|
||||
}"
|
||||
>
|
||||
Cancel Membership
|
||||
{{
|
||||
memberData.circle
|
||||
? capitalise(memberData.circle)
|
||||
: "Community"
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="membership-row">
|
||||
<span class="membership-k">Contribution</span>
|
||||
<span class="membership-v"
|
||||
>${{ memberData.contributionTier || 0 }} / month</span
|
||||
>
|
||||
</div>
|
||||
<div class="membership-row">
|
||||
<span class="membership-k">Member since</span>
|
||||
<span class="membership-v">{{
|
||||
formatMemberSince(memberData.createdAt)
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</PageSection>
|
||||
|
||||
<PageSection divider="top">
|
||||
<div class="section-label">Email</div>
|
||||
|
||||
<div v-if="!showEmailEdit" class="email-display">
|
||||
<span class="email-value">{{ memberData.email }}</span>
|
||||
<button class="btn btn-inline" @click="showEmailEdit = true">
|
||||
Change
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-else class="email-edit">
|
||||
<div class="field">
|
||||
<label>New email address</label>
|
||||
<input
|
||||
type="email"
|
||||
v-model="newEmail"
|
||||
placeholder="you@example.com"
|
||||
@keydown.enter="handleUpdateEmail"
|
||||
@keydown.escape="cancelEmailEdit"
|
||||
autofocus
|
||||
/>
|
||||
</div>
|
||||
<div class="email-edit-actions">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="handleUpdateEmail"
|
||||
:disabled="isUpdatingEmail || !newEmail.trim()"
|
||||
>
|
||||
{{ isUpdatingEmail ? "Saving…" : "Save" }}
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
@click="cancelEmailEdit"
|
||||
:disabled="isUpdatingEmail"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="email-hint">
|
||||
Used for login magic links and notifications
|
||||
</div>
|
||||
</PageSection>
|
||||
|
||||
<PageSection divider="top" class="danger-section">
|
||||
<div class="section-label danger">Danger Zone</div>
|
||||
<div class="danger-zone">
|
||||
<p>
|
||||
Cancelling your membership will immediately revoke access to
|
||||
member-only resources, events, and the Slack workspace.
|
||||
<strong>This action cannot be easily undone.</strong>
|
||||
</p>
|
||||
<div v-if="showCancelConfirm" class="cancel-confirm">
|
||||
<p class="cancel-confirm-prompt">
|
||||
Are you sure? This cannot be easily undone.
|
||||
</p>
|
||||
<div class="cancel-confirm-actions">
|
||||
<button
|
||||
class="btn btn-danger"
|
||||
@click="confirmCancelMembership"
|
||||
:disabled="isCancelling"
|
||||
>
|
||||
{{ isCancelling ? "Cancelling…" : "Yes, Cancel" }}
|
||||
</button>
|
||||
<button class="btn" @click="showCancelConfirm = false">
|
||||
Nevermind
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-danger"
|
||||
@click="handleCancelMembership"
|
||||
:disabled="isCancelling"
|
||||
>
|
||||
Cancel Membership
|
||||
</button>
|
||||
</div>
|
||||
</PageSection>
|
||||
</template>
|
||||
|
||||
<!-- RIGHT COLUMN: Change Contribution & Circle -->
|
||||
<div class="account-col-right">
|
||||
<section class="account-section">
|
||||
<div class="account-col-inset">
|
||||
<div class="section-label">Change Contribution</div>
|
||||
<template #right>
|
||||
<PageSection>
|
||||
<div class="section-label">Change Contribution</div>
|
||||
|
||||
<TierPicker v-model="selectedTier" :tiers="tiers" />
|
||||
<div class="tier-hint">
|
||||
Changes take effect on your next billing cycle
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary btn-section"
|
||||
@click="handleUpdateTier"
|
||||
:disabled="
|
||||
selectedTier === Number(memberData.contributionTier || 0) ||
|
||||
isUpdating
|
||||
"
|
||||
>
|
||||
{{ isUpdating ? "Updating…" : "Update Contribution" }}
|
||||
</button>
|
||||
<TierPicker v-model="selectedTier" :tiers="tiers" />
|
||||
<div class="tier-hint">
|
||||
Changes take effect on your next billing cycle
|
||||
</div>
|
||||
</section>
|
||||
<button
|
||||
class="btn btn-primary btn-section"
|
||||
@click="handleUpdateTier"
|
||||
:disabled="
|
||||
selectedTier === Number(memberData.contributionTier || 0) ||
|
||||
isUpdating
|
||||
"
|
||||
>
|
||||
{{ isUpdating ? "Updating…" : "Update Contribution" }}
|
||||
</button>
|
||||
</PageSection>
|
||||
|
||||
<section class="account-section">
|
||||
<div class="account-col-inset">
|
||||
<div class="section-label">Change Circle</div>
|
||||
<PageSection divider="top">
|
||||
<div class="section-label">Change Circle</div>
|
||||
|
||||
<CirclePicker
|
||||
v-model="selectedCircle"
|
||||
:circles="circleOptions"
|
||||
/>
|
||||
<button
|
||||
class="btn btn-primary btn-section"
|
||||
@click="handleUpdateCircle"
|
||||
:disabled="selectedCircle === memberData.circle || isUpdating"
|
||||
>
|
||||
{{ isUpdating ? "Updating…" : "Update Circle" }}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</SidebarLayout>
|
||||
</div>
|
||||
</div>
|
||||
<CirclePicker
|
||||
v-model="selectedCircle"
|
||||
:circles="circleOptions"
|
||||
/>
|
||||
<button
|
||||
class="btn btn-primary btn-section"
|
||||
@click="handleUpdateCircle"
|
||||
:disabled="selectedCircle === memberData.circle || isUpdating"
|
||||
>
|
||||
{{ isUpdating ? "Updating…" : "Update Circle" }}
|
||||
</button>
|
||||
</PageSection>
|
||||
</template>
|
||||
</ColumnsLayout>
|
||||
</ColumnsLayout>
|
||||
</template>
|
||||
</PageShell>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
|
@ -385,82 +375,11 @@ const confirmCancelMembership = async () => {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
.member-account-page {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.loading {
|
||||
flex: 1;
|
||||
padding: 48px 32px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.account-authenticated {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* ---- TWO-COLUMN LAYOUT ---- */
|
||||
.account-columns {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
align-items: stretch;
|
||||
min-height: 0;
|
||||
}
|
||||
.account-col-left,
|
||||
.account-col-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
align-self: stretch;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
.account-col-left {
|
||||
border-right: 1px dashed var(--border);
|
||||
}
|
||||
|
||||
/* Full-column rules: border on block-level section */
|
||||
.account-section {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
.account-section + .account-section {
|
||||
margin-top: 24px;
|
||||
border-top: 1px dashed var(--border);
|
||||
padding-top: 20px;
|
||||
}
|
||||
.account-section + .account-section.account-section--danger {
|
||||
margin-top: 24px;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.account-col-left > .account-section:first-child .account-col-inset,
|
||||
.account-col-right > .account-section:first-child .account-col-inset {
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.account-col-left > .account-section:last-child .account-col-inset,
|
||||
.account-col-right > .account-section:last-child .account-col-inset {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.account-col-left .account-col-inset {
|
||||
padding-left: 28px;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.account-col-right .account-col-inset {
|
||||
padding-left: 24px;
|
||||
padding-right: 28px;
|
||||
}
|
||||
|
||||
/* ---- MEMBERSHIP CARD ---- */
|
||||
.membership-card {
|
||||
border: 1px dashed var(--border);
|
||||
|
|
@ -567,14 +486,14 @@ const confirmCancelMembership = async () => {
|
|||
}
|
||||
|
||||
/* ---- DANGER ZONE ---- */
|
||||
.account-section--danger {
|
||||
.danger-section {
|
||||
background: var(--ember-bg);
|
||||
}
|
||||
|
||||
.account-section--danger .section-label.danger {
|
||||
.danger-section .section-label.danger {
|
||||
color: var(--ember);
|
||||
}
|
||||
.account-section--danger .danger-zone p {
|
||||
.danger-section .danger-zone p {
|
||||
color: var(--text-dim);
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
|
|
@ -610,19 +529,4 @@ const confirmCancelMembership = async () => {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
/* ---- RESPONSIVE ---- */
|
||||
@media (max-width: 1024px) {
|
||||
.account-columns {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.account-col-left {
|
||||
border-right: none;
|
||||
border-bottom: 1px dashed var(--border);
|
||||
}
|
||||
.account-col-left .account-col-inset,
|
||||
.account-col-right .account-col-inset {
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue