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:
54
supabase/migrations/20250708_add_event_image_support.sql
Normal file
54
supabase/migrations/20250708_add_event_image_support.sql
Normal file
@@ -0,0 +1,54 @@
|
||||
-- Add image_url column to events table
|
||||
ALTER TABLE events ADD COLUMN image_url TEXT;
|
||||
|
||||
-- Create storage bucket for event images
|
||||
INSERT INTO storage.buckets (id, name, public)
|
||||
VALUES ('event-images', 'event-images', true);
|
||||
|
||||
-- Create storage policy for authenticated users to upload event images
|
||||
CREATE POLICY "Users can upload event images" ON storage.objects
|
||||
FOR INSERT WITH CHECK (
|
||||
bucket_id = 'event-images' AND
|
||||
auth.uid() IS NOT NULL
|
||||
);
|
||||
|
||||
-- Create storage policy for public read access to event images
|
||||
CREATE POLICY "Public read access to event images" ON storage.objects
|
||||
FOR SELECT USING (bucket_id = 'event-images');
|
||||
|
||||
-- Create storage policy for users to delete their own event images
|
||||
CREATE POLICY "Users can delete their own event images" ON storage.objects
|
||||
FOR DELETE USING (
|
||||
bucket_id = 'event-images' AND
|
||||
auth.uid() IS NOT NULL
|
||||
);
|
||||
|
||||
-- Update RLS policy for events to include image_url in SELECT
|
||||
DROP POLICY IF EXISTS "Users can view events in their organization" ON events;
|
||||
CREATE POLICY "Users can view events in their organization" ON events
|
||||
FOR SELECT USING (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- Update RLS policy for events to include image_url in INSERT
|
||||
DROP POLICY IF EXISTS "Users can create events in their organization" ON events;
|
||||
CREATE POLICY "Users can create events in their organization" ON events
|
||||
FOR INSERT WITH CHECK (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- Update RLS policy for events to include image_url in UPDATE
|
||||
DROP POLICY IF EXISTS "Users can update events in their organization" ON events;
|
||||
CREATE POLICY "Users can update events in their organization" ON events
|
||||
FOR UPDATE USING (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- Add index on image_url for faster queries
|
||||
CREATE INDEX IF NOT EXISTS idx_events_image_url ON events(image_url) WHERE image_url IS NOT NULL;
|
||||
203
supabase/migrations/20250708_add_marketing_kit_support.sql
Normal file
203
supabase/migrations/20250708_add_marketing_kit_support.sql
Normal file
@@ -0,0 +1,203 @@
|
||||
-- Marketing Kit support for Event Marketing Toolkit
|
||||
-- This migration adds tables to store marketing kit assets and templates
|
||||
|
||||
-- Table to store marketing kit assets for each event
|
||||
CREATE TABLE IF NOT EXISTS marketing_kit_assets (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
event_id UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE,
|
||||
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
asset_type TEXT NOT NULL CHECK (asset_type IN ('social_post', 'flyer', 'email_template', 'qr_code')),
|
||||
platform TEXT, -- For social posts: 'facebook', 'instagram', 'twitter', 'linkedin'
|
||||
title TEXT NOT NULL,
|
||||
content TEXT, -- JSON content for templates/configs
|
||||
image_url TEXT, -- Generated image URL
|
||||
download_url TEXT, -- Direct download URL for assets
|
||||
file_format TEXT, -- 'png', 'jpg', 'html', 'txt', etc.
|
||||
dimensions JSON, -- {"width": 1080, "height": 1080} for images
|
||||
generated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
expires_at TIMESTAMP WITH TIME ZONE, -- For temporary download links
|
||||
metadata JSON, -- Additional configuration/settings
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Table to store reusable marketing templates
|
||||
CREATE TABLE IF NOT EXISTS marketing_templates (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
template_type TEXT NOT NULL CHECK (template_type IN ('social_post', 'flyer', 'email_template')),
|
||||
platform TEXT, -- For social posts: 'facebook', 'instagram', 'twitter', 'linkedin'
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
template_data JSON NOT NULL, -- Template configuration and layout
|
||||
preview_image_url TEXT,
|
||||
is_default BOOLEAN DEFAULT false,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_by UUID REFERENCES users(id),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Table to track marketing kit generations and downloads
|
||||
CREATE TABLE IF NOT EXISTS marketing_kit_generations (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
event_id UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE,
|
||||
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||
generated_by UUID REFERENCES users(id),
|
||||
generation_type TEXT NOT NULL CHECK (generation_type IN ('full_kit', 'individual_asset')),
|
||||
assets_included TEXT[], -- Array of asset types included
|
||||
zip_file_url TEXT, -- URL to download complete kit
|
||||
zip_expires_at TIMESTAMP WITH TIME ZONE,
|
||||
generation_status TEXT DEFAULT 'pending' CHECK (generation_status IN ('pending', 'processing', 'completed', 'failed')),
|
||||
error_message TEXT,
|
||||
download_count INTEGER DEFAULT 0,
|
||||
last_downloaded_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Indexes for performance
|
||||
CREATE INDEX IF NOT EXISTS idx_marketing_kit_assets_event_id ON marketing_kit_assets(event_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_marketing_kit_assets_org_id ON marketing_kit_assets(organization_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_marketing_kit_assets_type ON marketing_kit_assets(asset_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_marketing_templates_org_id ON marketing_templates(organization_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_marketing_templates_type ON marketing_templates(template_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_marketing_kit_generations_event_id ON marketing_kit_generations(event_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_marketing_kit_generations_org_id ON marketing_kit_generations(organization_id);
|
||||
|
||||
-- RLS Policies for multi-tenant security
|
||||
ALTER TABLE marketing_kit_assets ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE marketing_templates ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE marketing_kit_generations ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Policies for marketing_kit_assets
|
||||
CREATE POLICY "Users can view marketing kit assets from their organization" ON marketing_kit_assets
|
||||
FOR SELECT USING (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can create marketing kit assets for their organization" ON marketing_kit_assets
|
||||
FOR INSERT WITH CHECK (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can update marketing kit assets from their organization" ON marketing_kit_assets
|
||||
FOR UPDATE USING (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can delete marketing kit assets from their organization" ON marketing_kit_assets
|
||||
FOR DELETE USING (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- Policies for marketing_templates
|
||||
CREATE POLICY "Users can view marketing templates from their organization or default templates" ON marketing_templates
|
||||
FOR SELECT USING (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE id = auth.uid()
|
||||
)
|
||||
OR organization_id IS NULL -- Global default templates
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can create marketing templates for their organization" ON marketing_templates
|
||||
FOR INSERT WITH CHECK (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can update marketing templates from their organization" ON marketing_templates
|
||||
FOR UPDATE USING (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can delete marketing templates from their organization" ON marketing_templates
|
||||
FOR DELETE USING (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- Policies for marketing_kit_generations
|
||||
CREATE POLICY "Users can view marketing kit generations from their organization" ON marketing_kit_generations
|
||||
FOR SELECT USING (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can create marketing kit generations for their organization" ON marketing_kit_generations
|
||||
FOR INSERT WITH CHECK (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can update marketing kit generations from their organization" ON marketing_kit_generations
|
||||
FOR UPDATE USING (
|
||||
organization_id IN (
|
||||
SELECT organization_id FROM users WHERE id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- Admin bypass policies
|
||||
CREATE POLICY "Admins can manage all marketing kit assets" ON marketing_kit_assets
|
||||
FOR ALL USING (is_admin(auth.uid()));
|
||||
|
||||
CREATE POLICY "Admins can manage all marketing templates" ON marketing_templates
|
||||
FOR ALL USING (is_admin(auth.uid()));
|
||||
|
||||
CREATE POLICY "Admins can manage all marketing kit generations" ON marketing_kit_generations
|
||||
FOR ALL USING (is_admin(auth.uid()));
|
||||
|
||||
-- Insert some default templates
|
||||
INSERT INTO marketing_templates (name, description, template_type, platform, template_data, is_default, organization_id) VALUES
|
||||
-- Facebook Post Template
|
||||
('Default Facebook Post', 'Standard Facebook event promotion post', 'social_post', 'facebook',
|
||||
'{"background": "gradient-blue", "textColor": "#FFFFFF", "layout": "center", "includeQR": true, "dimensions": {"width": 1200, "height": 630}}',
|
||||
true, NULL),
|
||||
|
||||
-- Instagram Post Template
|
||||
('Default Instagram Post', 'Square Instagram event promotion post', 'social_post', 'instagram',
|
||||
'{"background": "gradient-purple", "textColor": "#FFFFFF", "layout": "center", "includeQR": true, "dimensions": {"width": 1080, "height": 1080}}',
|
||||
true, NULL),
|
||||
|
||||
-- Twitter Post Template
|
||||
('Default Twitter Post', 'Twitter event promotion post', 'social_post', 'twitter',
|
||||
'{"background": "gradient-blue", "textColor": "#FFFFFF", "layout": "left", "includeQR": true, "dimensions": {"width": 1200, "height": 675}}',
|
||||
true, NULL),
|
||||
|
||||
-- Email Template
|
||||
('Default Email Template', 'Standard event promotion email template', 'email_template', NULL,
|
||||
'{"subject": "You''re Invited: {EVENT_TITLE}", "headerImage": true, "includeQR": true, "ctaText": "Get Your Tickets", "layout": "centered"}',
|
||||
true, NULL),
|
||||
|
||||
-- Flyer Template
|
||||
('Default Event Flyer', 'Standard event flyer design', 'flyer', NULL,
|
||||
'{"background": "gradient-blue", "textColor": "#FFFFFF", "layout": "poster", "includeQR": true, "dimensions": {"width": 1080, "height": 1350}}',
|
||||
true, NULL);
|
||||
|
||||
-- Trigger to update updated_at timestamp
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
CREATE TRIGGER update_marketing_kit_assets_updated_at BEFORE UPDATE ON marketing_kit_assets FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_marketing_templates_updated_at BEFORE UPDATE ON marketing_templates FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_marketing_kit_generations_updated_at BEFORE UPDATE ON marketing_kit_generations FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
25
supabase/migrations/20250708_add_referral_tracking.sql
Normal file
25
supabase/migrations/20250708_add_referral_tracking.sql
Normal file
@@ -0,0 +1,25 @@
|
||||
-- Add referral tracking columns to purchase_attempts table
|
||||
ALTER TABLE purchase_attempts
|
||||
ADD COLUMN IF NOT EXISTS referral_source TEXT,
|
||||
ADD COLUMN IF NOT EXISTS utm_campaign TEXT,
|
||||
ADD COLUMN IF NOT EXISTS utm_medium TEXT,
|
||||
ADD COLUMN IF NOT EXISTS utm_source TEXT,
|
||||
ADD COLUMN IF NOT EXISTS utm_term TEXT,
|
||||
ADD COLUMN IF NOT EXISTS utm_content TEXT,
|
||||
ADD COLUMN IF NOT EXISTS referrer_url TEXT,
|
||||
ADD COLUMN IF NOT EXISTS landing_page TEXT;
|
||||
|
||||
-- Add indexes for better query performance
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_attempts_referral_source ON purchase_attempts(referral_source);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_attempts_utm_source ON purchase_attempts(utm_source);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_attempts_utm_campaign ON purchase_attempts(utm_campaign);
|
||||
|
||||
-- Add comments to explain the columns
|
||||
COMMENT ON COLUMN purchase_attempts.referral_source IS 'High-level referral source (e.g., google, facebook, direct, email)';
|
||||
COMMENT ON COLUMN purchase_attempts.utm_campaign IS 'Campaign name from UTM parameters';
|
||||
COMMENT ON COLUMN purchase_attempts.utm_medium IS 'Medium from UTM parameters (e.g., email, social, paid)';
|
||||
COMMENT ON COLUMN purchase_attempts.utm_source IS 'Source from UTM parameters (e.g., google, facebook, newsletter)';
|
||||
COMMENT ON COLUMN purchase_attempts.utm_term IS 'Term from UTM parameters (paid search keywords)';
|
||||
COMMENT ON COLUMN purchase_attempts.utm_content IS 'Content from UTM parameters (ad variant)';
|
||||
COMMENT ON COLUMN purchase_attempts.referrer_url IS 'Full HTTP referrer URL';
|
||||
COMMENT ON COLUMN purchase_attempts.landing_page IS 'Page where user first landed on the site';
|
||||
30
supabase/migrations/20250708_add_social_media_links.sql
Normal file
30
supabase/migrations/20250708_add_social_media_links.sql
Normal file
@@ -0,0 +1,30 @@
|
||||
-- Add social media links and website to events table for marketing kit
|
||||
ALTER TABLE events ADD COLUMN IF NOT EXISTS social_links JSON DEFAULT '{}';
|
||||
ALTER TABLE events ADD COLUMN IF NOT EXISTS website_url TEXT;
|
||||
ALTER TABLE events ADD COLUMN IF NOT EXISTS contact_email TEXT;
|
||||
|
||||
-- Add social media links to organizations table as well for branding
|
||||
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS social_links JSON DEFAULT '{}';
|
||||
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS website_url TEXT;
|
||||
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS contact_email TEXT;
|
||||
|
||||
-- Update the marketing templates to include social handles
|
||||
UPDATE marketing_templates
|
||||
SET template_data = jsonb_set(
|
||||
template_data::jsonb,
|
||||
'{includeSocialHandles}',
|
||||
'true'::jsonb
|
||||
)
|
||||
WHERE template_type = 'social_post';
|
||||
|
||||
-- Add some example social links structure as comments
|
||||
-- Social links JSON structure:
|
||||
-- {
|
||||
-- "facebook": "https://facebook.com/yourpage",
|
||||
-- "instagram": "https://instagram.com/yourhandle",
|
||||
-- "twitter": "https://twitter.com/yourhandle",
|
||||
-- "linkedin": "https://linkedin.com/company/yourcompany",
|
||||
-- "youtube": "https://youtube.com/channel/yourchannel",
|
||||
-- "tiktok": "https://tiktok.com/@yourhandle",
|
||||
-- "website": "https://yourwebsite.com"
|
||||
-- }
|
||||
Reference in New Issue
Block a user