Files
claudetools/projects/msp-tools/quote-wizard/session-logs/2026-03-09-session.md
Mike Swanson fa15b03180 sync: Auto-sync from ACG-M-L5090 at 2026-03-10 19:11:00
Synced files:
- Quote wizard frontend (all components, hooks, types, config)
- API updates (config, models, routers, schemas, services)
- Client work (bg-builders, gurushow)
- Scripts (BGB Lesley termination, CIPP, Datto, migration)
- Temp files (Bardach contacts, VWP investigation, misc)
- Credentials and session logs
- Email service, PHP API, session logs

Machine: ACG-M-L5090
Timestamp: 2026-03-10 19:11:00

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 19:59:08 -07:00

210 lines
8.5 KiB
Markdown

# MSP Quote Wizard Session Log - 2026-03-09
## Session Summary
Major deployment session for the MSP Quote Wizard. Started from code pulled from MacBook Air (commit a1a19f8), reviewed the full project, fixed 15+ backend model/schema mismatches, deployed frontend to azcomputerguru.com/quote on IX cPanel, debugged and fixed PHP reverse proxy, and applied comprehensive responsive design fixes to all wizard components.
### Key Accomplishments
1. Full backend model alignment with MariaDB schema (12+ field/table/enum fixes)
2. Frontend deployed to production at https://azcomputerguru.com/quote/
3. PHP reverse proxy debugged and fixed (CURLOPT_FOLLOWLOCATION for FastAPI 307 redirects)
4. Comprehensive responsive design fixes across all 9 wizard components
5. End-to-end API flow verified: create -> get -> add item -> submit
### Key Decisions
- Used PHP curl reverse proxy instead of direct API exposure (API on 172.16.3.30:8001, frontend on IX 172.16.3.10)
- Made contact_name/contact_email nullable in DB to support draft quotes
- Wrapped QuoteActivity details in JSON for MariaDB json_valid() CHECK constraint
- Used `CURLOPT_FOLLOWLOCATION` to handle FastAPI trailing-slash 307 redirects
- SSH to IX requires `-o IdentitiesOnly=yes -i ~/.ssh/id_ed25519` as root (too many keys causes auth failure)
---
## Infrastructure
### Servers
- **API Server:** 172.16.3.30:8001 (FastAPI/Uvicorn, production ClaudeTools API)
- **IX Server (Hosting):** 172.16.3.10 (cPanel/WHM, Apache, PHP 8.1.33)
- SSH: `ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 root@172.16.3.10`
- Root password: Gptf*77ttb!@#!@#
- Site path: /home/azcomputerguru/public_html/quote/
- cPanel account: azcomputerguru
- **Database:** 172.16.3.30:3306 / MariaDB 10.6.22
- DB: claudetools
- User: claudetools
- Password: CT_e8fcd5a3952030a79ed6debae6c954ed
### Deployment Architecture
```
Browser -> Cloudflare -> IX (172.16.3.10:443)
-> /quote/ -> index.html (SPA)
-> /quote/api/* -> .htaccess rewrite -> api-proxy.php -> curl -> 172.16.3.30:8001/api/*
```
### Files on IX (/home/azcomputerguru/public_html/quote/)
- index.html - SPA entry point
- assets/ - JS/CSS bundles
- api-proxy.php - PHP reverse proxy to API
- .htaccess - Rewrite rules (API proxy + SPA routing)
---
## Backend Fixes Applied
### Model Alignment (api/models/quote.py)
- Status enum: draft/submitted/viewed/followed_up/converted/expired (was reviewing/approved/rejected)
- ServiceCategory enum: gps_monitoring/support_plan/voip/web_hosting/email/hardware/addon
- BillingFrequency enum: monthly/yearly/one_time (was quarterly/annual)
- NotificationType enum: email/webhook (was email_sent/sms_sent/admin_alert/reminder_sent)
- Removed columns: notes, admin_notes, annual_total (don't exist in DB)
- Fixed reserved word: metadata -> details (SQLAlchemy reserves metadata)
- Fixed table name: quote_activities -> quote_activity
- Removed TimestampMixin from QuoteItem/QuoteActivity/QuoteNotification (no updated_at)
- Made contact_name/contact_email Optional for draft support
- QuoteItem fields: service_name->product_name, setup_fee->setup_price, is_required->is_recommended, added product_code/tier, removed sort_order
### Database ALTERs Applied
```sql
ALTER TABLE quotes MODIFY contact_name VARCHAR(255) NULL;
ALTER TABLE quotes MODIFY contact_email VARCHAR(255) NULL;
```
### Service Layer (api/services/quote_service.py)
- calculate_totals() returns (monthly, setup) tuple (removed annual)
- log_activity() wraps details in json.dumps({"message": details}) for json_valid() constraint
- Removed all references to notes/admin_notes/annual_total
- Syncro API key moved to env var SYNCRO_API_KEY
- Admin email from env var ADMIN_NOTIFICATION_EMAIL
### API Routers
- api/routers/quotes.py - 6 public endpoints (create, get, update, add item, remove item, submit)
- api/routers/admin_quotes.py - 5 admin endpoints (list, stats, detail, update status, sync-syncro)
- Both registered in api/main.py
### Dependencies Installed on Production
```bash
pip install email-validator httpx
```
---
## Frontend Changes
### Vite Config
- base: '/quote/' for subdirectory deployment
- build.outDir and sourcemap: false
### API Client (src/lib/api.ts)
- Complete rewrite to match actual backend endpoints
- Exports: createQuote, getQuote, updateQuote, addQuoteItem, removeQuoteItem, submitQuote, getQuotePdf
### Responsive Design Fixes (Applied 2026-03-09)
All wizard components updated for mobile-first responsive design:
**WizardContainer.tsx:**
- Running totals bar: responsive padding (p-2.5 sm:p-4), text sizes (text-lg sm:text-2xl)
- Step header: responsive padding (px-4 sm:px-6 md:px-8), icon sizes, truncation
- Content area: responsive padding
**Step1CompanyProfile.tsx:**
- Endpoint count input: flex-col on mobile, w-full sm:w-32
**Step2GPSMonitoring.tsx:**
- Tier grid: grid-cols-1 sm:grid-cols-2 md:grid-cols-3
- Equipment section: flex-shrink-0 on toggle, min-w-0 on text, responsive text sizes
- Monthly total: responsive text (text-2xl sm:text-3xl), whitespace-nowrap
**Step3SupportPlan.tsx:**
- Plan grid: grid-cols-1 sm:grid-cols-2 lg:grid-cols-4
- Block time grid: grid-cols-1 sm:grid-cols-3
- Toggle headers: flex-shrink-0, min-w-0, responsive text sizes
- Monthly total: responsive sizing
**Step4VoIP.tsx:**
- Toggle header: responsive icon/text sizes, flex-shrink-0
- User count: flex-col sm:flex-row, w-full sm:w-24
- Tier grid: grid-cols-1 sm:grid-cols-2 lg:grid-cols-4
- Hardware items: completely restructured - stacked layout with flex-wrap controls
- Monthly total: responsive sizing
**Step5WebEmail.tsx:**
- All tier grids: sm:grid-cols-2 md:grid-cols-3 (was md:grid-cols-3 only)
- Toggle headers: responsive icon/text/padding, flex-shrink-0
- Mailbox count: flex-col sm:flex-row
- Monthly total: responsive sizing
**Step6Summary.tsx:**
- Grand total: flex-col sm:flex-row for monthly investment header
- Text: text-3xl sm:text-4xl
- SummarySection header: responsive padding, truncation, flex-shrink-0
**Step7Contact.tsx:**
- Quote preview: flex-col sm:flex-row, responsive text
- Contact preferences: flex-wrap
- Trust indicators: flex-col sm:flex-row (was grid-cols-1 md:grid-cols-3)
---
## PHP Reverse Proxy (api-proxy.php)
### Key Fix: CURLOPT_FOLLOWLOCATION
FastAPI returns 307 redirects for trailing-slash URLs. PHP curl doesn't follow redirects by default, causing empty response bodies. Fixed by adding:
```php
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
```
### Important: Host Header Required
When testing from internal network, must use `Host: azcomputerguru.com` header. Direct IP access (172.16.3.10) hits wrong Apache vhost and PHP doesn't execute. Browser access works fine since it sends correct Host header.
```bash
# WORKS:
curl -s -H "Host: azcomputerguru.com" "http://172.16.3.10/quote/api/quotes" -X POST -H "Content-Type: application/json" -d '{"employee_count":5}'
# FAILS (wrong vhost):
curl -s "http://172.16.3.10/quote/api/quotes" -X POST ...
```
---
## Pending/Next Steps
1. **Frontend polish:** Run through wizard in browser to visually verify responsive fixes
2. **Admin dashboard:** No admin UI yet for viewing submitted quotes (admin API endpoints exist)
3. **Email notifications:** ADMIN_NOTIFICATION_EMAIL env var needs to be set on production
4. **Syncro integration:** SYNCRO_API_KEY env var needs to be set for lead sync
5. **Remove debug endpoint:** Already done (removed _debug path from api-proxy.php)
6. **SSL/CORS:** Currently CORS is wide open (Access-Control-Allow-Origin: *) - consider restricting
7. **Quote PDF generation:** Endpoint exists but likely needs implementation
8. **Production env vars to set:**
- ADMIN_NOTIFICATION_EMAIL
- SYNCRO_API_KEY
- SYNCRO_API_BASE_URL (defaults to computerguru.syncromsp.com)
---
## Commands Reference
### Deploy frontend to IX
```bash
cd D:/ClaudeTools/projects/msp-tools/quote-wizard/frontend
npm run build
scp -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 -r dist/index.html dist/assets/ root@172.16.3.10:/home/azcomputerguru/public_html/quote/
ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 root@172.16.3.10 'chown -R azcomputerguru:azcomputerguru /home/azcomputerguru/public_html/quote/'
```
### Deploy api-proxy.php
```bash
scp -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 dist/api-proxy.php root@172.16.3.10:/home/azcomputerguru/public_html/quote/api-proxy.php
```
### Test API through proxy
```bash
curl -s -H "Host: azcomputerguru.com" -X POST -H "Content-Type: application/json" -d '{"employee_count":5}' "http://172.16.3.10/quote/api/quotes"
```
### Test API directly
```bash
curl -s -X POST -H "Content-Type: application/json" -d '{"employee_count":5}' "http://172.16.3.30:8001/api/quotes/"
```