747 lines
16 KiB
Vue
747 lines
16 KiB
Vue
<template>
|
|
<div class="auth-page">
|
|
<div class="auth-container auth-container--wide">
|
|
<!-- Progress Bar -->
|
|
<div class="progress-bar">
|
|
<div
|
|
class="progress-bar__fill"
|
|
:style="{ width: `${(step / 3) * 100}%` }"
|
|
></div>
|
|
</div>
|
|
|
|
<!-- Step 1: Account Info -->
|
|
<div
|
|
v-if="step === 1"
|
|
v-motion
|
|
:initial="{ opacity: 0, x: 50 }"
|
|
:enter="{ opacity: 1, x: 0 }"
|
|
class="signup-step"
|
|
>
|
|
<div class="auth-logo">
|
|
<img src="/logo.svg" alt="MonacoUSA" />
|
|
<h1>MonacoUSA Portal</h1>
|
|
</div>
|
|
|
|
<div class="auth-header">
|
|
<h2>Create Your Account</h2>
|
|
<p>Join Monaco's premier business community</p>
|
|
</div>
|
|
|
|
<form class="auth-form" @submit.prevent="nextStep">
|
|
<div class="form-row">
|
|
<FloatingInput
|
|
v-model="form.firstName"
|
|
label="First Name"
|
|
variant="glass"
|
|
leftIcon="user"
|
|
required
|
|
/>
|
|
<FloatingInput
|
|
v-model="form.lastName"
|
|
label="Last Name"
|
|
variant="glass"
|
|
leftIcon="user"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<FloatingInput
|
|
v-model="form.email"
|
|
label="Email Address"
|
|
type="email"
|
|
variant="glass"
|
|
leftIcon="mail"
|
|
helperText="We'll use this for account notifications"
|
|
required
|
|
/>
|
|
|
|
<FloatingInput
|
|
v-model="form.password"
|
|
label="Password"
|
|
type="password"
|
|
variant="glass"
|
|
leftIcon="lock"
|
|
helperText="Minimum 8 characters with uppercase and number"
|
|
required
|
|
/>
|
|
|
|
<FloatingInput
|
|
v-model="form.confirmPassword"
|
|
label="Confirm Password"
|
|
type="password"
|
|
variant="glass"
|
|
leftIcon="lock"
|
|
:error="passwordError"
|
|
required
|
|
/>
|
|
|
|
<div class="password-strength">
|
|
<span class="password-strength__label">Password Strength:</span>
|
|
<div class="password-strength__bars">
|
|
<span
|
|
v-for="i in 4"
|
|
:key="i"
|
|
class="password-strength__bar"
|
|
:class="{ 'password-strength__bar--filled': i <= passwordStrength }"
|
|
></span>
|
|
</div>
|
|
<span class="password-strength__text">{{ passwordStrengthText }}</span>
|
|
</div>
|
|
|
|
<MonacoButton
|
|
type="submit"
|
|
variant="primary"
|
|
size="lg"
|
|
block
|
|
>
|
|
Continue to Profile
|
|
</MonacoButton>
|
|
</form>
|
|
|
|
<div class="auth-footer">
|
|
<p>Already have an account? <a href="/auth/login" class="link">Sign in</a></p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 2: Profile Info -->
|
|
<div
|
|
v-if="step === 2"
|
|
v-motion
|
|
:initial="{ opacity: 0, x: 50 }"
|
|
:enter="{ opacity: 1, x: 0 }"
|
|
class="signup-step"
|
|
>
|
|
<div class="step-header">
|
|
<button @click="previousStep" class="back-button">
|
|
<Icon name="arrow-left" />
|
|
Back
|
|
</button>
|
|
<h2>Professional Information</h2>
|
|
</div>
|
|
|
|
<form class="auth-form" @submit.prevent="nextStep">
|
|
<FloatingInput
|
|
v-model="form.company"
|
|
label="Company Name"
|
|
variant="glass"
|
|
leftIcon="building"
|
|
required
|
|
/>
|
|
|
|
<FloatingInput
|
|
v-model="form.title"
|
|
label="Job Title"
|
|
variant="glass"
|
|
leftIcon="briefcase"
|
|
required
|
|
/>
|
|
|
|
<div class="form-row">
|
|
<FloatingInput
|
|
v-model="form.phone"
|
|
label="Phone Number"
|
|
type="tel"
|
|
variant="glass"
|
|
leftIcon="phone"
|
|
required
|
|
/>
|
|
<FloatingInput
|
|
v-model="form.linkedin"
|
|
label="LinkedIn Profile"
|
|
variant="glass"
|
|
leftIcon="link"
|
|
helperText="Optional"
|
|
/>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Industry</label>
|
|
<select v-model="form.industry" class="form-select">
|
|
<option value="">Select your industry</option>
|
|
<option value="finance">Finance & Banking</option>
|
|
<option value="tech">Technology</option>
|
|
<option value="realestate">Real Estate</option>
|
|
<option value="hospitality">Hospitality</option>
|
|
<option value="retail">Retail & Luxury</option>
|
|
<option value="consulting">Consulting</option>
|
|
<option value="legal">Legal Services</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Bio</label>
|
|
<textarea
|
|
v-model="form.bio"
|
|
class="form-textarea"
|
|
placeholder="Tell us about yourself and your business interests..."
|
|
rows="4"
|
|
></textarea>
|
|
</div>
|
|
|
|
<MonacoButton
|
|
type="submit"
|
|
variant="primary"
|
|
size="lg"
|
|
block
|
|
>
|
|
Continue to Membership
|
|
</MonacoButton>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Step 3: Membership -->
|
|
<div
|
|
v-if="step === 3"
|
|
v-motion
|
|
:initial="{ opacity: 0, x: 50 }"
|
|
:enter="{ opacity: 1, x: 0 }"
|
|
class="signup-step"
|
|
>
|
|
<div class="step-header">
|
|
<button @click="previousStep" class="back-button">
|
|
<Icon name="arrow-left" />
|
|
Back
|
|
</button>
|
|
<h2>Choose Your Membership</h2>
|
|
</div>
|
|
|
|
<div class="membership-plans">
|
|
<div
|
|
v-for="plan in membershipPlans"
|
|
:key="plan.id"
|
|
class="plan-card"
|
|
:class="{ 'plan-card--selected': form.membershipPlan === plan.id }"
|
|
@click="form.membershipPlan = plan.id"
|
|
>
|
|
<div class="plan-card__header">
|
|
<h3 class="plan-card__name">{{ plan.name }}</h3>
|
|
<span class="plan-card__price">${{ plan.price }}/year</span>
|
|
</div>
|
|
<ul class="plan-card__features">
|
|
<li v-for="feature in plan.features" :key="feature">
|
|
<Icon name="check" />
|
|
{{ feature }}
|
|
</li>
|
|
</ul>
|
|
<span v-if="plan.popular" class="plan-card__badge">Most Popular</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="terms-section">
|
|
<label class="checkbox-label">
|
|
<input type="checkbox" v-model="form.agreeTerms" />
|
|
<span>
|
|
I agree to the <a href="/terms" class="link">Terms of Service</a>
|
|
and <a href="/privacy" class="link">Privacy Policy</a>
|
|
</span>
|
|
</label>
|
|
|
|
<label class="checkbox-label">
|
|
<input type="checkbox" v-model="form.agreeNewsletter" />
|
|
<span>Send me updates about events and opportunities</span>
|
|
</label>
|
|
</div>
|
|
|
|
<MonacoButton
|
|
variant="primary"
|
|
size="lg"
|
|
block
|
|
:disabled="!form.agreeTerms || !form.membershipPlan"
|
|
@click="handleSignup"
|
|
>
|
|
Complete Registration
|
|
</MonacoButton>
|
|
</div>
|
|
|
|
<!-- Success State -->
|
|
<div
|
|
v-if="step === 4"
|
|
v-motion
|
|
:initial="{ opacity: 0, scale: 0.9 }"
|
|
:enter="{ opacity: 1, scale: 1 }"
|
|
class="success-state"
|
|
>
|
|
<div class="success-icon">🎉</div>
|
|
<h2>Welcome to MonacoUSA!</h2>
|
|
<p>Your account has been created successfully.</p>
|
|
<p>Please check your email to verify your account.</p>
|
|
<MonacoButton variant="primary" size="lg" @click="goToLogin">
|
|
Go to Login
|
|
</MonacoButton>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed } from 'vue'
|
|
import FloatingInput from '~/components/ui/FloatingInput.vue'
|
|
import MonacoButton from '~/components/ui/MonacoButton.vue'
|
|
import Icon from '~/components/ui/Icon.vue'
|
|
|
|
const step = ref(1)
|
|
|
|
const form = ref({
|
|
// Step 1
|
|
firstName: '',
|
|
lastName: '',
|
|
email: '',
|
|
password: '',
|
|
confirmPassword: '',
|
|
// Step 2
|
|
company: '',
|
|
title: '',
|
|
phone: '',
|
|
linkedin: '',
|
|
industry: '',
|
|
bio: '',
|
|
// Step 3
|
|
membershipPlan: '',
|
|
agreeTerms: false,
|
|
agreeNewsletter: true
|
|
})
|
|
|
|
const membershipPlans = [
|
|
{
|
|
id: 'basic',
|
|
name: 'Basic',
|
|
price: 250,
|
|
features: [
|
|
'Access to member directory',
|
|
'Monthly newsletter',
|
|
'Event invitations',
|
|
'Basic networking features'
|
|
]
|
|
},
|
|
{
|
|
id: 'professional',
|
|
name: 'Professional',
|
|
price: 500,
|
|
popular: true,
|
|
features: [
|
|
'Everything in Basic',
|
|
'Priority event registration',
|
|
'Enhanced profile features',
|
|
'Business matchmaking',
|
|
'Quarterly exclusive events'
|
|
]
|
|
},
|
|
{
|
|
id: 'executive',
|
|
name: 'Executive',
|
|
price: 1000,
|
|
features: [
|
|
'Everything in Professional',
|
|
'VIP event access',
|
|
'Personal concierge service',
|
|
'Board meeting participation',
|
|
'Guest passes (5/year)',
|
|
'Premium networking tools'
|
|
]
|
|
}
|
|
]
|
|
|
|
const passwordError = computed(() => {
|
|
if (form.value.confirmPassword && form.value.password !== form.value.confirmPassword) {
|
|
return 'Passwords do not match'
|
|
}
|
|
return ''
|
|
})
|
|
|
|
const passwordStrength = computed(() => {
|
|
const password = form.value.password
|
|
if (!password) return 0
|
|
|
|
let strength = 0
|
|
if (password.length >= 8) strength++
|
|
if (/[A-Z]/.test(password)) strength++
|
|
if (/[0-9]/.test(password)) strength++
|
|
if (/[^A-Za-z0-9]/.test(password)) strength++
|
|
|
|
return strength
|
|
})
|
|
|
|
const passwordStrengthText = computed(() => {
|
|
const texts = ['', 'Weak', 'Fair', 'Good', 'Strong']
|
|
return texts[passwordStrength.value]
|
|
})
|
|
|
|
const nextStep = () => {
|
|
step.value++
|
|
}
|
|
|
|
const previousStep = () => {
|
|
step.value--
|
|
}
|
|
|
|
const handleSignup = () => {
|
|
console.log('Signup with:', form.value)
|
|
step.value = 4
|
|
}
|
|
|
|
const goToLogin = () => {
|
|
window.location.href = '/auth/login'
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.auth-page {
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: linear-gradient(135deg, #fef2f2 0%, #ffffff 100%);
|
|
padding: 2rem;
|
|
}
|
|
|
|
.auth-container {
|
|
max-width: 500px;
|
|
width: 100%;
|
|
background: rgba(255, 255, 255, 0.9);
|
|
backdrop-filter: blur(20px);
|
|
border-radius: 24px;
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1);
|
|
overflow: hidden;
|
|
|
|
&--wide {
|
|
max-width: 800px;
|
|
}
|
|
}
|
|
|
|
.progress-bar {
|
|
height: 4px;
|
|
background: rgba(220, 38, 38, 0.1);
|
|
|
|
&__fill {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, #dc2626 0%, #b91c1c 100%);
|
|
transition: width 0.3s ease;
|
|
}
|
|
}
|
|
|
|
.signup-step {
|
|
padding: 3rem;
|
|
}
|
|
|
|
.auth-logo {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
margin-bottom: 2rem;
|
|
|
|
img {
|
|
width: 48px;
|
|
height: 48px;
|
|
}
|
|
|
|
h1 {
|
|
margin: 0;
|
|
font-size: 1.5rem;
|
|
font-weight: 700;
|
|
color: #dc2626;
|
|
}
|
|
}
|
|
|
|
.auth-header {
|
|
margin-bottom: 2rem;
|
|
|
|
h2 {
|
|
margin: 0 0 0.5rem;
|
|
font-size: 2rem;
|
|
font-weight: 700;
|
|
color: #27272a;
|
|
}
|
|
|
|
p {
|
|
margin: 0;
|
|
color: #6b7280;
|
|
}
|
|
}
|
|
|
|
.step-header {
|
|
margin-bottom: 2rem;
|
|
|
|
h2 {
|
|
margin: 0.5rem 0 0;
|
|
font-size: 1.5rem;
|
|
font-weight: 700;
|
|
color: #27272a;
|
|
}
|
|
}
|
|
|
|
.back-button {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 0;
|
|
background: none;
|
|
border: none;
|
|
color: #6b7280;
|
|
font-size: 0.875rem;
|
|
cursor: pointer;
|
|
transition: color 0.2s;
|
|
|
|
&:hover {
|
|
color: #dc2626;
|
|
}
|
|
}
|
|
|
|
.auth-form {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.form-row {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.form-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.form-label {
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
color: #27272a;
|
|
}
|
|
|
|
.form-select,
|
|
.form-textarea {
|
|
padding: 0.75rem 1rem;
|
|
background: rgba(255, 255, 255, 0.7);
|
|
backdrop-filter: blur(20px);
|
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
border-radius: 12px;
|
|
font-size: 1rem;
|
|
color: #27272a;
|
|
transition: all 0.2s;
|
|
|
|
&:focus {
|
|
outline: none;
|
|
border-color: #dc2626;
|
|
box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.1);
|
|
}
|
|
}
|
|
|
|
.form-textarea {
|
|
resize: vertical;
|
|
min-height: 100px;
|
|
font-family: inherit;
|
|
}
|
|
|
|
.password-strength {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
padding: 0.75rem;
|
|
background: rgba(220, 38, 38, 0.05);
|
|
border-radius: 8px;
|
|
|
|
&__label {
|
|
font-size: 0.875rem;
|
|
color: #6b7280;
|
|
}
|
|
|
|
&__bars {
|
|
display: flex;
|
|
gap: 0.25rem;
|
|
flex: 1;
|
|
}
|
|
|
|
&__bar {
|
|
height: 4px;
|
|
flex: 1;
|
|
background: #e5e5e5;
|
|
border-radius: 2px;
|
|
transition: background 0.3s;
|
|
|
|
&--filled {
|
|
background: #dc2626;
|
|
}
|
|
}
|
|
|
|
&__text {
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
color: #dc2626;
|
|
}
|
|
}
|
|
|
|
.membership-plans {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 1rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.plan-card {
|
|
position: relative;
|
|
padding: 1.5rem;
|
|
background: white;
|
|
border: 2px solid #e5e5e5;
|
|
border-radius: 12px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
|
|
&:hover {
|
|
border-color: #dc2626;
|
|
transform: translateY(-4px);
|
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
&--selected {
|
|
border-color: #dc2626;
|
|
background: rgba(220, 38, 38, 0.05);
|
|
}
|
|
|
|
&__header {
|
|
margin-bottom: 1rem;
|
|
padding-bottom: 1rem;
|
|
border-bottom: 1px solid #e5e5e5;
|
|
}
|
|
|
|
&__name {
|
|
margin: 0 0 0.5rem;
|
|
font-size: 1.125rem;
|
|
font-weight: 600;
|
|
color: #27272a;
|
|
}
|
|
|
|
&__price {
|
|
font-size: 1.5rem;
|
|
font-weight: 700;
|
|
color: #dc2626;
|
|
}
|
|
|
|
&__features {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
|
|
li {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
margin-bottom: 0.75rem;
|
|
font-size: 0.875rem;
|
|
color: #6b7280;
|
|
|
|
svg {
|
|
width: 1rem;
|
|
height: 1rem;
|
|
color: #10b981;
|
|
flex-shrink: 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
&__badge {
|
|
position: absolute;
|
|
top: -0.5rem;
|
|
right: 1rem;
|
|
padding: 0.25rem 0.75rem;
|
|
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
|
|
color: white;
|
|
border-radius: 20px;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
|
|
.terms-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.checkbox-label {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 0.5rem;
|
|
cursor: pointer;
|
|
|
|
input[type="checkbox"] {
|
|
margin-top: 0.125rem;
|
|
width: 1.25rem;
|
|
height: 1.25rem;
|
|
accent-color: #dc2626;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
span {
|
|
font-size: 0.875rem;
|
|
color: #6b7280;
|
|
line-height: 1.5;
|
|
}
|
|
}
|
|
|
|
.link {
|
|
color: #dc2626;
|
|
text-decoration: none;
|
|
font-weight: 500;
|
|
|
|
&:hover {
|
|
text-decoration: underline;
|
|
}
|
|
}
|
|
|
|
.auth-footer {
|
|
margin-top: 2rem;
|
|
text-align: center;
|
|
|
|
p {
|
|
margin: 0;
|
|
font-size: 0.875rem;
|
|
color: #6b7280;
|
|
}
|
|
}
|
|
|
|
.success-state {
|
|
padding: 4rem;
|
|
text-align: center;
|
|
|
|
.success-icon {
|
|
font-size: 4rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
h2 {
|
|
margin: 0 0 1rem;
|
|
font-size: 2rem;
|
|
font-weight: 700;
|
|
color: #27272a;
|
|
}
|
|
|
|
p {
|
|
margin: 0 0 0.5rem;
|
|
color: #6b7280;
|
|
|
|
&:last-of-type {
|
|
margin-bottom: 2rem;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Responsive
|
|
@media (max-width: 768px) {
|
|
.membership-plans {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.form-row {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.signup-step {
|
|
padding: 2rem;
|
|
}
|
|
}
|
|
</style> |