Texas Hail & Dent Co.
Two-layer delivery: a storm-coded public platform built for automotive PDR conversion, plus a custom role-based invoice management system handling the full field operations lifecycle.
Public platform — storm-coded design language, urgency-forward layout, direct conversion path for vehicle owners evaluating automotive PDR after a hail event.
One engagement, two distinct systems
Layer 1
Public Platform
Storm-coded marketing site built for vehicle owners and fleet operators evaluating automotive PDR. Dark, authoritative design with video hero, before/after proof, trust signals, and a direct conversion path.
Layer 2
Business Application
Custom RBAC invoice management system with four distinct roles, a trigger-enforced lifecycle state machine, content lock after finalization, and mobile-first field tooling for technicians on job sites.
A field service business that outgrew standard platforms
Paintless dent repair businesses operate across multiple contexts simultaneously: field technicians creating estimates on job sites, owners needing full operational visibility, and external stakeholders requiring read-only access. No off-the-shelf platform handled this combination without significant compromise.
The engagement required both a conversion-first public presence — built for the urgency window immediately after a storm event — and a role-aware internal application with clear, enforceable boundaries between what each role can see and do.
Built for the storm window
The public site is designed for a specific conversion context: vehicle owners evaluating automotive PDR options immediately after a hail event. The design language is intentional — dark, authoritative, storm-coded — with visual proof of work at the center of every decision path.

Before/after transformation slider — proof of craft at the center of the conversion path, not buried in a gallery.
- ✓Hail event video hero — establishes urgency and authority on first load
- ✓Before/after transformation slider — visual proof drives conversion more than copy
- ✓Trust signal grid — certifications, service scope, operator credibility
- ✓Structured FAQ — addresses objections before the customer asks
- ✓Frictionless CTA funnel — single path from awareness to contact
Role-based access control
The invoice system supports four roles with distinct interfaces and database-level access enforcement via Supabase Row Level Security. Roles are resolved through a security-definer SQL function — preventing recursion in the RLS policies themselves.
| Role | Access Scope |
|---|---|
| Owner | Full access — all invoices, user management, settings |
| Technician | Create and edit own invoices only — field-scoped visibility |
| Viewer | Read-only across all invoices — for external stakeholders |
| Admin | SQL-level access only — never assignable through the UI |
RLS policies enforce access at the PostgREST layer — application code cannot override them regardless of what is sent to the API.
One-way state machine, enforced by the database
Invoice status progresses through a one-way state machine enforced by a database trigger. No role can reverse a status transition. A second trigger locks content columns after Finalized is reached — preventing retroactive alteration of billed records.
Draft
Created by technician in the field
Submitted
Sent for owner review
Reviewed
Owner has reviewed — ready to finalize
Finalized
Fields lock — content becomes immutable
Signed
Client signature recorded
Sent
Invoice delivered to client via email


Structured field documentation
Field technicians annotate vehicle damage on a structured canvas component during invoice creation. The tool is scoped to tablets and desktop — hidden below the 768px breakpoint where technicians work in a capture-and-move workflow — and visible to office and ownership for documentation review. Annotations persist to the invoice record and render on the print view.
- ✓Hidden on phones — visible on tablet and desktop
- ✓Vehicle outline with positional annotation input
- ✓Persists to invoice — annotations survive status transitions
- ✓Print-ready at /invoices/[id]/print for physical records

DamageCanvas on invoice creation — positional annotation on a vehicle outline, integrated with the invoice record.
Built on Supabase with careful client separation
The application separates the service role client from the authenticated user client by design. The base createClient (not the SSR variant) is used for service role operations — preventing the SSR layer from overriding the service role key with a session JWT and silently demoting its permissions. Schema is migration-driven with schema.sql maintained as the authoritative reference.
- ✓Supabase Auth + PostgREST with RLS on all tables
- ✓Security-definer get_my_role() — no recursive policy evaluation
- ✓Explicit GRANT per role — rolbypassrls does not replace table privileges
- ✓Trigger-enforced state machine + content lock after finalization
- ✓Migration-driven schema — schema.sql kept in sync
- ✓Resend delivery with pre-send status validation
What was chosen — and what was ruled out
Application code can be bypassed — a direct API call, a misconfigured route, or a future code change can skip an application-level check. PostgREST RLS executes at the database before any data is returned, regardless of what the calling code sends. It is not a layer you can accidentally skip.
Triggers fire on every write to the table — direct SQL, an API route, a cron job, a future admin tool. An API route validator only runs when that specific route is called. By moving the state machine into a trigger, the one-way progression rule is guaranteed at the storage layer regardless of how the record is modified.
The SSR client overrides the service role key with the session JWT when making PostgREST requests — effectively demoting it to the authenticated user's permissions. For operations that need to legitimately bypass RLS (admin tools, email delivery), the base client preserves the service role key and its actual capabilities.