Add coming soon landing page with waitlist signup, add waitlist API endpoint
This commit is contained in:
parent
8186e9e71c
commit
b91e42d005
|
|
@ -177,6 +177,28 @@ func main() {
|
|||
|
||||
v1 := r.Group("/api/v1")
|
||||
{
|
||||
// Public waitlist signup (no auth required)
|
||||
waitlist := v1.Group("/waitlist")
|
||||
waitlist.Use(middleware.RateLimit(0.2, 3))
|
||||
{
|
||||
waitlist.POST("", func(c *gin.Context) {
|
||||
var req struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, gin.H{"error": "Valid email required"})
|
||||
return
|
||||
}
|
||||
_, err := dbPool.Exec(c.Request.Context(),
|
||||
`INSERT INTO public.waitlist (email) VALUES ($1) ON CONFLICT (email) DO NOTHING`, req.Email)
|
||||
if err != nil {
|
||||
c.JSON(500, gin.H{"error": "Failed to join waitlist"})
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{"message": "You're on the list!"})
|
||||
})
|
||||
}
|
||||
|
||||
auth := v1.Group("/auth")
|
||||
auth.Use(middleware.RateLimit(0.5, 3))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -102,26 +102,56 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CTA -->
|
||||
<div class="inline-block relative group">
|
||||
<div
|
||||
class="absolute -inset-1 bg-gradient-to-r from-egyptianBlue to-purple-600 rounded-lg blur opacity-25 group-hover:opacity-100 transition duration-1000 group-hover:duration-200">
|
||||
</div>
|
||||
<button
|
||||
class="relative px-8 py-4 bg-egyptianBlue text-white font-bold rounded-lg shadow-lg hover:bg-blue-800 transition flex items-center gap-2">
|
||||
<span>Coming Soon to Google Play Store</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-8.707l-3-3a1 1 0 00-1.414 1.414L10.586 9H7a1 1 0 100 2h3.586l-1.293 1.293a1 1 0 101.414 1.414l3-3a1 1 0 000-1.414z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<!-- Email Signup -->
|
||||
<div class="max-w-md mx-auto mb-6">
|
||||
<p class="text-lg font-semibold text-egyptianBlue mb-4">Coming Soon</p>
|
||||
<p class="text-sm text-gray-600 mb-6">Be the first to know when Sojorn launches. Drop your email and we'll notify you.</p>
|
||||
<form id="waitlistForm" class="flex gap-3">
|
||||
<input type="email" id="waitlistEmail" required placeholder="your@email.com"
|
||||
class="flex-1 px-4 py-3 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-egyptianBlue/50 focus:border-egyptianBlue text-sm">
|
||||
<button type="submit" id="waitlistBtn"
|
||||
class="px-6 py-3 bg-egyptianBlue text-white font-bold rounded-lg shadow-lg hover:bg-blue-800 transition text-sm whitespace-nowrap">
|
||||
Notify Me
|
||||
</button>
|
||||
</form>
|
||||
<p id="waitlistMsg" class="mt-3 text-sm hidden"></p>
|
||||
</div>
|
||||
|
||||
<p class="mt-6 text-sm text-gray-500">
|
||||
Join the waitlist by emailing <a href="mailto:waitlist@sojorn.net"
|
||||
class="text-egyptianBlue underline">waitlist@sojorn.net</a>
|
||||
<p class="text-xs text-gray-400">
|
||||
Available soon on iOS, Android, and Web.
|
||||
</p>
|
||||
|
||||
<script>
|
||||
document.getElementById('waitlistForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const email = document.getElementById('waitlistEmail').value;
|
||||
const btn = document.getElementById('waitlistBtn');
|
||||
const msg = document.getElementById('waitlistMsg');
|
||||
btn.disabled = true;
|
||||
btn.textContent = '...';
|
||||
try {
|
||||
const res = await fetch('https://api.sojorn.net/api/v1/waitlist', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
msg.textContent = "You're on the list! We'll be in touch.";
|
||||
msg.className = 'mt-3 text-sm text-green-600';
|
||||
document.getElementById('waitlistEmail').value = '';
|
||||
} else {
|
||||
msg.textContent = data.error || 'Something went wrong.';
|
||||
msg.className = 'mt-3 text-sm text-red-500';
|
||||
}
|
||||
} catch {
|
||||
msg.textContent = 'Connection error. Try again later.';
|
||||
msg.className = 'mt-3 text-sm text-red-500';
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Notify Me';
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue