Add notification step before member card

This commit is contained in:
Dorian
2026-05-15 12:22:14 -05:00
parent 90297036e2
commit 60af9d2752

View File

@@ -191,6 +191,9 @@ const isEventEditorOpen = ref(false)
const notificationStats = ref({ configured: false, subscriberCount: 0 })
const notificationMessage = ref('')
const notificationError = ref('')
const signupNotificationMessage = ref('')
const signupNotificationError = ref('')
const isSignupNotificationLoading = ref(false)
const payments = ref([])
const cards = ref([])
const accessLogs = ref([])
@@ -361,6 +364,7 @@ const canContinue = computed(() => {
if (signupStep.value === 4) return DATA_IMAGE_PATTERN.test(form.signature)
return true
})
const signupNotificationSupport = computed(() => notificationSupport())
const pwaInstallTitle = computed(() => {
if (isPwaStandalone.value) return 'PWA installed'
if (installPlatform.value === 'ios') return 'Install L484 on iPhone'
@@ -772,11 +776,14 @@ const openSignup = () => {
loadBitcoinPrice()
refreshPwaStandalone()
installPlatform.value = detectInstallPlatform()
signupStep.value = currentMember.value ? 5 : 0
signupStep.value = currentMember.value ? 6 : 0
createdMember.value = currentMember.value
isCardRevealing.value = false
generatedCredentials.value = null
formError.value = ''
signupNotificationMessage.value = ''
signupNotificationError.value = ''
isSignupNotificationLoading.value = false
}
const openMemberSignin = () => {
@@ -839,6 +846,7 @@ const resetForm = () => {
const nextStep = () => {
formError.value = ''
signupNotificationError.value = ''
sanitizeForm()
if (signupStep.value === 2 && !validateApplicant()) {
@@ -960,6 +968,7 @@ const handleSignerCompletion = async () => {
}
const createMembership = async () => {
formError.value = ''
sanitizeForm()
if (!validateApplicant()) {
return
@@ -1011,7 +1020,7 @@ const createMembership = async () => {
saveMembers()
resetForm()
isCardRevealing.value = true
signupStep.value = 5
signupStep.value = 6
window.setTimeout(() => {
isCardRevealing.value = false
@@ -1021,6 +1030,21 @@ const createMembership = async () => {
}
}
const enableSignupNotificationsAndCreate = async () => {
signupNotificationMessage.value = ''
signupNotificationError.value = ''
isSignupNotificationLoading.value = true
try {
const result = await subscribeToNotifications()
signupNotificationMessage.value = `Notifications enabled. ${result.subscriberCount || 1} device subscribed.`
await createMembership()
} catch (error) {
signupNotificationError.value = error instanceof Error ? error.message : 'Could not enable notifications.'
} finally {
isSignupNotificationLoading.value = false
}
}
const syncSignatureCanvas = () => {
const canvas = signatureCanvas.value
if (!canvas) return
@@ -3229,23 +3253,25 @@ watch(mobileMenuOpen, (open) => {
</div>
<div v-if="isSignupOpen" class="modal-backdrop" @click.self="closeSignup">
<div class="signup-modal" :class="{ 'card-modal': signupStep === 5 }">
<div class="signup-modal" :class="{ 'card-modal': signupStep === 6 }">
<div class="flex items-center justify-between gap-4 border-b border-white/10 p-5">
<div>
<p class="section-kicker">L484 Membership</p>
<h2 class="text-2xl font-black uppercase leading-none">
{{ signupStep === 5 ? 'Membership Card' : 'Become a member' }}
{{ signupStep === 6 ? 'Membership Card' : 'Become a member' }}
</h2>
</div>
<button class="modal-close" type="button" aria-label="Close" @click="closeSignup"></button>
</div>
<div v-if="signupStep > 1 && signupStep < 5" class="step-row">
<div v-if="signupStep > 1 && signupStep < 6" class="step-row">
<span :class="{ active: signupStep >= 2 }">1</span>
<i></i>
<span :class="{ active: signupStep >= 3 }">2</span>
<i></i>
<span :class="{ active: signupStep >= 4 }">3</span>
<i></i>
<span :class="{ active: signupStep >= 5 }">4</span>
</div>
<div class="modal-body">
@@ -3380,7 +3406,40 @@ watch(mobileMenuOpen, (open) => {
</div>
</div>
<div v-if="signupStep === 5 && createdMember" class="card-modal-content space-y-6">
<div v-if="signupStep === 5" class="pwa-install-step">
<div class="pwa-install-hero">
<img src="/images/app-icon-192.png" alt="" aria-hidden="true" />
<div>
<p class="section-kicker">Notifications</p>
<h3>Enable member alerts</h3>
<p>
L484 can send updates about your membership, payment, card pickup, and private events directly to this installed app.
</p>
</div>
</div>
<div class="signin-options">
<div class="pwa-install-card active">
<strong>Recommended</strong>
<small>Enable notifications before creating your card so this device can receive L484 updates.</small>
</div>
<div class="pwa-install-card">
<strong>Status</strong>
<small>
Permission {{ notificationPermission }} ·
{{ signupNotificationSupport.supported ? 'Push supported' : 'Push unavailable' }} ·
{{ signupNotificationSupport.secure ? 'Secure context' : 'HTTPS required' }}
</small>
</div>
</div>
<p v-if="signupNotificationMessage" class="validation-message rounded border border-amber-400/30 bg-amber-400/10 p-3 text-sm text-amber-100">
{{ signupNotificationMessage }}
</p>
<p v-if="signupNotificationError" class="validation-message rounded border border-red-400/40 bg-red-500/10 p-3 text-sm text-red-200">
{{ signupNotificationError }}
</p>
</div>
<div v-if="signupStep === 6 && createdMember" class="card-modal-content space-y-6">
<div class="card-reveal-stage mx-auto" :class="{ 'is-revealing': isCardRevealing }">
<div v-if="isCardRevealing" class="card-spinner" aria-hidden="true">
<img src="/images/small-logo.svg" alt="" />
@@ -3446,11 +3505,11 @@ watch(mobileMenuOpen, (open) => {
</div>
<div class="modal-footer border-t border-white/10 p-5">
<button v-if="signupStep > 0 && signupStep < 5" class="secondary-action" type="button" @click="previousStep">
<button v-if="signupStep > 0 && signupStep < 6" class="secondary-action" type="button" @click="previousStep">
Back
</button>
<template v-if="signupStep === 5">
<template v-if="signupStep === 6">
<button class="secondary-action" type="button" @click="openBackup">
Export
</button>
@@ -3464,12 +3523,17 @@ watch(mobileMenuOpen, (open) => {
<button v-else-if="signupStep === 1" class="primary-action" type="button" @click="handlePwaInstallPrimary">
{{ pwaInstallPrimaryLabel }}
</button>
<button v-else-if="signupStep < 4" class="primary-action" type="button" @click="nextStep">
<button v-else-if="signupStep < 5" class="primary-action" type="button" @click="nextStep">
Continue
</button>
<button v-else-if="signupStep === 4" class="primary-action" type="button" @click="createMembership">
Create card
</button>
<template v-else-if="signupStep === 5">
<button class="secondary-action" type="button" :disabled="isSignupNotificationLoading" @click="createMembership">
Continue without alerts
</button>
<button class="primary-action" type="button" :disabled="isSignupNotificationLoading" @click="enableSignupNotificationsAndCreate">
{{ isSignupNotificationLoading ? 'Enabling...' : 'Enable & create card' }}
</button>
</template>
</div>
</div>
</div>