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:
147
src/lib/qr-generator.ts
Normal file
147
src/lib/qr-generator.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import QRCode from 'qrcode';
|
||||
|
||||
interface QRCodeOptions {
|
||||
size?: number;
|
||||
margin?: number;
|
||||
color?: {
|
||||
dark?: string;
|
||||
light?: string;
|
||||
};
|
||||
errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H';
|
||||
}
|
||||
|
||||
interface QRCodeResult {
|
||||
dataUrl: string;
|
||||
svg: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export class QRCodeGenerator {
|
||||
private defaultOptions: QRCodeOptions = {
|
||||
size: 256,
|
||||
margin: 2,
|
||||
color: {
|
||||
dark: '#000000',
|
||||
light: '#FFFFFF'
|
||||
},
|
||||
errorCorrectionLevel: 'M'
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate QR code for event ticket URL
|
||||
*/
|
||||
async generateEventQR(eventSlug: string, options: QRCodeOptions = {}): Promise<QRCodeResult> {
|
||||
const ticketUrl = `${import.meta.env.PUBLIC_SITE_URL || 'https://portal.blackcanyontickets.com'}/e/${eventSlug}`;
|
||||
return this.generateQRCode(ticketUrl, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate QR code for any URL
|
||||
*/
|
||||
async generateQRCode(url: string, options: QRCodeOptions = {}): Promise<QRCodeResult> {
|
||||
const mergedOptions = { ...this.defaultOptions, ...options };
|
||||
|
||||
try {
|
||||
// Generate data URL (base64 PNG)
|
||||
const dataUrl = await QRCode.toDataURL(url, {
|
||||
width: mergedOptions.size,
|
||||
margin: mergedOptions.margin,
|
||||
color: mergedOptions.color,
|
||||
errorCorrectionLevel: mergedOptions.errorCorrectionLevel
|
||||
});
|
||||
|
||||
// Generate SVG
|
||||
const svg = await QRCode.toString(url, {
|
||||
type: 'svg',
|
||||
width: mergedOptions.size,
|
||||
margin: mergedOptions.margin,
|
||||
color: mergedOptions.color,
|
||||
errorCorrectionLevel: mergedOptions.errorCorrectionLevel
|
||||
});
|
||||
|
||||
return {
|
||||
dataUrl,
|
||||
svg,
|
||||
size: mergedOptions.size || this.defaultOptions.size!
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error generating QR code:', error);
|
||||
throw new Error('Failed to generate QR code');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate QR code with custom branding/logo overlay
|
||||
*/
|
||||
async generateBrandedQR(
|
||||
url: string,
|
||||
logoDataUrl?: string,
|
||||
options: QRCodeOptions = {}
|
||||
): Promise<QRCodeResult> {
|
||||
const qrResult = await this.generateQRCode(url, {
|
||||
...options,
|
||||
errorCorrectionLevel: 'H' // Higher error correction for logo overlay
|
||||
});
|
||||
|
||||
if (!logoDataUrl) {
|
||||
return qrResult;
|
||||
}
|
||||
|
||||
// If logo is provided, we'll need to composite it onto the QR code
|
||||
// This would typically be done server-side with canvas or image processing
|
||||
// For now, we'll return the base QR code and handle logo overlay in the image generation
|
||||
return qrResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate URL before QR generation
|
||||
*/
|
||||
private validateUrl(url: string): boolean {
|
||||
try {
|
||||
new URL(url);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get optimal QR code size for different use cases
|
||||
*/
|
||||
getRecommendedSize(useCase: 'social' | 'flyer' | 'email' | 'print'): number {
|
||||
switch (useCase) {
|
||||
case 'social':
|
||||
return 200;
|
||||
case 'flyer':
|
||||
return 300;
|
||||
case 'email':
|
||||
return 150;
|
||||
case 'print':
|
||||
return 600;
|
||||
default:
|
||||
return 256;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate multiple QR code formats for different use cases
|
||||
*/
|
||||
async generateMultiFormat(url: string): Promise<{
|
||||
social: QRCodeResult;
|
||||
flyer: QRCodeResult;
|
||||
email: QRCodeResult;
|
||||
print: QRCodeResult;
|
||||
}> {
|
||||
const [social, flyer, email, print] = await Promise.all([
|
||||
this.generateQRCode(url, { size: this.getRecommendedSize('social') }),
|
||||
this.generateQRCode(url, { size: this.getRecommendedSize('flyer') }),
|
||||
this.generateQRCode(url, { size: this.getRecommendedSize('email') }),
|
||||
this.generateQRCode(url, { size: this.getRecommendedSize('print') })
|
||||
]);
|
||||
|
||||
return { social, flyer, email, print };
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const qrGenerator = new QRCodeGenerator();
|
||||
Reference in New Issue
Block a user