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:
2025-07-08 18:30:26 -06:00
parent 23f190c7a7
commit e8b95231b7
76 changed files with 26728 additions and 7101 deletions

View File

@@ -0,0 +1,124 @@
---
interface Props {
eventId: string;
}
const { eventId } = Astro.props;
---
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
<div class="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-6 shadow-lg hover:shadow-xl hover:scale-105 transition-all duration-200">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-semibold text-white/80 uppercase tracking-wide">Tickets Sold</p>
<p id="tickets-sold" class="text-3xl font-light text-white mt-1">0</p>
</div>
<div class="w-12 h-12 bg-emerald-500/20 rounded-xl flex items-center justify-center">
<svg class="w-6 h-6 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 5v2m0 4v2m0 4v2M5 5a2 2 0 00-2 2v3a2 2 0 110 4v3a2 2 0 002 2h14a2 2 0 002-2v-3a2 2 0 110-4V7a2 2 0 00-2-2H5z"></path>
</svg>
</div>
</div>
</div>
<div class="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-6 shadow-lg hover:shadow-xl hover:scale-105 transition-all duration-200">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-semibold text-white/80 uppercase tracking-wide">Available</p>
<p id="tickets-available" class="text-3xl font-light text-white mt-1">--</p>
</div>
<div class="w-12 h-12 bg-blue-500/20 rounded-xl flex items-center justify-center">
<svg class="w-6 h-6 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"></path>
</svg>
</div>
</div>
</div>
<div class="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-6 shadow-lg hover:shadow-xl hover:scale-105 transition-all duration-200">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-semibold text-white/80 uppercase tracking-wide">Check-ins</p>
<p id="checked-in" class="text-3xl font-light text-white mt-1">0</p>
</div>
<div class="w-12 h-12 bg-purple-500/20 rounded-xl flex items-center justify-center">
<svg class="w-6 h-6 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
</div>
<div class="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-6 shadow-lg hover:shadow-xl hover:scale-105 transition-all duration-200">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-semibold text-white/80 uppercase tracking-wide">Net Revenue</p>
<p id="net-revenue" class="text-3xl font-light text-white mt-1">$0</p>
</div>
<div class="w-12 h-12 bg-amber-500/20 rounded-xl flex items-center justify-center">
<svg class="w-6 h-6 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1"></path>
</svg>
</div>
</div>
</div>
</div>
<script define:vars={{ eventId }}>
// Initialize quick stats when page loads
document.addEventListener('DOMContentLoaded', async () => {
await loadQuickStats();
});
async function loadQuickStats() {
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 ticket sales data
const { data: tickets } = await supabase
.from('tickets')
.select(`
id,
price_paid,
checked_in,
ticket_types (
id,
quantity
)
`)
.eq('event_id', eventId)
.eq('status', 'confirmed');
// Load ticket types for capacity calculation
const { data: ticketTypes } = await supabase
.from('ticket_types')
.select('id, quantity')
.eq('event_id', eventId)
.eq('is_active', true);
// Calculate stats
const ticketsSold = tickets?.length || 0;
const totalRevenue = tickets?.reduce((sum, ticket) => sum + ticket.price_paid, 0) || 0;
const netRevenue = totalRevenue * 0.97; // Assuming 3% platform fee
const checkedIn = tickets?.filter(ticket => ticket.checked_in).length || 0;
const totalCapacity = ticketTypes?.reduce((sum, type) => sum + type.quantity, 0) || 0;
const ticketsAvailable = totalCapacity - ticketsSold;
// Update UI
document.getElementById('tickets-sold').textContent = ticketsSold.toString();
document.getElementById('tickets-available').textContent = ticketsAvailable.toString();
document.getElementById('checked-in').textContent = checkedIn.toString();
document.getElementById('net-revenue').textContent = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(netRevenue / 100);
} catch (error) {
console.error('Error loading quick stats:', error);
}
}
</script>