Add comprehensive platform administration panel

- Database: Add login_history, ai_recommendation_log tables; is_platform_owner
  column on users; subscription fields on organizations (payment_date,
  confirmation_number, renewal_date)
- Backend: New AdminAnalyticsService with platform metrics, tenant detail, and
  health score calculations (0-100 based on activity, budget, transactions,
  members, AI usage)
- Backend: Login/org-switch now records to login_history; AI recommendations
  logged to ai_recommendation_log; platform owner protected from superadmin toggle
- Frontend: 4-tab admin panel (Dashboard, Organizations, Users, Tenant Health)
  with tenant detail drawer, subscription management, health scoring visualization
- Platform owner account (admin@hoaledgeriq.com) auto-redirects to admin panel
- Seed data includes platform owner account and sample login history

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-26 08:51:39 -05:00
parent 0bd30a0eb8
commit a32d4cc179
20 changed files with 3183 additions and 317 deletions

View File

@@ -16,6 +16,31 @@
-- Enable UUID generation
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- ============================================================
-- 0. Create platform owner account (admin@hoaledgeriq.com)
-- ============================================================
DO $$
DECLARE
v_platform_owner_id UUID;
BEGIN
SELECT id INTO v_platform_owner_id FROM shared.users WHERE email = 'admin@hoaledgeriq.com';
IF v_platform_owner_id IS NULL THEN
INSERT INTO shared.users (id, email, password_hash, first_name, last_name, is_superadmin, is_platform_owner)
VALUES (
uuid_generate_v4(),
'admin@hoaledgeriq.com',
-- bcrypt hash of platform owner password (cost 12)
'$2b$12$QRJEJYsjy.24Va.57h13Te7UX7nMTN9hWhW19bwuCAkr1Dm0FWqrm',
'Platform',
'Admin',
true,
true
) RETURNING id INTO v_platform_owner_id;
END IF;
-- Platform owner has NO org memberships — admin-only account
RAISE NOTICE 'Platform Owner: admin@hoaledgeriq.com (id: %)', v_platform_owner_id;
END $$;
-- ============================================================
-- 1. Create test user and organization
-- ============================================================
@@ -836,7 +861,42 @@ EXECUTE format('INSERT INTO %I.capital_projects (name, description, estimated_co
(''Perimeter Fence Repair'', ''Replace damaged fence sections and repaint'', 8000, $1 + 4, 8, ''planned'', ''reserve'', 4)
', v_schema) USING v_year;
-- Add subscription data to the organization
UPDATE shared.organizations
SET payment_date = (CURRENT_DATE - INTERVAL '15 days')::DATE,
confirmation_number = 'PAY-2026-SVH-001',
renewal_date = (CURRENT_DATE + INTERVAL '350 days')::DATE
WHERE schema_name = v_schema;
-- ============================================================
-- 13. Seed login_history for demo analytics
-- ============================================================
-- Admin user: regular logins over the past 30 days
FOR v_month IN 0..29 LOOP
INSERT INTO shared.login_history (user_id, organization_id, logged_in_at, ip_address, user_agent)
VALUES (
v_user_id,
v_org_id,
NOW() - (v_month || ' days')::INTERVAL - (random() * 8 || ' hours')::INTERVAL,
'192.168.1.' || (10 + (random() * 50)::INT),
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)'
);
END LOOP;
-- Viewer user: occasional logins (every 3-5 days)
FOR v_month IN 0..9 LOOP
INSERT INTO shared.login_history (user_id, organization_id, logged_in_at, ip_address, user_agent)
VALUES (
(SELECT id FROM shared.users WHERE email = 'viewer@sunrisevalley.org'),
v_org_id,
NOW() - ((v_month * 3) || ' days')::INTERVAL - (random() * 12 || ' hours')::INTERVAL,
'10.0.0.' || (100 + (random() * 50)::INT),
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)'
);
END LOOP;
RAISE NOTICE 'Seed data created successfully for Sunrise Valley HOA!';
RAISE NOTICE 'Platform Owner: admin@hoaledgeriq.com (SuperAdmin + Platform Owner)';
RAISE NOTICE 'Admin Login: admin@sunrisevalley.org / password123 (SuperAdmin + President)';
RAISE NOTICE 'Viewer Login: viewer@sunrisevalley.org / password123 (Homeowner)';