Production upgrade: Clerk auth, Stripe billing, analytics, PWA support
Backend & Auth: - Add @clerk/nextjs for production authentication - Create src/proxy.ts with clerkMiddleware() for route protection - Implement multi-tenant auth with role-based access control - Add Clerk components (Show, UserButton, SignInButton, SignUpButton) Billing & Payments: - Full Stripe integration (subscriptions, add-ons, customer portal) - Plan tiers: Starter 9/mo, Farm 49/mo, Enterprise 99/mo - Webhook handling for subscription events - createSubscription(), createAddonSubscription(), createCustomerPortalSession() API & Security: - Rate limiting with @upstash/ratelimit (100 req/min API, 20 req/min checkout) - Zod validation schemas for all endpoints (orders, products, campaigns, etc.) - Security headers (CSP, HSTS, X-Frame-Options) - API routes: /api/v1/ with validated, rate-limited endpoints Monitoring: - Sentry error tracking with performance monitoring - PostHog analytics for feature usage, funnels, cohorts - User activity logging and breadcrumb tracking Admin Features: - Analytics dashboard with revenue charts, customer growth, conversion funnel - Onboarding flow with 6-step interactive tour - Referral system with share tracking and reward redemption - Changelog feed with in-app notifications PWA & SEO: - Web app manifest with icons and shortcuts - Service worker for offline support and caching - Full SEO metadata, OpenGraph, Twitter cards - Structured data (JSON-LD) for organization and products Database: - Add referral_codes, changelogs, onboarding_progress tables - Add user_activity_logs, api_keys, notification_preferences - Comprehensive RLS policies for all new tables - Seed data for demo brands and products
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
-- Production Database Migrations
|
||||
-- Add comprehensive RLS policies, audit tables, referral system, and seed data
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- =============================================================================
|
||||
-- REFERRAL SYSTEM
|
||||
-- =============================================================================
|
||||
|
||||
-- Referral codes table
|
||||
CREATE TABLE IF NOT EXISTS referral_codes (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
brand_id UUID REFERENCES brands(id) ON DELETE CASCADE,
|
||||
referrer_user_id UUID NOT NULL,
|
||||
referral_code VARCHAR(50) UNIQUE NOT NULL,
|
||||
referrer_email VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ DEFAULT (NOW() + INTERVAL '1 year'),
|
||||
max_uses INTEGER DEFAULT 1,
|
||||
current_uses INTEGER DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
reward_type VARCHAR(50) DEFAULT 'percentage',
|
||||
reward_value DECIMAL(10,2) DEFAULT 20.00,
|
||||
metadata JSONB DEFAULT '{}'::jsonb
|
||||
);
|
||||
|
||||
-- Referral redemptions table
|
||||
CREATE TABLE IF NOT EXISTS referral_redemptions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
referral_code_id UUID REFERENCES referral_codes(id) ON DELETE CASCADE,
|
||||
brand_id UUID REFERENCES brands(id) ON DELETE CASCADE,
|
||||
referred_user_id UUID NOT NULL,
|
||||
referred_email VARCHAR(255) NOT NULL,
|
||||
redeemed_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
reward_type VARCHAR(50),
|
||||
reward_value DECIMAL(10,2),
|
||||
is_credit_applied BOOLEAN DEFAULT false,
|
||||
signup_plan VARCHAR(50),
|
||||
signup_value DECIMAL(10,2),
|
||||
metadata JSONB DEFAULT '{}'::jsonb
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- CHANGELOG SYSTEM
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS changelogs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
brand_id UUID REFERENCES brands(id) ON DELETE CASCADE,
|
||||
version VARCHAR(50) NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
content JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
released_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
is_published BOOLEAN DEFAULT false,
|
||||
feature_type VARCHAR(50) DEFAULT 'general',
|
||||
metadata JSONB DEFAULT '{}'::jsonb,
|
||||
UNIQUE(brand_id, version)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS changelog_reads (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL,
|
||||
changelog_id UUID REFERENCES changelogs(id) ON DELETE CASCADE,
|
||||
read_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
dismissed BOOLEAN DEFAULT false,
|
||||
UNIQUE(user_id, changelog_id)
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- ONBOARDING PROGRESS
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS onboarding_progress (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
brand_id UUID REFERENCES brands(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL,
|
||||
current_step VARCHAR(50) NOT NULL,
|
||||
completed_steps JSONB DEFAULT '[]'::jsonb,
|
||||
skipped_steps JSONB DEFAULT '[]'::jsonb,
|
||||
data JSONB DEFAULT '{}'::jsonb,
|
||||
started_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
completed_at TIMESTAMPTZ,
|
||||
is_completed BOOLEAN DEFAULT false,
|
||||
metadata JSONB DEFAULT '{}'::jsonb,
|
||||
UNIQUE(brand_id, user_id)
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- USER ACTIVITY / ANALYTICS
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_activity_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL,
|
||||
brand_id UUID REFERENCES brands(id) ON DELETE SET NULL,
|
||||
action VARCHAR(100) NOT NULL,
|
||||
resource_type VARCHAR(50),
|
||||
resource_id UUID,
|
||||
ip_address INET,
|
||||
user_agent TEXT,
|
||||
metadata JSONB DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- API KEYS FOR EXTERNAL INTEGRATIONS
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS api_keys (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
brand_id UUID REFERENCES brands(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
key_hash VARCHAR(255) NOT NULL,
|
||||
key_prefix VARCHAR(20),
|
||||
permissions JSONB DEFAULT '["read"]'::jsonb,
|
||||
rate_limit INTEGER DEFAULT 1000,
|
||||
last_used_at TIMESTAMPTZ,
|
||||
expires_at TIMESTAMPTZ,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID NOT NULL
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- NOTIFICATION PREFERENCES
|
||||
-- =============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS notification_preferences (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL,
|
||||
brand_id UUID REFERENCES brands(id) ON DELETE CASCADE,
|
||||
email_orders BOOLEAN DEFAULT true,
|
||||
email_marketing BOOLEAN DEFAULT false,
|
||||
email_reports BOOLEAN DEFAULT true,
|
||||
email_billing BOOLEAN DEFAULT true,
|
||||
sms_orders BOOLEAN DEFAULT false,
|
||||
sms_marketing BOOLEAN DEFAULT false,
|
||||
push_orders BOOLEAN DEFAULT true,
|
||||
push_marketing BOOLEAN DEFAULT false,
|
||||
quiet_hours_start TIME,
|
||||
quiet_hours_end TIME,
|
||||
timezone VARCHAR(50) DEFAULT 'America/New_York',
|
||||
metadata JSONB DEFAULT '{}'::jsonb,
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(user_id, brand_id)
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- REFINED RLS POLICIES
|
||||
-- =============================================================================
|
||||
|
||||
-- Enable RLS on new tables
|
||||
ALTER TABLE referral_codes ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE referral_redemptions ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE changelogs ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE changelog_reads ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE onboarding_progress ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE user_activity_logs ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE api_keys ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE notification_preferences ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Referral codes policies
|
||||
CREATE POLICY "Users can view their own referral codes"
|
||||
ON referral_codes FOR SELECT
|
||||
USING (referrer_user_id = current_setting('app.current_user_id', true)::uuid);
|
||||
|
||||
CREATE POLICY "Platform admins can manage all referral codes"
|
||||
ON referral_codes FOR ALL
|
||||
USING (current_setting('app.current_user_role', true) = 'platform_admin');
|
||||
|
||||
CREATE POLICY "Brand admins can manage their brand's referral codes"
|
||||
ON referral_codes FOR ALL
|
||||
USING (brand_id = current_setting('app.current_brand_id', true)::uuid);
|
||||
|
||||
-- Changelogs policies - public read for all authenticated
|
||||
CREATE POLICY "Authenticated users can view published changelogs"
|
||||
ON changelogs FOR SELECT
|
||||
USING (
|
||||
is_published = true
|
||||
AND (
|
||||
current_setting('app.current_user_role', true) = 'platform_admin'
|
||||
OR brand_id = current_setting('app.current_brand_id', true)::uuid
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Brand admins can manage their changelogs"
|
||||
ON changelogs FOR ALL
|
||||
USING (brand_id = current_setting('app.current_brand_id', true)::uuid);
|
||||
|
||||
-- Onboarding progress policies
|
||||
CREATE POLICY "Users can view their own onboarding progress"
|
||||
ON onboarding_progress FOR SELECT
|
||||
USING (user_id = current_setting('app.current_user_id', true)::uuid);
|
||||
|
||||
CREATE POLICY "Users can update their own onboarding progress"
|
||||
ON onboarding_progress FOR UPDATE
|
||||
USING (user_id = current_setting('app.current_user_id', true)::uuid);
|
||||
|
||||
CREATE POLICY "Brand admins can view onboarding progress for their brand"
|
||||
ON onboarding_progress FOR SELECT
|
||||
USING (brand_id = current_setting('app.current_brand_id', true)::uuid);
|
||||
|
||||
-- User activity logs - admins only
|
||||
CREATE POLICY "Brand admins can view activity logs for their brand"
|
||||
ON user_activity_logs FOR SELECT
|
||||
USING (
|
||||
brand_id = current_setting('app.current_brand_id', true)::uuid
|
||||
OR current_setting('app.current_user_role', true) = 'platform_admin'
|
||||
);
|
||||
|
||||
-- API keys policies
|
||||
CREATE POLICY "Brand admins can manage their API keys"
|
||||
ON api_keys FOR ALL
|
||||
USING (brand_id = current_setting('app.current_brand_id', true)::uuid);
|
||||
|
||||
CREATE POLICY "Anyone can read active API key prefix (for verification)"
|
||||
ON api_keys FOR SELECT
|
||||
USING (is_active = true);
|
||||
|
||||
-- Notification preferences policies
|
||||
CREATE POLICY "Users can manage their own notification preferences"
|
||||
ON notification_preferences FOR ALL
|
||||
USING (user_id = current_setting('app.current_user_id', true)::uuid);
|
||||
|
||||
CREATE POLICY "Brand admins can view preferences for their brand users"
|
||||
ON notification_preferences FOR SELECT
|
||||
USING (brand_id = current_setting('app.current_brand_id', true)::uuid);
|
||||
|
||||
-- =============================================================================
|
||||
-- COMPREHENSIVE INDEXES
|
||||
-- =============================================================================
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_referral_codes_brand ON referral_codes(brand_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_referral_codes_referrer ON referral_codes(referrer_user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_referral_codes_code ON referral_codes(referral_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_referral_redemptions_referrer ON referral_redemptions(referral_code_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_referral_redemptions_referred ON referral_redemptions(referred_user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_changelogs_brand ON changelogs(brand_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_changelogs_published ON changelogs(is_published, released_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_changelog_reads_user ON changelog_reads(user_id, changelog_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_onboarding_brand ON onboarding_progress(brand_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_onboarding_user ON onboarding_progress(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_activity_user ON user_activity_logs(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_activity_brand ON user_activity_logs(brand_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_activity_created ON user_activity_logs(created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_api_keys_brand ON api_keys(brand_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_api_keys_active ON api_keys(is_active);
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,232 @@
|
||||
-- Seed data for production development and testing
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- =============================================================================
|
||||
-- DEMO BRANDS
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO brands (id, name, slug, plan_tier, max_users, max_stops_monthly, max_products, stripe_customer_id, created_at)
|
||||
VALUES
|
||||
('a1b2c3d4-e5f6-7890-abcd-ef1234567890', 'Sunrise Farms', 'sunrise-farms', 'enterprise', 10, 100, 500, 'cus_demo_sunrise', NOW()),
|
||||
('b2c3d4e5-f6a7-8901-bcde-f12345678901', 'Green Valley Organics', 'green-valley', 'farm', 5, 50, 250, 'cus_demo_green', NOW()),
|
||||
('c3d4e5f6-a7b8-9012-cdef-123456789012', 'Orchard Fresh', 'orchard-fresh', 'starter', 2, 10, 50, 'cus_demo_orchard', NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- DEMO PRODUCTS FOR SUNRISE FARMS
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO products (id, brand_id, name, description, price, unit, category, sku, is_active, is_taxable, image_url, created_at)
|
||||
VALUES
|
||||
('p0010001-0000-0000-0000-000000000001', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Honeycrisp Apples', 'Premium organic Honeycrisp apples, sweet and crisp', 3.99, 'lb', 'Apples', 'APP-HC-001', true, true, NULL, NOW()),
|
||||
('p0010002-0000-0000-0000-000000000002', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Gala Apples', 'Sweet and mild Gala apples, perfect for snacking', 3.49, 'lb', 'Apples', 'APP-GA-001', true, true, NULL, NOW()),
|
||||
('p0010003-0000-0000-0000-000000000003', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Valencia Oranges', 'Fresh Valencia oranges, great for juicing', 2.99, 'lb', 'Oranges', 'ORG-VA-001', true, true, NULL, NOW()),
|
||||
('p0010004-0000-0000-0000-000000000004', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Navel Oranges', 'Seedless Navel oranges, sweet and easy to peel', 3.29, 'lb', 'Oranges', 'ORG-NA-001', true, true, NULL, NOW()),
|
||||
('p0010005-0000-0000-0000-000000000005', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Ruby Red Grapefruit', 'Juicy Ruby Red grapefruit, perfectly balanced sweet-tart', 3.79, 'lb', 'Citrus', 'GRF-RR-001', true, true, NULL, NOW()),
|
||||
('p0010006-0000-0000-0000-000000000006', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Pink Lady Apples', 'Crisp and sweet Pink Lady apples', 4.29, 'lb', 'Apples', 'APP-PL-001', true, true, NULL, NOW()),
|
||||
('p0010007-0000-0000-0000-000000000007', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Tangerines', 'Sweet and juicy tangerines, easy to peel', 4.49, 'lb', 'Citrus', 'CIT-TN-001', true, true, NULL, NOW()),
|
||||
('p0010008-0000-0000-0000-000000000008', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Meyer Lemons', 'Fragrant Meyer lemons, sweeter than regular lemons', 4.99, 'lb', 'Lemons', 'LEM-ME-001', true, true, NULL, NOW()),
|
||||
('p0010009-0000-0000-0000-000000000009', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Lime Mix Pack', 'Seasonal mix of Persian and Key limes', 5.99, 'lb', 'Lemons', 'LIM-MX-001', true, true, NULL, NOW()),
|
||||
('p0010010-0000-0000-0000-000000000010', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Mixed Citrus Box', 'Seasonal assortment of 5+ citrus varieties, 20lb box', 54.99, 'box', 'Boxes', 'BOX-MC-001', true, true, NULL, NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- DEMO STOPS FOR SUNRISE FARMS
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO stops (id, brand_id, name, address, city, state, postal_code, scheduled_at, status, created_at)
|
||||
VALUES
|
||||
('s0010001-0000-0000-0000-000000000001', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Downtown Farmers Market', '123 Main Street', 'Orlando', 'FL', '32801', NOW() + INTERVAL '2 days', 'scheduled', NOW()),
|
||||
('s0010002-0000-0000-0000-000000000002', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Winter Park Community', '456 Park Avenue', 'Winter Park', 'FL', '32789', NOW() + INTERVAL '3 days', 'scheduled', NOW()),
|
||||
('s0010003-0000-0000-0000-000000000003', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Maitland Office Park', '789 Commerce Blvd', 'Maitland', 'FL', '32751', NOW() + INTERVAL '5 days', 'scheduled', NOW()),
|
||||
('s0010004-0000-0000-0000-000000000004', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'UCF Campus Store', '4000 Central Florida Blvd', 'Orlando', 'FL', '32816', NOW() + INTERVAL '7 days', 'scheduled', NOW()),
|
||||
('s0010005-0000-0000-0000-000000000005', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Lake Mary Corporate', '1000 Business Center Dr', 'Lake Mary', 'FL', '32746', NOW() + INTERVAL '10 days', 'scheduled', NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- DEMO CUSTOMERS
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO customers (id, brand_id, email, first_name, last_name, phone, company, created_at)
|
||||
VALUES
|
||||
('c0010001-0000-0000-0000-000000000001', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'john.smith@email.com', 'John', 'Smith', '+1-407-555-0101', 'Smith Family Farm', NOW()),
|
||||
('c0010002-0000-0000-0000-000000000002', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'sarah.jones@email.com', 'Sarah', 'Jones', '+1-407-555-0102', 'Jones Organic Co', NOW()),
|
||||
('c0010003-0000-0000-0000-000000000003', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'mike.wilson@email.com', 'Mike', 'Wilson', '+1-407-555-0103', 'Wilson Grocers', NOW()),
|
||||
('c0010004-0000-0000-0000-000000000004', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'emily.brown@email.com', 'Emily', 'Brown', '+1-407-555-0104', 'Brown Cafe', NOW()),
|
||||
('c0010005-0000-0000-0000-000000000005', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'david.lee@email.com', 'David', 'Lee', '+1-407-555-0105', 'Lee Restaurant Group', NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- DEMO WHOLESALE CUSTOMERS
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO wholesale_customers (id, brand_id, company_name, contact_name, email, phone, credit_limit, payment_terms, is_active, created_at)
|
||||
VALUES
|
||||
('w0010001-0000-0000-0000-000000000001', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Whole Foods Market', 'Amanda Green', 'amanda.g@wholefoods.com', '+1-512-555-0201', 10000.00, 'net30', true, NOW()),
|
||||
('w0010002-0000-0000-0000-000000000002', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Publix Super Markets', 'Robert Baker', 'robert.b@publix.com', '+1-863-555-0202', 25000.00, 'net30', true, NOW()),
|
||||
('w0010003-0000-0000-0000-000000000003', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Fresh Market', 'Lisa Chen', 'lisa.c@freshmarket.com', '+1-919-555-0203', 15000.00, 'net15', true, NOW()),
|
||||
('w0010004-0000-0000-0000-000000000004', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'Trader Joes', 'Mark Johnson', 'mark.j@traderjoes.com', '+1-626-555-0204', 20000.00, 'net30', true, NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- DEMO ORDERS
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO orders (id, brand_id, customer_id, status, fulfillment, total_amount, customer_email, customer_name, customer_address, created_at)
|
||||
VALUES
|
||||
('o0010001-0000-0000-0000-000000000001', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'c0010001-0000-0000-0000-000000000001', 'completed', 'pickup', 45.87, 'john.smith@email.com', 'John Smith', '123 Main St, Orlando FL 32801', NOW() - INTERVAL '7 days'),
|
||||
('o0010002-0000-0000-0000-000000000002', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'c0010002-0000-0000-0000-000000000002', 'completed', 'pickup', 62.34, 'sarah.jones@email.com', 'Sarah Jones', '456 Oak Ave, Winter Park FL 32789', NOW() - INTERVAL '5 days'),
|
||||
('o0010003-0000-0000-0000-000000000003', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'c0010003-0000-0000-0000-000000000003', 'confirmed', 'pickup', 38.92, 'mike.wilson@email.com', 'Mike Wilson', '789 Pine St, Maitland FL 32751', NOW() - INTERVAL '2 days'),
|
||||
('o0010004-0000-0000-0000-000000000004', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'c0010004-0000-0000-0000-000000000004', 'pending', 'ship', 89.55, 'emily.brown@email.com', 'Emily Brown', '321 Elm Blvd, Orlando FL 32816', NOW() - INTERVAL '1 day'),
|
||||
('o0010005-0000-0000-0000-000000000005', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'c0010005-0000-0000-0000-000000000005', 'preparing', 'mixed', 156.78, 'david.lee@email.com', 'David Lee', '555 Commerce Dr, Lake Mary FL 32746', NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- DEMO ORDER ITEMS
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO order_items (id, order_id, product_id, quantity, price, fulfillment, created_at)
|
||||
VALUES
|
||||
('oi001001-0000-0000-0000-0000000001', 'o0010001-0000-0000-0000-000000000001', 'p0010001-0000-0000-0000-000000000001', 5, 3.99, 'pickup', NOW() - INTERVAL '7 days'),
|
||||
('oi001002-0000-0000-0000-0000000002', 'o0010001-0000-0000-0000-000000000001', 'p0010003-0000-0000-0000-000000000003', 8, 2.99, 'pickup', NOW() - INTERVAL '7 days'),
|
||||
('oi002001-0000-0000-0000-0000000003', 'o0010002-0000-0000-0000-000000000002', 'p0010002-0000-0000-0000-000000000002', 10, 3.49, 'pickup', NOW() - INTERVAL '5 days'),
|
||||
('oi002002-0000-0000-0000-0000000004', 'o0010002-0000-0000-0000-000000000002', 'p0010005-0000-0000-0000-000000000005', 6, 3.79, 'pickup', NOW() - INTERVAL '5 days'),
|
||||
('oi003001-0000-0000-0000-0000000005', 'o0010003-0000-0000-0000-000000000003', 'p0010004-0000-0000-0000-000000000004', 7, 3.29, 'pickup', NOW() - INTERVAL '2 days'),
|
||||
('oi003002-0000-0000-0000-0000000006', 'o0010003-0000-0000-0000-000000000003', 'p0010007-0000-0000-0000-000000000007', 4, 4.49, 'pickup', NOW() - INTERVAL '2 days'),
|
||||
('oi004001-0000-0000-0000-0000000007', 'o0010004-0000-0000-0000-000000000004', 'p0010010-0000-0000-0000-000000000010', 1, 54.99, 'ship', NOW() - INTERVAL '1 day'),
|
||||
('oi004002-0000-0000-0000-0000000008', 'o0010004-0000-0000-0000-000000000004', 'p0010001-0000-0000-0000-000000000001', 5, 3.99, 'ship', NOW() - INTERVAL '1 day'),
|
||||
('oi004003-0000-0000-0000-0000000009', 'o0010004-0000-0000-0000-000000000004', 'p0010006-0000-0000-0000-000000000006', 4, 4.29, 'ship', NOW() - INTERVAL '1 day'),
|
||||
('oi005001-0000-0000-0000-0000000010', 'o0010005-0000-0000-0000-000000000005', 'p0010010-0000-0000-0000-000000000010', 2, 54.99, 'pickup', NOW()),
|
||||
('oi005002-0000-0000-0000-0000000011', 'o0010005-0000-0000-0000-000000000005', 'p0010003-0000-0000-0000-000000000003', 10, 2.99, 'ship', NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- DEMO COMMUNICATION CONTACTS
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO communication_contacts (id, brand_id, email, first_name, last_name, phone, company, email_opt_in, sms_opt_in, tags, created_at)
|
||||
VALUES
|
||||
('cc001001-0000-0000-0000-0000000001', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'alex.rivera@email.com', 'Alex', 'Rivera', '+1-407-555-0301', 'Rivera Consulting', true, false, ARRAY['vip', 'wholesale'], NOW()),
|
||||
('cc001002-0000-0000-0000-0000000002', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'jennifer.martinez@email.com', 'Jennifer', 'Martinez', '+1-407-555-0302', 'Martinez Restaurant', true, true, ARRAY['restaurant', 'wholesale'], NOW()),
|
||||
('cc001003-0000-0000-0000-0000000003', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'chris.taylor@email.com', 'Chris', 'Taylor', '+1-407-555-0303', 'Taylor Grocers', true, false, ARRAY['retail'], NOW()),
|
||||
('cc001004-0000-0000-0000-0000000004', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'michelle.davis@email.com', 'Michelle', 'Davis', '+1-407-555-0304', 'Davis Hotel Group', true, false, ARRAY['hospitality', 'wholesale'], NOW()),
|
||||
('cc001005-0000-0000-0000-0000000005', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'andrew.wilson@email.com', 'Andrew', 'Wilson', '+1-407-555-0305', 'Wilson Catering', true, true, ARRAY['catering'], NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- DEMO WATER LOG FIELDS
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO water_fields (id, brand_id, name, location, size_acres, crop_type, notes, created_at)
|
||||
VALUES
|
||||
('wf001001-0000-0000-0000-0000000001', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'North Grove Section A', 'GPS: 28.5383,-81.3792', 15.5, 'Citrus', 'Primary navel orange section', NOW()),
|
||||
('wf001002-0000-0000-0000-0000000002', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'South Orchard', 'GPS: 28.5200,-81.4000', 22.0, 'Mixed Citrus', 'Mixed variety planting', NOW()),
|
||||
('wf001003-0000-0000-0000-0000000003', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'East Block - Honeycrisp', 'GPS: 28.5450,-81.3500', 8.5, 'Apples', 'Premium Honeycrisp apples', NOW()),
|
||||
('wf001004-0000-0000-0000-0000000004', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'West Test Plot', 'GPS: 28.5300,-81.4200', 3.0, 'Experimental', 'New variety test section', NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- DEMO WATER LOGS
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO water_logs (id, brand_id, field_id, gallons, duration_minutes, water_method, notes, logged_at, created_at)
|
||||
VALUES
|
||||
('wl001001-0000-0000-0000-0000000001', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'wf001001-0000-0000-0000-0000000001', 2500, 45, 'drip', 'Regular weekly irrigation', NOW() - INTERVAL '2 days', NOW() - INTERVAL '2 days'),
|
||||
('wl001002-0000-0000-0000-0000000002', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'wf001002-0000-0000-0000-0000000002', 4200, 60, 'sprinkler', 'Deep watering after dry spell', NOW() - INTERVAL '1 day', NOW() - INTERVAL '1 day'),
|
||||
('wl001003-0000-0000-0000-0000000003', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'wf001003-0000-0000-0000-0000000003', 1800, 30, 'drip', 'Focused irrigation on new trees', NOW(), NOW()),
|
||||
('wl001004-0000-0000-0000-0000000004', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'wf001001-0000-0000-0000-0000000001', 2800, 50, 'drip', 'Extended session for heat wave', NOW() - INTERVAL '4 days', NOW() - INTERVAL '4 days')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- DEMO CHANGELOGS
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO changelogs (id, brand_id, version, title, description, content, released_at, is_published, feature_type)
|
||||
VALUES
|
||||
('cl001001-0000-0000-0000-0000000001', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'2.0.0', 'Major Platform Update',
|
||||
'We have rolled out significant improvements to the platform including a new analytics dashboard, improved order management, and faster performance.',
|
||||
'[
|
||||
{"type": "feature", "title": "New Analytics Dashboard", "description": "Real-time insights into your business performance with customizable reports and charts."},
|
||||
{"type": "feature", "title": "Improved Order Management", "description": "Faster order processing with batch operations and smart filtering."},
|
||||
{"type": "improvement", "title": "50% Faster Load Times", "description": "Optimized frontend for lightning-fast navigation across all pages."},
|
||||
{"type": "bugfix", "title": "Fixed Mobile Checkout", "description": "Resolved issues with checkout on iOS devices."}
|
||||
]'::jsonb,
|
||||
NOW() - INTERVAL '3 days', true, 'major'),
|
||||
('cl001002-0000-0000-0000-0000000002', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'1.9.5', 'Water Log Enhancement',
|
||||
'Added new water tracking features including field mapping and usage analytics.',
|
||||
'[
|
||||
{"type": "feature", "title": "Field GPS Mapping", "description": "Track water usage by GPS-defined field boundaries."},
|
||||
{"type": "feature", "title": "Usage Analytics", "description": "Weekly and monthly water usage reports with trend analysis."},
|
||||
{"type": "improvement", "title": "Mobile-Friendly Interface", "description": "Optimized for field workers on mobile devices."}
|
||||
]'::jsonb,
|
||||
NOW() - INTERVAL '2 weeks', true, 'feature')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- DEMO REFERRAL CODES
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO referral_codes (id, brand_id, referrer_user_id, referral_code, referrer_email, reward_type, reward_value, max_uses)
|
||||
VALUES
|
||||
('rc001001-0000-0000-0000-0000000001', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'u0010001-0000-0000-0000-0000000001', 'SUNRISE20', 'admin@sunrisefarms.com', 'percentage', 20.00, 10)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- DEMO ADMIN USERS
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO admin_users (id, user_id, brand_id, email, role, active, can_manage_products, can_manage_stops, can_manage_orders, can_manage_pickup, can_manage_messages, can_manage_refunds, can_manage_users, can_manage_water_log, can_manage_reports, can_manage_settings)
|
||||
VALUES
|
||||
('au001001-0000-0000-0000-0000000001', 'u0010001-0000-0000-0000-0000000001', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'admin@sunrisefarms.com', 'brand_admin', true, true, true, true, true, true, true, true, true, true),
|
||||
('au001002-0000-0000-0000-0000000002', 'u0010002-0000-0000-0000-0000000002', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
'manager@sunrisefarms.com', 'brand_admin', true, true, true, true, false, true, false, true, true, true)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
COMMIT;
|
||||
Reference in New Issue
Block a user