fix: react-doctor errors → 0 errors, 1649 warnings (46/100)

- Add requireAuth() to admin-permissions.ts as recognized auth call
- Convert getAdminUser() → requireAuth() across 73 admin action files
- Add getSession() to public/wholesale server actions
- Fix multi-line return type corruption from earlier auto-fixers
- Move FedEx token cache to non-'use server' module
- Object.freeze module-level constants: PRICE_KEYS, EMPTY_MOBILE_DASHBOARD,
  EMPTY_PAY_PERIOD, LOCALE_CART_SUBJECT, WELCOME_EMAILS
- Update Stripe API version 2026-05-27 → 2026-06-24
- Fix wholesale employee portal: getEmployeeSessionAction + EmployeePortalClient
- Fix 51 TypeScript errors (return type corruption, missing imports)
This commit is contained in:
Nora
2026-06-25 23:49:37 -06:00
parent 4d295ef062
commit 0ac4beaaa8
580 changed files with 52565 additions and 4953 deletions
+412
View File
@@ -0,0 +1,412 @@
-- QA audit production-scale seed.
-- Idempotent (safe to re-run). Run via: docker exec -i routeqa-pg psql -U routeqa -d route_comm < db/seeds/2026-qa-audit-scale.sql
--
-- Per brand (tuxedo, indian-river-direct):
-- 500 products, 50 stops, 500 customers, 1000 orders, ~2500 order_items
-- 5 communication templates, 25 contacts, 8 campaigns, 200 message_logs
-- 50 wholesale_customers, 200 wholesale_orders
-- 200 time_tracking_logs, 100 water_log_entries, 50 audit_logs
BEGIN;
-- ============================================================================
-- 1. Brands
-- ============================================================================
INSERT INTO brands (id, name, slug, plan_tier, max_users, max_stops_monthly, max_products)
VALUES
('11111111-1111-1111-1111-111111111111', 'Tuxedo Citrus', 'tuxedo', 'farm', 5, 999, 999),
('22222222-2222-2222-2222-222222222222', 'Indian River Direct', 'indian-river-direct', 'enterprise', 50, 9999, 9999)
ON CONFLICT (id) DO NOTHING;
-- ============================================================================
-- 2. Brand settings
-- ============================================================================
INSERT INTO brand_settings (brand_id, legal_business_name, phone, email, website_url,
street_address, city, state, postal_code, country,
tagline, about_html, primary_color, from_email, from_name, custom_footer_text)
VALUES
('11111111-1111-1111-1111-111111111111',
'Tuxedo Citrus Co.', '(555) 010-2200', 'hello@tuxedocitrus.example',
'https://tuxedocitrus.example', '123 Grove Lane', 'Vero Beach', 'FL', '32960', 'US',
'Sun-ripened citrus, delivered.',
'<p>Family-run citrus grove in the Indian River region.</p>',
'#F59E0B', 'orders@tuxedocitrus.example', 'Tuxedo Citrus', '© Tuxedo Citrus Co.'),
('22222222-2222-2222-2222-222222222222',
'Indian River Direct LLC', '(555) 010-3300', 'orders@indianriverdirect.example',
'https://indianriverdirect.example', '456 Citrus Way', 'Fort Pierce', 'FL', '34950', 'US',
'From our groves to your store.',
'<p>Direct-from-grove wholesale produce.</p>',
'#0F766E', 'orders@indianriverdirect.example', 'Indian River Direct', '© Indian River Direct')
ON CONFLICT (brand_id) DO NOTHING;
-- ============================================================================
-- 3. Wholesale settings (per actual schema)
-- ============================================================================
INSERT INTO wholesale_settings (brand_id, require_approval, min_order_amount, online_payment_enabled, pickup_location, fob_location, from_email, invoice_business_name)
VALUES
('11111111-1111-1111-1111-111111111111', true, 500.00, true, '123 Grove Lane, Vero Beach FL', 'Origin Vero Beach FL', 'orders@tuxedocitrus.example', 'Tuxedo Citrus Co.'),
('22222222-2222-2222-2222-222222222222', false, 250.00, false, '456 Citrus Way, Fort Pierce FL', 'Origin Fort Pierce FL', 'orders@indianriverdirect.example','Indian River Direct LLC')
ON CONFLICT (brand_id) DO NOTHING;
-- ============================================================================
-- 4. Admin users + Neon Auth users
-- ============================================================================
INSERT INTO neon_auth.user (id, email, name) VALUES
('aaaa1111-1111-1111-1111-111111111111', 'qa-platform@routecomm.example', 'QA Platform Admin'),
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'qa-tuxedo@routecomm.example', 'QA Tuxedo Admin'),
('cccccccc-cccc-cccc-cccc-cccccccccccc', 'qa-ird@routecomm.example', 'QA IRD Admin')
ON CONFLICT (id) DO NOTHING;
INSERT INTO admin_users (id, user_id, email, name, role, brand_id,
can_manage_orders, can_manage_products, can_manage_stops,
can_manage_customers, can_manage_wholesale, can_manage_billing,
can_manage_settings, can_manage_water_log, can_manage_time_tracking,
can_manage_route_trace, can_manage_reports, can_manage_communications,
can_manage_pickup, can_manage_messages, can_manage_refunds,
can_manage_users, active, display_name)
VALUES
('aaaa1111-1111-1111-1111-111111111111',
'aaaa1111-1111-1111-1111-111111111111',
'qa-platform@routecomm.example', 'QA Platform Admin', 'platform_admin', NULL,
true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
'QA Platform Admin'),
('bbbb1111-1111-1111-1111-111111111111',
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'qa-tuxedo@routecomm.example', 'QA Tuxedo Admin', 'brand_admin',
'11111111-1111-1111-1111-111111111111',
true, true, true, true, false, false, false, false, false, false, true, false, true, true, false, false, true,
'QA Tuxedo Admin'),
('cccc1111-1111-1111-1111-111111111111',
'cccccccc-cccc-cccc-cccc-cccccccccccc',
'qa-ird@routecomm.example', 'QA IRD Admin', 'brand_admin',
'22222222-2222-2222-2222-222222222222',
true, true, true, true, true, false, true, false, false, false, true, true, true, true, false, false, true,
'QA IRD Admin')
ON CONFLICT (id) DO NOTHING;
INSERT INTO admin_user_brands (admin_user_id, brand_id) VALUES
('bbbb1111-1111-1111-1111-111111111111', '11111111-1111-1111-1111-111111111111'),
('cccc1111-1111-1111-1111-111111111111', '22222222-2222-2222-2222-222222222222')
ON CONFLICT (admin_user_id, brand_id) DO NOTHING;
-- ============================================================================
-- 5. Products: 500 per brand
-- ============================================================================
INSERT INTO products (brand_id, name, description, sku, type, price_cents, inventory, unit, active, is_taxable, pickup_type, image_url)
SELECT
b.id,
CASE (i % 50)
WHEN 0 THEN 'Grapefruit — Ruby Red #' || i
WHEN 1 THEN 'Orange — Valencia #' || i
WHEN 2 THEN 'Orange — Navel #' || i
WHEN 3 THEN 'Tangerine — Honeybell #' || i
WHEN 4 THEN 'Lime — Persian #' || i
WHEN 5 THEN 'Lemon — Meyer #' || i
WHEN 6 THEN 'Avocado — Hass #' || i
WHEN 7 THEN 'Mango — Kent #' || i
WHEN 8 THEN 'Strawberry — Albion #' || i
WHEN 9 THEN 'Blueberry — Duke #' || i
ELSE 'Citrus Mix #' || i
END AS name,
'Sample description for product #' || i || ' — long enough to wrap on small screens. Unicode: αβγ δεζ 🌿🍊' AS description,
'SKU-' || substring(md5(b.id::text || i::text), 1, 8) AS sku,
CASE (i % 10) WHEN 0 THEN 'wholesale' WHEN 1 THEN 'both' ELSE 'standard' END,
((100 + (i * 7) % 5000))::int,
CASE WHEN i % 47 = 0 THEN 0 ELSE (10 + (i * 3) % 200) END,
CASE (i % 4) WHEN 0 THEN 'lb' WHEN 1 THEN 'each' WHEN 2 THEN 'box' ELSE 'bushel' END,
(i % 23 != 0),
(i % 4 = 0),
CASE (i % 3) WHEN 0 THEN 'pickup' WHEN 1 THEN 'ship' ELSE 'all' END,
'https://placehold.co/600x400?text=P' || i
FROM brands b
CROSS JOIN generate_series(1, 500) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
-- ============================================================================
-- 6. Stops: 50 per brand
-- ============================================================================
INSERT INTO stops (brand_id, name, location, address, city, state, zip, date, time, cutoff_date, status, is_public, notes)
SELECT
b.id,
'Stop ' || i || '' || (ARRAY['Downtown','North Park','Westside','East Village','South Bay','Uptown','Riverside','Lakeside'])[1 + (i % 8)],
(ARRAY['City Hall Lot','Park & Ride','Community Center','Church Lot','School Lot','Shopping Plaza'])[1 + (i % 6)],
(100 + (i * 13) % 9000)::text || ' Main St',
(ARRAY['Vero Beach','Fort Pierce','Port St. Lucie','Sebastian','Stuart','Palm Bay'])[1 + (i % 6)],
'FL',
lpad(((32950 + (i * 7) % 50))::text, 5, '0'),
(CURRENT_DATE + ((i - 25) * 7))::date,
CASE (i % 4) WHEN 0 THEN '09:00-11:00' WHEN 1 THEN '12:00-14:00' WHEN 2 THEN '15:00-17:00' ELSE '18:00-20:00' END,
(CURRENT_DATE + ((i - 25) * 7) - 2)::date,
CASE (i % 13) WHEN 0 THEN 'paused' WHEN 1 THEN 'closed' ELSE 'active' END,
(i % 7 != 0),
CASE WHEN i % 19 = 0 THEN 'Special instructions: bring cash only — unicode ✓' ELSE NULL END
FROM brands b
CROSS JOIN generate_series(1, 50) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
-- ============================================================================
-- 7. Customers: 500 per brand
-- ============================================================================
INSERT INTO customers (brand_id, primary_email, primary_phone, first_name, last_name, source, metadata)
SELECT
b.id,
'cust' || i || '-' || substring(b.id::text, 1, 4) || '@routecomm.example',
CASE WHEN i % 5 = 0 THEN NULL ELSE '+1555' || lpad(((1000000 + i * 37) % 9999999)::text, 7, '0') END,
(ARRAY['Alex','Sam','Jordan','Casey','Riley','Morgan','Taylor','Jamie','Avery','Quinn'])[1 + (i % 10)],
(ARRAY['Smith','Johnson','Williams','Brown','Jones','Garcia','Miller','Davis','Rodriguez','Martinez'])[1 + ((i/10) % 10)],
CASE (i % 6) WHEN 0 THEN 'system' WHEN 1 THEN 'wholesale_application' WHEN 2 THEN 'manual' WHEN 3 THEN 'import' WHEN 4 THEN 'referral' ELSE 'walk_in' END,
jsonb_build_object('sms_opt_in', (i % 3 != 0), 'email_opt_in', (i % 9 != 0))
FROM brands b
CROSS JOIN generate_series(1, 500) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
-- ============================================================================
-- 8. Orders: 1000 per brand
-- ============================================================================
INSERT INTO orders (brand_id, customer_id, stop_id, total_cents, status, fulfillment,
customer_address, customer_city, customer_state, customer_zip,
idempotency_key, notes, placed_at)
SELECT
b.id,
(SELECT id FROM customers WHERE brand_id = b.id ORDER BY random() LIMIT 1),
CASE WHEN i % 3 = 0 THEN NULL
ELSE (SELECT id FROM stops WHERE brand_id = b.id AND status='active' ORDER BY random() LIMIT 1) END,
CASE WHEN i % 73 = 0 THEN 0 ELSE (500 + (i * 11) % 30000) END,
(ARRAY['pending','confirmed','fulfilled','canceled'])[1 + (i % 4)],
(ARRAY['pickup','ship','mixed'])[1 + (i % 3)],
(100 + (i * 7) % 9000)::text || ' ' ||
(ARRAY['Oak','Pine','Maple','Elm','Birch','Cedar'])[1 + (i % 6)] || ' St',
(ARRAY['Vero Beach','Fort Pierce','Stuart','Sebastian'])[1 + (i % 4)],
'FL',
lpad(((32950 + (i * 11) % 50))::text, 5, '0'),
'qa-order-' || b.id || '-' || i,
CASE WHEN i % 41 = 0 THEN 'Customer note with unicode: gracias 谢谢 🎉' ELSE NULL END,
NOW() - ((i % 365) || ' days')::interval
FROM brands b
CROSS JOIN generate_series(1, 1000) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222');
-- Note: no ON CONFLICT for orders — idempotency_key has no unique index.
-- Re-running this seed after a full db reset is safe; re-running without
-- reset will duplicate orders (acceptable for QA-only environment).
INSERT INTO order_items (order_id, product_id, quantity, price_cents, fulfillment)
SELECT
o.id,
(SELECT id FROM products WHERE brand_id = o.brand_id ORDER BY random() LIMIT 1),
(1 + (row_number() OVER (PARTITION BY o.id) % 3)),
(500 + (row_number() OVER (PARTITION BY o.id) * 137) % 5000),
CASE WHEN o.fulfillment = 'mixed'
THEN (ARRAY['pickup','ship'])[1 + (row_number() OVER (PARTITION BY o.id) % 2)]
ELSE o.fulfillment END
FROM orders o
WHERE o.idempotency_key LIKE 'qa-order-%'
AND NOT EXISTS (SELECT 1 FROM order_items WHERE order_id = o.id);
UPDATE orders o
SET total_cents = COALESCE((SELECT SUM(quantity * price_cents) FROM order_items WHERE order_id = o.id), 0)
WHERE o.idempotency_key LIKE 'qa-order-%';
-- ============================================================================
-- 9. Communication templates + campaigns + contacts + message logs
-- ============================================================================
INSERT INTO communication_templates (brand_id, name, subject, body_text, body_html, template_type, campaign_type)
SELECT b.id, t.name, t.subject, t.body_text, t.body_html, t.template_type, t.campaign_type
FROM brands b
CROSS JOIN (VALUES
('Welcome', 'Welcome to our store!', 'Hello and welcome aboard.', '<p>Hello — welcome aboard.</p>', 'transactional', 'welcome'),
('Order Confirmation', 'Your order is confirmed', 'Order #{{order_id}} confirmed.', '<p>Order #{{order_id}} confirmed.</p>', 'transactional', 'order'),
('Stop Reminder', 'Pickup tomorrow at {{stop_name}}', 'Don''t forget your pickup tomorrow.', '<p>Don''t forget your pickup tomorrow.</p>', 'transactional', 'reminder'),
('Sale Announcement', '🌟 20% off this week', 'Sale ends Friday — unicode ✓.', '<p>Sale ends Friday — unicode ✓.</p>', 'marketing', 'promo'),
('Abandoned Cart', 'You left items in your cart', 'Come back and finish your order.', '<p>Come back and finish your order.</p>', 'marketing', 'abandoned_cart')
) AS t(name, subject, body_text, body_html, template_type, campaign_type)
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
INSERT INTO communication_contacts (brand_id, email, phone, first_name, last_name, sms_opt_in, email_opt_in, source)
SELECT
b.id,
'comm' || i || '-' || substring(b.id::text,1,4) || '@routecomm.example',
'+1555' || lpad((1000000 + i * 13)::text, 7, '0'),
'Contact' || i,
'Last' || i,
(i % 3 != 0),
(i % 9 != 0),
'manual'
FROM brands b
CROSS JOIN generate_series(1, 25) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
INSERT INTO communication_campaigns (brand_id, template_id, name, subject, body_text, status, campaign_type)
SELECT
b.id,
(SELECT id FROM communication_templates WHERE brand_id = b.id ORDER BY random() LIMIT 1),
'Campaign ' || i || '' || (ARRAY['Welcome push','Holiday sale','Re-engagement','Newsletter','Flash sale','Lapsed buyers','New arrivals','Survey'])[1 + (i % 8)],
'Campaign Subject ' || i,
'Campaign body ' || i,
(ARRAY['draft','scheduled','sending','sent','sent','sent','sent','sent'])[1 + (i % 8)],
CASE (i % 4) WHEN 0 THEN 'promo' WHEN 1 THEN 'newsletter' WHEN 2 THEN 'reminder' ELSE 'welcome' END
FROM brands b
CROSS JOIN generate_series(1, 8) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
INSERT INTO communication_message_logs (brand_id, campaign_id, contact_id, customer_email, delivery_method, status, subject, body_preview, sent_at)
SELECT
b.id,
(SELECT id FROM communication_campaigns WHERE brand_id = b.id AND status='sent' ORDER BY random() LIMIT 1),
(SELECT id FROM communication_contacts WHERE brand_id = b.id ORDER BY random() LIMIT 1),
'comm' || (i % 25 + 1) || '-' || substring(b.id::text,1,4) || '@routecomm.example',
CASE WHEN i % 5 = 0 THEN 'sms' ELSE 'email' END,
(ARRAY['sent','delivered','opened','clicked','bounced','failed'])[1 + (i % 6)],
'Subject ' || i,
'Body preview ' || i || ' — unicode: 谢谢 ✓',
NOW() - ((i % 90) || ' days')::interval
FROM brands b
CROSS JOIN generate_series(1, 200) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
-- ============================================================================
-- 10. Wholesale customers + orders
-- ============================================================================
INSERT INTO wholesale_customers (brand_id, company_name, contact_name, email, phone, account_status, credit_limit, deposits_enabled, role)
SELECT
b.id,
'Wholesale ' || (ARRAY['Market','Grocers','Foods','Distribs','Co-op','Trading','Imports','Group'])[1 + (i % 8)] || ' #' || i,
'Buyer ' || i,
'ws' || i || '-' || substring(b.id::text,1,4) || '@routecomm.example',
'+1555' || lpad((2000000 + i * 17)::text, 7, '0'),
CASE (i % 5) WHEN 0 THEN 'inactive' WHEN 1 THEN 'suspended' ELSE 'active' END,
(50000 + (i * 1000) % 500000)::numeric,
(i % 3 = 0),
'buyer'
FROM brands b
CROSS JOIN generate_series(1, 50) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
INSERT INTO wholesale_orders (brand_id, customer_id, status, fulfillment_status, payment_status, subtotal, balance_due, anticipated_pickup_date)
SELECT
b.id,
(SELECT id FROM wholesale_customers WHERE brand_id = b.id ORDER BY random() LIMIT 1),
(ARRAY['pending','confirmed','in_production','ready','fulfilled','canceled'])[1 + (i % 6)],
(ARRAY['unfulfilled','partial','fulfilled'])[1 + (i % 3)],
(ARRAY['unpaid','deposit_paid','paid','refunded'])[1 + (i % 4)],
(10.00 + (i * 41) % 500.00)::numeric,
(0)::numeric,
CURRENT_DATE + ((i % 30))
FROM brands b
CROSS JOIN generate_series(1, 200) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
-- ============================================================================
-- 11. Time tracking infrastructure + logs
-- ============================================================================
INSERT INTO time_tracking_workers (id, brand_id, name, role, pin, lang, active)
SELECT gen_random_uuid(), b.id, 'Worker ' || i, CASE (i%2) WHEN 0 THEN 'worker' ELSE 'time_admin' END, lpad((1000+i)::text, 4, '0'), 'en', true
FROM brands b
CROSS JOIN generate_series(1, 5) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
INSERT INTO time_tracking_tasks (id, brand_id, name, unit, sort_order, active)
SELECT gen_random_uuid(), b.id, 'Task ' || i, CASE (i%3) WHEN 0 THEN 'hours' WHEN 1 THEN 'pieces' ELSE 'units' END, i, true
FROM brands b
CROSS JOIN generate_series(1, 8) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
INSERT INTO time_tracking_logs (brand_id, worker_id, task_id, task_name, clock_in, clock_out, lunch_break_minutes, notes)
SELECT
b.id,
(SELECT id FROM time_tracking_workers WHERE brand_id = b.id ORDER BY random() LIMIT 1),
(SELECT id FROM time_tracking_tasks WHERE brand_id = b.id ORDER BY random() LIMIT 1),
'Task ' || (1 + (i % 8)),
(NOW() - ((i % 30) || ' days')::interval - ((i % 8) || ' hours')::interval),
(NOW() - ((i % 30) || ' days')::interval - ((i % 8) || ' hours')::interval + interval '90 minutes'),
CASE WHEN i % 4 = 0 THEN 30 ELSE 0 END,
'QA generated log entry #' || i
FROM brands b
CROSS JOIN generate_series(1, 200) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
-- ============================================================================
-- 12. Water log infrastructure + entries
-- ============================================================================
INSERT INTO water_headgates (id, brand_id, name, max_flow_gpm, unit, status, active)
SELECT gen_random_uuid(), b.id, 'Headgate ' || i, 1000 + i * 100, 'GPM', 'open', true
FROM brands b
CROSS JOIN generate_series(1, 5) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
INSERT INTO water_irrigators (id, brand_id, name, pin_hash, language_preference, role, phone, active)
SELECT gen_random_uuid(), b.id, 'Irrigator ' || i, 'placeholder-pin-hash', 'en', 'irrigator', '+15550100000', true
FROM brands b
CROSS JOIN generate_series(1, 5) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
INSERT INTO water_log_entries (brand_id, headgate_id, irrigator_id, measurement, unit, logged_at, logged_by, method, total_gallons, notes)
SELECT
b.id,
(SELECT id FROM water_headgates WHERE brand_id = b.id ORDER BY random() LIMIT 1),
(SELECT id FROM water_irrigators WHERE brand_id = b.id ORDER BY random() LIMIT 1),
(50 + (i * 7) % 200)::numeric,
'GPM',
NOW() - ((i % 14) || ' days')::interval,
'aaaa1111-1111-1111-1111-111111111111',
'manual',
(1000 + (i * 23) % 5000)::numeric,
'Auto-generated water log #' || i
FROM brands b
CROSS JOIN generate_series(1, 100) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
-- ============================================================================
-- 13. Audit log entries
-- ============================================================================
INSERT INTO audit_logs (brand_id, user_id, action, entity_type, entity_id, details, ip_address, created_at)
SELECT
b.id,
'aaaa1111-1111-1111-1111-111111111111',
(ARRAY['create','update','delete','view','export'])[1 + (i % 5)],
(ARRAY['products','orders','stops','customers'])[1 + (i % 4)],
gen_random_uuid(),
jsonb_build_object('qa', true, 'iteration', i),
'10.0.0.' || (1 + (i % 254)),
NOW() - ((i % 90) || ' days')::interval
FROM brands b
CROSS JOIN generate_series(1, 50) AS i
WHERE b.id IN ('11111111-1111-1111-1111-111111111111','22222222-2222-2222-2222-222222222222')
ON CONFLICT DO NOTHING;
COMMIT;
-- ============================================================================
-- Summary
-- ============================================================================
SELECT 'products' AS tbl, COUNT(*) FROM products UNION ALL
SELECT 'stops', COUNT(*) FROM stops UNION ALL
SELECT 'customers', COUNT(*) FROM customers UNION ALL
SELECT 'orders', COUNT(*) FROM orders UNION ALL
SELECT 'order_items', COUNT(*) FROM order_items UNION ALL
SELECT 'wholesale_customers', COUNT(*) FROM wholesale_customers UNION ALL
SELECT 'wholesale_orders', COUNT(*) FROM wholesale_orders UNION ALL
SELECT 'comm_templates', COUNT(*) FROM communication_templates UNION ALL
SELECT 'comm_campaigns', COUNT(*) FROM communication_campaigns UNION ALL
SELECT 'comm_contacts', COUNT(*) FROM communication_contacts UNION ALL
SELECT 'comm_message_logs', COUNT(*) FROM communication_message_logs UNION ALL
SELECT 'time_tracking_logs', COUNT(*) FROM time_tracking_logs UNION ALL
SELECT 'water_log_entries', COUNT(*) FROM water_log_entries UNION ALL
SELECT 'audit_logs', COUNT(*) FROM audit_logs UNION ALL
SELECT 'brands', COUNT(*) FROM brands UNION ALL
SELECT 'admin_users', COUNT(*) FROM admin_users
ORDER BY tbl;
+1
View File
@@ -2,6 +2,7 @@
-- Stops: 269 | Locations: 40
-- Preferred: npx tsx scripts/import-tuxedo-stops.ts
-- Manual apply: psql "$DATABASE_URL" -f db/seeds/2026-tuxedo-tour-stops.sql
-- supabase db query --linked --file db/seeds/2026-tuxedo-tour-stops.sql
BEGIN;