Major fixes and improvements: - Fixed edit event button functionality with proper event handlers and DOM ready state checking - Added status column to tickets table via Supabase migration to resolve 500 API errors - Updated stats API to correctly calculate revenue from decimal price values - Resolved authentication redirect loops by fixing cookie configuration for Docker environment - Fixed Permissions-Policy header syntax errors - Added comprehensive debugging and error handling for event management - Implemented modal-based event editing with form validation and API integration - Enhanced event data loading with proper error handling and user feedback 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
247 lines
7.8 KiB
JavaScript
247 lines
7.8 KiB
JavaScript
const { createClient } = require('@supabase/supabase-js');
|
|
require('dotenv').config();
|
|
|
|
const supabaseUrl = process.env.PUBLIC_SUPABASE_URL;
|
|
const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
|
|
if (!supabaseUrl || !supabaseServiceKey) {
|
|
console.error('❌ Missing required environment variables');
|
|
process.exit(1);
|
|
}
|
|
|
|
const supabase = createClient(supabaseUrl, supabaseServiceKey, {
|
|
auth: {
|
|
autoRefreshToken: false,
|
|
persistSession: false
|
|
}
|
|
});
|
|
|
|
async function createTestData() {
|
|
const eventId = '7ac12bd2-8509-4db3-b1bc-98a808646311';
|
|
|
|
console.log('🎫 Creating test ticket types and data...');
|
|
|
|
try {
|
|
// Step 1: Check if ticket types already exist
|
|
const { data: existingTicketTypes, error: checkError } = await supabase
|
|
.from('ticket_types')
|
|
.select('id, name, price')
|
|
.eq('event_id', eventId);
|
|
|
|
if (checkError) {
|
|
console.error('❌ Error checking existing ticket types:', checkError);
|
|
return;
|
|
}
|
|
|
|
if (existingTicketTypes && existingTicketTypes.length > 0) {
|
|
console.log('✓ Ticket types already exist:', existingTicketTypes.map(t => t.name).join(', '));
|
|
} else {
|
|
console.log('Creating ticket types...');
|
|
|
|
// Create ticket types
|
|
const ticketTypes = [
|
|
{
|
|
event_id: eventId,
|
|
name: 'General Admission',
|
|
description: 'Standard entry to the event',
|
|
price: 50.00,
|
|
quantity_available: 100,
|
|
quantity_sold: 0,
|
|
is_active: true,
|
|
sort_order: 0
|
|
},
|
|
{
|
|
event_id: eventId,
|
|
name: 'VIP',
|
|
description: 'VIP access with premium seating',
|
|
price: 125.00,
|
|
quantity_available: 25,
|
|
quantity_sold: 0,
|
|
is_active: true,
|
|
sort_order: 1
|
|
},
|
|
{
|
|
event_id: eventId,
|
|
name: 'Student',
|
|
description: 'Discounted tickets for students',
|
|
price: 25.00,
|
|
quantity_available: 50,
|
|
quantity_sold: 0,
|
|
is_active: true,
|
|
sort_order: 2
|
|
}
|
|
];
|
|
|
|
const { data: newTicketTypes, error: createError } = await supabase
|
|
.from('ticket_types')
|
|
.insert(ticketTypes)
|
|
.select();
|
|
|
|
if (createError) {
|
|
console.error('❌ Error creating ticket types:', createError);
|
|
return;
|
|
}
|
|
|
|
console.log('✓ Created ticket types:', newTicketTypes.map(t => t.name).join(', '));
|
|
}
|
|
|
|
// Step 2: Check if sample tickets exist
|
|
const { data: existingTickets, error: ticketCheckError } = await supabase
|
|
.from('tickets')
|
|
.select('id, price')
|
|
.eq('event_id', eventId);
|
|
|
|
if (ticketCheckError) {
|
|
console.error('❌ Error checking tickets:', ticketCheckError);
|
|
return;
|
|
}
|
|
|
|
if (existingTickets && existingTickets.length > 0) {
|
|
console.log(`✓ ${existingTickets.length} tickets already exist`);
|
|
} else {
|
|
console.log('Creating sample tickets...');
|
|
|
|
// Get ticket types to create tickets for
|
|
const { data: ticketTypes, error: ttError } = await supabase
|
|
.from('ticket_types')
|
|
.select('id, name, price')
|
|
.eq('event_id', eventId);
|
|
|
|
if (ttError || !ticketTypes || ticketTypes.length === 0) {
|
|
console.error('❌ No ticket types found to create tickets for');
|
|
return;
|
|
}
|
|
|
|
// Create some sample sold tickets
|
|
const sampleTickets = [];
|
|
|
|
// 5 general admission tickets
|
|
const gaType = ticketTypes.find(t => t.name === 'General Admission');
|
|
if (gaType) {
|
|
for (let i = 1; i <= 5; i++) {
|
|
sampleTickets.push({
|
|
event_id: eventId,
|
|
ticket_type_id: gaType.id,
|
|
price: gaType.price,
|
|
purchaser_email: `customer${i}@example.com`,
|
|
purchaser_name: `Customer ${i}`,
|
|
checked_in: i <= 2, // First 2 are checked in
|
|
scanned_at: i <= 2 ? new Date().toISOString() : null,
|
|
created_at: new Date(Date.now() - (i * 24 * 60 * 60 * 1000)).toISOString() // Spread over last 5 days
|
|
});
|
|
}
|
|
}
|
|
|
|
// 2 VIP tickets
|
|
const vipType = ticketTypes.find(t => t.name === 'VIP');
|
|
if (vipType) {
|
|
for (let i = 1; i <= 2; i++) {
|
|
sampleTickets.push({
|
|
event_id: eventId,
|
|
ticket_type_id: vipType.id,
|
|
price: vipType.price,
|
|
purchaser_email: `vip${i}@example.com`,
|
|
purchaser_name: `VIP Customer ${i}`,
|
|
checked_in: i === 1, // First VIP is checked in
|
|
scanned_at: i === 1 ? new Date().toISOString() : null,
|
|
created_at: new Date(Date.now() - (i * 12 * 60 * 60 * 1000)).toISOString() // Recent purchases
|
|
});
|
|
}
|
|
}
|
|
|
|
// 1 student ticket
|
|
const studentType = ticketTypes.find(t => t.name === 'Student');
|
|
if (studentType) {
|
|
sampleTickets.push({
|
|
event_id: eventId,
|
|
ticket_type_id: studentType.id,
|
|
price: studentType.price,
|
|
purchaser_email: 'student1@university.edu',
|
|
purchaser_name: 'Student User',
|
|
checked_in: false,
|
|
scanned_at: null,
|
|
created_at: new Date(Date.now() - (6 * 60 * 60 * 1000)).toISOString() // 6 hours ago
|
|
});
|
|
}
|
|
|
|
if (sampleTickets.length > 0) {
|
|
const { data: newTickets, error: createTicketsError } = await supabase
|
|
.from('tickets')
|
|
.insert(sampleTickets)
|
|
.select();
|
|
|
|
if (createTicketsError) {
|
|
console.error('❌ Error creating sample tickets:', createTicketsError);
|
|
return;
|
|
}
|
|
|
|
console.log(`✓ Created ${newTickets.length} sample tickets`);
|
|
}
|
|
}
|
|
|
|
// Step 3: Test the stats API by calling it directly
|
|
console.log('\n🧪 Testing stats calculation...');
|
|
|
|
const { data: tickets, error: ticketsError } = await supabase
|
|
.from('tickets')
|
|
.select(`
|
|
id,
|
|
ticket_type_id,
|
|
price,
|
|
checked_in,
|
|
scanned_at
|
|
`)
|
|
.eq('event_id', eventId);
|
|
|
|
if (ticketsError) {
|
|
console.error('❌ Error loading tickets for stats:', ticketsError);
|
|
return;
|
|
}
|
|
|
|
const { data: ticketTypes, error: ticketTypesError } = await supabase
|
|
.from('ticket_types')
|
|
.select('id, quantity_available, price')
|
|
.eq('event_id', eventId);
|
|
|
|
if (ticketTypesError) {
|
|
console.error('❌ Error loading ticket types for stats:', ticketTypesError);
|
|
return;
|
|
}
|
|
|
|
// Calculate stats manually to verify
|
|
const soldTickets = tickets || []; // All tickets in table are considered sold
|
|
const checkedInTickets = tickets?.filter(t => t.checked_in || t.scanned_at) || [];
|
|
|
|
const totalRevenue = soldTickets.reduce((sum, ticket) => {
|
|
const priceInDollars = ticket.price || 0;
|
|
return sum + Math.round(priceInDollars * 100); // Convert to cents
|
|
}, 0);
|
|
|
|
const totalAvailable = ticketTypes?.reduce((sum, type) => sum + (type.quantity_available || 0), 0) || 0;
|
|
const ticketsSold = soldTickets.length;
|
|
const ticketsAvailable = totalAvailable - ticketsSold;
|
|
|
|
const stats = {
|
|
totalRevenue,
|
|
ticketsSold,
|
|
ticketsAvailable,
|
|
checkedIn: checkedInTickets.length
|
|
};
|
|
|
|
console.log('✓ Calculated stats:', stats);
|
|
|
|
console.log('\n🎉 Test data setup complete!');
|
|
console.log('=====================================');
|
|
console.log(`Event ID: ${eventId}`);
|
|
console.log(`Ticket types: ${ticketTypes?.length || 0}`);
|
|
console.log(`Total tickets: ${tickets?.length || 0}`);
|
|
console.log(`Sold tickets: ${ticketsSold}`);
|
|
console.log(`Checked in: ${checkedInTickets.length}`);
|
|
console.log(`Total revenue: $${(totalRevenue / 100).toFixed(2)}`);
|
|
|
|
} catch (error) {
|
|
console.error('💥 Unexpected error:', error);
|
|
}
|
|
}
|
|
|
|
createTestData(); |