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:
136
src/components/EventHeader.astro
Normal file
136
src/components/EventHeader.astro
Normal file
@@ -0,0 +1,136 @@
|
||||
---
|
||||
interface Props {
|
||||
eventId: string;
|
||||
}
|
||||
|
||||
const { eventId } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="bg-white/10 backdrop-blur-xl border border-white/20 rounded-3xl shadow-2xl mb-8 overflow-hidden">
|
||||
<div class="px-8 py-12 text-white">
|
||||
<div class="flex justify-between items-start">
|
||||
<div class="flex-1">
|
||||
<h1 id="event-title" class="text-4xl font-light mb-3 tracking-wide">Loading...</h1>
|
||||
<div class="flex items-center space-x-6 text-slate-200 mb-4">
|
||||
<div class="flex items-center space-x-2">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
</svg>
|
||||
<span id="event-venue">--</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
</svg>
|
||||
<span id="event-date">--</span>
|
||||
</div>
|
||||
</div>
|
||||
<p id="event-description" class="text-slate-300 max-w-2xl leading-relaxed">Loading event details...</p>
|
||||
</div>
|
||||
<div class="flex flex-col items-end space-y-3">
|
||||
<div class="flex space-x-3">
|
||||
<a
|
||||
id="preview-link"
|
||||
href="#"
|
||||
target="_blank"
|
||||
class="bg-white/10 hover:bg-white/20 text-white px-6 py-3 rounded-xl font-medium transition-all duration-200 backdrop-blur-sm flex items-center gap-2"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
|
||||
</svg>
|
||||
Preview Page
|
||||
</a>
|
||||
<button
|
||||
id="embed-code-btn"
|
||||
class="bg-white/10 hover:bg-white/20 text-white px-6 py-3 rounded-xl font-medium transition-all duration-200 backdrop-blur-sm flex items-center gap-2"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path>
|
||||
</svg>
|
||||
Get Embed Code
|
||||
</button>
|
||||
<a
|
||||
href="/scan"
|
||||
class="bg-white/10 hover:bg-white/20 text-white px-6 py-3 rounded-xl font-medium transition-all duration-200 backdrop-blur-sm flex items-center gap-2"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h4m-6 0h-2v4m0-11v3m0 0h.01M12 12h.01M16 8h4m-6 0h-2v4m0-11v3m0 0h.01M12 12h.01"></path>
|
||||
</svg>
|
||||
Scanner
|
||||
</a>
|
||||
<button
|
||||
id="edit-event-btn"
|
||||
class="bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 text-white px-6 py-3 rounded-xl font-medium transition-all duration-200 flex items-center gap-2"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
|
||||
</svg>
|
||||
Edit Event
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-3xl font-semibold" id="total-revenue">$0</div>
|
||||
<div class="text-sm text-slate-300">Total Revenue</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script define:vars={{ eventId }}>
|
||||
// Initialize event header when page loads
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
await loadEventHeader();
|
||||
});
|
||||
|
||||
async function loadEventHeader() {
|
||||
try {
|
||||
const { createClient } = await import('@supabase/supabase-js');
|
||||
const supabase = createClient(
|
||||
import.meta.env.PUBLIC_SUPABASE_URL,
|
||||
import.meta.env.PUBLIC_SUPABASE_ANON_KEY
|
||||
);
|
||||
|
||||
// Load event data
|
||||
const { data: event, error } = await supabase
|
||||
.from('events')
|
||||
.select('*')
|
||||
.eq('id', eventId)
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
// Update UI
|
||||
document.getElementById('event-title').textContent = event.title;
|
||||
document.getElementById('event-venue').textContent = event.venue;
|
||||
document.getElementById('event-date').textContent = new Date(event.date).toLocaleDateString('en-US', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: '2-digit'
|
||||
});
|
||||
document.getElementById('event-description').textContent = event.description;
|
||||
document.getElementById('preview-link').href = `/e/${event.slug}`;
|
||||
|
||||
// Load stats
|
||||
const { data: tickets } = await supabase
|
||||
.from('tickets')
|
||||
.select('price_paid')
|
||||
.eq('event_id', eventId)
|
||||
.eq('status', 'confirmed');
|
||||
|
||||
const totalRevenue = tickets?.reduce((sum, ticket) => sum + ticket.price_paid, 0) || 0;
|
||||
document.getElementById('total-revenue').textContent = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD'
|
||||
}).format(totalRevenue / 100);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading event header:', error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user