Skip to content

iuriesula/CD-App

Repository files navigation

FCapp - Car Dealership CRM Project Brief for Claude

Context You are helping build FCapp, a lightweight CRM for car dealerships. This is being built from scratch (not using ERPNext or other existing ERPs) because the target users (car salespeople) have rejected traditional CRMs - "paper works better for them." The operator is a marketing agency that runs Meta ads for up to 10 classic car dealerships. Each dealership has 1-3 salespeople. The agency owner needs cross-dealership analytics to optimize ad campaigns. Core philosophy: The system must be smart so the salesperson can be simple. They work through a daily task list - the system handles the thinking.

Technical Stack LayerTechnologyRationaleFrontendNext.js (TypeScript)Modern, fast, can become PWADatabasePostgreSQLTraditional, portable, easy to migrate serversORMPrisma or DrizzleType-safe database accessAuthNextAuth.js or custom JWTSimple, self-containedVoIPYate PBX + custom API bridgeSeparate service, REST API for appEmailIMAP/SMTP integrationDirect connection to email providersDocumentsReact-PDF or PDFKitGenerate buyer's orders, invoicesSignaturesDocuSeal (self-hosted, open source)Digital signaturesMeta IntegrationMeta Marketing APICampaign data, lead attributionLLM (Phase 4)Anthropic Claude APILead summaries, analysis Deployment path: Local development first → can move to any VPS/server with PostgreSQL

Database Schema sql-- Multi-tenancy: Every query filters by dealership_id -- Agency admin role bypasses dealership filter for analytics

CREATE TYPE user_role AS ENUM ('salesperson', 'manager', 'agency_admin', 'contractor');

CREATE TYPE contractor_department AS ENUM ('IT', 'Marketing', 'Content');

CREATE TYPE request_status AS ENUM ('open', 'in_progress', 'resolved', 'closed');

CREATE TYPE request_priority AS ENUM ('low', 'normal', 'high', 'urgent');

CREATE TYPE lead_stage AS ENUM ( 'new_lead', 'interested', 'negotiating', 'buyers_order_sent', 'buyers_order_signed', 'invoice_sent', 'won_invoice_paid', 'won_preparing', 'won_ready_to_ship', 'won_arrived', 'lost', 'lost_unanswered' );

CREATE TYPE lead_source AS ENUM ( 'meta_ad', 'website_form', 'phone_call', 'walk_in', 'referral' );

CREATE TYPE activity_type AS ENUM ( 'email_sent', 'email_received', 'call_outbound', 'call_inbound', 'call_missed', 'voicemail_left', 'note_added', 'stage_changed', 'document_sent', 'document_signed' );

CREATE TYPE task_type AS ENUM ( 'follow_up_call', 'follow_up_email', 'review_document', 'custom' );

CREATE TYPE document_type AS ENUM ( 'buyers_order', 'invoice', 'loan_agreement' );

CREATE TYPE document_status AS ENUM ( 'draft', 'sent', 'viewed', 'signed' );

-- Dealerships (tenants) CREATE TABLE dealerships ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(255) NOT NULL, address TEXT, phone VARCHAR(50), settings JSONB DEFAULT '{}', created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() );

-- Users (salespeople, managers, agency admin, contractors) CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), dealership_id UUID REFERENCES dealerships(id), -- NULL for agency_admin and contractors email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, role user_role NOT NULL DEFAULT 'salesperson', voip_extension VARCHAR(20), is_active BOOLEAN DEFAULT true, contractor_department contractor_department, -- Only for contractors created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() );

-- Contractor-Dealership Access (many-to-many) CREATE TABLE contractor_dealerships ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), contractor_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, dealership_id UUID NOT NULL REFERENCES dealerships(id) ON DELETE CASCADE, created_at TIMESTAMP DEFAULT NOW(), UNIQUE(contractor_id, dealership_id) );

CREATE INDEX idx_contractor_dealerships_contractor ON contractor_dealerships(contractor_id); CREATE INDEX idx_contractor_dealerships_dealership ON contractor_dealerships(dealership_id);

-- Requests/Tickets CREATE TABLE requests ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), dealership_id UUID NOT NULL REFERENCES dealerships(id) ON DELETE CASCADE, created_by UUID NOT NULL REFERENCES users(id), assigned_to UUID REFERENCES users(id),

title VARCHAR(255) NOT NULL, description TEXT NOT NULL, department contractor_department NOT NULL, priority request_priority NOT NULL DEFAULT 'normal', status request_status NOT NULL DEFAULT 'open',

created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), resolved_at TIMESTAMP, closed_at TIMESTAMP );

CREATE INDEX idx_requests_dealership ON requests(dealership_id); CREATE INDEX idx_requests_department ON requests(department); CREATE INDEX idx_requests_status ON requests(status); CREATE INDEX idx_requests_assigned ON requests(assigned_to);

-- Request Messages (conversation thread) CREATE TABLE request_messages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), request_id UUID NOT NULL REFERENCES requests(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES users(id),

message TEXT NOT NULL, attachment_path TEXT, attachment_name VARCHAR(255),

created_at TIMESTAMP DEFAULT NOW() );

CREATE INDEX idx_request_messages_request ON request_messages(request_id);

-- Request Message Reads (read receipts) CREATE TABLE request_message_reads ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), message_id UUID NOT NULL REFERENCES request_messages(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES users(id), read_at TIMESTAMP DEFAULT NOW(), UNIQUE(message_id, user_id) );

CREATE INDEX idx_request_message_reads_message ON request_message_reads(message_id);

-- Leads (the core entity) CREATE TABLE leads ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), dealership_id UUID NOT NULL REFERENCES dealerships(id), assigned_to UUID REFERENCES users(id),

-- Pipeline stage lead_stage NOT NULL DEFAULT 'new_lead', win_probability DECIMAL(3,2) DEFAULT 0.10,

-- Source & Attribution source lead_source, meta_campaign_id VARCHAR(100), meta_adset_id VARCHAR(100), meta_ad_id VARCHAR(100),

-- Contact Info first_name VARCHAR(100), last_name VARCHAR(100), primary_email VARCHAR(255), primary_phone VARCHAR(50), alternate_emails TEXT[] DEFAULT '{}', alternate_phones TEXT[] DEFAULT '{}',

-- Location city VARCHAR(100), state VARCHAR(50), zip VARCHAR(20),

-- Interest interested_vehicle TEXT, notes TEXT,

-- Follow-up Tracking attempt_count INTEGER DEFAULT 0, last_attempt_at TIMESTAMP, next_follow_up_at TIMESTAMP,

-- Timestamps created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), closed_at TIMESTAMP,

-- Indexes for deduplication CONSTRAINT idx_lead_primary_email UNIQUE (dealership_id, primary_email) WHERE primary_email IS NOT NULL );

CREATE INDEX idx_leads_dealership ON leads(dealership_id); CREATE INDEX idx_leads_stage ON leads(stage); CREATE INDEX idx_leads_assigned ON leads(assigned_to); CREATE INDEX idx_leads_phone ON leads(primary_phone); CREATE INDEX idx_leads_next_followup ON leads(next_follow_up_at);

-- Activity Log (full history of interactions) CREATE TABLE activities ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), lead_id UUID NOT NULL REFERENCES leads(id) ON DELETE CASCADE, user_id UUID REFERENCES users(id), activity_type activity_type NOT NULL, details JSONB DEFAULT '{}', -- email content, call duration, old/new stage, etc. created_at TIMESTAMP DEFAULT NOW() );

CREATE INDEX idx_activities_lead ON activities(lead_id); CREATE INDEX idx_activities_created ON activities(created_at);

-- Tasks (auto-generated and manual) CREATE TABLE tasks ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), lead_id UUID NOT NULL REFERENCES leads(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES users(id), dealership_id UUID NOT NULL REFERENCES dealerships(id),

task_type task_type NOT NULL, title VARCHAR(255) NOT NULL, description TEXT,

due_date DATE NOT NULL, due_time TIME,

is_auto_generated BOOLEAN DEFAULT false, attempt_number INTEGER, -- For follow-up sequence (1-5)

completed BOOLEAN DEFAULT false, completed_at TIMESTAMP,

created_at TIMESTAMP DEFAULT NOW() );

CREATE INDEX idx_tasks_user_due ON tasks(user_id, due_date) WHERE completed = false; CREATE INDEX idx_tasks_dealership ON tasks(dealership_id);

-- Documents CREATE TABLE documents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), lead_id UUID NOT NULL REFERENCES leads(id) ON DELETE CASCADE, dealership_id UUID NOT NULL REFERENCES dealerships(id),

document_type document_type NOT NULL, status document_status DEFAULT 'draft',

file_path TEXT, -- Local file path or URL signature_request_id VARCHAR(255), -- From DocuSeal

created_at TIMESTAMP DEFAULT NOW(), sent_at TIMESTAMP, viewed_at TIMESTAMP, signed_at TIMESTAMP );

CREATE INDEX idx_documents_lead ON documents(lead_id);

-- Meta Campaign Mapping (links Meta campaigns to dealerships) CREATE TABLE meta_campaign_mappings ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), dealership_id UUID NOT NULL REFERENCES dealerships(id), meta_campaign_id VARCHAR(100) NOT NULL UNIQUE, campaign_name VARCHAR(255), created_at TIMESTAMP DEFAULT NOW() );

-- Email Messages (for in-app email) CREATE TABLE emails ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), lead_id UUID REFERENCES leads(id) ON DELETE SET NULL, user_id UUID REFERENCES users(id), dealership_id UUID NOT NULL REFERENCES dealerships(id),

direction VARCHAR(10) NOT NULL, -- 'inbound' or 'outbound' from_address VARCHAR(255) NOT NULL, to_address VARCHAR(255) NOT NULL, subject VARCHAR(500), body_text TEXT, body_html TEXT,

message_id VARCHAR(255), -- Email Message-ID header for threading in_reply_to VARCHAR(255),

is_read BOOLEAN DEFAULT false, opened_at TIMESTAMP, -- Tracking pixel detection

created_at TIMESTAMP DEFAULT NOW() );

CREATE INDEX idx_emails_lead ON emails(lead_id); CREATE INDEX idx_emails_user ON emails(user_id);

-- Call Logs (from Yate PBX) CREATE TABLE call_logs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), lead_id UUID REFERENCES leads(id) ON DELETE SET NULL, user_id UUID REFERENCES users(id), dealership_id UUID NOT NULL REFERENCES dealerships(id),

direction VARCHAR(10) NOT NULL, -- 'inbound' or 'outbound' from_number VARCHAR(50), to_number VARCHAR(50), did_used VARCHAR(50), -- Which DID was used for outbound

status VARCHAR(20), -- 'answered', 'missed', 'voicemail', 'busy', 'failed' duration_seconds INTEGER DEFAULT 0, recording_path TEXT,

yate_call_id VARCHAR(100),

started_at TIMESTAMP DEFAULT NOW(), ended_at TIMESTAMP );

CREATE INDEX idx_calls_lead ON call_logs(lead_id); CREATE INDEX idx_calls_user ON call_logs(user_id);


---

## Pipeline Stages & Win Probability

When a lead's stage changes, auto-update `win_probability`:

| Stage | Win % | Auto Follow-up? |
|-------|-------|-----------------|
| new_lead | 10% | YES |
| interested | 20% | NO |
| negotiating | 40% | NO |
| buyers_order_sent | 60% | Optional |
| buyers_order_signed | 85% | NO |
| invoice_sent | 95% | NO |
| won_invoice_paid | 100% | NO |
| won_preparing | 100% | NO |
| won_ready_to_ship | 100% | NO |
| won_arrived | 100% | NO |
| lost | 0% | NO |
| lost_unanswered | 0% | NO |

---

## Follow-Up Attempt Logic

**Applies to:** `new_lead` stage only

**Business logic to implement:**

When lead is created with stage = 'new_lead': → Set next_follow_up_at = NOW (immediate task) → Create Task: "Initial contact - Attempt 1"

When salesperson completes task and marks "No response": → Increment attempt_count → Based on attempt_count, calculate next_follow_up_at: Attempt 1 → 2: +1 day Attempt 2 → 3: +2 days
Attempt 3 → 4: +3 days Attempt 4 → 5: +3 days → Create new Task for next attempt

When attempt_count reaches 5 and still no response: → Auto-change stage to 'lost_unanswered' → Set closed_at = NOW → No more tasks generated

When lead responds with interest (salesperson marks "Responded"): → Change stage to 'interested' → Clear next_follow_up_at → Set attempt_count = 0 → Stop generating follow-up tasks


---

## Core User Interfaces

### 1. Pipeline Board (Kanban)

**Route:** `/dashboard` or `/pipeline`

**Layout:**
- Horizontal scrolling columns, one per stage
- Cards are draggable between columns
- Dropping a card on a column = stage change

**Card content:**
- Lead name
- Interested vehicle (truncated)
- Days in current stage
- Next task due indicator
- Source icon (Meta, phone, etc.)

**Filters:**
- By assigned salesperson
- By date range
- By source

**Actions:**
- Click card → Navigate to Lead Detail
- Drag card → Change stage (with confirmation for backward moves)

### 2. "My Day" Task List

**Route:** `/my-day` (default landing page for salespeople)

**This is where salespeople spend 80% of their time.**

**Layout:**
- Single column list of today's tasks
- Grouped: Overdue → Due Today → Upcoming
- Each task is expandable or links to lead

**Task card content:**
- Lead name + primary phone + primary email
- Task title ("Follow-up call - Attempt 3 of 5")
- Time due (if set)
- Quick action buttons

**Quick actions (without leaving the list):**
- 📞 Call → Initiates VoIP call
- ✉️ Email → Opens compose modal
- ✅ Mark Complete → Logs activity, asks for outcome
- ❌ No Response → Logs attempt, generates next task

**Outcome modal (on complete):**
- "Did they respond?"
  - Yes, interested → Move to Interested stage
  - Yes, not interested → Move to Lost
  - No answer → Increment attempt, schedule next

### 3. Lead Detail Page

**Route:** `/leads/[id]`

**Layout:** Single page with all lead info

**Sections:**

**Header:**
- Name, stage badge, win probability
- Click-to-call button, click-to-email button
- Edit button, Delete button

**Contact Info Panel:**
- Primary phone, primary email
- Alternate phones/emails
- Location (city, state)
- Source + Meta campaign (if applicable)

**Vehicle Interest:**
- Text field or dropdown

**Activity Timeline:**
- Chronological list of all activities
- Icons by type (📞 call, ✉️ email, 📝 note, 🔄 stage change)
- Expandable for details (email content, call duration)

**Notes:**
- Free-form text area
- Auto-saves

**Documents:**
- List of buyer's orders, invoices
- Status badges (draft, sent, signed)
- Generate new document button

**Tasks:**
- Upcoming tasks for this lead
- Complete/reschedule buttons

### 4. Agency Dashboard (Admin Only)

**Route:** `/admin/dashboard`

**Purpose:** Cross-dealership analytics for ad optimization

**Metrics:**
- Total leads by dealership
- Conversion rate by stage (funnel visualization)
- Average time in each stage
- Win rate by source
- Win rate by Meta campaign
- Geographic breakdown (which states produce buyers)
- Lead volume over time

### 5. Requests/Tickets Module

**Route:** `/requests`

**Purpose:** Dealership-contractor communication and task management

**Available to:** All users (filtered by role and permissions)

**Layout:**
- List view with request cards
- Filters: Status, Department, Priority
- Click card → Request detail with conversation thread

**Request Detail:**

**Header:**
- Title, department badge, priority badge, status badge
- Created by, assigned to, timestamps
- Status update dropdown (role-based permissions)
- Assign/Unassign contractor button

**Conversation Thread:**
- Chronological message list
- Each message shows: sender, timestamp, message content
- File attachments display with download links
- Read receipts: "Read by [names]" below each message
- Real-time updates (30 second polling)

**Message Input:**
- Text area for new messages
- File attachment button (images, PDFs, documents)
- Send button

**Actions:**
- Add message to thread
- Upload file attachment
- Change status (based on role)
- Assign to contractor
- Mark as resolved/closed

**Contractor View Differences:**
- See only requests for their department
- Can assign themselves to unassigned requests
- Update status as they work
- Add progress updates and file attachments

---

## API Routes (Next.js API or separate Express)

### Auth

POST /api/auth/login POST /api/auth/logout GET /api/auth/me POST /api/auth/change-password


### Dealerships (admin only)

GET /api/dealerships POST /api/dealerships GET /api/dealerships/:id PUT /api/dealerships/:id DELETE /api/dealerships/:id


### Users

GET /api/users # List users (filtered by dealership for non-admin) POST /api/users # Create user (admin or manager) GET /api/users/:id PUT /api/users/:id DELETE /api/users/:id


### Contractors

GET /api/contractors # List all contractors POST /api/contractors # Create contractor GET /api/contractors/:id # Get contractor details PUT /api/contractors/:id # Update contractor DELETE /api/contractors/:id # Delete contractor GET /api/contractors/:id/dealerships # Get assigned dealerships POST /api/contractors/:id/dealerships # Assign dealerships DELETE /api/contractors/:id/dealerships/:dealershipId # Remove assignment


### Requests

GET /api/requests # List requests (filtered by role) POST /api/requests # Create request GET /api/requests/:id # Get request details PUT /api/requests/:id # Update request DELETE /api/requests/:id # Delete request PUT /api/requests/:id/status # Update status PUT /api/requests/:id/assign # Assign contractor GET /api/requests/:id/messages # Get conversation thread POST /api/requests/:id/messages # Add message to thread PUT /api/requests/:id/messages/:messageId/read # Mark message as read POST /api/requests/:id/attachments # Upload attachment GET /api/requests/stats # Get request statistics GET /api/contractors/:id/requests # Get contractor's requests


### Leads

GET /api/leads # List with filters (stage, assigned, date range) POST /api/leads # Create (runs deduplication check) GET /api/leads/:id PUT /api/leads/:id DELETE /api/leads/:id PUT /api/leads/:id/stage # Change stage (logs activity, updates win_probability) POST /api/leads/:id/merge # Merge with another lead GET /api/leads/duplicates # Find potential duplicates


### Activities

GET /api/leads/:id/activities # Activity timeline for lead POST /api/leads/:id/activities # Log manual activity (note, call, email)


### Tasks

GET /api/tasks # My tasks (filtered by due date) GET /api/tasks/today # Today's tasks for current user POST /api/tasks # Create manual task PUT /api/tasks/:id PUT /api/tasks/:id/complete # Mark complete (with outcome) DELETE /api/tasks/:id


### Documents

GET /api/leads/:id/documents POST /api/leads/:id/documents # Generate document GET /api/documents/:id PUT /api/documents/:id/send # Send for signature GET /api/documents/:id/download # Download PDF


### VoIP (proxy to Yate)

POST /api/voip/call # Initiate outbound call POST /api/voip/hangup GET /api/voip/status/:callId POST /api/voip/webhook/incoming # Yate webhook for incoming calls POST /api/voip/webhook/ended # Yate webhook for call ended


### Email

GET /api/emails # Inbox for current user GET /api/emails/:id POST /api/emails # Send email POST /api/emails/webhook/incoming # Webhook for incoming emails


### Meta Integration

GET /api/meta/campaigns # List campaigns from Meta POST /api/meta/mappings # Map campaign to dealership GET /api/meta/mappings GET /api/meta/metrics/:campaignId POST /api/meta/webhook/lead # Meta Lead Ads webhook


### Analytics (admin only)

GET /api/analytics/overview # Dashboard summary GET /api/analytics/funnel # Conversion funnel GET /api/analytics/by-source GET /api/analytics/by-campaign GET /api/analytics/by-geography GET /api/analytics/by-dealership

Lead Deduplication Logic On lead creation, before inserting: javascriptasync function checkDuplicates(dealership_id, email, phone) { const duplicates = await db.query(SELECT id, first_name, last_name, primary_email, primary_phone, stage FROM leads WHERE dealership_id = $1 AND ( primary_email = $2 OR primary_phone = $3 OR $2 = ANY(alternate_emails) OR $3 = ANY(alternate_phones) ), [dealership_id, email, phone]);

return duplicates; }

// If duplicates found, return them to UI for user decision: // - Create anyway (different person, same contact) // - Merge with existing lead // - Cancel creation


**Merge logic:**
- Keep primary contact info from "winner" (user chooses)
- Add other lead's emails/phones to alternate arrays
- Combine activity histories
- Keep higher stage (further in pipeline)
- Sum attempt counts if both in new_lead stage
- Delete the "loser" lead

---

## VoIP Integration (Yate PBX)

**Yate runs as separate service** with its own API.

**DID rotation logic:**
- Each dealership has N DIDs assigned
- Track last used DID per dealership
- Round-robin: After every ~5 outbound calls, switch to next DID
- Prevents spam flagging

**Yate API service endpoints:**

POST /api/call/initiate Body: { from_extension, to_number, dealership_id } Returns: { call_id, did_used }

POST /api/call/hangup Body: { call_id }

GET /api/call/status/:call_id Returns: { status, duration }

Webhooks (Yate → App): POST /webhook/call/started { call_id, direction, from, to } POST /webhook/call/answered { call_id } POST /webhook/call/ended { call_id, duration, status }

Email Integration Architecture: IMAP polling + SMTP sending Per dealership configuration: json{ "imap_host": "imap.gmail.com", "imap_port": 993, "smtp_host": "smtp.gmail.com", "smtp_port": 587, "email_address": "sales@dealership.com", "password": "encrypted_password" }


**Email → Lead linking:**
1. On incoming email, extract sender address
2. Search leads where `primary_email = sender OR sender = ANY(alternate_emails)`
3. If found, link email to lead
4. If not found, create new lead with source = 'email'

**Open tracking:**
- Embed tracking pixel in outgoing emails
- Pixel URL: `/api/emails/track/:email_id`
- On pixel load, update `opened_at`

---

## Recent Improvements (Dec 19-22, 2024)

### Quick Wins Completed (Dec 19-22)

1. **Email Threading Enhancement** (Dec 19)
   - Implemented automatic email threading for lead conversations
   - Emails properly use In-Reply-To headers to maintain conversation threads
   - Replying to a lead automatically continues the existing email thread

2. **Email Composer Size Increase** (Dec 19)
   - Increased modal width from `max-w-4xl` to `max-w-5xl`
   - Increased modal height from `max-h-[90vh]` to `max-h-[95vh]`
   - Increased textarea height from `min-h-[350px]` to `min-h-[500px]`
   - Reduces scrolling when composing long emails

3. **Invoice Wire Transfer Details** (Dec 19)
   - Added fields for account name, account number, and routing number
   - Wire transfer instructions display complete banking information
   - Available in both wizard mode and full form mode
   - Shows in PDF print output with proper formatting

4. **Media Folder Recursive Deletion** (Dec 19)
   - Folders can now be deleted even when they contain files and subfolders
   - Automatically deletes all nested contents recursively
   - No need to manually empty folders before deletion

5. **Email Undo Feature** (Dec 19)
   - 10-second delay before sending emails (like Gmail/Titan)
   - Toast notification with countdown timer
   - One-click "Undo" button to cancel send
   - Restores email content when undone
   - Prevents accidental sends

6. **Email Module Access for Salespeople - COMPLETED** (Dec 22)
   - **Expanded Permissions**
     - Salespeople can now access Settings → Email module (previously manager-only)
     - Salespeople can create personal email templates
     - Salespeople can create dealership-wide signatures and templates
     - Contractors excluded from email module access
   - **Enhanced Email Composer**
     - Auto-insert default signature when composer opens
     - Visual signature separator (---) for clarity
     - Compact signature button in footer (replaced large dropdown)
     - Easy signature switching via dropdown menu
     - Real-time signature preview while composing
   - **Bug Fixes**
     - Fixed 15 Button component variants across 7 files (changed invalid `variant="outline"` to `variant="secondary"`)

7. **Phase 3: Contractor System & Requests - COMPLETED** (Dec 22)
   - **Contractor Role System**
     - New `contractor` user role with department specialization
     - Three departments: IT (full access), Marketing (leads/email), Content (media/inventory)
     - Multi-dealership assignment (many-to-many relationship)
     - Dealership selector in navigation (like agency admin)
     - Department-based permission system
   - **Request/Ticket System**
     - Create requests with department, priority, and file attachments
     - Full conversation threads with real-time updates
     - Read receipts showing who viewed messages
     - Status workflow: Open → In Progress → Resolved → Closed
     - Priority levels: Low, Normal, High, Urgent with color-coded badges
     - Request assignment to contractors
     - Filter by status, department, and dealership
     - File attachment support in messages
   - **Contractor Management UI**
     - Admin interface to create and manage contractors
     - Assign/unassign dealerships
     - Track contractor workload
     - Department badges for easy identification

8. **Media Module Enhancements - COMPLETED** (Dec 22)
   - **Image Viewer with Zoom, Pan, and Navigation**
     - Full-screen modal viewer with zoom 0.25x to 5x
     - Pan/drag when zoomed (grab cursor feedback)
     - Keyboard shortcuts: arrow keys for navigation, +/- for zoom, Esc to close
     - Next/Previous navigation buttons
     - Download functionality
     - Image dimensions and position display
   - **Folder Upload Support**
     - Upload entire folders preserving nested structure
     - Recursive folder creation
     - Maintains folder hierarchy and path structure
     - Batch file upload capability
   - **Insert Images into Email Composer**
     - Media picker modal with search functionality
     - Multi-select images from media library
     - Images render as visual thumbnails in email body (Gmail-style)
     - Available in both lead detail and main email module
     - Maintains text direction and formatting

---

## Contractor System & Request Management

### Overview

Phase 3 introduces a contractor role system and request/ticket module for dealerships to collaborate with external contractors (IT, Marketing, Content).

### User Roles & Departments

**Contractor Role:**
- External service providers with specialized department access
- Can be assigned to multiple dealerships
- Department determines permissions and visibility

**Contractor Departments:**

| Department | Access Permissions |
|-----------|-------------------|
| **IT** | Settings, Users, Media, Email, Inventory, Dealership Config |
| **Content** | Media, Inventory |
| **Marketing** | Leads, Email Templates, Media |

### For Agency Admins

**Managing Contractors:**

1. Navigate to **Admin > Contractors**
2. Click **"Create Contractor"** button
3. Fill in contractor details:
   - Name and email
   - Select department (IT, Marketing, Content)
   - Set password
4. Click **"Assign Dealerships"** to grant access
5. Select one or more dealerships
6. Monitor contractor activity in dashboard

**Contractor Management Features:**
- View all contractors with department badges
- Edit contractor information and department
- Reassign dealerships
- Deactivate contractor accounts
- Track contractor request workload

### For Dealership Users

**Creating Requests:**

1. Navigate to **Requests** in sidebar
2. Click **"New Request"** button
3. Fill in request details:
   - **Title**: Brief description
   - **Description**: Full details of the request
   - **Department**: IT, Marketing, or Content
   - **Priority**: Low, Normal, High, or Urgent
   - **Attachments** (optional): Upload files
4. Submit request

**Managing Requests:**

- View all requests in list with filters
- Filter by status (Open, In Progress, Resolved, Closed)
- Filter by department
- Click request to view full conversation thread
- Add messages to conversation
- Attach additional files as needed
- Mark as resolved when complete
- Reopen if issue persists

**Request Features:**
- Real-time conversation threads
- File attachments (images, documents)
- Read receipts showing who has viewed messages
- Priority badges for urgent items
- Status tracking through workflow

### For Contractors

**Switching Between Dealerships:**

- Use dealership selector in top navigation
- Switches to selected dealership's context
- View requests specific to that dealership

**Working with Requests:**

1. Navigate to **Requests** in sidebar
2. View requests filtered to your department
3. Click on unassigned request
4. Click **"Assign to Me"** to take ownership
5. Update status to **"In Progress"**
6. Respond in conversation thread
7. Upload work files or screenshots
8. Update status to **"Resolved"** when complete
9. Close request after client confirmation

**Contractor Workflow:**

- **Open**: New requests awaiting assignment
- **In Progress**: Actively working on request
- **Resolved**: Work complete, pending client approval
- **Closed**: Request fully complete and approved

**Best Practices:**
- Respond to requests within 24 hours
- Update status regularly to keep clients informed
- Use attachments to show progress
- Mark resolved only when work is complete
- Close requests after client confirms satisfaction

### Request Priority Levels

| Priority | Description | Badge Color |
|---------|-------------|------------|
| **Low** | Non-urgent, can be handled in normal schedule | Gray |
| **Normal** | Standard priority, handle in regular queue | Blue |
| **High** | Important, prioritize in daily work | Orange |
| **Urgent** | Critical issue, immediate attention required | Red |

### API Endpoints

**Contractor Management** (8 endpoints):
- `GET /api/contractors` - List contractors
- `POST /api/contractors` - Create contractor
- `GET /api/contractors/:id` - Get contractor details
- `PUT /api/contractors/:id` - Update contractor
- `DELETE /api/contractors/:id` - Delete contractor
- `GET /api/contractors/:id/dealerships` - Get assigned dealerships
- `POST /api/contractors/:id/dealerships` - Assign dealerships
- `DELETE /api/contractors/:id/dealerships/:dealershipId` - Remove assignment

**Request System** (13 endpoints):
- `GET /api/requests` - List requests (filtered by role)
- `POST /api/requests` - Create request
- `GET /api/requests/:id` - Get request details
- `PUT /api/requests/:id` - Update request
- `DELETE /api/requests/:id` - Delete request
- `PUT /api/requests/:id/status` - Update status
- `PUT /api/requests/:id/assign` - Assign contractor
- `GET /api/requests/:id/messages` - Get conversation
- `POST /api/requests/:id/messages` - Add message
- `PUT /api/requests/:id/messages/:messageId/read` - Mark read
- `POST /api/requests/:id/attachments` - Upload attachment
- `GET /api/requests/stats` - Get statistics
- `GET /api/contractors/:id/requests` - Get contractor's requests

For detailed API documentation, see `CONTRACTOR_API_REFERENCE.md`.

---

## Development Phases

### Phase 1: Core CRM (Priority - MVP) ✅ COMPLETED

**Goal:** Replace paper, get salespeople using it

- [x] Project setup (Next.js, TypeScript, PostgreSQL, Prisma)
- [x] Database schema creation
- [x] Auth system (login, sessions, JWT-based)
- [x] Dealership CRUD (admin only)
- [x] User CRUD (admin/manager)
- [x] Lead CRUD with deduplication
- [x] Pipeline board (Kanban with drag-drop)
- [x] Lead detail page
- [x] Activity logging (manual - "I called", "I emailed" buttons)
- [x] Task system with auto-generation
- [x] "My Day" task list view
- [x] Follow-up cadence logic (the 5-attempt system)

### Phase 2: Communications ✅ COMPLETED

**Goal:** Call and email from within the app

- [x] Email integration (IMAP/SMTP)
- [x] In-app email compose with templates and signatures
- [x] In-app inbox
- [x] Email-to-lead auto-linking
- [x] Email open tracking
- [x] **Email threading** - Automatic conversation threading with In-Reply-To headers
- [x] Email folder management (inbox, spam, trash, important)
- [ ] Yate PBX service setup (planned for future)
- [ ] Click-to-call from app (planned for future)
- [ ] Call logging (planned for future)
- [ ] Incoming call popup with lead lookup (planned for future)

### Phase 2.5: Media Management ✅ COMPLETED (Dec 22, 2024)

**Goal:** Organize and manage dealership media assets

- [x] Media folder structure with nested folders
- [x] File upload with drag-and-drop support
- [x] **Folder upload** - Upload entire folders preserving structure (Dec 22)
- [x] Grid and list view modes
- [x] **Image viewer with zoom** - Full-screen modal with pan, drag-to-pan, keyboard navigation (Dec 22)
  - Zoom: 0.25x to 5x with +/- keys or buttons
  - Pan: Click-drag when zoomed, arrow keys for navigation
  - Download button, image dimensions display
- [x] **Insert images into emails** - Rich contentEditable composer with visual thumbnails (Dec 22)
  - Media picker modal with search and multi-select
  - Images render as visual previews in email body
  - Available in lead detail and main email module
- [x] Image preview functionality
- [x] File operations (rename, move, delete, recursive folder deletion)
- [x] Search functionality
- [x] Breadcrumb navigation
- [x] Agency admin dealership selector with search
- [x] Permission system - all authenticated users can access media

### Phase 2.75: Documents & Invoicing ✅ COMPLETED (Dec 5, 2024)

**Goal:** Generate and sign paperwork digitally

- [x] Buyer's Order builder with wizard mode
- [x] LLC/DBA name formatting
- [x] Professional print layouts with brand colors
- [x] Invoice builder with wizard mode
- [x] Payment method selection (Wire Transfer, Cashier's Check)
- [x] **Wire transfer details** - Account name, account number, routing number
- [x] Buyer's Order linking to invoices
- [x] PDF generation and printing
- [x] Document status tracking (draft, sent, viewed, signed)
- [ ] DocuSeal integration for digital signatures (planned)
- [ ] Loan agreement template (planned)

### Phase 3: Contractor Access & Request System ✅ COMPLETED (Dec 22, 2024)

**Goal:** Enable external contractors (IT, Marketing, Content) to work with dealerships

#### Contractor Role System
- [x] New user role: `contractor` with department field (IT, Marketing, Content)
- [x] Contractor-to-dealership access mapping (many-to-many relationship)
- [x] Department-based permissions:
  - **IT contractors**: Full access (settings, users, media, email, inventory, dealership config)
  - **Content creators**: Media and inventory (upload photos/descriptions)
  - **Marketing contractors**: Leads, email templates, and media
- [x] Contractor management interface for agency admins
- [x] Dealership selector for contractors (like agency admin)

#### Request/Ticket System Module
- [x] New "Requests" module for dealerships and contractors
- [x] Create request with:
  - Department selection (IT, Marketing, Content)
  - Title and description
  - File attachments
  - Priority level (Low, Normal, High, Urgent)
- [x] Contractor notification of new requests
- [x] Full conversation thread per request:
  - Text messages
  - File attachments (images, documents)
  - Read receipts with "Read by" display
  - Timestamp for each message
  - Real-time updates via polling
- [x] Request status tracking (Open, In Progress, Resolved, Closed)
- [x] Request assignment to specific contractor
- [x] Filter requests by status, department, dealership
- [x] Priority badges and status indicators
- [x] Department routing to correct contractors

#### Media Module - Completed & Future Enhancements
- [x] **Folder upload** ✅ (Dec 22, 2024)
- [x] **Image viewer with zoom/pan/navigation** ✅ (Dec 22, 2024)
- [x] **Insert images into email composer** ✅ (Dec 22, 2024)
- [ ] **Vehicle-based organization** (Future):
  - Auto-create folder per vehicle (using VIN or stock number)
  - Subfolders: Exterior, Interior, Engine, Damage, Documents
  - Link vehicle media to inventory listings
  - Auto-display vehicle photos in email templates
  - Integration with vehicle inventory system

### Phase 4: Intelligence & Analytics

**Goal:** Optimize ads, add AI assistance

- [ ] Meta Marketing API integration
- [ ] Campaign-to-dealership mapping
- [ ] Lead attribution tracking
- [ ] Agency dashboard (cross-dealership analytics)
- [ ] Geographic analysis (buyer locations)
- [ ] LLM integration: Summarize lead communications
- [ ] LLM integration: Suggest responses
- [ ] LLM integration: Analyze which creatives perform best

---

## Troubleshooting

### Common Issues

**Q: Contractor can't see any requests**
- Verify contractor is assigned to the correct dealership in Admin > Contractors
- Check that contractor's department matches the request department
- Confirm contractor has selected the correct dealership from the selector

**Q: Unable to assign contractor to request**
- Ensure contractor's department matches the request department
  - IT contractors can only be assigned to IT requests
  - Marketing contractors only to Marketing requests
  - Content contractors only to Content requests
- Verify contractor has access to the request's dealership

**Q: File upload fails in request attachments**
- Check file size is under the maximum limit (typically 10MB)
- Verify file type is allowed (images, PDFs, documents)
- Ensure proper write permissions on upload directory
- Check browser console for detailed error messages

**Q: Read receipts not updating**
- Real-time updates use polling (30 second intervals)
- Refresh the page to force immediate update
- Check browser network tab for failed API calls

**Q: Can't change request status**
- Only assigned contractors can change status
- Dealership users can mark as resolved/closed
- Agency admins have full access to modify status

**Q: Contractor doesn't appear in assignment dropdown**
- Verify contractor is assigned to the dealership
- Check contractor's department matches request department
- Confirm contractor account is active (not deactivated)

**Q: Dealership selector not appearing for contractor**
- Ensure user role is set to `contractor` (not salesperson/manager)
- Verify contractor has at least one dealership assignment
- Check that contractor has logged out and back in after assignment

---

## File Structure (Suggested)

fcapp/ ├── prisma/ │ └── schema.prisma # Database schema ├── src/ │ ├── app/ # Next.js App Router │ │ ├── (auth)/ │ │ │ ├── login/ │ │ │ └── layout.tsx │ │ ├── (dashboard)/ │ │ │ ├── my-day/ │ │ │ ├── pipeline/ │ │ │ ├── leads/ │ │ │ │ └── [id]/ │ │ │ ├── settings/ │ │ │ └── layout.tsx │ │ ├── admin/ │ │ │ ├── dashboard/ │ │ │ ├── dealerships/ │ │ │ ├── users/ │ │ │ └── contractors/ # Contractor management │ │ ├── requests/ # Request/ticket system │ │ ├── api/ │ │ │ ├── auth/ │ │ │ ├── leads/ │ │ │ ├── tasks/ │ │ │ ├── activities/ │ │ │ ├── voip/ │ │ │ ├── emails/ │ │ │ ├── contractors/ # Contractor endpoints │ │ │ ├── requests/ # Request endpoints │ │ │ └── meta/ │ │ └── layout.tsx │ ├── components/ │ │ ├── ui/ # Shared UI components │ │ ├── pipeline/ # Kanban board │ │ ├── leads/ # Lead card, detail │ │ ├── tasks/ # Task list, task card │ │ └── layout/ # Nav, sidebar │ ├── lib/ │ │ ├── db.ts # Database client │ │ ├── auth.ts # Auth utilities │ │ ├── email.ts # Email service │ │ ├── voip.ts # Yate API client │ │ └── meta.ts # Meta API client │ ├── hooks/ # React hooks │ └── types/ # TypeScript types ├── yate-service/ # Separate Yate PBX service │ ├── Dockerfile │ ├── config/ │ └── api/ # Express API for Yate ├── docker-compose.yml ├── .env.example └── README.md

Environment Variables env# Database DATABASE_URL=postgresql://fcapp:password@localhost:5432/fcapp

Auth

JWT_SECRET=your-secret-key SESSION_DURATION_DAYS=7

Yate PBX

YATE_API_URL=http://localhost:3020

Email (default, can be overridden per dealership)

DEFAULT_IMAP_HOST=imap.gmail.com DEFAULT_SMTP_HOST=smtp.gmail.com

Meta

META_APP_ID=your-app-id META_APP_SECRET=your-app-secret

LLM (Phase 4)

ANTHROPIC_API_KEY=your-key

UI/UX Principles

"My Day" is home - Salespeople land here, work from here One-click actions - Call, email, complete task = single click Minimal navigation - Two main views: My Day + Pipeline Fast - Page loads under 1 second, no spinners for basic actions Paper simple - If paper was enough, this should feel equally light Mobile works but desktop first - They use Windows PCs at desks

Getting Started Commands bash# Initialize project npx create-next-app@latest fcapp --typescript --tailwind --app

Add dependencies

npm install @prisma/client prisma npm install next-auth npm install @tanstack/react-query npm install @dnd-kit/core @dnd-kit/sortable # For drag-drop

Initialize Prisma

npx prisma init

After creating schema

npx prisma migrate dev --name init npx prisma generate

Run dev server

npm run dev

Start with Phase 1. Get the pipeline board and task list working with fake data, then connect to PostgreSQL. Once a salesperson can drag leads and complete tasks, you have an MVP.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages