Dashboard
Daily Gross Breakdown
Revenue Mix
Recent Deals
Performance by Store
Performance by Agent
Log a Deal
📷 Scan Recap Sheet
Take a photo or upload an image of your finance recap — we'll extract deal info automatically.
Deal Details
Bulk Import
Drop PDF recap files here or click to browse
Supports multiple PDFs — we'll extract deal info from each one
Upload Documents
Drag & drop files here, or click to browse
PDF, JPG, PNG — will be scanned automatically
📈 Production Report
Invoices
All Invoices
Invoice Preview
🏢 Dealer Management
📂 Dealer Groups
All Dealers / Clients
Agents
Agent Roster
Administration
➕ Create New User
Pending Access Requests
All Profiles
🎯 Performance Goals
Set benchmarks that appear on the dashboard KPI cards. Goals are saved locally and shared across sessions on this device.
Current Performance vs Goals
Generate Weekly Performance Report
Report Preview
🎓 Onboarding Tour — Database Setup
Run this SQL once in your Supabase SQL Editor to enable the "Reset Tour" feature for users. This adds a tour_completed column to the profiles table so admins can reset the tour for any user.
-- Add tour_completed column to profiles table ALTER TABLE public.profiles ADD COLUMN IF NOT EXISTS tour_completed boolean DEFAULT false; -- Allow users to update their own tour_completed flag CREATE POLICY IF NOT EXISTS "Users can update own tour flag" ON public.profiles FOR UPDATE USING (auth.uid() = id) WITH CHECK (auth.uid() = id);
Without this column, tour completion is tracked in the user's browser only (localStorage). With the column, admins can reset the tour from User Management → 🎓 Reset Tour.
🗄 Required Database Updates
Run the following SQL in your Supabase SQL Editor to support the GAP field, maintenance/GAP separation, and all new penetration metrics.
-- Step 1: Drop dependent view and generated columns
DROP VIEW IF EXISTS public.deals_with_metrics;
ALTER TABLE public.deals DROP COLUMN IF EXISTS total_gross;
ALTER TABLE public.deals DROP COLUMN IF EXISTS has_warranty;
-- Step 2: Add gap_gross column
ALTER TABLE public.deals ADD COLUMN IF NOT EXISTS gap_gross numeric(10,2) DEFAULT 0;
-- Step 3: Recreate generated columns including GAP
ALTER TABLE public.deals
ADD COLUMN total_gross numeric(10,2)
GENERATED ALWAYS AS (
finance_gross + warranty_gross + maintenance_gross
+ COALESCE(misc_gross,0) + COALESCE(gap_gross,0)
) STORED;
ALTER TABLE public.deals
ADD COLUMN has_warranty boolean
GENERATED ALWAYS AS (warranty_gross > 0) STORED;
ALTER TABLE public.deals
ADD COLUMN has_maintenance boolean
GENERATED ALWAYS AS (maintenance_gross > 0) STORED;
ALTER TABLE public.deals
ADD COLUMN has_gap boolean
GENERATED ALWAYS AS (COALESCE(gap_gross,0) > 0) STORED;
-- Step 4: Recreate view
CREATE OR REPLACE VIEW public.deals_with_metrics AS
SELECT d.*,
p.full_name AS agent_full_name,
p.initials AS agent_initials,
p.home_state AS agent_home_state,
p.role AS agent_role
FROM public.deals d
LEFT JOIN public.profiles p ON d.agent_id = p.id;
SELECT 'Schema updated successfully' AS result;
gap_gross = 0 until updated.📁 Documents Table (Organized Storage)
Run this SQL to add organization columns to the documents table for store/week folder browsing.
-- Add organization columns to documents table ALTER TABLE public.documents ADD COLUMN IF NOT EXISTS store_name text; ALTER TABLE public.documents ADD COLUMN IF NOT EXISTS deal_date date; ALTER TABLE public.documents ADD COLUMN IF NOT EXISTS week_number integer; ALTER TABLE public.documents ADD COLUMN IF NOT EXISTS year integer; ALTER TABLE public.documents ADD COLUMN IF NOT EXISTS scan_text text; ALTER TABLE public.documents ADD COLUMN IF NOT EXISTS scan_data jsonb; ALTER TABLE public.documents ADD COLUMN IF NOT EXISTS uploaded_by_name text; -- Enable public read for admins (adjust RLS as needed) CREATE POLICY IF NOT EXISTS "Admins can view all documents" ON public.documents FOR SELECT USING (EXISTS (SELECT 1 FROM public.profiles WHERE id = auth.uid() AND role = 'admin')); SELECT 'Documents table updated' AS result;
🏢 Dealers / Stores Table
Run this SQL once to create the canonical dealers table and seed it from your existing deal data.
-- Step 1: Create stores table CREATE TABLE IF NOT EXISTS public.stores ( id uuid DEFAULT gen_random_uuid() PRIMARY KEY, name text NOT NULL UNIQUE, contact_name text, contact_email text, phone text, address text, city text, state text, notes text, active boolean DEFAULT true, created_at timestamptz DEFAULT now() ); -- Step 2: Seed from existing deal store names (deduplication) INSERT INTO public.stores (name) SELECT DISTINCT TRIM(store_name) FROM public.deals WHERE store_name IS NOT NULL AND TRIM(store_name) != '' ON CONFLICT (name) DO NOTHING; -- Step 3: Enable RLS (admins full access; agents read-only) ALTER TABLE public.stores ENABLE ROW LEVEL SECURITY; CREATE POLICY IF NOT EXISTS "Admins manage stores" ON public.stores FOR ALL USING (EXISTS (SELECT 1 FROM public.profiles WHERE id = auth.uid() AND role = 'admin')); CREATE POLICY IF NOT EXISTS "All users read stores" ON public.stores FOR SELECT USING (auth.uid() IS NOT NULL); SELECT 'Stores table ready — ' || COUNT(*) || ' dealers seeded' AS result FROM public.stores;
💳 Bill.com Integration Not configured
Connect your BILL account to send agent commission payments directly as ACH via the invoice builder. Credentials are stored locally in your browser session.
Agent → Vendor Mapping
Link each agent to their Bill.com Vendor ID for ACH payments🔔 Email Notifications
Connect EmailJS (free — emailjs.com) to automatically notify admins when someone requests access, and notify agents when they're approved or denied.
Setup steps: Create a free account at emailjs.com → Add an Email Service (Gmail, Outlook, etc.) → Create a Template with variables {{to_email}}, {{subject}}, {{message}} → Copy your IDs below.
• Admin receives email whenever an agent submits an access request
• Agent receives email when their request is approved or denied
• Notifications are silently skipped if EmailJS is not configured