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>
This commit is contained in:
2025-07-12 18:21:40 -06:00
parent a02d64a86c
commit 26a87d0d00
232 changed files with 33175 additions and 5365 deletions

View File

@@ -0,0 +1,321 @@
-- Auto-Approval Onboarding System Migration
-- This migration adds the database schema for auto-approval rules and account status tracking
-- Create auto-approval rules table
CREATE TABLE IF NOT EXISTS auto_approval_rules (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
rule_name VARCHAR(100) NOT NULL,
email_domain VARCHAR(255),
business_type VARCHAR(50),
minimum_score INTEGER DEFAULT 0,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Add account status and approval tracking to organizations
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS account_status VARCHAR(50) DEFAULT 'pending_approval';
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS approval_score INTEGER DEFAULT 0;
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS auto_approved BOOLEAN DEFAULT false;
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS approved_at TIMESTAMP WITH TIME ZONE;
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS approved_by UUID REFERENCES users(id);
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS approval_reason TEXT;
-- Add Stripe onboarding tracking
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS stripe_onboarding_status VARCHAR(50) DEFAULT 'not_started';
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS stripe_onboarding_url TEXT;
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS stripe_details_submitted BOOLEAN DEFAULT false;
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS stripe_charges_enabled BOOLEAN DEFAULT false;
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS stripe_payouts_enabled BOOLEAN DEFAULT false;
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS onboarding_completed_at TIMESTAMP WITH TIME ZONE;
-- Add business profile fields for better approval scoring
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS business_type VARCHAR(50);
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS business_description TEXT;
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS website_url TEXT;
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS phone_number VARCHAR(20);
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS address_line1 TEXT;
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS address_line2 TEXT;
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS city VARCHAR(100);
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS state VARCHAR(50);
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS postal_code VARCHAR(20);
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS country VARCHAR(2) DEFAULT 'US';
-- Create organization onboarding progress table
CREATE TABLE IF NOT EXISTS organization_onboarding_progress (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
organization_id UUID REFERENCES organizations(id) NOT NULL,
step_key VARCHAR(100) NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
completed_at TIMESTAMP WITH TIME ZONE,
data JSONB,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(organization_id, step_key)
);
-- Create audit log for approval actions
CREATE TABLE IF NOT EXISTS approval_audit_log (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
organization_id UUID REFERENCES organizations(id) NOT NULL,
action VARCHAR(50) NOT NULL, -- 'auto_approved', 'manually_approved', 'rejected'
actor_id UUID REFERENCES users(id), -- NULL for auto-approval
reason TEXT,
previous_status VARCHAR(50),
new_status VARCHAR(50),
metadata JSONB,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Insert default auto-approval rules
INSERT INTO auto_approval_rules (rule_name, email_domain, minimum_score, is_active) VALUES
('Trusted Educational Domains', 'edu', 80, true),
('Government Organizations', 'gov', 90, true),
('Known Venue Partners', 'eventbrite.com', 85, true),
('High Score Threshold', NULL, 95, true)
ON CONFLICT DO NOTHING;
-- Add indexes for performance
CREATE INDEX IF NOT EXISTS idx_organizations_account_status ON organizations(account_status);
CREATE INDEX IF NOT EXISTS idx_organizations_approval_score ON organizations(approval_score);
CREATE INDEX IF NOT EXISTS idx_organizations_stripe_onboarding_status ON organizations(stripe_onboarding_status);
CREATE INDEX IF NOT EXISTS idx_auto_approval_rules_active ON auto_approval_rules(is_active);
CREATE INDEX IF NOT EXISTS idx_onboarding_progress_org_id ON organization_onboarding_progress(organization_id);
CREATE INDEX IF NOT EXISTS idx_approval_audit_log_org_id ON approval_audit_log(organization_id);
CREATE INDEX IF NOT EXISTS idx_approval_audit_log_created_at ON approval_audit_log(created_at);
-- Create function to update updated_at timestamps
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
-- Add triggers for updated_at
CREATE TRIGGER update_auto_approval_rules_updated_at
BEFORE UPDATE ON auto_approval_rules
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_onboarding_progress_updated_at
BEFORE UPDATE ON organization_onboarding_progress
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- RLS Policies for new tables
ALTER TABLE auto_approval_rules ENABLE ROW LEVEL SECURITY;
ALTER TABLE organization_onboarding_progress ENABLE ROW LEVEL SECURITY;
ALTER TABLE approval_audit_log ENABLE ROW LEVEL SECURITY;
-- Auto-approval rules: Only admins can access
CREATE POLICY "Admins can manage auto-approval rules" ON auto_approval_rules
FOR ALL USING (auth.uid() IN (SELECT id FROM users WHERE role = 'admin'));
-- Onboarding progress: Users can access their own organization's progress
CREATE POLICY "Users can access their organization's onboarding progress" ON organization_onboarding_progress
FOR ALL USING (
organization_id IN (
SELECT organization_id FROM users WHERE id = auth.uid()
)
);
-- Audit log: Users can read their own organization's audit log, admins can read all
CREATE POLICY "Users can read their organization's audit log" ON approval_audit_log
FOR SELECT USING (
organization_id IN (
SELECT organization_id FROM users WHERE id = auth.uid()
) OR auth.uid() IN (SELECT id FROM users WHERE role = 'admin')
);
-- Only admins can write to audit log
CREATE POLICY "Only admins can write to audit log" ON approval_audit_log
FOR INSERT WITH CHECK (auth.uid() IN (SELECT id FROM users WHERE role = 'admin'));
-- Create function to calculate approval score
CREATE OR REPLACE FUNCTION calculate_approval_score(org_id UUID)
RETURNS INTEGER AS $$
DECLARE
score INTEGER DEFAULT 0;
org_record RECORD;
user_record RECORD;
BEGIN
-- Get organization and user data
SELECT * INTO org_record FROM organizations WHERE id = org_id;
SELECT * INTO user_record FROM users WHERE organization_id = org_id AND role = 'organizer' LIMIT 1;
IF org_record IS NULL OR user_record IS NULL THEN
RETURN 0;
END IF;
-- Base score for having an organization
score := 10;
-- Email domain scoring
IF user_record.email LIKE '%.edu' THEN
score := score + 40;
ELSIF user_record.email LIKE '%.gov' THEN
score := score + 50;
ELSIF user_record.email LIKE '%.org' THEN
score := score + 20;
END IF;
-- Business information completeness
IF org_record.business_type IS NOT NULL THEN
score := score + 10;
END IF;
IF org_record.business_description IS NOT NULL AND LENGTH(org_record.business_description) > 50 THEN
score := score + 15;
END IF;
IF org_record.website_url IS NOT NULL THEN
score := score + 10;
END IF;
IF org_record.phone_number IS NOT NULL THEN
score := score + 5;
END IF;
IF org_record.address_line1 IS NOT NULL THEN
score := score + 10;
END IF;
-- Update the organization's approval score
UPDATE organizations SET approval_score = score WHERE id = org_id;
RETURN score;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Create function to check if organization should be auto-approved
CREATE OR REPLACE FUNCTION should_auto_approve(org_id UUID)
RETURNS BOOLEAN AS $$
DECLARE
score INTEGER;
org_record RECORD;
user_record RECORD;
rule_record RECORD;
BEGIN
-- Calculate current score
score := calculate_approval_score(org_id);
-- Get organization and user data
SELECT * INTO org_record FROM organizations WHERE id = org_id;
SELECT * INTO user_record FROM users WHERE organization_id = org_id AND role = 'organizer' LIMIT 1;
-- Check domain-specific rules
FOR rule_record IN SELECT * FROM auto_approval_rules WHERE is_active = true AND email_domain IS NOT NULL LOOP
IF user_record.email LIKE '%' || rule_record.email_domain THEN
IF score >= rule_record.minimum_score THEN
RETURN true;
END IF;
END IF;
END LOOP;
-- Check general score threshold rules
FOR rule_record IN SELECT * FROM auto_approval_rules WHERE is_active = true AND email_domain IS NULL LOOP
IF score >= rule_record.minimum_score THEN
RETURN true;
END IF;
END LOOP;
RETURN false;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Create function to process auto-approval
CREATE OR REPLACE FUNCTION process_auto_approval(org_id UUID)
RETURNS BOOLEAN AS $$
DECLARE
should_approve BOOLEAN;
current_status VARCHAR(50);
BEGIN
-- Get current status
SELECT account_status INTO current_status FROM organizations WHERE id = org_id;
-- Only process if currently pending approval
IF current_status != 'pending_approval' THEN
RETURN false;
END IF;
-- Check if should auto-approve
should_approve := should_auto_approve(org_id);
IF should_approve THEN
-- Update organization status
UPDATE organizations SET
account_status = 'approved',
auto_approved = true,
approved_at = NOW(),
approval_reason = 'Auto-approved based on scoring rules'
WHERE id = org_id;
-- Log the approval
INSERT INTO approval_audit_log (
organization_id,
action,
actor_id,
reason,
previous_status,
new_status,
metadata
) VALUES (
org_id,
'auto_approved',
NULL,
'Auto-approved based on scoring rules',
'pending_approval',
'approved',
jsonb_build_object('approval_score', (SELECT approval_score FROM organizations WHERE id = org_id))
);
RETURN true;
END IF;
RETURN false;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Create trigger to process auto-approval when organization is created or updated
CREATE OR REPLACE FUNCTION trigger_auto_approval_check()
RETURNS TRIGGER AS $$
BEGIN
-- Only process if account_status is pending_approval
IF NEW.account_status = 'pending_approval' THEN
PERFORM process_auto_approval(NEW.id);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Add trigger to organizations table
CREATE TRIGGER check_auto_approval_on_update
AFTER UPDATE ON organizations
FOR EACH ROW
EXECUTE FUNCTION trigger_auto_approval_check();
-- Create view for admin dashboard
CREATE OR REPLACE VIEW admin_pending_approvals AS
SELECT
o.id,
o.name,
o.account_status,
o.approval_score,
o.business_type,
o.business_description,
o.created_at,
u.email as owner_email,
u.name as owner_name,
calculate_approval_score(o.id) as current_score,
should_auto_approve(o.id) as can_auto_approve
FROM organizations o
JOIN users u ON u.organization_id = o.id AND u.role = 'organizer'
WHERE o.account_status = 'pending_approval'
ORDER BY o.created_at DESC;
-- Grant permissions to authenticated users
GRANT SELECT ON admin_pending_approvals TO authenticated;
GRANT EXECUTE ON FUNCTION calculate_approval_score TO authenticated;
GRANT EXECUTE ON FUNCTION should_auto_approve TO authenticated;
GRANT EXECUTE ON FUNCTION process_auto_approval TO authenticated;