Tests, UX improvements.
This commit is contained in:
parent
4e6f5d36b8
commit
0ae18f495e
63 changed files with 1384 additions and 2330 deletions
|
|
@ -19,197 +19,187 @@
|
|||
/>
|
||||
|
||||
<!-- CONTENT AREA WITH EVENTS SIDEBAR -->
|
||||
<div class="content-area">
|
||||
<div class="page-content">
|
||||
<div class="account-columns">
|
||||
<!-- 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>
|
||||
<SidebarLayout>
|
||||
<div class="account-columns">
|
||||
<!-- 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>
|
||||
|
||||
<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 class="membership-row">
|
||||
<span class="membership-k">Circle</span>
|
||||
<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)
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="account-section">
|
||||
<div class="account-col-inset">
|
||||
<div class="section-label">Email</div>
|
||||
<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>
|
||||
<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-inline"
|
||||
@click="showEmailEdit = true"
|
||||
class="btn btn-primary"
|
||||
@click="handleUpdateEmail"
|
||||
:disabled="isUpdatingEmail || !newEmail.trim()"
|
||||
>
|
||||
Change
|
||||
{{ isUpdatingEmail ? "Saving…" : "Save" }}
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
@click="cancelEmailEdit"
|
||||
:disabled="isUpdatingEmail"
|
||||
>
|
||||
Cancel
|
||||
</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>
|
||||
<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 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 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>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-danger"
|
||||
@click="handleCancelMembership"
|
||||
:disabled="isCancelling"
|
||||
>
|
||||
Cancel Membership
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<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
|
||||
"
|
||||
v-else
|
||||
class="btn btn-danger"
|
||||
@click="handleCancelMembership"
|
||||
:disabled="isCancelling"
|
||||
>
|
||||
{{ isUpdating ? "Updating…" : "Update Contribution" }}
|
||||
Cancel Membership
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="account-section">
|
||||
<div class="account-col-inset">
|
||||
<div class="section-label">Change Circle</div>
|
||||
<!-- 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>
|
||||
|
||||
<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>
|
||||
<TierPicker v-model="selectedTier" :tiers="tiers" />
|
||||
<div class="tier-hint">
|
||||
Changes take effect on your next billing cycle
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary btn-section"
|
||||
@click="handleUpdateTier"
|
||||
:disabled="
|
||||
selectedTier === Number(memberData.contributionTier || 0) ||
|
||||
isUpdating
|
||||
"
|
||||
>
|
||||
{{ isUpdating ? "Updating…" : "Update Contribution" }}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="account-section">
|
||||
<div class="account-col-inset">
|
||||
<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>
|
||||
|
||||
<!-- EVENTS MINI SIDEBAR -->
|
||||
<EventsMiniSidebar :events="upcomingEvents" />
|
||||
</div>
|
||||
</SidebarLayout>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -281,11 +271,6 @@ watchEffect(() => {
|
|||
}
|
||||
});
|
||||
|
||||
const { data: upcomingEvents } = await useFetch("/api/events", {
|
||||
query: { limit: 3, upcoming: true },
|
||||
default: () => [],
|
||||
});
|
||||
|
||||
const formatMemberSince = (dateStr) => {
|
||||
if (!dateStr) return "";
|
||||
return new Date(dateStr).toLocaleDateString("en-US", {
|
||||
|
|
@ -420,23 +405,6 @@ const confirmCancelMembership = async () => {
|
|||
min-height: 0;
|
||||
}
|
||||
|
||||
/* ---- CONTENT AREA ---- */
|
||||
.content-area {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 200px;
|
||||
align-items: stretch;
|
||||
min-height: 0;
|
||||
}
|
||||
.page-content {
|
||||
min-width: 0;
|
||||
align-self: stretch;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* ---- TWO-COLUMN LAYOUT ---- */
|
||||
.account-columns {
|
||||
flex: 1;
|
||||
|
|
@ -644,9 +612,6 @@ const confirmCancelMembership = async () => {
|
|||
|
||||
/* ---- RESPONSIVE ---- */
|
||||
@media (max-width: 1024px) {
|
||||
.content-area {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.account-columns {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue