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>
|
<template>
|
||||||
<div class="member-account-page">
|
<PageShell>
|
||||||
<!-- Unauthenticated -->
|
<!-- Unauthenticated -->
|
||||||
<div v-if="!memberData" class="loading">
|
<div v-if="!memberData" class="loading">
|
||||||
<p>Please sign in to access your account settings.</p>
|
<p>Please sign in to access your account settings.</p>
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="account-authenticated">
|
<template v-else>
|
||||||
<!-- PAGE HEADER -->
|
<!-- PAGE HEADER -->
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Account Settings"
|
title="Account Settings"
|
||||||
|
|
@ -19,189 +19,179 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- CONTENT AREA WITH EVENTS SIDEBAR -->
|
<!-- CONTENT AREA WITH EVENTS SIDEBAR -->
|
||||||
<SidebarLayout>
|
<ColumnsLayout cols="events-sidebar">
|
||||||
<div class="account-columns">
|
<ColumnsLayout cols="2">
|
||||||
<!-- LEFT COLUMN: Membership Status & Email -->
|
<!-- LEFT COLUMN: Membership Status & Email -->
|
||||||
<div class="account-col-left">
|
<template #left>
|
||||||
<section class="account-section">
|
<PageSection>
|
||||||
<div class="account-col-inset">
|
<div class="section-label">Current Membership</div>
|
||||||
<div class="section-label">Current Membership</div>
|
|
||||||
|
|
||||||
<div class="membership-card">
|
<div class="membership-card">
|
||||||
<div class="membership-row">
|
<div class="membership-row">
|
||||||
<span class="membership-k">Status</span>
|
<span class="membership-k">Status</span>
|
||||||
<span class="membership-v status-v">
|
<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>
|
|
||||||
<span
|
<span
|
||||||
class="membership-v"
|
class="status-dot"
|
||||||
:style="{
|
:class="memberData.status || 'active'"
|
||||||
color: `var(--c-${memberData.circle || 'community'})`,
|
></span>
|
||||||
}"
|
<span>{{
|
||||||
>
|
formatStatus(memberData.status || "active")
|
||||||
{{
|
|
||||||
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>
|
}}</span>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="membership-row">
|
||||||
</section>
|
<span class="membership-k">Circle</span>
|
||||||
|
<span
|
||||||
<section class="account-section">
|
class="membership-v"
|
||||||
<div class="account-col-inset">
|
:style="{
|
||||||
<div class="section-label">Email</div>
|
color: `var(--c-${memberData.circle || 'community'})`,
|
||||||
|
}"
|
||||||
<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"
|
|
||||||
>
|
>
|
||||||
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</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 -->
|
<!-- RIGHT COLUMN: Change Contribution & Circle -->
|
||||||
<div class="account-col-right">
|
<template #right>
|
||||||
<section class="account-section">
|
<PageSection>
|
||||||
<div class="account-col-inset">
|
<div class="section-label">Change Contribution</div>
|
||||||
<div class="section-label">Change Contribution</div>
|
|
||||||
|
|
||||||
<TierPicker v-model="selectedTier" :tiers="tiers" />
|
<TierPicker v-model="selectedTier" :tiers="tiers" />
|
||||||
<div class="tier-hint">
|
<div class="tier-hint">
|
||||||
Changes take effect on your next billing cycle
|
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>
|
|
||||||
</div>
|
</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">
|
<PageSection divider="top">
|
||||||
<div class="account-col-inset">
|
<div class="section-label">Change Circle</div>
|
||||||
<div class="section-label">Change Circle</div>
|
|
||||||
|
|
||||||
<CirclePicker
|
<CirclePicker
|
||||||
v-model="selectedCircle"
|
v-model="selectedCircle"
|
||||||
:circles="circleOptions"
|
:circles="circleOptions"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary btn-section"
|
class="btn btn-primary btn-section"
|
||||||
@click="handleUpdateCircle"
|
@click="handleUpdateCircle"
|
||||||
:disabled="selectedCircle === memberData.circle || isUpdating"
|
:disabled="selectedCircle === memberData.circle || isUpdating"
|
||||||
>
|
>
|
||||||
{{ isUpdating ? "Updating…" : "Update Circle" }}
|
{{ isUpdating ? "Updating…" : "Update Circle" }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</PageSection>
|
||||||
</section>
|
</template>
|
||||||
</div>
|
</ColumnsLayout>
|
||||||
</div>
|
</ColumnsLayout>
|
||||||
</SidebarLayout>
|
</template>
|
||||||
</div>
|
</PageShell>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
@ -385,82 +375,11 @@ const confirmCancelMembership = async () => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.member-account-page {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
flex: 1;
|
|
||||||
padding: 48px 32px;
|
padding: 48px 32px;
|
||||||
color: var(--text-dim);
|
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 ---- */
|
||||||
.membership-card {
|
.membership-card {
|
||||||
border: 1px dashed var(--border);
|
border: 1px dashed var(--border);
|
||||||
|
|
@ -567,14 +486,14 @@ const confirmCancelMembership = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- DANGER ZONE ---- */
|
/* ---- DANGER ZONE ---- */
|
||||||
.account-section--danger {
|
.danger-section {
|
||||||
background: var(--ember-bg);
|
background: var(--ember-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.account-section--danger .section-label.danger {
|
.danger-section .section-label.danger {
|
||||||
color: var(--ember);
|
color: var(--ember);
|
||||||
}
|
}
|
||||||
.account-section--danger .danger-zone p {
|
.danger-section .danger-zone p {
|
||||||
color: var(--text-dim);
|
color: var(--text-dim);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
|
|
@ -610,19 +529,4 @@ const confirmCancelMembership = async () => {
|
||||||
text-align: center;
|
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>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue