     1→import { NextRequest, NextResponse } from 'next/server';
     2→import { checkRateLimit, getClientIP, createRateLimitResponse } from '@/lib/rate-limit';
     3→
     4→// Cache to store logo URLs (in production, use Redis or database)
     5→const logoCache = new Map<string, { logoUrl: string | null; source: string; timestamp: number }>();
     6→const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
     7→
     8→interface LogoResponse {
     9→  logoUrl: string | null;
    10→  source: 'clearbit' | 'google' | 'none';
    11→}
    12→
    13→// Rate limit: 30 requests per 15 minutes per IP
    14→const LOGO_RATE_LIMIT = 30;
    15→const LOGO_WINDOW_MS = 15 * 60 * 1000;
    16→
    17→async function checkLogoExists(url: string): Promise<boolean> {
    18→  try {
    19→    const response = await fetch(url, {
    20→      method: 'HEAD',
    21→      headers: {
    22→        'User-Agent': 'RateRight/1.0',
    23→      },
    24→    });
    25→    return response.ok;
    26→  } catch {
    27→    return false;
    28→  }
    29→}
    30→
    31→export async function GET(request: NextRequest) {
    32→  try {
    33→    // --- Rate limiting ---
    34→    const clientIP = getClientIP(request);
    35→    const rateLimit = checkRateLimit(`company-logo:${clientIP}`, LOGO_RATE_LIMIT, LOGO_WINDOW_MS);
    36→
    37→    if (!rateLimit.allowed) {
    38→      const retryAfter = Math.ceil((rateLimit.resetTime - Date.now()) / 1000);
    39→      return createRateLimitResponse(clientIP, retryAfter);
    40→    }
    41→
    42→    const searchParams = request.nextUrl.searchParams;
    43→    const domain = searchParams.get('domain');
    44→
    45→    if (!domain) {
    46→      return NextResponse.json<LogoResponse>(
    47→        { logoUrl: null, source: 'none' },
    48→        { status: 400, headers: getCORSHeaders(request) }
    49→      );
    50→    }
    51→
    52→    // Clean the domain (remove protocol if present)
    53→    const cleanDomain = domain.replace(/^https?:\/\//, '').replace(/\/.*$/, '');
    54→
    55→    // Check cache first
    56→    const cacheKey = cleanDomain;
    57→    const cached = logoCache.get(cacheKey);
    58→    if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
    59→      return NextResponse.json<LogoResponse>({
    60→        logoUrl: cached.logoUrl,
    61→        source: cached.source as LogoResponse['source'],
    62→      }, {
    63→        headers: getCORSHeaders(request),
    64→      });
    65→    }
    66→
    67→    // Try Clearbit Logo API first
    68→    const clearbitUrl = `https://logo.clearbit.com/${cleanDomain}`;
    69→    const clearbitExists = await checkLogoExists(clearbitUrl);
    70→
    71→    if (clearbitExists) {
    72→      logoCache.set(cacheKey, {
    73→        logoUrl: clearbitUrl,
    74→        source: 'clearbit',
    75→        timestamp: Date.now(),
    76→      });
    77→
    78→      return NextResponse.json<LogoResponse>({
    79→        logoUrl: clearbitUrl,
    80→        source: 'clearbit',
    81→      }, {
    82→        headers: getCORSHeaders(request),
    83→      });
    84→    }
    85→
    86→    // Try Google Favicon as fallback
    87→    const googleUrl = `https://www.google.com/s2/favicons?domain=${cleanDomain}&sz=128`;
    88→    const googleExists = await checkLogoExists(googleUrl);
    89→
    90→    if (googleExists) {
    91→      logoCache.set(cacheKey, {
    92→        logoUrl: googleUrl,
    93→        source: 'google',
    94→        timestamp: Date.now(),
    95→      });
    96→
    97→      return NextResponse.json<LogoResponse>({
    98→        logoUrl: googleUrl,
    99→        source: 'google',
   100→      }, {
   101→        headers: getCORSHeaders(request),
   102→      });
   103→    }
   104→
   105→    // No logo found
   106→    logoCache.set(cacheKey, {
   107→      logoUrl: null,
   108→      source: 'none',
   109→      timestamp: Date.now(),
   110→    });
   111→
   112→    return NextResponse.json<LogoResponse>({
   113→      logoUrl: null,
   114→      source: 'none',
   115→    }, {
   116→      headers: getCORSHeaders(request),
   117→    });
   118→
   119→  } catch (error) {
   120→    console.error('Logo fetch error:', error);
   121→
   122→    return NextResponse.json<LogoResponse>(
   123→      { logoUrl: null, source: 'none' },
   124→      { status: 500, headers: getCORSHeaders(request) }
   125→    );
   126→  }
   127→}
   128→
   129→// CORS configuration - restrict to known origins
   130→const ALLOWED_ORIGINS = [
   131→  process.env.NEXT_PUBLIC_APP_URL, // Production URL from env
   132→  'http://localhost:3000',         // Local development
   133→  'https://localhost:3000',        // Local development with HTTPS
   134→].filter(Boolean) as string[];
   135→
   136→function getCORSHeaders(request: NextRequest): Record<string, string> {
   137→  const origin = request.headers.get('origin');
   138→
   139→  // Only allow specific origins, no wildcards (H5 fix)
   140→  const allowedOrigin = origin && ALLOWED_ORIGINS.includes(origin)
   141→    ? origin
   142→    : ALLOWED_ORIGINS[0] || 'null';
   143→
   144→  return {
   145→    'Access-Control-Allow-Origin': allowedOrigin,
   146→    'Access-Control-Allow-Methods': 'GET, OPTIONS',
   147→    'Access-Control-Allow-Headers': 'Content-Type',
   148→    'Vary': 'Origin',
   149→  };
   150→}
   151→
   152→// Add OPTIONS handler for CORS
   153→export async function OPTIONS(request: NextRequest) {
   154→  return new NextResponse(null, {
   155→    status: 200,
   156→    headers: getCORSHeaders(request),
   157→  });
   158→}

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


Now let me check the remaining authenticated pages and compare all three projects:


     1→"use client";
     2→
     3→import { useEffect, useState, useRef } from "react";
     4→import Link from "next/link";
     5→import { createClient } from "@/lib/supabase/client";
     6→import { Card, CardContent } from "@/components/ui/card";
     7→import { Badge } from "@/components/ui/badge";
     8→import { Button } from "@/components/ui/button";
     9→import { BottomNav } from "@/components/bottom-nav";
    10→import {
    11→  ArrowLeft,
    12→  Loader2,
    13→  Star,
    14→  MapPin,
    15→  Clock,
    16→  CheckCircle2,
    17→  UserCheck,
    18→  Briefcase,
    19→  AlertCircle,
    20→} from "lucide-react";
    21→import type { Job } from "@/lib/types";
    22→
    23→interface MatchWithWorker {
    24→  id: string;
    25→  job_id: string;
    26→  worker_id: string;
    27→  status: string;
    28→  match_score: number | null;
    29→  ai_summary: string | null;
    30→  contractor_action: string;
    31→  worker_action: string;
    32→  hired_at: string | null;
    33→  fee_charged: boolean;
    34→  created_at: string;
    35→  worker: {
    36→    id: string;
    37→    name: string | null;
    38→    suburb: string | null;
    39→    avatar_url: string | null;
    40→  } | null;
    41→  worker_profile: {
    42→    trades: string[];
    43→    experience_years: number | null;
    44→    certifications: string[];
    45→    availability: string | null;
    46→  } | null;
    47→}
    48→
    49→interface SwipeableMatchCardProps {
    50→  match: MatchWithWorker;
    51→  isHired: boolean;
    52→  justHired: boolean;
    53→  hiring: boolean;
    54→  onHire: (match: MatchWithWorker) => void;
    55→  onReject: (match: MatchWithWorker) => void;
    56→}
    57→
    58→function SwipeableMatchCard({ match, isHired, justHired, hiring, onHire, onReject }: SwipeableMatchCardProps) {
    59→  const [swipeX, setSwipeX] = useState(0);
    60→  const [swiping, setSwiping] = useState(false);
    61→  const startX = useRef(0);
    62→  const currentX = useRef(0);
    63→
    64→  const summary = match.ai_summary || generateAiSummary(match.worker, match.worker_profile);
    65→
    66→  const handleTouchStart = (e: React.TouchEvent) => {
    67→    if (isHired) return;
    68→    startX.current = e.touches[0].clientX;
    69→    setSwiping(true);
    70→  };
    71→
    72→  const handleTouchMove = (e: React.TouchEvent) => {
    73→    if (!swiping || isHired) return;
    74→    currentX.current = e.touches[0].clientX;
    75→    const diff = currentX.current - startX.current;
    76→    // Allow both directions
    77→    setSwipeX(Math.max(-150, Math.min(diff, 150)));
    78→  };
    79→
    80→  const handleTouchEnd = () => {
    81→    if (!swiping || isHired) return;
    82→    setSwiping(false);
    83→
    84→    // If swiped right > 100px, hire
    85→    if (swipeX > 100) {
    86→      onHire(match);
    87→    }
    88→    // If swiped left > 100px, reject
    89→    else if (swipeX < -100) {
    90→      onReject(match);
    91→    }
    92→
    93→    // Reset
    94→    setSwipeX(0);
    95→  };
    96→
    97→  const swipeProgress = Math.abs(swipeX) / 100;
    98→  const swipeDirection = swipeX > 0 ? "right" : "left";
    99→
   100→  return (

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→"use client";
     2→
     3→import { useState } from "react";
     4→import { useRouter } from "next/navigation";
     5→import Link from "next/link";
     6→import { createClient } from "@/lib/supabase/client";
     7→import { Button } from "@/components/ui/button";
     8→import { Card, CardContent } from "@/components/ui/card";
     9→import { Input } from "@/components/ui/input";
    10→import { Label } from "@/components/ui/label";
    11→import { Badge } from "@/components/ui/badge";
    12→import {
    13→  ArrowLeft,
    14→  ArrowRight,
    15→  Loader2,
    16→  Sparkles,
    17→  Check,
    18→  MapPin,
    19→  Calendar,
    20→  Users,
    21→  DollarSign,
    22→  HardHat,
    23→  Wrench,
    24→  Hammer,
    25→  Blocks,
    26→  Fence,
    27→  CircleDot,
    28→  Shovel,
    29→} from "lucide-react";
    30→
    31→const TRADE_CATEGORIES = [
    32→  { id: "steel-fixer", label: "Steel Fixer", icon: Wrench, color: "bg-blue-500/10 border-blue-500/30 text-blue-700" },
    33→  { id: "formworker", label: "Formworker", icon: Blocks, color: "bg-amber-500/10 border-amber-500/30 text-amber-700" },
    34→  { id: "labourer", label: "Labourer", icon: Shovel, color: "bg-green-500/10 border-green-500/30 text-green-700" },
    35→  { id: "concreter", label: "Concreter", icon: CircleDot, color: "bg-gray-500/10 border-gray-500/30 text-gray-700" },
    36→  { id: "carpenter", label: "Carpenter", icon: Hammer, color: "bg-orange-500/10 border-orange-500/30 text-orange-700" },
    37→  { id: "scaffolder", label: "Scaffolder", icon: Fence, color: "bg-purple-500/10 border-purple-500/30 text-purple-700" },
    38→  { id: "other", label: "Other", icon: HardHat, color: "bg-muted border-border text-foreground" },
    39→];
    40→
    41→const AI_SUGGESTIONS: Record<string, { title: string; description: string; certs: string[]; rateMin: number; rateMax: number; rateType: "hourly" | "daily" }> = {
    42→  "steel-fixer": {
    43→    title: "Steel Fixer Needed",
    44→    description: "Experienced steel fixer required for commercial construction project. Must be able to read drawings, tie rebar, and work with mesh. PPE required on site at all times. Early starts — 6:30am.",
    45→    certs: ["White Card", "Working at Heights"],
    46→    rateMin: 45,
    47→    rateMax: 65,
    48→    rateType: "hourly",
    49→  },
    50→  "formworker": {
    51→    title: "Formworker Required",
    52→    description: "Skilled formworker needed for concrete pours on a residential build. Experience with timber and steel formwork systems. Must be comfortable with early starts and physical work.",
    53→    certs: ["White Card"],
    54→    rateMin: 42,
    55→    rateMax: 58,
    56→    rateType: "hourly",
    57→  },
    58→  "labourer": {
    59→    title: "General Labourer Needed",
    60→    description: "Reliable labourer needed for site clean-up, material handling, and general duties. No specific trade experience needed but must be fit and punctual. PPE and boots required.",
    61→    certs: ["White Card"],
    62→    rateMin: 32,
    63→    rateMax: 42,
    64→    rateType: "hourly",
    65→  },
    66→  "concreter": {
    67→    title: "Concreter Required",
    68→    description: "Experienced concreter needed for slab pours and finishing work. Must know how to screed, float, and finish concrete to a high standard. Early morning pours expected.",
    69→    certs: ["White Card"],
    70→    rateMin: 45,
    71→    rateMax: 65,
    72→    rateType: "hourly",
    73→  },
    74→  "carpenter": {
    75→    title: "Carpenter Needed",
    76→    description: "Qualified carpenter required for framing and fit-out work. Must have own basic tools. Experience with residential or commercial builds preferred.",
    77→    certs: ["White Card", "Trade Certificate"],
    78→    rateMin: 45,
    79→    rateMax: 65,
    80→    rateType: "hourly",
    81→  },
    82→  "scaffolder": {
    83→    title: "Scaffolder Required",
    84→    description: "Licensed scaffolder needed for erection and dismantling of scaffolding systems. Must hold current scaffolding licence. Safety-first attitude essential.",
    85→    certs: ["White Card", "Scaffolding Licence", "Working at Heights"],
    86→    rateMin: 48,
    87→    rateMax: 68,
    88→    rateType: "hourly",
    89→  },
    90→  "other": {
    91→    title: "Construction Worker Needed",
    92→    description: "Worker needed for construction project. Please specify the role and requirements in the description.",
    93→    certs: ["White Card"],
    94→    rateMin: 35,
    95→    rateMax: 55,
    96→    rateType: "hourly",
    97→  },
    98→};
    99→
   100→export default function PostJobPage() {

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


Bash

find /home/ccuser/the-50-dollar-app -name "*.md" | xargs wc -l | tail -1


93011 total


Perfect. Now let me get a summary of the database structure and key differences:


# RateRight v2 — Product Specification
## AI-First Construction Hiring Platform

**Created:** 2026-02-04
**Author:** Rivet (COO System)
**Status:** DRAFT — Awaiting Michael's Review

---

## 1. Vision

**One sentence:** The simplest way to hire construction workers in Australia — AI does the work, you just tap.

**The problem:** Hiring construction workers is slow, manual, and expensive. Contractors waste hours posting jobs, reviewing profiles, making calls. Workers waste time filling out forms and applying to jobs they won't get.

**The solution:** An AI-powered marketplace where:
- Contractors type their company name → AI builds their profile and writes their job posts
- Workers send a voice note → AI builds their entire profile
- One tap to post. One tap to hire. $50 flat fee. Done.

**USP:** Every competitor makes the human do the work. We make the AI do the work.

---

## 2. Business Model

- **$50 flat fee** per confirmed hire
- **Workers pay $0** — always free for workers
- **Contractors pay $0 to post** — only pay when they actually hire someone
- No subscriptions, no percentages, no hidden fees
- Revenue = $50 × number of hires per month

---

## 3. User Personas

### Contractor (Buyer)
- **Who:** Site foreman, project manager, small builder, subcontractor
- **Age:** 30-55
- **Pain:** Needs 3 labourers by Monday. Doesn't have time to post jobs, review 50 applications, make calls
- **Device:** iPhone/Android, often checking on site between tasks
- **Wants:** Fast, reliable workers. No bullshit. "I need 2 steel fixers tomorrow in Parramatta"

### Worker (Seller)
- **Who:** Irish/British backpacker with White Card, experienced tradesperson, labourer looking for work
- **Age:** 20-40
- **Pain:** Filling out profiles is tedious. Doesn't know market rates. Wants to get on a job fast
- **Device:** Phone only. Often doesn't have a laptop
- **Wants:** Get matched to jobs near me. Know what I'll earn. Start this week

---

## 4. User Flows

### 4.1 Contractor Signup (< 2 minutes)
```
1. Download app / visit site
2. Tap "I'm hiring"
3. Enter company name OR ABN
4. AI researches company:
   - ABN lookup → business name, address, registration status
   - Google → reviews, website, services
   - LinkedIn → company size, key people
5. AI pre-fills profile: company name, address, industry, size, logo
6. Contractor reviews → one tap to confirm
7. Add phone number → SMS verification
8. Done. Ready to post first job.
```

### 4.2 Worker Signup (< 3 minutes)
```
1. Download app / visit site
2. Tap "I'm looking for work"
3. Two options:
   a. TYPE: Answer 5 simple questions (trade, experience, location, availability, certs)
   b. VOICE: Send a 30-second voice note about yourself
      → AI transcribes and extracts: trade, years experience, certifications,
        availability, location, languages, past employers
4. AI builds complete profile with all extracted info
5. Worker reviews → edit anything wrong → one tap to confirm
6. Upload White Card photo (AI reads and verifies card details)
7. Done. Start getting matched.
```

### 4.3 Post a Job (< 1 minute)
```
1. Contractor taps "Post a Job"
2. Selects trade category (steel fixer, formworker, labourer, etc.)
3. AI suggests:
   - Job title based on trade
   - Description template based on common requirements
   - Market rate for that trade in that area
   - Required certifications
   - Suggested start date based on their past patterns
4. Contractor adjusts anything (or accepts defaults)
5. One tap → job is live
6. AI immediately matches with available workers
7. Push notifications sent to matching workers
```

### 4.4 Get Matched & Hire
```
1. AI ranks workers by: proximity, experience, ratings, availability
2. Contractor sees top 5 matches with:
   - Name, trade, experience, rating
   - Distance from site
   - Availability
   - AI summary: "Steel fixer, 5 years experience, 4.8★, 3km away, available tomorrow"
3. Contractor taps "Hire" on preferred worker
4. Worker gets notification → one tap to accept
5. Both get each other's contact details
6. $50 fee charged to contractor
7. Done. Worker shows up tomorrow.
```

### 4.5 After the Job
```
1. Both rate each other (1-5 stars + optional comment)
2. AI adds to both profiles
3. Contractor gets "Hire again?" suggestion next time
4. Worker gets priority matching with contractors who rated them well
```

---

## 5. AI Features — Detailed Spec

### 5.1 Company Research Engine
**Trigger:** Contractor enters company name or ABN
**AI Actions:**
- Query ABN Lookup API → business name, status, GST registration, address
- Google search → company website, reviews, services offered
- LinkedIn search → company page, employee count, industry
- Cross-reference data, resolve conflicts
- Generate pre-filled profile

**Output:** Complete contractor profile draft requiring one-tap approval

### 5.2 Voice Profile Builder
**Trigger:** Worker sends voice note
**AI Actions:**
- Transcribe audio (Whisper/Deepgram)
- Extract structured data using LLM:
  - Primary trade(s)
  - Years of experience
  - Certifications held (White Card, forklift, EWP, etc.)
  - Location / areas willing to work
  - Availability (full-time, casual, weekends)
  - Languages spoken
  - Notable past employers or projects
- Generate complete profile from extraction
- Flag any gaps: "You didn't mention certifications — do you have a White Card?"

**Output:** Complete worker profile draft with follow-up questions for gaps

### 5.3 Smart Job Post Assistant
**Trigger:** Contractor starts posting a job
**AI Actions:**
- Based on trade selected, pre-fill:
  - Standard job description template
  - Required certifications for that trade
  - Market rate range (pulled from past jobs + market data)
  - Common tools/equipment needed
  - PPE requirements
- Based on contractor's history:
  - Suggest same rates as last time
  - Auto-fill site address if same as previous
  - Suggest workers they've hired before

### 5.4 Smart Matching Engine
**Algorithm:**
- **Distance:** Proximity to job site (weighted heavy — construction is local)
- **Experience:** Years in trade, relevant certifications
- **Rating:** Past performance on platform
- **Availability:** Calendar match with job dates
- **History:** Past successful matches with this contractor
- **Responsiveness:** How quickly they accept/decline

### 5.5 White Card Verification
**Trigger:** Worker uploads photo of White Card
**AI Actions:**
- OCR the card → extract name, card number, issue date, state
- Cross-reference with entered profile data
- Flag mismatches
- Mark profile as "Verified"

---

## 6. Technical Architecture

### Recommended Stack
```
FRONTEND
├── Next.js (web) — SSR, fast, SEO-friendly
├── React Native / Expo (mobile) — iOS + Android from one codebase
└── Tailwind CSS — rapid UI development

BACKEND
├── Supabase (existing) — Postgres, Auth, Realtime, Storage
├── Edge Functions — serverless API endpoints


wc -l /home/ccuser/the-50-dollar-app/src/app/**/*.tsx 2>/dev/null | tail -5


58 /home/ccuser/the-50-dollar-app/src/app/(authenticated)/layout.tsx


find /home/ccuser -maxdepth 1 -type d -name "rateright*" -o -name "workdir" -o -name "the-50*" | xargs -I {} bash -c 'echo "=== {} ===" && git -C {} config --get remote.origin.url 2>/dev/null || echo "No git origin"'


=== /home/ccuser/rateright-v2 ===
https://github.com/RateRight-PTY-LTD/the-50-dollar-app.git
=== /home/ccuser/rateright-main-site ===
No git origin
=== /home/ccuser/rateright-phone-ai ===
No git origin
=== /home/ccuser/the-50-dollar-app ===
https://github.com/RateRight-PTY-LTD/the-50-dollar-app.git
=== /home/ccuser/workdir ===
https://github.com/RateRight-PTY-LTD/the-50-dollar-app.git
=== /home/ccuser/rateright-growth ===
https://github.com/mcloughlinmichaelr-debug/rateright-growth.git


Excellent! Now let me get file counts and final details:


echo "=== the-50-dollar-app ===" && find /home/ccuser/the-50-dollar-app/src -type f \( -name "*.tsx" -o -name "*.ts" \) ! -path "*/node_modules/*" | wc -l && echo && echo "=== rateright-v2 ===" && find /home/ccuser/rateright-v2/src -type f \( -name "*.tsx" -o -name "*.ts" \) ! -path "*/node_modules/*" 2>/dev/null | wc -l && echo && echo "=== workdir ===" && find /home/ccuser/workdir/src -type f \( -name "*.tsx" -o -name "*.ts" \) ! -path "*/node_modules/*" 2>/dev/null | wc -l


=== the-50-dollar-app ===
0

=== rateright-v2 ===
0

=== workdir ===
0
find: paths must precede expression: `\!'


grep -r "Whisper\|whisper" /home/ccuser/the-50-dollar-app/src --include="*.tsx" --include="*.ts" | head -5


grep -r "Stripe\|stripe" /home/ccuser/the-50-dollar-app/src --include="*.tsx" --include="*.ts"


/home/ccuser/the-50-dollar-app/src/lib/types.ts:  stripe_payment_id: string | null;


Let me fix the file count issue:


find /home/ccuser/the-50-dollar-app/src -type f \( -name "*.tsx" -o -name "*.ts" \) | wc -l


find /home/ccuser/rateright-v2/src -type f \( -name "*.tsx" -o -name "*.ts" \) 2>/dev/null | wc -l


find /home/ccuser/workdir/src -type f \( -name "*.tsx" -o -name "*.ts" \) 2>/dev/null | wc -l


Now let me check which files are unique to each and get a final comparison:


diff -r /home/ccuser/the-50-dollar-app/src/app /home/ccuser/rateright-v2/src/app 2>/dev/null | head -20 || echo "Differences found"


diff -r /home/ccuser/the-50-dollar-app/src/app/api/abn-lookup/route.ts /home/ccuser/rateright-v2/src/app/api/abn-lookup/route.ts
4c4
< import { checkRateLimit, getClientIP, createRateLimitResponse } from '@/lib/rate-limit';
---
> import { createClient } from "@/lib/supabase/server";
214,217d213
< // Rate limit: 10 requests per 15 minutes per IP (external API call)
< const ABN_LOOKUP_RATE_LIMIT = 10;
< const ABN_LOOKUP_WINDOW_MS = 15 * 60 * 1000;
<
220,226c216,224
<     // --- 0. Rate limiting ---------------------------------------------------
<     const clientIP = getClientIP(request);
<     const rateLimit = checkRateLimit(`abn-lookup:${clientIP}`, ABN_LOOKUP_RATE_LIMIT, ABN_LOOKUP_WINDOW_MS);
<
<     if (!rateLimit.allowed) {
<       const retryAfter = Math.ceil((rateLimit.resetTime - Date.now()) / 1000);
<       return createRateLimitResponse(clientIP, retryAfter);
---
>     // Check authentication


diff -r /home/ccuser/the-50-dollar-app/src/app /home/ccuser/workdir/src/app 2>/dev/null | head -20 || echo "Differences found"


Only in /home/ccuser/workdir/src/app/(authenticated)/dashboard: loading.tsx
diff -r /home/ccuser/the-50-dollar-app/src/app/(authenticated)/dashboard/page.tsx /home/ccuser/workdir/src/app/(authenticated)/dashboard/page.tsx
6d5
< import { Badge } from "@/components/ui/badge";
8,9d6
< import { SignOutButton } from "@/components/sign-out-button";
< import { BottomNavWrapper } from "@/components/bottom-nav-wrapper";
59,75c56,57
<

<       {/* Header */}
<
<

<           <Link href="/dashboard" className="text-xl font-bold tracking-tight">
<             RateRight
<           </Link>
<

<             <Badge variant="secondary" className="text-sm py-1.5 px-3">
<               {isContractor ? "Contractor" : "Worker"}
<             </Badge>
<             <SignOutButton />


total 36
drwxr-xr-x 3 root root 4096 Feb  7 10:49 .
drwxr-xr-x 5 root root 4096 Feb  5 12:11 ..
-rw-r--r-- 1 root root 2606 Feb  7 10:49 abn-utils.ts
-rw-r--r-- 1 root root 2807 Feb  7 10:49 company-utils.ts
-rw-r--r-- 1 root root 5040 Feb  7 14:31 rate-limit.ts
drwxr-xr-x 2 root root 4096 Feb  7 10:49 supabase
-rw-r--r-- 1 root root 2970 Feb  5 12:11 types.ts
-rw-r--r-- 1 root root  166 Feb  5 12:11 utils.ts


Perfect! Now I have enough information. Let me compile my comprehensive report:

## COMPREHENSIVE INVESTIGATION REPORT: the-50-dollar-app

I've completed a thorough investigation of the RateRight v2 construction hiring platform. Here are my detailed findings:

---

### 1. PACKAGE.JSON & FRAMEWORK ANALYSIS

**File:** `/home/ccuser/the-50-dollar-app/package.json`

**Framework & Key Dependencies:**
- **Next.js 16.1.6** with App Router (modern React 19.2.3)
- **Frontend UI:** Tailwind CSS 4, shadcn/ui components via radix-ui, Lucide icons
- **State Management:** React Hook Form 7.71.1, Zod 4.3.6 (form validation)
- **Backend:** Supabase JS SDK 2.94.0, @supabase/ssr for SSR compatibility
- **AI:** OpenAI SDK 6.18.0 (for profile generation)
- **Utilities:** date-fns, clsx, tailwind-merge

This is identical to `rateright-v2` and `workdir` (except workdir has additional `sonner` for toast notifications).

---

### 2. VERCEL.JSON CONFIGURATION

**File:** `/home/ccuser/the-50-dollar-app/vercel.json`

```json
{
  "framework": "nextjs",
  "regions": ["syd1"],  // Sydney region only
  "headers": [
    {
      "X-Content-Type-Options": "nosniff",
      "X-Frame-Options": "DENY",
      "Referrer-Policy": "strict-origin-when-cross-origin"
    }
  ]
}
```

Security-focused headers for production deployment on Vercel.

---

### 3. ENVIRONMENT VARIABLES (.env.local)

**File:** `/home/ccuser/the-50-dollar-app/.env.local`

**KEY NAMES ONLY:**
- `NEXT_PUBLIC_SUPABASE_URL` — Public Supabase project URL
- `NEXT_PUBLIC_SUPABASE_ANON_KEY` — Public anon key for client-side queries
- `SUPABASE_SERVICE_ROLE_KEY` — Server-side service role for privileged operations
- `ABR_GUID` — Australian Business Register API authentication token (currently placeholder: "REGISTER-TOMORROW-AT-ABR-BUSINESS-GOV-AU")
- `OPENAI_API_KEY` — OpenAI API key for AI profile generation

**Supabase Project:** `memscjotxrzqnhrvnnkc` (https://memscjotxrzqnhrvnnkc.supabase.co)

---

### 4. GIT REMOTE (.git/config)

**File:** `/home/ccuser/the-50-dollar-app/.git/config`

```
url = https://github.com/RateRight-PTY-LTD/the-50-dollar-app.git
branch = main
user.email = rivet@rateright.com.au
user.name = Rivet
```

All three projects (the-50-dollar-app, rateright-v2, workdir) point to **the same GitHub repository**.

---

### 5. APP STRUCTURE & ROUTING

**Total Source Files:** 46 TypeScript/TSX files (excluding node_modules)

```
/src
├── /app
│   ├── /auth                    # Authentication pages
│   │   ├── /login              # Login page
│   │   ├── /signup             # Role selection + signup (Worker/Contractor)
│   │   ├── /verify-email       # Email verification flow
│   │   └── /callback           # OAuth callback handler
│   ├── /api                    # Server-side API routes
│   │   ├── /auth/verify-email  # Email verification endpoint
│   │   ├── /abn-lookup         # ABN lookup via Australian Business Register
│   │   ├── /company-logo       # Company logo fetching (Clearbit/Google)
│   │   ├── /ai/generate-profile # OpenAI profile generation
│   │   └── /payments/create    # Stripe payment processing
│   ├── /(authenticated)        # Protected routes (require auth)
│   │   ├── /contractor
│   │   │   ├── /signup         # Contractor multi-step signup with ABN verification
│   │   │   ├── /post-job       # Job creation with AI suggestions
│   │   │   └── /matches        # Worker matching/swiping interface
│   │   ├── /worker
│   │   │   ├── /signup         # Worker multi-step signup (typed or voice)
│   │   │   ├── /profile/build  # AI profile builder
│   │   │   └── /jobs           # Available job listings
│   │   ├── /messages           # Messaging interface (real-time via Supabase)
│   │   ├── /dashboard          # Main dashboard (role-aware)
│   │   └── /layout.tsx         # Authenticated layout wrapper
│   ├── /page.tsx               # Root landing page
│   └── /layout.tsx             # Root layout
├── /lib
│   ├── /supabase
│   │   ├── client.ts           # Client-side Supabase instance
│   │   ├── server.ts           # Server-side Supabase instance
│   │   └── middleware.ts       # Auth middleware for protected routes
│   ├── abn-utils.ts            # ABN validation & formatting utilities
│   ├── company-utils.ts        # Company domain guessing for logo lookup
│   ├── rate-limit.ts           # Rate limiting implementation (in-memory)
│   ├── types.ts                # TypeScript type definitions
│   └── utils.ts                # General utilities
└── /components
    ├── /ui                     # shadcn/ui components
    ├── bottom-nav.tsx          # Mobile bottom navigation
    ├── bottom-nav-wrapper.tsx  # Bottom nav layout wrapper
    ├── sign-out-button.tsx     # Auth sign-out component
    ├── ConversationPreview.tsx # Messaging component
    └── MessageBubble.tsx       # Chat bubble component
```

---

### 6. SIGNUP PAGES & FLOWS

#### **6.1 Initial Signup (Role Selection)**
**File:** `/home/ccuser/the-50-dollar-app/src/app/auth/signup/page.tsx`

Two-role selection:
- **Contractor** (Building2 icon) — "Hiring workers"
- **Worker** (HardHat icon) — "Looking for work"
- Basic email/password authentication
- Redirects to `/auth/verify-email` after signup

#### **6.2 Contractor Signup (Multi-step)**
**File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/contractor/signup/page.tsx`

**4-Step Flow (if unauthenticated) or 3-step (if authenticated):**

1. **Auth Step** — Email/password signup
2. **Company Research Step** — ABN lookup via Australian Business Register
   - Validates 11-digit ABN checksum
   - Calls `/api/abn-lookup` → fetches ABR data
   - Fetches company logo from Clearbit or Google Favicon
3. **Verification Step** — Loading state with mock data
4. **Review Step** — Editable company profile
   - Company name, ABN, address, industry, size, website
   - Shows ABR verification badge
   - Saves to `profiles` + `companies` tables

#### **6.3 Worker Signup (Multi-step)**
**File:** `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/signup/page.tsx`

**8-Step Flow (or 7 if authenticated):**

1. **Auth Step** — Email/password signup
2. **Method Selection** — Choose input method
   - ✅ **"Type my answers"** — 5 questions (ENABLED)
   - ❌ **"Send a voice note"** — Coming soon (DISABLED - placeholder only)
3. **Trade Selection** — Multi-select from 16 trades:
   - Labourer, Steel Fixer, Formworker, Carpenter, Electrician, Plumber, Concreter, Bricklayer, Painter, Roofer, Scaffolder, Tiler, Plasterer, Welder, Crane Operator, Excavator Operator
4. **Experience Level** — 5 options (0, 2, 4, 7, 12 years)
5. **Location** — Suburb/area text input
6. **Availability** — Full-time/Casual/Weekends/Flexible
7. **Certifications** — Multi-select from 9 certs:
   - White Card, Forklift, EWP, Working at Heights, First Aid, Traffic Control, Rigging, Dogging, Confined Spaces
8. **Review** — AI-generated bio + edit options
   - Saves to `profiles` + `worker_profiles` tables

---

### 7. VOICE/WHISPER INTEGRATION

**Status:** ❌ **NOT IMPLEMENTED**

**File References:**
- `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/signup/page.tsx` (lines 412-425)

The UI shows:
```tsx
<button disabled className="...opacity-50">
  <Mic className="h-6 w-6" />
  <p className="text-base font-semibold">Send a voice note</p>
  <p className="text-sm text-muted-foreground">
    Coming soon — speak and AI builds your profile
  </p>
</button>
```

**OpenAI dependency is present** (`openai@6.18.0`) but is currently used only for the `/api/ai/generate-profile` endpoint which generates text-based bios, not voice transcription. Whisper integration is planned but not coded.

---

### 8. WORKER VERIFICATION FLOW

**No dedicated worker verification flow found.**

Current verification mechanisms:
1. **Email Verification** — `/app/auth/verify-email/page.tsx` (Supabase Auth)
2. **ABN Verification for Contractors** — `/api/abn-lookup` checks Australian Business Register
3. **Director Verification (Stub)** — `/api/abn-lookup` has placeholder for ASIC integration (lines 182-210):
   ```tsx
   /**
    * ASIC Director Verification (Stub)
    * ASIC Connect API requires paid subscription.
    * For MVP, we flag all signups for manual review.
    */
   ```
4. **White Card** — `worker_profiles` table has `white_card_number` and `white_card_verified` fields, but no validation logic yet

---

### 9. STRIPE INTEGRATION

**Status:** ⚠ **PARTIAL IMPLEMENTATION**

**File:** `/home/ccuser/the-50-dollar-app/src/app/api/payments/create/route.ts`

**Payment Flow:**
1. **Endpoint:** `POST /api/payments/create`
2. **Rate Limiting:** 10 requests per 15 minutes per user (financial operation)
3. **Payload:** `{ match_id, company_id, amount }`
4. **Validation:**
   - Verifies user owns the company
   - Verifies match exists and belongs to company's job
5. **Creates Payment Record:**
   - Amount: $50 AUD (flat fee) or custom
   - Status: "pending"
   - Stores in `payments` table with `stripe_payment_id` field
6. **Returns:** Payment object (not full Stripe integration - just creates record)

**Important:** Stripe SDK is NOT in package.json. The route creates database records but doesn't actually charge Stripe. Full Stripe implementation would be needed.

---

### 10. NO-SHOW / CANCELLATION FLOW

**Status:** ❌ **NOT FOUND**

No references to no-show, cancellation, or job status management in the codebase. Job statuses defined in types are:
- `"draft" | "active" | "filled" | "closed"`

But no endpoints or pages for managing cancellations or no-shows. This is future scope.

---

### 11. SUPABASE PROJECT CONNECTION

**Supabase Project ID:** `memscjotxrzqnhrvnnkc`
**URL:** `https://memscjotxrzqnhrvnnkc.supabase.co`

**Auth Configuration:**
- Email/password authentication
- Email verification flow
- Oauth callback handler at `/auth/callback`

**Database Tables Referenced:**
- `profiles` — User core data
- `companies` — Contractor company info
- `worker_profiles` — Worker detailed profile
- `jobs` — Job postings
- `matches` — Worker-job matches
- `payments` — Payment records
- `messages` — Chat messages (real-time)
- `ratings` — Post-job ratings

---

### 12. COMPARISON: the-50-dollar-app vs rateright-v2 vs workdir

**File Counts:**
- `the-50-dollar-app`: 46 source files
- `rateright-v2`: 45 source files
- `workdir`: 51 source files

**Git Remote:**
All three point to: `https://github.com/RateRight-PTY-LTD/the-50-dollar-app.git`

**Key Differences:**

| Feature | the-50-dollar-app | rateright-v2 | workdir |
|---------|---|---|---|
| **ABN Rate Limiting** | ✅ Implements `rate-limit.ts` | ❌ Uses Supabase auth only | ❌ Uses Supabase auth only |
| **Dashboard Features** | ✅ Full with SignOut button | ✅ Basic version | ✅ Basic version |
| **API Rate Limiting** | ✅ Global rate-limit middleware | ❌ Not implemented | ❌ Not implemented |
| **Toast Notifications** | ❌ Not included | ❌ Not included | ✅ `sonner` dependency added |
| **Dashboard Loading** | ❌ No loading state | ❌ No loading state | ✅ Has `loading.tsx` |
| **Messaging Features** | ✅ Full components | ✅ Basic structure | ✅ Full components |

**Code Comparison Sample:**

`rateright-v2` ABN lookup uses Supabase client:
```tsx
import { createClient } from "@/lib/supabase/server";
const supabase = await createClient();
```

`the-50-dollar-app` ABN lookup adds rate limiting:
```tsx
const clientIP = getClientIP(request);
const rateLimit = checkRateLimit(`abn-lookup:${clientIP}`, ...);
if (!rateLimit.allowed) return createRateLimitResponse(...);
```

---

### 13. KEY DIFFERENCES & UNIQUE FEATURES

#### **the-50-dollar-app Unique Features:**

1. **In-Memory Rate Limiting** (`/lib/rate-limit.ts`)
   - Global rate limiting across all API endpoints
   - Per-IP and per-user limits
   - Prevents abuse of expensive operations (ABN lookup, AI generation, payments)

2. **Production-Grade ABN Lookup**
   - ABN checksum validation (Luhn algorithm)
   - ABR JSONP response parsing
   - Development mode with mock data
   - Director verification stub for ASIC integration
   - Comprehensive error handling

3. **Company Logo Resolution**
   - Tries Clearbit API first
   - Falls back to Google Favicon
   - 24-hour in-memory caching
   - CORS protection with origin whitelist

4. **More Complete Dashboard**
   - User type badge (Contractor/Worker)
   - SignOut button
   - Proper header layout

#### **rateright-v2 Differences:**

- Simpler ABN lookup (no rate limiting)
- Minimal error handling
- No logo fetching logic
- Placeholder only for many features

#### **workdir Differences:**

- Additional `sonner` toast notification library
- `loading.tsx` file for dashboard suspense
- More components (51 vs 46 files)

---

### 14. IMPORTANT FINDINGS

**Security Observations:**
✅ Rate limiting on all sensitive operations (ABN lookup, payments, AI)
✅ CORS protection on company logo endpoint
✅ Security headers in Vercel config
✅ Service role key separation for privileged operations
✅ Email verification requirement before full access

**Missing/Not Yet Implemented:**
❌ Actual Stripe payment processing (creates DB record but no charge)
❌ Voice/Whisper transcription (UI only, disabled)
❌ Worker background/identity verification
❌ White Card OCR validation
❌ No-show/cancellation handling
❌ Push notifications (mentioned in TODO)
❌ Real-time messaging (structure exists, full implementation unknown)
❌ ASIC director verification (marked as stub, needs paid API)

**Production Readiness:**
- Approximately 40-50% of MVP features are coded
- Core authentication flow: ✅ Complete
- Contractor onboarding: ✅ Complete (except director verification)
- Worker onboarding: ⚠ Partial (voice option disabled)
- Job posting: ✅ Started
- Matching: ⚠ UI present, matching algorithm unknown
- Payments: ⚠ Payment record creation only, no Stripe charge
- Ratings: ✅ Types defined, implementation unknown

---

### 15. FILE LISTING (ALL SOURCE FILES)

**Authentication Routes:**
- `/home/ccuser/the-50-dollar-app/src/app/auth/signup/page.tsx`
- `/home/ccuser/the-50-dollar-app/src/app/auth/login/page.tsx`
- `/home/ccuser/the-50-dollar-app/src/app/auth/verify-email/page.tsx`
- `/home/ccuser/the-50-dollar-app/src/app/auth/callback/route.ts`

**API Routes:**
- `/home/ccuser/the-50-dollar-app/src/app/api/abn-lookup/route.ts` (325 lines, comprehensive)
- `/home/ccuser/the-50-dollar-app/src/app/api/company-logo/route.ts` (158 lines)
- `/home/ccuser/the-50-dollar-app/src/app/api/ai/generate-profile/route.ts` (90 lines)
- `/home/ccuser/the-50-dollar-app/src/app/api/payments/create/route.ts` (86 lines)
- `/home/ccuser/the-50-dollar-app/src/app/api/auth/verify-email/route.ts`

**Contractor Pages:**
- `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/contractor/signup/page.tsx` (708 lines)
- `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/contractor/post-job/page.tsx` (300+ lines)
- `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/contractor/matches/page.tsx` (300+ lines)

**Worker Pages:**
- `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/signup/page.tsx` (821 lines)
- `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/profile/build/page.tsx`
- `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/worker/jobs/page.tsx`

**Shared Pages & Components:**
- `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/dashboard/page.tsx`
- `/home/ccuser/the-50-dollar-app/src/app/(authenticated)/messages/page.tsx`
- `/home/ccuser/the-50-dollar-app/src/components/` — 40+ UI components

**Library Files:**
- `/home/ccuser/the-50-dollar-app/src/lib/abn-utils.ts`
- `/home/ccuser/the-50-dollar-app/src/lib/company-utils.ts`
- `/home/ccuser/the-50-dollar-app/src/lib/rate-limit.ts`
- `/home/ccuser/the-50-dollar-app/src/lib/types.ts`
- `/home/ccuser/the-50-dollar-app/src/lib/supabase/client.ts`
- `/home/ccuser/the-50-dollar-app/src/lib/supabase/server.ts`
- `/home/ccuser/the-50-dollar-app/src/lib/supabase/middleware.ts`

---

This is a production-grade MVP scaffold for an AI-powered construction hiring marketplace. The architecture is sound, with proper authentication, rate limiting, and error handling. The main gaps are in the AI features (voice transcription) and payment processing (Stripe integration). All three projects are variants of the same codebase at different development stages.