Enhance application structure: Add runtime configuration for environment variables, integrate new dependencies for Cloudinary and UI components, and refactor member management features including improved forms and member dashboard. Update styles and layout for better user experience.

This commit is contained in:
Jennie Robinson Faber 2025-08-27 16:49:51 +01:00
parent 6e7e27ac4e
commit e4a0a9ab0f
61 changed files with 7902 additions and 950 deletions

View file

@ -1,63 +1,357 @@
<!-- pages/join.vue -->
<template>
<UContainer class="py-12">
<UForm :state="form" @submit="handleSubmit">
<!-- Step 1: Basic Info -->
<UFormField label="Email" name="email">
<UInput v-model="form.email" type="email" />
</UFormField>
<div>
<!-- Page Header -->
<PageHeader
title="Join"
subtitle="Become a member of our cooperative community and start building the future of game development together"
theme="blue"
size="large"
/>
<!-- Step 2: Choose Circle -->
<URadioGroup
v-model="form.circle"
:options="circleOptions"
name="circle" />
<!-- Membership Sign Up Form -->
<section class="py-20 bg-white dark:bg-gray-900">
<UContainer class="max-w-4xl">
<div class="text-center mb-12">
<h2 class="text-3xl font-bold text-blue-600 dark:text-blue-400 mb-4">
Membership Sign Up
</h2>
<p class="text-gray-600 dark:text-gray-300">
Choose your circle and contribution level to get started
</p>
</div>
<!-- Step 3: Choose Contribution -->
<URadioGroup
v-model="form.contribution"
:options="contributionOptions"
name="contribution" />
<div class="bg-white dark:bg-gray-800 rounded-2xl p-8 shadow-xl border border-blue-200 dark:border-blue-800">
<UForm :state="form" class="space-y-8" @submit="handleSubmit">
<!-- Personal Information -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<UFormField label="Full Name" name="name" required>
<UInput
v-model="form.name"
placeholder="Enter your full name"
size="xl"
class="w-full"
/>
</UFormField>
<!-- Step 4: Helcim Checkout -->
<div id="helcim-payment"></div>
<UFormField label="Email Address" name="email" required>
<UInput
v-model="form.email"
type="email"
size="xl"
class="w-full"
placeholder="Enter your email address"
/>
</UFormField>
</div>
<UButton type="submit" block> Complete Membership </UButton>
</UForm>
</UContainer>
<!-- Circle Selection -->
<div>
<h3 class="text-lg font-semibold mb-4">Choose Your Circle</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<label
v-for="option in circleOptions"
:key="option.value"
class="flex flex-col p-6 rounded-lg border-2 cursor-pointer transition-all hover:shadow-md"
:class="
form.circle === option.value
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20'
: 'border-gray-200 dark:border-gray-700 hover:border-blue-300'
"
>
<input
v-model="form.circle"
type="radio"
:value="option.value"
name="circle"
class="mb-3"
>
<div class="font-medium text-lg mb-2">{{ option.label }}</div>
<div class="text-sm text-gray-600 dark:text-gray-400">
{{ option.description }}
</div>
</label>
</div>
</div>
<!-- Contribution Selection -->
<div>
<h3 class="text-lg font-semibold mb-4">Choose Your Monthly Contribution</h3>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<label
v-for="option in contributionOptions"
:key="option.value"
class="flex flex-col p-6 rounded-lg border-2 cursor-pointer transition-all hover:shadow-md"
:class="
form.contributionTier === option.value
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20'
: 'border-gray-200 dark:border-gray-700 hover:border-blue-300'
"
>
<input
v-model="form.contributionTier"
type="radio"
:value="option.value"
name="contributionTier"
class="mb-3"
>
<div class="font-medium text-lg mb-2">{{ option.label }}</div>
<ul class="text-sm text-gray-600 dark:text-gray-400 space-y-1">
<li v-for="feature in option.features.slice(0, 2)" :key="feature">
{{ feature }}
</li>
</ul>
</label>
</div>
</div>
<!-- Submit Button -->
<div class="flex justify-center pt-6">
<UButton
type="submit"
:loading="isSubmitting"
:disabled="!isFormValid"
size="xl"
class="px-12"
>
Continue to Payment
</UButton>
</div>
</UForm>
</div>
</UContainer>
</section>
<!-- Membership Benefits -->
<section class="py-20 bg-gray-50 dark:bg-gray-800">
<UContainer>
<div class="text-center mb-12">
<h2 class="text-3xl font-bold text-blue-600 dark:text-blue-400 mb-4">
Membership Benefits
</h2>
<p class="text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Join our community and unlock these amazing benefits.
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<div class="bg-white dark:bg-gray-900 rounded-xl p-8 shadow-lg">
<div class="w-12 h-12 bg-blue-100 dark:bg-blue-900/30 rounded-lg flex items-center justify-center mb-6">
<div class="w-6 h-6 bg-blue-500 rounded" />
</div>
<h3 class="text-xl font-semibold mb-4 text-gray-900 dark:text-white">
Community Access
</h3>
<div class="space-y-2 mb-4">
<div class="h-1 bg-blue-500 rounded-full w-full" />
<div class="h-1 bg-blue-300 rounded-full w-3/4" />
<div class="h-1 bg-blue-200 rounded-full w-1/2" />
</div>
<p class="text-gray-600 dark:text-gray-400">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Access to forums and resources.
</p>
</div>
<div class="bg-white dark:bg-gray-900 rounded-xl p-8 shadow-lg">
<div class="w-12 h-12 bg-emerald-100 dark:bg-emerald-900/30 rounded-lg flex items-center justify-center mb-6">
<div class="w-6 h-6 bg-emerald-500" style="clip-path: polygon(50% 0%, 0% 100%, 100% 100%)" />
</div>
<h3 class="text-xl font-semibold mb-4 text-gray-900 dark:text-white">
Learning Resources
</h3>
<div class="space-y-2 mb-4">
<div class="h-1 bg-emerald-500 rounded-full w-full" />
<div class="h-1 bg-emerald-300 rounded-full w-2/3" />
<div class="h-1 bg-emerald-200 rounded-full w-3/4" />
</div>
<p class="text-gray-600 dark:text-gray-400">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Educational content and workshops.
</p>
</div>
<div class="bg-white dark:bg-gray-900 rounded-xl p-8 shadow-lg md:col-span-2 lg:col-span-1">
<div class="w-12 h-12 bg-purple-100 dark:bg-purple-900/30 rounded-lg flex items-center justify-center mb-6">
<div class="w-6 h-6 bg-purple-500 rounded-full" />
</div>
<h3 class="text-xl font-semibold mb-4 text-gray-900 dark:text-white">
Network & Support
</h3>
<div class="space-y-2 mb-4">
<div class="h-1 bg-purple-500 rounded-full w-5/6" />
<div class="h-1 bg-purple-300 rounded-full w-full" />
<div class="h-1 bg-purple-200 rounded-full w-2/3" />
</div>
<p class="text-gray-600 dark:text-gray-400">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Connect with like-minded professionals.
</p>
</div>
</div>
</UContainer>
</section>
<!-- How to Join -->
<section class="py-20 bg-white dark:bg-gray-900">
<UContainer>
<div class="text-center mb-16">
<h2 class="text-3xl font-bold text-blue-600 dark:text-blue-400 mb-4">
How to Join
</h2>
<p class="text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Follow these simple steps to become a member.
</p>
</div>
<div class="max-w-4xl mx-auto">
<div class="space-y-12">
<div class="flex flex-col md:flex-row items-center gap-8">
<div class="flex-shrink-0">
<div class="w-16 h-16 bg-blue-500 rounded-full flex items-center justify-center text-white font-bold text-xl">
1
</div>
</div>
<div class="flex-1 text-center md:text-left">
<div class="space-y-3 mb-4">
<div class="h-2 bg-blue-500 rounded-full max-w-md mx-auto md:mx-0" />
<div class="h-2 bg-blue-300 rounded-full max-w-sm mx-auto md:mx-0" />
<div class="h-2 bg-blue-200 rounded-full max-w-xs mx-auto md:mx-0" />
</div>
<h3 class="text-xl font-semibold mb-2 text-gray-900 dark:text-white">
Choose Your Circle
</h3>
<p class="text-gray-600 dark:text-gray-400">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Select the circle that matches your interests.
</p>
</div>
</div>
<div class="flex flex-col md:flex-row items-center gap-8">
<div class="flex-shrink-0">
<div class="w-16 h-16 bg-blue-500 rounded-full flex items-center justify-center text-white font-bold text-xl">
2
</div>
</div>
<div class="flex-1 text-center md:text-left">
<div class="space-y-3 mb-4">
<div class="h-2 bg-blue-500 rounded-full max-w-lg mx-auto md:mx-0" />
<div class="h-2 bg-blue-300 rounded-full max-w-md mx-auto md:mx-0" />
<div class="h-2 bg-blue-200 rounded-full max-w-sm mx-auto md:mx-0" />
</div>
<h3 class="text-xl font-semibold mb-2 text-gray-900 dark:text-white">
Set Your Contribution
</h3>
<p class="text-gray-600 dark:text-gray-400">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Choose a contribution level based on your means.
</p>
</div>
</div>
<div class="flex flex-col md:flex-row items-center gap-8">
<div class="flex-shrink-0">
<div class="w-16 h-16 bg-blue-500 rounded-full flex items-center justify-center text-white font-bold text-xl">
3
</div>
</div>
<div class="flex-1 text-center md:text-left">
<div class="space-y-3 mb-4">
<div class="h-2 bg-blue-500 rounded-full max-w-sm mx-auto md:mx-0" />
<div class="h-2 bg-blue-300 rounded-full max-w-lg mx-auto md:mx-0" />
<div class="h-2 bg-blue-200 rounded-full max-w-md mx-auto md:mx-0" />
</div>
<h3 class="text-xl font-semibold mb-2 text-gray-900 dark:text-white">
Complete Registration
</h3>
<p class="text-gray-600 dark:text-gray-400">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Finalize your membership and start participating.
</p>
</div>
</div>
</div>
</div>
</UContainer>
</section>
<!-- Ready to Join CTA -->
<section class="py-20 bg-blue-50 dark:bg-blue-900/20">
<UContainer>
<div class="text-center max-w-3xl mx-auto">
<h2 class="text-3xl font-bold text-blue-600 dark:text-blue-400 mb-8">
Ready to Join?
</h2>
<div class="flex flex-col md:flex-row items-center justify-center gap-8 mb-8">
<div class="bg-white dark:bg-gray-800 rounded-2xl p-8 shadow-lg border border-blue-200 dark:border-blue-800 flex-1 max-w-md">
<div class="text-2xl font-bold text-blue-600 dark:text-blue-400 mb-2">
Start Today
</div>
<p class="text-gray-600 dark:text-gray-300 mb-4">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
<div class="space-y-2">
<div class="h-1 bg-blue-500 rounded-full" />
<div class="h-1 bg-blue-300 rounded-full w-3/4" />
<div class="h-1 bg-blue-200 rounded-full w-1/2" />
</div>
</div>
<div class="flex flex-col items-center gap-4">
<UButton
to="#membership-form"
size="xl"
color="primary"
class="px-8 py-4"
>
Join Now
</UButton>
<div class="h-2 bg-blue-500 rounded-full w-32" />
</div>
</div>
<p class="text-gray-600 dark:text-gray-300">
Questions? Contact us at <a href="mailto:hello@ghostguild.org" class="text-blue-600 dark:text-blue-400 hover:underline">hello@ghostguild.org</a>
</p>
</div>
</UContainer>
</section>
</div>
</template>
<script setup>
import { reactive, onMounted } from 'vue';
import { reactive, ref, computed } from 'vue'
import { getCircleOptions } from '~/config/circles'
import { getContributionOptions } from '~/config/contributions'
// Form state
const form = reactive({
email: "",
name: "",
circle: "community",
contribution: "15",
});
email: '',
name: '',
circle: 'community',
contributionTier: '15',
})
const circleOptions = [
{ value: 'community', label: 'Community Circle - $15/month' },
{ value: 'support', label: 'Support Circle - $25/month' },
{ value: 'sustaining', label: 'Sustaining Circle - $50/month' }
];
// UI state
const isSubmitting = ref(false)
const contributionOptions = [
{ value: '15', label: '$15/month' },
{ value: '25', label: '$25/month' },
{ value: '50', label: '$50/month' },
{ value: 'custom', label: 'Custom amount' }
];
// Circle options from central config
const circleOptions = getCircleOptions()
const handleSubmit = () => {
console.log('Form submitted:', form);
};
// Contribution options from central config
const contributionOptions = getContributionOptions()
// Load Helcim.js
onMounted(() => {
const script = document.createElement("script");
script.src = "https://secure.helcim.app/helcim-pay.js";
document.head.appendChild(script);
});
</script>
// Form validation
const isFormValid = computed(() => {
return form.name && form.email && form.circle && form.contributionTier
})
// Form submission - redirect to detailed form
const handleSubmit = async () => {
if (isSubmitting.value) return
// For now, just scroll to the form or redirect to detailed signup
const formElement = document.getElementById('membership-form')
if (formElement) {
formElement.scrollIntoView({ behavior: 'smooth' })
} else {
// Could redirect to a detailed form page
await navigateTo('/join/details')
}
}
</script>