Add pricing-page waitlist (name + email signup)
Replaces the disabled "Coming soon" buttons on the pricing page with a waitlist band so visitors can be notified when alerts launch — separate from registered users. - waitlist_subscribers table (name, email unique, source, referrer) - WaitlistService::subscribe — normalises email, idempotent - Public POST /api/waitlist (throttle:10,1), thin controller + form request - Read-only Filament resource with streamed CSV export - Vue: useWaitlist composable + WaitlistForm, rendered below the grid while any tier is still "coming soon"; sends source + document.referrer Announcement send mechanism deferred to a later task. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -80,13 +80,16 @@
|
||||
<button v-else type="button" disabled class="w-full py-3 px-4 bg-zinc-100 rounded-xl text-center font-bold text-zinc-400 cursor-not-allowed">Coming soon</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WaitlistForm v-if="hasComingSoon" source="pricing" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useAuth } from '../composables/useAuth.js'
|
||||
import WaitlistForm from './WaitlistForm.vue'
|
||||
|
||||
const { isAuthenticated, userTier } = useAuth()
|
||||
|
||||
@@ -102,6 +105,8 @@ const PRICE_SUFFIX = { monthly: '/mo', annual: '/yr' }
|
||||
// disabled until then. Remove a tier from this list to make its button live.
|
||||
const COMING_SOON = ['basic', 'plus']
|
||||
|
||||
const hasComingSoon = computed(() => COMING_SOON.length > 0)
|
||||
|
||||
function isComingSoon(tier) {
|
||||
return COMING_SOON.includes(tier)
|
||||
}
|
||||
|
||||
67
resources/js/components/WaitlistForm.vue
Normal file
67
resources/js/components/WaitlistForm.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="max-w-2xl mx-auto mt-12">
|
||||
<div class="bg-white border border-zinc-300 rounded-3xl p-8 text-center">
|
||||
<template v-if="success">
|
||||
<div class="flex flex-col items-center gap-3">
|
||||
<span class="inline-flex items-center justify-center w-12 h-12 rounded-full bg-accent/10 text-accent">
|
||||
<iconify-icon class="text-2xl" icon="lucide:check"></iconify-icon>
|
||||
</span>
|
||||
<h3 class="text-xl font-bold font-display text-zinc-800">You're on the list</h3>
|
||||
<p class="text-zinc-500 text-sm">We'll email you the moment price alerts go live.</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<h3 class="text-xl font-bold font-display text-zinc-800 mb-1">Want in when alerts launch?</h3>
|
||||
<p class="text-zinc-500 text-sm mb-6">Leave your details and we'll let you know the day alerts go live.</p>
|
||||
|
||||
<form class="flex flex-col sm:flex-row gap-3" @submit.prevent="onSubmit">
|
||||
<input
|
||||
v-model.trim="name"
|
||||
type="text"
|
||||
autocomplete="name"
|
||||
placeholder="Your name"
|
||||
required
|
||||
:disabled="loading"
|
||||
class="flex-1 px-4 py-3 border border-zinc-300 rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-accent disabled:opacity-60"
|
||||
>
|
||||
<input
|
||||
v-model.trim="email"
|
||||
type="email"
|
||||
autocomplete="email"
|
||||
placeholder="you@example.com"
|
||||
required
|
||||
:disabled="loading"
|
||||
class="flex-1 px-4 py-3 border border-zinc-300 rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-accent disabled:opacity-60"
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
:disabled="loading"
|
||||
class="px-6 py-3 bg-accent text-white rounded-xl font-bold text-sm shadow-lg hover:bg-primary-dark transition-all disabled:opacity-60 disabled:cursor-not-allowed whitespace-nowrap"
|
||||
>
|
||||
{{ loading ? 'Joining…' : 'Notify me' }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p v-if="error" class="text-red-600 text-sm mt-3 text-left">{{ error }}</p>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useWaitlist } from '../composables/useWaitlist.js'
|
||||
|
||||
const props = defineProps({
|
||||
source: { type: String, default: 'pricing' },
|
||||
})
|
||||
|
||||
const name = ref('')
|
||||
const email = ref('')
|
||||
const { loading, success, error, submit } = useWaitlist()
|
||||
|
||||
function onSubmit() {
|
||||
submit(name.value, email.value, props.source)
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user