Files
blackcanyontickets/src/lib/marketing-kit.ts
dzinesco 26a87d0d00 feat: Complete platform enhancement with multi-tenant architecture
Major additions:
- Territory manager system with application workflow
- Custom pricing and page builder with Craft.js
- Enhanced Stripe Connect onboarding
- CodeReadr QR scanning integration
- Kiosk mode for venue sales
- Super admin dashboard and analytics
- MCP integration for AI-powered operations

Infrastructure improvements:
- Centralized API client and routing system
- Enhanced authentication with organization context
- Comprehensive theme management system
- Advanced event management with custom tabs
- Performance monitoring and accessibility features

Database schema updates:
- Territory management tables
- Custom pages and pricing structures
- Kiosk PIN system
- Enhanced organization profiles
- CodeReadr integration tables

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-12 18:21:40 -06:00

317 lines
8.4 KiB
TypeScript

import { createClient } from '@supabase/supabase-js';
import type { Database } from './database.types';
const supabase = createClient<Database>(
import.meta.env.PUBLIC_SUPABASE_URL,
import.meta.env.PUBLIC_SUPABASE_ANON_KEY
);
// OpenAI configuration
const OPENAI_API_KEY = import.meta.env.OPENAI_API_KEY;
const OPENAI_API_URL = 'https://api.openai.com/v1/chat/completions';
export interface MarketingAsset {
id: string;
event_id: string;
asset_type: 'flyer' | 'social_post' | 'email_banner' | 'web_banner' | 'print_ad';
asset_url: string;
asset_data: any;
created_at: string;
}
export interface MarketingKitData {
event: {
id: string;
title: string;
description: string;
start_time: string;
venue: string;
image_url?: string;
};
assets: MarketingAsset[];
social_links: {
facebook?: string;
twitter?: string;
instagram?: string;
website?: string;
};
}
export interface SocialMediaContent {
id?: string;
platform: 'facebook' | 'twitter' | 'instagram' | 'linkedin';
content: string;
hashtags: string[];
image_url?: string;
tone?: 'professional' | 'casual' | 'exciting' | 'informative' | 'urgent';
votes?: number;
generated_at?: string;
}
export interface EmailTemplate {
subject: string;
html_content: string;
text_content: string;
preview_text: string;
}
export async function loadMarketingKit(eventId: string): Promise<MarketingKitData | null> {
try {
// Load event data
const { data: event, error: eventError } = await supabase
.from('events')
.select('id, title, description, start_time, venue, image_url')
.eq('id', eventId)
.single();
if (eventError) {
return null;
}
// Since marketing_kit_assets table doesn't exist, return empty assets
// This can be implemented later when the table is created
return {
event: {
...event,
start_time: event.start_time || '',
description: event.description || ''
},
assets: [], // Empty assets for now
social_links: {}
};
} catch (error) {
return null;
}
}
export async function generateMarketingKit(eventId: string): Promise<MarketingKitData | null> {
try {
const response = await fetch(`/api/events/${eventId}/marketing-kit`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error('Failed to generate marketing kit');
}
const data = await response.json();
return data;
} catch (error) {
return null;
}
}
export async function saveMarketingAsset(eventId: string, assetType: string, assetData: any): Promise<MarketingAsset | null> {
try {
// Since marketing_kit_assets table doesn't exist, return a mock asset
// This can be implemented later when the table is created
return {
id: `temp-${Date.now()}`,
event_id: eventId,
asset_type: assetType as any,
asset_url: assetData.url || '',
asset_data: assetData,
created_at: new Date().toISOString()
};
} catch (error) {
return null;
}
}
export async function updateSocialLinks(eventId: string, socialLinks: Record<string, string>): Promise<boolean> {
try {
const { error } = await supabase
.from('events')
.update({ social_links: socialLinks })
.eq('id', eventId);
if (error) {
return false;
}
return true;
} catch (error) {
return false;
}
}
export function generateSocialMediaContent(event: MarketingKitData['event']): SocialMediaContent[] {
const eventDate = new Date(event.date).toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
});
const baseHashtags = ['#event', '#tickets', '#blackcanyontickets'];
const eventHashtags = event.title.toLowerCase()
.split(' ')
.filter(word => word.length > 3)
.map(word => `#${word.replace(/[^a-zA-Z0-9]/g, '')}`);
const allHashtags = [...baseHashtags, ...eventHashtags.slice(0, 3)];
return [
{
platform: 'facebook',
content: `🎉 Don't miss ${event.title}! Join us on ${eventDate} at ${event.venue}.
${event.description}
Get your tickets now! Link in bio.`,
hashtags: allHashtags,
image_url: event.image_url
},
{
platform: 'twitter',
content: `🎫 ${event.title} - ${eventDate} at ${event.venue}. Get tickets now!`,
hashtags: allHashtags,
image_url: event.image_url
},
{
platform: 'instagram',
content: `${event.title}
📅 ${eventDate}
📍 ${event.venue}
${event.description}
Tickets available now! Link in bio 🎟️`,
hashtags: allHashtags,
image_url: event.image_url
},
{
platform: 'linkedin',
content: `We're excited to announce ${event.title}, taking place on ${eventDate} at ${event.venue}.
${event.description}
Professional networking and entertainment combined. Reserve your spot today.`,
hashtags: allHashtags.slice(0, 3), // LinkedIn prefers fewer hashtags
image_url: event.image_url
}
];
}
export function generateEmailTemplate(event: MarketingKitData['event']): EmailTemplate {
const eventDate = new Date(event.date).toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: '2-digit'
});
const subject = `Don't Miss ${event.title} - ${eventDate}`;
const previewText = `Join us for an unforgettable experience at ${event.venue}`;
const htmlContent = `
<html>
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
<div style="max-width: 600px; margin: 0 auto; padding: 20px;">
${event.image_url ? `<img src="${event.image_url}" alt="${event.title}" style="width: 100%; max-width: 600px; height: auto; border-radius: 8px; margin-bottom: 20px;">` : ''}
<h1 style="color: #2563eb; margin-bottom: 20px;">${event.title}</h1>
<div style="background: #f8fafc; padding: 20px; border-radius: 8px; margin-bottom: 20px;">
<h2 style="margin-top: 0; color: #1e293b;">Event Details</h2>
<p><strong>Date:</strong> ${eventDate}</p>
<p><strong>Venue:</strong> ${event.venue}</p>
</div>
<p style="font-size: 16px; margin-bottom: 20px;">${event.description}</p>
<div style="text-align: center; margin: 30px 0;">
<a href="#" style="background: linear-gradient(135deg, #2563eb 0%, #7c3aed 100%); color: white; padding: 15px 30px; text-decoration: none; border-radius: 8px; font-weight: bold; display: inline-block;">Get Tickets Now</a>
</div>
<div style="border-top: 1px solid #e2e8f0; padding-top: 20px; margin-top: 30px; text-align: center; color: #64748b; font-size: 14px;">
<p>Powered by Black Canyon Tickets</p>
</div>
</div>
</body>
</html>
`;
const textContent = `
${event.title}
Event Details:
Date: ${eventDate}
Venue: ${event.venue}
${event.description}
Get your tickets now: [TICKET_LINK]
Powered by Black Canyon Tickets
`;
return {
subject,
html_content: htmlContent,
text_content: textContent,
preview_text: previewText
};
}
export function generateFlyerData(event: MarketingKitData['event']): any {
return {
title: event.title,
date: new Date(event.date).toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: '2-digit'
}),
venue: event.venue,
description: event.description,
image_url: event.image_url,
qr_code_url: `https://portal.blackcanyontickets.com/e/${event.id}`,
template: 'premium',
colors: {
primary: '#2563eb',
secondary: '#7c3aed',
accent: '#06b6d4',
text: '#1e293b'
}
};
}
export async function downloadAsset(assetUrl: string, filename: string): Promise<void> {
try {
const response = await fetch(assetUrl);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
}
}
export function copyToClipboard(text: string): Promise<void> {
return navigator.clipboard.writeText(text);
}