Total Deals
deals logged
Total Gross
combined gross
Avg PVR
per vehicle retail
Finance Gross
total finance reserve
VSC / Warranty %
penetration
Maintenance %
penetration
GAP %
penetration
Other Products
total other products gross

Daily Gross Breakdown

Revenue Mix

Recent Deals

Performance by Store

Performance by Agent

📷 Scan Recap Sheet

Take a photo or upload an image of your finance recap — we'll extract deal info automatically.

Scanned recap
Reading document…

Deal Details

Finance Reserve$0
VSC / Warranty$0
Maintenance$0
GAP$0
Other Products$0
Total PVR$0

Drop PDF recap files here or click to browse

Supports multiple PDFs — we'll extract deal info from each one

📁 Documents are automatically organized by Store → Year → Week and scanned for deal data. Admins can browse all documents in Admin → Documents.

Drag & drop files here, or click to browse

PDF, JPG, PNG — will be scanned automatically

Select a week to view production data.
Total Invoices
0
Draft
0
Sent
0
Paid
0
Total Billed
$0

All Invoices

No invoices yet. Click "Auto-Bill All Deals" or use 📄 on any deal row, or click "Build Batch Invoice" to create a multi-deal invoice.

Invoice Preview

📂 Dealer Groups

All Dealers / Clients

Agent Roster

➕ Create New User

✅ Auto-confirm is ON — users can log in immediately with the password you set. No confirmation email required.

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

Select a week and click Download or Email to generate a report.

🎓 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;
⚠ Run this once. Existing deals will have 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;
After running this, use the 🏢 Dealers nav tab to manage your dealer roster. The 📊 button on any dealer row drills into their dashboard instantly.

💳 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.

Found in EmailJS → Account → API Keys
Found in EmailJS → Email Services
Found in EmailJS → Email Templates
Where to send new access request alerts
When notifications fire:
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