Many an update!
This commit is contained in:
parent
85195d6c7a
commit
d588c49946
35 changed files with 3528 additions and 1142 deletions
|
|
@ -11,7 +11,7 @@
|
|||
<UContainer class="">
|
||||
<!-- Loading State -->
|
||||
<div
|
||||
v-if="!memberData || authPending"
|
||||
v-if="authPending"
|
||||
class="flex justify-center items-center py-20"
|
||||
>
|
||||
<div class="text-center">
|
||||
|
|
@ -22,8 +22,27 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Unauthenticated State -->
|
||||
<div
|
||||
v-else-if="!memberData"
|
||||
class="flex justify-center items-center py-20"
|
||||
>
|
||||
<div class="text-center max-w-md">
|
||||
<div class="w-16 h-16 bg-ghost-800 border border-ghost-600 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Icon name="heroicons:lock-closed" class="w-8 h-8 text-ghost-400" />
|
||||
</div>
|
||||
<h2 class="text-xl font-semibold text-ghost-100 mb-2">Sign in required</h2>
|
||||
<p class="text-ghost-400 mb-6">Please sign in to access your member dashboard.</p>
|
||||
<UButton @click="openLoginModal({ title: 'Sign in to your dashboard', description: 'Enter your email to access your member dashboard' })">
|
||||
Sign In
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dashboard Content -->
|
||||
<div v-else class="space-y-8">
|
||||
<!-- Member Status Banner -->
|
||||
<MemberStatusBanner :dismissible="true" />
|
||||
<!-- Welcome Card -->
|
||||
<UCard
|
||||
class="sparkle-field"
|
||||
|
|
@ -39,7 +58,16 @@
|
|||
<h1 class="text-2xl font-bold text-ghost-100 ethereal-text">
|
||||
Welcome to Ghost Guild, {{ memberData?.name }}!
|
||||
</h1>
|
||||
<p class="text-ghost-300 mt-2">Your membership is active!</p>
|
||||
<p
|
||||
:class="[
|
||||
'mt-2',
|
||||
isActive ? 'text-ghost-300' : statusConfig.textColor,
|
||||
]"
|
||||
>
|
||||
{{
|
||||
isActive ? "Your membership is active!" : statusConfig.label
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex-shrink-0" v-if="memberData?.avatar">
|
||||
<img
|
||||
|
|
@ -92,18 +120,29 @@
|
|||
<UButton
|
||||
to="/members?peerSupport=true"
|
||||
variant="outline"
|
||||
class="border-ghost-600 text-ghost-200 hover:bg-ghost-800 hover:border-whisper-500 justify-start"
|
||||
:disabled="!canPeerSupport"
|
||||
:class="[
|
||||
'border-ghost-600 text-ghost-200 justify-start',
|
||||
canPeerSupport
|
||||
? 'hover:bg-ghost-800 hover:border-whisper-500'
|
||||
: 'opacity-50 cursor-not-allowed',
|
||||
]"
|
||||
block
|
||||
:title="
|
||||
!canPeerSupport
|
||||
? 'Complete your membership to book peer sessions'
|
||||
: ''
|
||||
"
|
||||
>
|
||||
Book a Peer Session
|
||||
</UButton>
|
||||
|
||||
<UButton
|
||||
disabled
|
||||
to="https://wiki.ghostguild.org"
|
||||
target="_blank"
|
||||
variant="outline"
|
||||
class="border-ghost-600 text-ghost-500 cursor-not-allowed justify-start"
|
||||
class="border-ghost-600 text-ghost-200 hover:bg-ghost-800 hover:border-whisper-500 justify-start"
|
||||
block
|
||||
title="Coming soon"
|
||||
>
|
||||
Browse Resources
|
||||
</UButton>
|
||||
|
|
@ -309,6 +348,8 @@
|
|||
|
||||
<script setup>
|
||||
const { memberData, checkMemberStatus } = useAuth();
|
||||
const { isActive, statusConfig, isPendingPayment, canPeerSupport } = useMemberStatus();
|
||||
const { completePayment, isProcessingPayment } = useMemberPayment();
|
||||
|
||||
const registeredEvents = ref([]);
|
||||
const loadingEvents = ref(false);
|
||||
|
|
@ -340,6 +381,8 @@ const copyCalendarLink = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
const { openLoginModal } = useLoginModal();
|
||||
|
||||
// Handle authentication check on page load
|
||||
const { pending: authPending } = await useLazyAsyncData(
|
||||
"dashboard-auth",
|
||||
|
|
@ -347,52 +390,38 @@ const { pending: authPending } = await useLazyAsyncData(
|
|||
// Only check authentication on client side
|
||||
if (process.server) return null;
|
||||
|
||||
console.log(
|
||||
"📊 Dashboard auth check - memberData exists:",
|
||||
!!memberData.value,
|
||||
);
|
||||
|
||||
// If no member data, try to authenticate
|
||||
if (!memberData.value) {
|
||||
console.log(" - No member data, checking authentication...");
|
||||
const isAuthenticated = await checkMemberStatus();
|
||||
console.log(" - Auth result:", isAuthenticated);
|
||||
|
||||
if (!isAuthenticated) {
|
||||
console.log(" - Redirecting to login");
|
||||
await navigateTo("/login");
|
||||
// Show login modal instead of redirecting
|
||||
openLoginModal({
|
||||
title: "Sign in to your dashboard",
|
||||
description: "Enter your email to access your member dashboard",
|
||||
dismissible: true,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(" - ✅ Dashboard auth successful");
|
||||
return memberData.value;
|
||||
},
|
||||
);
|
||||
|
||||
// Load registered events
|
||||
const loadRegisteredEvents = async () => {
|
||||
console.log(
|
||||
"🔍 memberData.value:",
|
||||
JSON.stringify(memberData.value, null, 2),
|
||||
);
|
||||
console.log("🔍 memberData.value._id:", memberData.value?._id);
|
||||
console.log("🔍 memberData.value.id:", memberData.value?.id);
|
||||
|
||||
const memberId = memberData.value?._id || memberData.value?.id;
|
||||
|
||||
if (!memberId) {
|
||||
console.log("❌ No member ID available");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("📅 Loading events for member:", memberId);
|
||||
loadingEvents.value = true;
|
||||
try {
|
||||
const response = await $fetch("/api/members/my-events", {
|
||||
params: { memberId },
|
||||
});
|
||||
console.log("📅 Events response:", response);
|
||||
registeredEvents.value = response.events;
|
||||
} catch (error) {
|
||||
console.error("Failed to load registered events:", error);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<section class="py-12 px-4">
|
||||
<UContainer class="px-4">
|
||||
<!-- Stats -->
|
||||
<div v-if="!pending" class="mb-8 flex items-center justify-between">
|
||||
<div v-if="isAuthenticated && !pending" class="mb-8 flex items-center justify-between">
|
||||
<div class="text-ghost-300">
|
||||
<span class="text-2xl font-bold text-ghost-100">{{ total }}</span>
|
||||
{{ total === 1 ? "update" : "updates" }} posted
|
||||
|
|
@ -31,6 +31,23 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Unauthenticated State -->
|
||||
<div
|
||||
v-else-if="!isAuthenticated"
|
||||
class="flex justify-center items-center py-20"
|
||||
>
|
||||
<div class="text-center max-w-md">
|
||||
<div class="w-16 h-16 bg-ghost-800 border border-ghost-600 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Icon name="heroicons:lock-closed" class="w-8 h-8 text-ghost-400" />
|
||||
</div>
|
||||
<h2 class="text-xl font-semibold text-ghost-100 mb-2">Sign in required</h2>
|
||||
<p class="text-ghost-400 mb-6">Please sign in to view your updates.</p>
|
||||
<UButton @click="openLoginModal({ title: 'Sign in to view your updates', description: 'Enter your email to access your updates' })">
|
||||
Sign In
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Updates List -->
|
||||
<div v-else-if="updates.length" class="space-y-6">
|
||||
<UpdateCard
|
||||
|
|
@ -111,6 +128,7 @@
|
|||
|
||||
<script setup>
|
||||
const { isAuthenticated, checkMemberStatus } = useAuth();
|
||||
const { openLoginModal } = useLoginModal();
|
||||
|
||||
const updates = ref([]);
|
||||
const pending = ref(false);
|
||||
|
|
@ -127,7 +145,11 @@ onMounted(async () => {
|
|||
if (!isAuthenticated.value) {
|
||||
const authenticated = await checkMemberStatus();
|
||||
if (!authenticated) {
|
||||
await navigateTo("/login");
|
||||
// Show login modal instead of redirecting
|
||||
openLoginModal({
|
||||
title: "Sign in to view your updates",
|
||||
description: "Enter your email to access your updates",
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@
|
|||
|
||||
<section class="py-12">
|
||||
<UContainer>
|
||||
<!-- Loading State -->
|
||||
<div
|
||||
v-if="!memberData || loading"
|
||||
v-if="loading"
|
||||
class="flex justify-center items-center py-20"
|
||||
>
|
||||
<div class="text-center">
|
||||
|
|
@ -23,6 +24,23 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Unauthenticated State -->
|
||||
<div
|
||||
v-else-if="!memberData"
|
||||
class="flex justify-center items-center py-20"
|
||||
>
|
||||
<div class="text-center max-w-md">
|
||||
<div class="w-16 h-16 bg-ghost-800 border border-ghost-600 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Icon name="heroicons:lock-closed" class="w-8 h-8 text-ghost-400" />
|
||||
</div>
|
||||
<h2 class="text-xl font-semibold text-ghost-100 mb-2">Sign in required</h2>
|
||||
<p class="text-ghost-400 mb-6">Please sign in to access your profile settings.</p>
|
||||
<UButton @click="openLoginModal({ title: 'Sign in to your profile', description: 'Enter your email to manage your profile settings' })">
|
||||
Sign In
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<UTabs :items="tabItems" v-model="activeTab">
|
||||
<template #profile>
|
||||
|
|
@ -49,7 +67,6 @@
|
|||
<UInput
|
||||
v-model="formData.name"
|
||||
placeholder="Your name"
|
||||
disabled
|
||||
class="w-full"
|
||||
/>
|
||||
</UFormField>
|
||||
|
|
@ -551,6 +568,9 @@
|
|||
|
||||
<template #account>
|
||||
<div class="space-y-8 mt-8">
|
||||
<!-- Member Status Banner -->
|
||||
<MemberStatusBanner :dismissible="false" />
|
||||
|
||||
<!-- Current Membership -->
|
||||
<div>
|
||||
<h2
|
||||
|
|
@ -562,6 +582,42 @@
|
|||
<div
|
||||
class="backdrop-blur-sm bg-white/80 dark:bg-ghost-800/50 border border-gray-200 dark:border-ghost-700 rounded-lg p-6 space-y-4"
|
||||
>
|
||||
<!-- Status Badge -->
|
||||
<div
|
||||
class="flex items-center justify-between pb-4 border-b border-gray-200 dark:border-ghost-700"
|
||||
>
|
||||
<div>
|
||||
<p class="text-sm text-gray-600 dark:text-ghost-400">
|
||||
Membership Status
|
||||
</p>
|
||||
<div class="flex items-center gap-2 mt-1">
|
||||
<Icon
|
||||
:name="statusConfig.icon"
|
||||
:class="['w-5 h-5', statusConfig.textColor]"
|
||||
/>
|
||||
<p
|
||||
:class="[
|
||||
'text-lg font-medium',
|
||||
statusConfig.textColor,
|
||||
]"
|
||||
>
|
||||
{{ statusConfig.label }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
:class="[
|
||||
'px-4 py-2 rounded-full text-xs font-medium',
|
||||
statusConfig.bgColor,
|
||||
statusConfig.borderColor,
|
||||
'border',
|
||||
statusConfig.textColor,
|
||||
]"
|
||||
>
|
||||
{{ statusConfig.label }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-600 dark:text-ghost-400">
|
||||
|
|
@ -752,6 +808,8 @@
|
|||
|
||||
<script setup>
|
||||
const { memberData, checkMemberStatus } = useAuth();
|
||||
const { openLoginModal } = useLoginModal();
|
||||
const { statusConfig } = useMemberStatus();
|
||||
const route = useRoute();
|
||||
|
||||
// Initialize active tab from URL hash or default to 'profile'
|
||||
|
|
@ -1156,13 +1214,10 @@ const updateContributionLevel = async () => {
|
|||
if (!oldTierRequiresPayment && newTierRequiresPayment) {
|
||||
// Always show payment popup when upgrading from free to paid
|
||||
// Even if they have a customer ID, they might not have an active subscription
|
||||
console.log("Upgrading from free to paid - showing payment popup");
|
||||
await handlePaymentSetup();
|
||||
console.log("Payment setup completed successfully, returning early");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Calling update-contribution API");
|
||||
// Call API to update contribution
|
||||
await $fetch("/api/members/update-contribution", {
|
||||
method: "POST",
|
||||
|
|
@ -1193,7 +1248,6 @@ const updateContributionLevel = async () => {
|
|||
|
||||
if (requiresPayment) {
|
||||
// Show payment modal
|
||||
console.log("Showing payment modal - payment setup required");
|
||||
try {
|
||||
await handlePaymentSetup();
|
||||
// If successful, return early without showing error
|
||||
|
|
@ -1228,13 +1282,6 @@ const handlePaymentSetup = async () => {
|
|||
|
||||
customerId.value = customerResponse.customerId;
|
||||
customerCode.value = customerResponse.customerCode;
|
||||
|
||||
console.log(
|
||||
customerResponse.existing
|
||||
? "Using existing Helcim customer:"
|
||||
: "Created new Helcim customer:",
|
||||
customerId.value,
|
||||
);
|
||||
} else {
|
||||
// Get customer code from existing customer via server-side endpoint
|
||||
const customerResponse = await $fetch("/api/helcim/customer-code");
|
||||
|
|
@ -1247,7 +1294,6 @@ const handlePaymentSetup = async () => {
|
|||
|
||||
// Show payment modal
|
||||
const paymentResult = await verifyPayment();
|
||||
console.log("Payment result:", paymentResult);
|
||||
|
||||
if (!paymentResult.success) {
|
||||
throw new Error("Payment verification failed");
|
||||
|
|
@ -1328,7 +1374,11 @@ onMounted(async () => {
|
|||
loading.value = false;
|
||||
|
||||
if (!isAuthenticated) {
|
||||
await navigateTo("/login");
|
||||
// Show login modal instead of redirecting
|
||||
openLoginModal({
|
||||
title: "Sign in to your profile",
|
||||
description: "Enter your email to manage your profile settings",
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue