Skip to content
des369codePublic

About

A marketplace to find and post repair jobs in Malaysia. Post a job with photos and budget, get responses from repairers, close the deal over WhatsApp. Next.js + Supabase.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

RepairLah.

Live demo License: MIT Next.js React Supabase Tests

Repairlah is a marketplace that connects Malaysians who need something fixed with repair technicians — post a repair job with photos and a budget, get responses from repairers near you, and close the deal over WhatsApp. Free, mobile-first, and live at repairlah.vercel.app.

RepairLah — Find & Post Repair Jobs in Malaysia

What This Is

  • Full auth flow (src/app/auth, src/components/AuthForm.tsx) — Supabase email sign-up with verification, forgot/reset password, and a role chooser (poster or repairer).
  • Job posting with real constraints (src/app/post, src/lib/validation.ts) — title, description, tags, budget range in RM, photos uploaded to Supabase Storage, city + Malaysian state. Every field is zod-validated client-side and CHECK-constrained in Postgres.
  • A filterable feed (src/components/FeedClient.tsx, StateFilter.tsx) — browse, search, and filter jobs by Malaysian state; each job card is shareable with its own Open Graph link preview (WhatsApp/WhatsApp-ready metadata).
  • Bids and one-tap responses (src/app/api/bids, src/app/api/respond) — repairers bid on jobs; responding is limited to one per user per job (enforced by a UNIQUE constraint) and triggers an atomic auto-close plus a WhatsApp handoff so the conversation starts in the app people actually use.
  • Job lifecycle (src/app/api/jobs/[id]/complete) — owner-only completion with a status lifecycle of open → in_progress → completed / expired enforced in the schema.
  • Trust primitives — public profiles, abuse reporting (src/app/api/report), and row-level security on every table (supabase/migrations/001_schema.sql).

How It Works

     1. AUTH                      2. POST A JOB                       3. FEED                    4. BID + RESPOND                5. COMPLETE                      6. TRUST
+------------------------+    +---------------------------+    +---------------------------+    +------------------------+    +------------------------+    +--------------------------+
| Supabase email auth:   |    | title, description, tags, |    | browse + search the feed, |    | repairers bid on jobs; |    | owner marks completed; |    | profiles, abuse reports, |
| verification, forgot / |    | budget range (RM),        |    | filter by Malaysian       |    | responding is one per  |    | lifecycle: open ->     |    | row-level security on    |
| reset password,        |    | photos -> Supabase        |    | state, shareable job      |    | user per job -> atomic |    | in_progress ->         |    | every table              |
| choose role            |  > | Storage, city + state     |  > | cards with OG link        |  > | auto-close + WhatsApp  |  > | completed / expired    |  > |                          |
| (poster or repairer)   |    |                           |    | previews                  |    | handoff                |    |                        |    |                          |
+------------------------+    +---------------------------+    +---------------------------+    +------------------------+    +------------------------+    +--------------------------+

All database access goes through Supabase with server-side auth cookie handling (src/lib/supabase/server.ts, middleware.ts); API routes re-verify the session on every mutation (src/app/api/*). Photos land in the job-photos storage bucket and are served by public URL.

Tech Stack

Layer Choice
Framework Next.js 16 (App Router) + React 19
Styling Tailwind CSS v4 (black & white + cobalt design system, see DESIGN.md)
Backend Supabase — Postgres, Auth (email), Storage, row-level security
Validation zod, mirrored by Postgres CHECK constraints
Icons lucide-react
Tests Vitest + React Testing Library (src/test/)
Deploy Vercel

Data Model

Five tables, all with row-level security enabled — four in supabase/migrations/001_schema.sql, plus bids (supabase/bids-table.sql):

Table Purpose Notable constraints
jobs Repair jobs title 5–200 chars, description 20–5000, budget ≤ 50,000 RM, status ∈ {open, in_progress, completed, expired}
bids Repairer bids on a job (budget range) UNIQUE (job_id, user_id), authenticated users read
responses Repairer responses / WhatsApp handoffs UNIQUE (job_id, user_id) — one response per user per job
profiles User profiles with account type keyed to auth.users, cascade delete
reports Abuse reports on jobs/users —

Getting Started

npm install

# 1. Create a Supabase project, then set these:
#    NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY
npm run dev            # http://localhost:3000

# 2. Apply the schema (in the Supabase SQL editor or CLI):
#    supabase/migrations/001_schema.sql … 006_*.sql, then supabase/bids-table.sql

# 3. Tests
npx vitest run

No secrets are needed beyond the two public Supabase environment variables; the anon key is public-by-design and all authorization is enforced server-side by row-level security policies.

Repository Layout

├── middleware.ts               # Supabase session refresh
├── src/
│   ├── app/                    # App Router pages + /api routes (jobs, bids, respond, report, profile)
│   ├── components/             # 18 UI components (feed, job cards, auth forms, tab bar, lightbox…)
│   ├── hooks/
│   ├── lib/                    # supabase clients (browser/server), validation, image utils
│   └── test/                   # Vitest suites (auth, components, utils, validation)
├── supabase/
│   └── migrations/             # ordered SQL schema migrations
├── DESIGN.md                   # the black & white + cobalt design system
├── docs/superpowers/specs/     # design spec from the UI revamp
└── public/                     # manifest, favicon, OG image

Modeling Assumptions and Limitations

  • WhatsApp is the handoff, not the record — once a response redirects to WhatsApp, the conversation lives outside the app; the platform keeps only the response record (whatsapp_redirected) and job status.
  • Single-response auto-close — a job closes atomically when someone responds; there is currently no multi-bid negotiation inside the app beyond the bids API.
  • Malaysia-first — states, RM budgets, and WhatsApp-centric flow are intentional; localization beyond that is not built in.
  • Moderation is minimal — reports are collected; there is no admin review UI.

AI-Agentic Development

Built through an agentic AI workflow: 98 commits between May and July 2026, from the first schema migration through the UI revamp — design specs (docs/superpowers/), agent-driven implementation, and systematic bugfix passes (mobile OG previews, session-handling fixes, render regressions). The human role was direction and judgment: the product idea, the feature set, and every accept/reject call.

Author

Built by D. Desmaan.

Licensed under the MIT License.

About

A marketplace to find and post repair jobs in Malaysia. Post a job with photos and budget, get responses from repairers, close the deal over WhatsApp. Next.js + Supabase.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages