QwikFix integration: replace catch-all contractor in legacy RepairQuoteModal

Summary

Replace the legacy catch-all repair contractor (today: a RepairCompany with recommendedIds: ['*']) with a first-party QwikFix integration in the non-ProPair repair quote flow (reportSettings.proPairExperience = false). QwikFix becomes the featured, top-of-modal catch-all provider; existing specialty contractors remain below for items matched by recommendation_idrecommendedIds.

Product reference: QwikFix API docs (staging) Design mock: (attach index.html / modal mock files when ready)


QuickFix Modal.zip

QuickFix Modal.zip

Problem / current state

| Area | Today | | -- | -- | | Entry | Report sidebar → "GET FREE REPAIR QUOTES" opens RepairQuoteModal when ProPair is off (RepairListSidebar.handleGetQuotes) | | Catch-all | Items without a specialty match route to a repair company with recommendedIds containing '*' (RepairQuoteModal.assignRepairItemsToCompanies) | | Send path | POST /repair-list/:id/request-quotes → PDF per contractor → email via Resend (sendQuoteRequestEmail) | | Modal UX | Flat list of contractor cards; 3-step flow (select → confirm contacts → success) | | Config | Repair companies managed under Report Settings; no QwikFix-specific settings |

QwikFix does not exist in the codebase today (no qwikfix references).


Goals

  1. QwikFix-first modal — featured hero card at top; specialty contractors below a divider.
  2. QwikFix API submission — backend calls QwikFix POST /api/v1/quote-request/create (authenticated) instead of emailing a catch-all contractor.
  3. Report settings — inspector company configures which repair company record represents QwikFix (branding/logo in modal); API credentials live server-side.
  4. Preserve specialty routing — existing recommendation_id / recommendedIds matching unchanged; only the catch-all bucket moves to QwikFix.
  5. Independent send paths — user can send to QwikFix and/or one or more specialty contractors in the same session (see open questions).

Non-goals (this issue)

  • ProPair / Thumbtack flow (/repair-list/[id]/request/*, quote-submission endpoint)
  • Replacing specialty contractor email/PDF flow (unless explicitly scoped as follow-up)
  • QwikFix pricing display in modal (future)

UX spec (modal structure) - SEE MOCK

Applies when: proPairExperience === false and user has repair-quote / repair-quote-fallback.

Layout (top → bottom)

  1. Header$ icon, "Get Free Repair Quotes", subcopy, close
  2. Quote summary strip — "N new items ready to quote · N options"; stats (total items, ~24h turnaround)
  3. QwikFix card (featured) — dark teal hero #01445A, amber accent #f5a524
  • Wordmark + "Recommended · Catch-all" pill
  • Tagline: "Send the whole report — one quote, 24 hours."
  • Primary CTA: "Send N items"
  • 4-up benefits grid (24h quotes, full report, vetted contractors, customer advocate)
  • Toggles:
    • shareWithClient (default ON) — "CC the buyer on quote updates & messaging"
    • quoteWholeReport (default OFF) — dynamic subcopy: "Repair list only · N of M findings" / "Quoting all M findings"
  • Compact 2-col item grid; live-updates with quoteWholeReport; "Whole report" badge when on
  1. Divider — "Or use a specialty contractor for matched items"
  2. Specialty contractor cards — compact cream-bordered; only items matching recommendedIds; hide if zero matches; per-card SEND QUOTE
  3. Footer — disclosure + Learn more; Cancel + Send to QwikFix (N)

Visual tokens (scoped to QwikFix card)

--qf-deep: #01445A;
--qf-accent: #f5a524;
--qf-accent-soft: #fff3da;

Attik orange #d75400 remains primary footer CTA. Modal: min(720px, 100%), max-height: calc(100vh - 48px), internal scroll.

State machine

| State | Default | Scope | Effect | | -- | -- | -- | -- | | shareWithClient | true | per QwikFix request | Buyer CC on quote/messaging (map to API / email behavior) | | quoteWholeReport | false | per QwikFix request | false: items in repair list only; true: all report findings | | qwikSent | false | per session | Disables QwikFix CTAs; shows "Sent to QwikFix" | | contractorsSent[id] | {} | per contractor | Independent sent state per specialty card |

Item routing

  • QwikFix: all items in scope (filtered by quoteWholeReport + repair list membership)
  • Specialty: item.recommendation_idrepairCompany.recommendedIds (existing logic); exclude '*' wildcard company from specialty list
  • Unassigned bucket: absorbed by QwikFix (replaces wildcard contractor), not shown as "cannot be quoted"

Send behavior

  • QwikFix and specialty contractors are independent sends (no shared confirmation step in v1; per-card "Sent" feedback)
  • Remove or simplify legacy Step 2 contact picker for QwikFix path; specialty path may still use additionalRecipients recap emails (confirm in implementation)

QwikFix API integration

Base URL (staging): https://staging.mngmt.theqwikfix.com Docs: POST quote-request/create

We have the API ready for both demo and live sites.   https://demo.theqwikfix.com/docs/index.html#authenticating-requests

@Chris Scott please navigate our demo site, https://demo.mngmt.theqwikfix.com/Qw1kFixDemo Create/Sign Up for an account as Vendor user type.  We recommend an email that is for the purpose of setting up the account.  We do not yet have notifications for things like API updates/changes yet.  

The discount code for the demo site is: DEMO

You should be able to begin testing right away, then we can move to the live site next. 

Auth

  • POST /api/v1/auth/loginaccess_token (Bearer)
  • Store credentials in env / secrets (not report settings UI): QWIKFIX_API_EMAIL, QWIKFIX_API_PASSWORD, QWIKFIX_BASE_URL
  • Cache token in Redis or memory with TTL until expiry

Create quote request

POST /api/v1/quote-request/create with Bearer token.

Required mapping (Attik → QwikFix):

| QwikFix field | Attik source | | -- | -- | | client_name / client_email / client_phone / client_role | Primary buyer/homeowner from inspection.people (role match: Home Buyer, Homeowner) | | agent_name / agent_email / agent_phone / agent_role | Buyers Agent or Listing Agent from inspection.people | | property_address / city / state / zip_code | inspection.property | | closing_date | Inspection/job closing date if available; else sensible default / optional | | inspection_report_url | Signed portal URL to published report PDF or report view | | repair_request_url | URL to generated repair-list PDF (existing generateRepairQuotePDFs or dedicated export) | | webhook_url | Attik webhook endpoint for QwikFix status updates (new route) | | google_place_id | Property place ID if stored on inspection | | discount_code | Optional; from report settings if needed |

Phone format: US E.164-style per API regex (8775551234 style in docs).

Response: persist qwik_quote_uuid, dashboard_url on repair list record.

Webhook (new)

  • POST /webhooks/qwikfix (or under repair-list) — receive quote status updates; validate signature if QwikFix provides one
  • Update repair list / quote submission status for job portal display

Technical scope

1. Data model & settings

reportSettings (backend + frontend types)

  • _qwikFixRepairCompanyId?: string — ref to RepairCompany used for modal branding (logo, name display)
  • Optional: qwikFixDiscountCode?: string
  • Settings UI in ReportSettingsForm.tsx — picker of active repair companies (or dedicated QwikFix block)

repairList schema — track QwikFix submissions (choose one):

  • Option A: Extend quoteSubmissions with submissionType: 'qwikfix' + providerMetadata: { qwikQuoteUuid, dashboardUrl, shareWithClient, quoteWholeReport, itemIds }
  • Option B: New qwikFixRequests[] subdocument mirroring quoteRequests

Recommend Option A for consistency with ProPair submissions and job portal RepairQuotesDisplay.

Env / secrets

  • QWIKFIX_BASE_URL, QWIKFIX_API_EMAIL, QWIKFIX_API_PASSWORD
  • Production base URL TBD

2. Backend (attik-backend)

| Task | Details | | -- | -- | | QwikFix client module | src/util/integrations/qwikfix/getAccessToken(), createQuoteRequest(payload) | | New endpoint | POST /repair-list/:id/request-qwikfix-quote or extend request-quotes with { provider: 'qwikfix', ... } | | Payload | shareWithClient, quoteWholeReport, includedItemIds[], optional additionalRecipients | | PDF / URLs | Reuse inspection population + report link generation from sendQuoteRequestEmail; generate repair-list PDF for repair_request_url | | People mapping | Helper: resolve client vs agent from inspection.people + role names → QwikFix enum values | | Wildcard removal | Stop creating quoteRequests for recommendedIds: ['*'] company | | Webhook route | Persist quote status; link to repair list + inspection | | Portal permissions | Add pattern in portalPermissions.ts for new route | | Tests | Unit: people mapping, payload builder; integration: mocked QwikFix API |

Existing files to touch:

  • src/routes/repairList.ts
  • src/models/repairListSchema.ts
  • src/models/reportSettingsSchema.ts
  • src/util/functions/pdf/generatePDF.ts (repair PDF URL)

3. Frontend (toolsv2)

| Task | Details | | -- | -- | | Rebuild RepairQuoteModal.tsx | Match UX spec; extract QwikFixCard, ContractorCard, toggles | | Item scope helpers | inRepairList flag on items; quoteWholeReport expands to all findings from report | | Routing | Filter specialty companies: recommendedIds without '*'; QwikFix gets remainder | | API call | New server action → request-qwikfix-quote | | Sent states | qwikSent, contractorsSent local + refresh from repair list response | | Report settings UI | Select QwikFix repair company record | | Job portal | RepairQuotesDisplay.tsx — show QwikFix submission status + dashboard link when proPairExperience off | | Remove unassigned amber section | Items previously "cannot be quoted" now route to QwikFix |

Existing files to touch:

  • src/app/client/reports/components/RepairQuoteModal.tsx
  • src/app/client/reports/components/RepairListSidebar.tsx
  • src/app/tools/settings/report-settings/ReportSettingsForm.tsx
  • src/util/types/serverTypeCollection/reportSettings.ts
  • src/app/client/job/[slug]/components/RepairQuotesDisplay.tsx

4. Repair company admin

  • Document that wildcard '*' recommendedIds is deprecated in favor of QwikFix
  • Migration note: companies using a catch-all Repair Nation / wildcard row should enable QwikFix in report settings instead

Acceptance criteria

Modal & routing

  • [ ] With ProPair off, opening repair quotes shows QwikFix card first, then specialty contractors with matched items only
  • [ ] Wildcard '*' repair company no longer appears as a specialty card
  • [ ] quoteWholeReport toggle updates item count and list live; default OFF (repair list items only)
  • [ ] shareWithClient toggle default ON; passed to backend on QwikFix send
  • [ ] QwikFix and specialty sends are independent; per-card sent state after success
  • [ ] Footer primary CTA sends to QwikFix with correct item count

Backend & API

  • [ ] Successful QwikFix send calls staging API and stores qwik_quote_uuid + dashboard_url on repair list
  • [ ] inspection_report_url and repair_request_url are valid, accessible URLs
  • [ ] Client/agent fields populated per QwikFix validation rules
  • [ ] Webhook endpoint receives updates and updates repair list status (minimum: log + store)
  • [ ] Existing POST request-quotes still works for specialty contractors
  • [ ] ProPair flow unchanged when proPairExperience on

Settings

  • [ ] Inspector can assign QwikFix repair company in report settings
  • [ ] Modal uses assigned company for logo/name display

QA

  • [ ] Sample report: QwikFix send disabled or test mode (mirror Thumbtack test pattern if needed)
  • [ ] Integration test with mocked QwikFix HTTP

Open questions (product — resolve before or during implementation)

  1. Confirmation flow — Per-card "Sent" only, or full success/timeline screen post-send?
  2. shareWithClient — Immediate buyer email summary, or only after QwikFix delivers quote?
  3. inRepairList source — Inspector flag on item, severity threshold, or "in current saved repair list" only?
  4. Dual send — Intentional that user can send to both QwikFix and specialty? If exclusive, define disable rules.
  5. Specialty contact recap — Keep Step 2 additionalRecipients / recap emails for specialty sends only?
  6. Closing date — Required by QwikFix; what if inspection has no closing date?
  7. Production QwikFix base URL and credential provisioning per Attik environment
  8. Discount code — Per-company in report settings or global env?

Implementation phases (suggested)

Phase 1 — Backend foundation

Report settings field, QwikFix client, request-qwikfix-quote endpoint, payload mapping, persist response, basic tests.

Phase 2 — Modal redesign

QwikFix card + toggles + routing; wire to new endpoint; specialty cards unchanged send path.

Phase 3 — Webhook & job portal

Webhook handler, RepairQuotesDisplay QwikFix status, deprecate wildcard contractor docs.

Phase 4 — Polish

Learn more link, disclosure copy, error states, token refresh, production config.


Key file reference

Frontend

  • toolsv2/src/app/client/reports/components/RepairQuoteModal.tsx
  • toolsv2/src/app/client/reports/components/RepairListSidebar.tsx
  • toolsv2/src/app/tools/settings/report-settings/ReportSettingsForm.tsx

Backend

  • attik-backend/src/routes/repairList.ts (POST /:id/request-quotes, helpers)
  • attik-backend/src/models/repairListSchema.ts
  • attik-backend/src/models/reportSettingsSchema.ts
  • attik-backend/src/util/functions/pdf/generatePDF.ts

API

Please authenticate to join the conversation.

Upvoters
Status

Completed

Board
🏠

Main App

Date

12 days ago

Author

Linear

Subscribe to post

Get notified by email when there are changes.