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:
321
supabase/migrations/20250109_onboarding_system.sql
Normal file
321
supabase/migrations/20250109_onboarding_system.sql
Normal 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;
|
||||
Reference in New Issue
Block a user