feat: Modularize event management system - 98.7% reduction in main file size
BREAKING CHANGES: - Refactored monolithic manage.astro (7,623 lines) into modular architecture - Original file backed up as manage-old.astro NEW ARCHITECTURE: ✅ 5 Utility Libraries: - event-management.ts: Event data operations & formatting - ticket-management.ts: Ticket CRUD operations & sales data - seating-management.ts: Seating map management & layout generation - sales-analytics.ts: Sales metrics, reporting & data export - marketing-kit.ts: Marketing asset generation & social media ✅ 5 Shared Components: - TicketTypeModal.tsx: Reusable ticket type creation/editing - SeatingMapModal.tsx: Advanced seating map editor with drag-and-drop - EmbedCodeModal.tsx: Widget embedding with customization - OrdersTable.tsx: Comprehensive orders table with sorting/pagination - AttendeesTable.tsx: Attendee management with export capabilities ✅ 11 Tab Components: - TicketsTab.tsx: Ticket management with card/list views - VenueTab.tsx: Seating map management & venue configuration - OrdersTab.tsx: Sales data & order management - AttendeesTab.tsx: Attendee check-in & management - PresaleTab.tsx: Presale code generation & tracking - DiscountTab.tsx: Discount code management - AddonsTab.tsx: Add-on product management - PrintedTab.tsx: Printed ticket barcode management - SettingsTab.tsx: Event configuration & custom fields - MarketingTab.tsx: Marketing kit with social media templates - PromotionsTab.tsx: Campaign & promotion management ✅ 4 Infrastructure Components: - TabNavigation.tsx: Responsive tab navigation system - EventManagement.tsx: Main orchestration component - EventHeader.astro: Event information header - QuickStats.astro: Statistics dashboard BENEFITS: - 98.7% reduction in main file size (7,623 → ~100 lines) - Dramatic improvement in maintainability and team collaboration - Component-level testing now possible - Reusable components across multiple features - Lazy loading support for better performance - Full TypeScript support with proper interfaces - Separation of concerns: business logic separated from UI 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
294
src/pages/login.astro
Normal file
294
src/pages/login.astro
Normal file
@@ -0,0 +1,294 @@
|
||||
---
|
||||
import LoginLayout from '../layouts/LoginLayout.astro';
|
||||
import { generateCSRFToken } from '../lib/auth';
|
||||
|
||||
// Generate CSRF token for the form
|
||||
const csrfToken = generateCSRFToken();
|
||||
---
|
||||
|
||||
<LoginLayout title="Login - Black Canyon Tickets">
|
||||
<main class="h-screen relative overflow-hidden flex flex-col">
|
||||
<!-- Premium Hero Background with Animated Gradients -->
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-indigo-900 via-purple-900 to-slate-900">
|
||||
<!-- Animated Background Elements -->
|
||||
<div class="absolute inset-0 opacity-20">
|
||||
<div class="absolute top-20 left-20 w-64 h-64 bg-gradient-to-br from-blue-400 to-purple-500 rounded-full blur-3xl animate-pulse"></div>
|
||||
<div class="absolute bottom-20 right-20 w-96 h-96 bg-gradient-to-br from-purple-400 to-pink-500 rounded-full blur-3xl animate-pulse delay-1000"></div>
|
||||
<div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-80 h-80 bg-gradient-to-br from-cyan-400 to-blue-500 rounded-full blur-3xl animate-pulse delay-500"></div>
|
||||
</div>
|
||||
|
||||
<!-- Geometric Patterns -->
|
||||
<div class="absolute inset-0 opacity-10">
|
||||
<svg class="w-full h-full" viewBox="0 0 100 100" preserveAspectRatio="none">
|
||||
<defs>
|
||||
<pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse">
|
||||
<path d="M 10 0 L 0 0 0 10" fill="none" stroke="white" stroke-width="0.5"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="100" height="100" fill="url(#grid)" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative z-10 flex-1 container mx-auto px-4 py-4 lg:py-6 flex items-center">
|
||||
<div class="grid lg:grid-cols-2 gap-8 lg:gap-12 items-center max-w-6xl mx-auto w-full">
|
||||
|
||||
<!-- Left Column: Brand & Features -->
|
||||
<div class="lg:pr-8">
|
||||
<!-- Brand Header -->
|
||||
<div class="text-center lg:text-left mb-8">
|
||||
|
||||
<h1 class="text-4xl md:text-5xl lg:text-6xl font-light text-white mb-4 tracking-tight">
|
||||
Black Canyon
|
||||
<span class="font-bold bg-gradient-to-r from-blue-400 via-purple-400 to-pink-400 bg-clip-text text-transparent">
|
||||
Tickets
|
||||
</span>
|
||||
</h1>
|
||||
<p class="text-lg lg:text-xl text-white/80 mb-6 lg:mb-8 max-w-2xl leading-relaxed">
|
||||
Elegant ticketing platform for Colorado's most prestigious venues
|
||||
</p>
|
||||
<div class="flex flex-wrap justify-center lg:justify-start gap-4 text-xs lg:text-sm text-white/70 mb-6">
|
||||
<span class="flex items-center">
|
||||
<span class="w-2 h-2 bg-blue-400 rounded-full mr-2"></span>
|
||||
Self-serve event setup
|
||||
</span>
|
||||
<span class="flex items-center">
|
||||
<span class="w-2 h-2 bg-purple-400 rounded-full mr-2"></span>
|
||||
Automated Stripe payouts
|
||||
</span>
|
||||
<span class="flex items-center">
|
||||
<span class="w-2 h-2 bg-pink-400 rounded-full mr-2"></span>
|
||||
Mobile QR scanning — no apps required
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Core Features -->
|
||||
<div class="grid sm:grid-cols-3 lg:grid-cols-3 gap-4 mb-6">
|
||||
<div class="bg-white/10 backdrop-blur-lg rounded-xl p-4 border border-white/20 text-center">
|
||||
<div class="w-10 h-10 bg-gradient-to-br from-blue-500 to-purple-500 rounded-lg flex items-center justify-center mx-auto mb-2">
|
||||
<span class="text-lg">💡</span>
|
||||
</div>
|
||||
<h3 class="font-semibold text-white text-sm mb-1">Quick Setup</h3>
|
||||
<p class="text-xs text-white/70">Create events in minutes</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white/10 backdrop-blur-lg rounded-xl p-4 border border-white/20 text-center">
|
||||
<div class="w-10 h-10 bg-gradient-to-br from-green-500 to-emerald-500 rounded-lg flex items-center justify-center mx-auto mb-2">
|
||||
<span class="text-lg">💸</span>
|
||||
</div>
|
||||
<h3 class="font-semibold text-white text-sm mb-1">Fast Payments</h3>
|
||||
<p class="text-xs text-white/70">Automated Stripe payouts</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white/10 backdrop-blur-lg rounded-xl p-4 border border-white/20 text-center">
|
||||
<div class="w-10 h-10 bg-gradient-to-br from-purple-500 to-pink-500 rounded-lg flex items-center justify-center mx-auto mb-2">
|
||||
<span class="text-lg">📊</span>
|
||||
</div>
|
||||
<h3 class="font-semibold text-white text-sm mb-1">Live Analytics</h3>
|
||||
<p class="text-xs text-white/70">Dashboard + exports</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Right Column: Login Form -->
|
||||
<div class="max-w-md mx-auto w-full">
|
||||
<div class="relative group">
|
||||
<div class="absolute inset-0 bg-gradient-to-r from-blue-600 to-purple-600 rounded-2xl blur opacity-20 group-hover:opacity-30 transition-opacity"></div>
|
||||
<div class="relative bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-6 lg:p-8 shadow-2xl">
|
||||
<!-- Form Header -->
|
||||
<div class="text-center mb-8">
|
||||
<h2 class="text-2xl font-bold text-white mb-2">Organizer Login</h2>
|
||||
<p class="text-sm text-white/70">Manage your events and track ticket sales</p>
|
||||
</div>
|
||||
|
||||
<!-- Login Form -->
|
||||
<div id="auth-form">
|
||||
<form id="login-form" class="space-y-6">
|
||||
<input type="hidden" id="csrf-token" value={csrfToken} />
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium text-white mb-2">
|
||||
Email address
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
autocomplete="email"
|
||||
required
|
||||
class="appearance-none block w-full px-4 py-3 bg-white/10 backdrop-blur-lg border border-white/20 rounded-lg shadow-sm placeholder-white/60 text-white focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-blue-400 transition-colors"
|
||||
placeholder="Enter your email"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password" class="block text-sm font-medium text-white mb-2">
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
required
|
||||
class="appearance-none block w-full px-4 py-3 bg-white/10 backdrop-blur-lg border border-white/20 rounded-lg shadow-sm placeholder-white/60 text-white focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-blue-400 transition-colors"
|
||||
placeholder="Enter your password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="name-field" class="hidden">
|
||||
<label for="name" class="block text-sm font-medium text-white mb-2">
|
||||
Full Name
|
||||
</label>
|
||||
<input
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
autocomplete="name"
|
||||
class="appearance-none block w-full px-4 py-3 bg-white/10 backdrop-blur-lg border border-white/20 rounded-lg shadow-sm placeholder-white/60 text-white focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-blue-400 transition-colors"
|
||||
placeholder="Enter your full name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-lg text-sm font-medium text-white bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all duration-200 hover:shadow-xl"
|
||||
>
|
||||
Sign in
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<button
|
||||
type="button"
|
||||
id="toggle-mode"
|
||||
class="text-sm text-blue-400 hover:text-blue-300 font-medium transition-colors"
|
||||
>
|
||||
Don't have an account? Sign up
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="error-message" class="mt-4 text-sm text-red-400 hidden"></div>
|
||||
|
||||
<!-- Privacy Policy and Terms Links -->
|
||||
<div class="mt-6 pt-6 border-t border-white/20">
|
||||
<div class="text-center text-xs text-white/60">
|
||||
By signing up, you agree to our
|
||||
<a href="/terms" target="_blank" class="text-blue-400 hover:text-blue-300 underline">
|
||||
Terms of Service
|
||||
</a>
|
||||
and
|
||||
<a href="/privacy" target="_blank" class="text-blue-400 hover:text-blue-300 underline">
|
||||
Privacy Policy
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Minimal Footer -->
|
||||
<footer class="relative z-10 py-4 lg:py-6">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="flex flex-col items-center space-y-2">
|
||||
<div class="flex space-x-6">
|
||||
<a href="/support" class="text-white/40 hover:text-white/60 text-xs lg:text-sm transition-colors">
|
||||
Support
|
||||
</a>
|
||||
<a href="/terms" class="text-white/40 hover:text-white/60 text-xs lg:text-sm transition-colors">
|
||||
Terms
|
||||
</a>
|
||||
<a href="/privacy" class="text-white/40 hover:text-white/60 text-xs lg:text-sm transition-colors">
|
||||
Privacy
|
||||
</a>
|
||||
</div>
|
||||
<p class="text-white/30 text-xs">
|
||||
© 2024 Black Canyon Tickets • Montrose, CO
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</LoginLayout>
|
||||
|
||||
<script>
|
||||
import { supabase } from '../lib/supabase';
|
||||
|
||||
const loginForm = document.getElementById('login-form') as HTMLFormElement;
|
||||
const toggleMode = document.getElementById('toggle-mode') as HTMLButtonElement;
|
||||
const nameField = document.getElementById('name-field') as HTMLDivElement;
|
||||
const nameInput = document.getElementById('name') as HTMLInputElement;
|
||||
const submitButton = loginForm.querySelector('button[type="submit"]') as HTMLButtonElement;
|
||||
const errorMessage = document.getElementById('error-message') as HTMLDivElement;
|
||||
|
||||
let isSignUpMode = false;
|
||||
|
||||
toggleMode.addEventListener('click', () => {
|
||||
isSignUpMode = !isSignUpMode;
|
||||
if (isSignUpMode) {
|
||||
nameField.classList.remove('hidden');
|
||||
nameInput.required = true;
|
||||
submitButton.textContent = 'Sign up';
|
||||
toggleMode.textContent = 'Already have an account? Sign in';
|
||||
} else {
|
||||
nameField.classList.add('hidden');
|
||||
nameInput.required = false;
|
||||
submitButton.textContent = 'Sign in';
|
||||
toggleMode.textContent = "Don't have an account? Sign up";
|
||||
}
|
||||
});
|
||||
|
||||
loginForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(loginForm);
|
||||
const email = formData.get('email') as string;
|
||||
const password = formData.get('password') as string;
|
||||
const name = formData.get('name') as string;
|
||||
|
||||
try {
|
||||
errorMessage.classList.add('hidden');
|
||||
|
||||
if (isSignUpMode) {
|
||||
const { error } = await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: {
|
||||
data: {
|
||||
name,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
alert('Check your email for the confirmation link!');
|
||||
} else {
|
||||
const { error } = await supabase.auth.signInWithPassword({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
window.location.pathname = '/dashboard';
|
||||
}
|
||||
} catch (error) {
|
||||
errorMessage.textContent = error.message;
|
||||
errorMessage.classList.remove('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// Check if user is already logged in
|
||||
supabase.auth.getSession().then(({ data: { session } }) => {
|
||||
if (session) {
|
||||
window.location.pathname = '/dashboard';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user