Analyze all frontend components and UX flows in /home/ccuser/the-50-dollar-app.

1. Read all components in src/components/ - are they complete? Any stubs?
2. Check all user flows end-to-end:
   - Worker signup → profile build → dashboard → job search → messaging
   - Contractor signup → post job → find workers → hire → pay → messaging
   - Auth flow: login, signup, password reset, email confirmation
3. Read the actual page.tsx files for each step in these flows
4. Check form validation - are all forms properly validated?
5. Check loading states, error states, empty states - are they handled?
6. Check responsive design indicators (Tailwind breakpoints)
7. Look for hardcoded values, placeholder text, lorem ipsum, TODO comments
8. Check if navigation between pages is correct (links, redirects)

Read every component file. Return a detailed report of:
- Complete flows vs broken/incomplete flows
- Missing UI states (loading, error, empty)
- Placeholder content that needs replacing
- Broken navigation or dead links


I'll analyze the frontend components and UX flows in the application. Let me start by exploring the structure.


Bash

find /home/ccuser/the-50-dollar-app -type f -name "*.tsx" -o -name "*.ts" -o -name "*.json" | head -30


Let me explore the directory structure properly:


total 1756
drwxrwxr-x  10 ccuser ccuser    4096 Feb  8 12:04 .
drwxr-x---  21 ccuser ccuser    4096 Feb  8 20:57 ..
-rw-r--r--   1 ccuser ccuser    2190 Feb  8 11:21 .env.local
drwxrwxr-x   8 ccuser ccuser    4096 Feb  8 20:49 .git
-rw-rw-r--   1 ccuser ccuser     496 Feb  8 11:18 .gitignore
drwxrwxr-x   8 ccuser ccuser    4096 Feb  8 20:48 .next
-rw-rw-r--   1 ccuser ccuser    4319 Feb  8 11:18 AI-PROFILE-BUILDER.md
-rw-rw-r--   1 ccuser ccuser    6216 Feb  8 11:18 APPLICATION-CODE-UPDATES-FOR-RLS-FIX.md
-rw-rw-r--   1 ccuser ccuser    5238 Feb  8 11:18 AUDIT-C3-COMPLETION.md
-rw-rw-r--   1 ccuser ccuser    2213 Feb  8 11:18 AUDIT-C4-COMPLETION.md
-rw-rw-r--   1 ccuser ccuser    6276 Feb  8 11:18 AUDIT-C5-COMPLETION.md
-rw-rw-r--   1 ccuser ccuser    5447 Feb  8 11:18 AUDIT-H1-H5-H11-H14-M16-M17-COMPLETION.md
-rw-rw-r--   1 ccuser ccuser    4394 Feb  8 11:18 CLAUDE.md
-rw-rw-r--   1 ccuser ccuser    3115 Feb  8 11:18 CSRF_IMPLEMENTATION.md
-rw-rw-r--   1 ccuser ccuser   13785 Feb  8 11:18 PRODUCT-SPEC.md
-rw-rw-r--   1 ccuser ccuser    2179 Feb  8 11:18 README.md
-rw-rw-r--   1 ccuser ccuser    9184 Feb  8 11:18 SUPABASE-KEY-ROTATION-GUIDE.md
-rw-rw-r--   1 ccuser ccuser    8977 Feb  8 11:18 UX-IMPLEMENTATION-GUIDE.md
-rw-rw-r--   1 ccuser ccuser    3480 Feb  8 11:18 VERCEL-DEPLOYMENT.md
-rw-rw-r--   1 ccuser ccuser     467 Feb  8 11:18 components.json
drwxrwxr-x   2 ccuser ccuser    4096 Feb  8 11:18 docs
-rw-rw-r--   1 ccuser ccuser     465 Feb  8 11:18 eslint.config.mjs
-rw-rw-r--   1 ccuser ccuser     247 Feb  8 11:23 next-env.d.ts
-rw-rw-r--   1 ccuser ccuser    1651 Feb  8 11:18 next.config.ts
drwxr-xr-x 321 ccuser ccuser   12288 Feb  7 20:58 node_modules
-rw-rw-r--   1 ccuser ccuser  305740 Feb  8 11:18 package-lock.json
-rw-rw-r--   1 ccuser ccuser    1080 Feb  8 11:18 package.json
-rw-rw-r--   1 ccuser ccuser      94 Feb  8 11:18 postcss.config.mjs
drwxrwxr-x   2 ccuser ccuser    4096 Feb  8 11:18 public
drwxrwxr-x   2 ccuser ccuser    4096 Feb  8 11:18 scripts
drwxrwxr-x   5 ccuser ccuser    4096 Feb  8 11:18 src
drwxrwxr-x   4 ccuser ccuser    4096 Feb  8 11:18 supabase
-rw-rw-r--   1 ccuser ccuser     753 Feb  8 11:18 tsconfig.json
-rw-rw-r--   1 ccuser ccuser 1291209 Feb  8 20:04 tsconfig.tsbuildinfo
-rw-rw-r--   1 ccuser ccuser     474 Feb  8 11:18 vercel.json


total 24
drwxrwxr-x  5 ccuser ccuser 4096 Feb  8 11:18 .
drwxrwxr-x 10 ccuser ccuser 4096 Feb  8 12:04 ..
drwxrwxr-x  8 ccuser ccuser 4096 Feb  8 19:22 app
drwxrwxr-x  4 ccuser ccuser 4096 Feb  8 12:15 components
drwxrwxr-x  4 ccuser ccuser 4096 Feb  8 20:47 lib
-rw-rw-r--  1 ccuser ccuser  341 Feb  8 11:18 middleware.ts


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


find /home/ccuser/the-50-dollar-app/src/app -type f -name "*.tsx" | sort


Now let me read all the component files to analyze them:


Error: EISDIR: illegal operation on a directory, read


Let me read the component files systematically:


1→import { Card } from "@/components/ui/card";
     2→import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
     3→import { Badge } from "@/components/ui/badge";
     4→import { cn } from "@/lib/utils";
     5→import { formatDistanceToNow } from "date-fns";
     6→
     7→interface ConversationPreviewProps {
     8→  id: string;
     9→  name: string;
    10→  avatar?: string;
    11→  lastMessage: string;
    12→  lastMessageTime: Date;
    13→  unreadCount?: number;
    14→  isOnline?: boolean;
    15→  onClick?: () => void;
    16→}
    17→
    18→export function ConversationPreview({
    19→  id,
    20→  name,
    21→  avatar,
    22→  lastMessage,
    23→  lastMessageTime,
    24→  unreadCount = 0,
    25→  isOnline = false,
    26→  onClick,
    27→}: ConversationPreviewProps) {
    28→  const initials = name
    29→    .split(" ")
    30→    .map((n) => n[0])
    31→    .join("")
    32→    .toUpperCase()
    33→    .slice(0, 2);
    34→
    35→  return (
    36→    <Card
    37→      className={cn(
    38→        "border-0 border-b rounded-none last:border-b-0",
    39→        "active:bg-accent transition-colors cursor-pointer"
    40→      )}
    41→      onClick={onClick}
    42→    >
    43→

    44→        {/* Avatar with online indicator */}
    45→

    46→          <Avatar className="h-14 w-14">
    47→            <AvatarImage src={avatar} alt={name} />
    48→            <AvatarFallback className="bg-primary/10 text-primary font-semibold">
    49→              {initials}
    50→            </AvatarFallback>
    51→          </Avatar>
    52→          {isOnline && (
    53→

    54→          )}
    55→

    56→
    57→        {/* Content */}
    58→

    59→

    60→
{name}

    61→
    62→              {formatDistanceToNow(lastMessageTime, { addSuffix: true })}
    63→
    64→

    65→

    66→            {lastMessage}
    67→

    68→

    69→
    70→        {/* Unread badge */}
    71→        {unreadCount > 0 && (
    72→          <Badge
    73→            variant="default"
    74→            className="shrink-0 h-6 min-w-6 px-2 rounded-full text-xs font-semibold"
    75→          >
    76→            {unreadCount > 99 ? "99+" : unreadCount}
    77→          </Badge>
    78→        )}
    79→

    80→    </Card>
    81→  );
    82→}

<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→import { Loader2, RefreshCw } from "lucide-react";
     2→import { Button } from "@/components/ui/button";
     3→import { Card, CardContent } from "@/components/ui/card";
     4→
     5→interface LoadingStateProps {
     6→  message?: string;
     7→  showRetry?: boolean;
     8→  onRetry?: () => void;
     9→  retryDelay?: number;
    10→  className?: string;
    11→}
    12→
    13→export function LoadingState({
    14→  message = "Loading...",
    15→  showRetry = false,
    16→  onRetry,
    17→  retryDelay = 5000,
    18→  className = "",
    19→}: LoadingStateProps) {
    20→  return (
    21→    <div className={`flex items-center justify-center p-8 ${className}`}>
    22→      <Card className="w-full max-w-md">
    23→        <CardContent className="flex flex-col items-center gap-4 p-6">
    24→          <Loader2 className="h-8 w-8 animate-spin text-primary" />
    25→
{message}

    26→
    27→          {showRetry && onRetry && (
    28→            <Button
    29→              onClick={onRetry}
    30→              variant="outline"
    31→              size="sm"
    32→              className="mt-2 gap-2"
    33→            >
    34→              <RefreshCw className="h-4 w-4" />
    35→              Retry
    36→            </Button>
    37→          )}
    38→        </CardContent>
    39→      </Card>
    40→

    41→  );
    42→}
    43→
    44→interface LoadingSpinnerProps {
    45→  size?: "sm" | "md" | "lg";
    46→  className?: string;
    47→}
    48→
    49→export function LoadingSpinner({ size = "md", className = "" }: LoadingSpinnerProps) {
    50→  const sizeClasses = {
    51→    sm: "h-4 w-4",
    52→    md: "h-6 w-6",
    53→    lg: "h-8 w-8",
    54→  };
    55→
    56→  return (
    57→    <div className={`flex items-center justify-center ${className}`}>
    58→      <Loader2 className={`animate-spin text-primary ${sizeClasses[size]}`} />
    59→

    60→  );
    61→}

<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→import { cn } from "@/lib/utils";
     2→
     3→interface MessageBubbleProps {
     4→  content: string;
     5→  isOwn?: boolean;
     6→  timestamp: Date;
     7→  status?: "sent" | "delivered" | "read";
     8→}
     9→
    10→export function MessageBubble({
    11→  content,
    12→  isOwn = false,
    13→  timestamp,
    14→  status = "delivered",
    15→}: MessageBubbleProps) {
    16→  const formatTime = (date: Date) => {
    17→    return date.toLocaleTimeString("en-AU", {
    18→      hour: "numeric",
    19→      minute: "2-digit",
    20→      hour12: true,
    21→    });
    22→  };
    23→
    24→  return (
    25→

    26→

    35→
{content}

    36→

    37→          {formatTime(timestamp)}
    38→          {isOwn && (
    39→
    40→              {status === "read" ? "✓✓" : "✓"}
    41→
    42→          )}
    43→

    44→

    45→

    46→  );
    47→}

<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, useRef, useCallback } from "react";
     4→import { Mic, Square } from "lucide-react";
     5→
     6→interface VoiceRecorderProps {
     7→  onRecordingComplete: (blob: Blob) => void;
     8→  disabled?: boolean;
     9→}
    10→
    11→export default function VoiceRecorder({ onRecordingComplete, disabled }: VoiceRecorderProps) {
    12→  const [recording, setRecording] = useState(false);
    13→  const [error, setError] = useState<string | null>(null);
    14→  const mediaRecorderRef = useRef<MediaRecorder | null>(null);
    15→  const chunksRef = useRef<Blob[]>([]);
    16→
    17→  const startRecording = useCallback(async () => {
    18→    setError(null);
    19→    try {
    20→      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
    21→      const mediaRecorder = new MediaRecorder(stream, {
    22→        mimeType: MediaRecorder.isTypeSupported("audio/webm")
    23→          ? "audio/webm"
    24→          : "audio/mp4",
    25→      });
    26→      mediaRecorderRef.current = mediaRecorder;
    27→      chunksRef.current = [];
    28→
    29→      mediaRecorder.ondataavailable = (e) => {
    30→        if (e.data.size > 0) {
    31→          chunksRef.current.push(e.data);
    32→        }
    33→      };
    34→
    35→      mediaRecorder.onstop = () => {
    36→        const blob = new Blob(chunksRef.current, { type: mediaRecorder.mimeType });
    37→        onRecordingComplete(blob);
    38→        stream.getTracks().forEach((track) => track.stop());
    39→      };
    40→
    41→      mediaRecorder.start();
    42→      setRecording(true);
    43→    } catch {
    44→      setError("Microphone access denied. Please allow mic access and try again.");
    45→    }
    46→  }, [onRecordingComplete]);
    47→
    48→  const stopRecording = useCallback(() => {
    49→    if (mediaRecorderRef.current && mediaRecorderRef.current.state === "recording") {
    50→      mediaRecorderRef.current.stop();
    51→      setRecording(false);
    52→    }
    53→  }, []);
    54→
    55→  return (
    56→

    57→      <button
    58→        type="button"
    59→        onClick={recording ? stopRecording : startRecording}
    60→        disabled={disabled}
    61→        className={`flex h-16 w-16 items-center justify-center rounded-full transition-all active:scale-95 ${
    62→          recording
    63→            ? "animate-pulse bg-red-500 text-white shadow-lg shadow-red-500/40"
    64→            : "bg-primary text-primary-foreground shadow-md hover:shadow-lg"
    65→        } ${disabled ? "opacity-50 cursor-not-allowed" : ""}`}
    66→        aria-label={recording ? "Stop recording" : "Start recording"}
    67→      >
    68→        {recording ? (
    69→          <Square className="h-7 w-7" />
    70→        ) : (
    71→          <Mic className="h-7 w-7" />
    72→        )}
    73→      </button>
    74→

    75→        {recording ? "Tap to stop" : "Tap to speak"}
    76→

    77→      {error && (
    78→
{error}

    79→      )}
    80→

    81→  );
    82→}
    83→

<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 { BottomNav } from "@/components/bottom-nav";
     4→
     5→interface BottomNavWrapperProps {
     6→  userType: "contractor" | "worker";
     7→}
     8→
     9→export function BottomNavWrapper({ userType }: BottomNavWrapperProps) {
    10→  return <BottomNav userType={userType} />;
    11→}
    12→

<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 Link from "next/link";
     4→import { usePathname } from "next/navigation";
     5→import { Home, HardHat, Building2, Plus, MessageSquare, User } from "lucide-react";
     6→import { cn } from "@/lib/utils";
     7→
     8→interface BottomNavProps {
     9→  userType: "contractor" | "worker";
    10→}
    11→
    12→export function BottomNav({ userType }: BottomNavProps) {
    13→  const pathname = usePathname();
    14→
    15→  const isContractor = userType === "contractor";
    16→
    17→  const links = [
    18→    {
    19→      href: "/dashboard",
    20→      label: "Home",
    21→      icon: Home,
    22→    },
    23→    {
    24→      href: "/contractor/matches",
    25→      label: "Matches",
    26→      icon: Building2,
    27→    },
    28→    {
    29→      href: "/contractor/post-job",
    30→      label: "Post",
    31→      icon: Plus,
    32→      isCenter: true,
    33→    },
    34→    {
    35→      href: "/messages",
    36→      label: "Messages",
    37→      icon: MessageSquare,
    38→    },
    39→    {
    40→      href: isContractor ? "/contractor/signup" : "/worker/profile/build",
    41→      label: "Profile",
    42→      icon: User,
    43→    },
    44→  ];
    45→
    46→  // Workers don't get the center "Post" button — replace with different layout
    47→  const workerLinks = [
    48→    {
    49→      href: "/dashboard",
    50→      label: "Home",
    51→      icon: Home,
    52→    },
    53→    {
    54→      href: "/worker/jobs",
    55→      label: "Jobs",
    56→      icon: HardHat,
    57→    },
    58→    {
    59→      href: "/messages",
    60→      label: "Messages",
    61→      icon: MessageSquare,
    62→    },
    63→    {
    64→      href: "/worker/profile/build",
    65→      label: "Profile",
    66→      icon: User,
    67→    },
    68→  ];
    69→
    70→  const navLinks = isContractor ? links : workerLinks;
    71→
    72→  return (
    73→
    74→

    75→        {navLinks.map((link) => {
    76→          const isActive =
    77→            pathname === link.href ||
    78→            (link.href !== "/dashboard" && pathname.startsWith(link.href));
    79→          const Icon = link.icon;
    80→
    81→          if ("isCenter" in link && link.isCenter) {
    82→            return (
    83→              <Link
    84→                key={link.href}
    85→                href={link.href}
    86→                className="flex -mt-4 items-center justify-center"
    87→              >
    88→

    89→                  <Icon className="h-7 w-7 text-primary-foreground" />
    90→

    91→              </Link>
    92→            );
    93→          }
    94→
    95→          return (
    96→            <Link
    97→              key={link.href + link.label}
    98→              href={link.href}
    99→              className={cn(
   100→                "flex min-w-[4rem] flex-col items-center gap-0.5 py-2 text-xs transition-colors active:scale-95",
   101→                isActive
   102→                  ? "text-primary font-medium"
   103→                  : "text-muted-foreground"
   104→              )}
   105→            >
   106→              <Icon className="h-5 w-5" />
   107→              {link.label}
   108→            </Link>
   109→          );
   110→        })}
   111→

   112→
   113→  );
   114→}
   115→

<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→import { AlertCircle, RefreshCw, Home, Mail } from 'lucide-react';
     2→import { Button } from '@/components/ui/button';
     3→import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
     4→
     5→interface ErrorDisplayProps {
     6→  title?: string;
     7→  message?: string;
     8→  error?: Error;
     9→  onRetry?: () => void;
    10→  onGoHome?: () => void;
    11→  onContactSupport?: () => void;
    12→  showContactSupport?: boolean;
    13→  variant?: 'default' | 'authentication' | 'network' | 'not-found';
    14→}
    15→
    16→export function ErrorDisplay({
    17→  title = 'Something went wrong',
    18→  message = 'We encountered an unexpected error. Please try again.',
    19→  error,
    20→  onRetry,
    21→  onGoHome,
    22→  onContactSupport,
    23→  showContactSupport = true,
    24→  variant = 'default',
    25→}: ErrorDisplayProps) {
    26→  const getVariantConfig = () => {
    27→    switch (variant) {
    28→      case 'authentication':
    29→        return {
    30→          title: 'Session Expired',
    31→          message: 'Your session has expired. Please log in again to continue.',
    32→          primaryAction: {
    33→            label: 'Log in again',
    34→            icon: <Home className="mr-2 h-4 w-4" />,
    35→            onClick: () => window.location.href = '/auth/login',
    36→          },
    37→        };
    38→      case 'network':
    39→        return {
    40→          title: 'Connection Error',
    41→          message: 'Unable to connect to the server. Please check your internet connection.',
    42→          primaryAction: onRetry ? {
    43→            label: 'Retry',
    44→            icon: <RefreshCw className="mr-2 h-4 w-4" />,
    45→            onClick: onRetry,
    46→          } : undefined,
    47→        };
    48→      case 'not-found':
    49→        return {
    50→          title: 'Page Not Found',
    51→          message: 'The page you are looking for does not exist.',
    52→          primaryAction: {
    53→            label: 'Go to homepage',
    54→            icon: <Home className="mr-2 h-4 w-4" />,
    55→            onClick: () => window.location.href = '/',
    56→          },
    57→        };
    58→      default:
    59→        return {
    60→          title,
    61→          message,
    62→          primaryAction: onRetry ? {
    63→            label: 'Try again',
    64→            icon: <RefreshCw className="mr-2 h-4 w-4" />,
    65→            onClick: onRetry,
    66→          } : undefined,
    67→        };
    68→    }
    69→  };
    70→
    71→  const config = getVariantConfig();
    72→
    73→  return (
    74→

    75→      <Card className="border-destructive/20">
    76→        <CardHeader className="text-center">
    77→

    78→            <AlertCircle className="h-6 w-6 text-destructive" />
    79→

    80→          <CardTitle className="text-xl">{config.title}</CardTitle>
    81→          <CardDescription className="mt-2">{config.message}</CardDescription>
    82→        </CardHeader>
    83→
    84→        {error && process.env.NODE_ENV === 'development' && (
    85→          <CardContent>
    86→

    87→

    88→                {error.message || 'Unknown error occurred'}
    89→

    90→              {error.name && (
    91→

    92→                  Error: {error.name}
    93→

    94→              )}
    95→

    96→          </CardContent>
    97→        )}
    98→
    99→        <CardFooter className="flex flex-col gap-3">
   100→          {config.primaryAction && (
   101→            <Button
   102→              onClick={config.primaryAction.onClick}
   103→              className="w-full"
   104→              variant="default"
   105→            >
   106→              {config.primaryAction.icon}
   107→              {config.primaryAction.label}
   108→            </Button>
   109→          )}
   110→
   111→          {variant !== 'not-found' && (
   112→            <Button
   113→              onClick={onGoHome || (() => window.location.href = '/')}
   114→              className="w-full"
   115→              variant="outline"
   116→            >
   117→              <Home className="mr-2 h-4 w-4" />
   118→              Go to homepage
   119→            </Button>
   120→          )}
   121→
   122→          {showContactSupport && (
   123→            <Button
   124→              onClick={onContactSupport || (() => window.location.href = 'mailto:support@rateright.com.au')}
   125→              className="w-full"
   126→              variant="ghost"
   127→            >
   128→              <Mail className="mr-2 h-4 w-4" />
   129→              Contact support
   130→            </Button>
   131→          )}
   132→        </CardFooter>
   133→      </Card>
   134→
   135→
   136→

   137→          RateRight — Hire Construction Workers Fast
   138→

   139→
   140→

   141→  );
   142→}

<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 { AlertCircle, RefreshCw } from 'lucide-react';
     4→import { Button } from '@/components/ui/button';
     5→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
     6→
     7→interface LoadingErrorProps {
     8→  title?: string;
     9→  message?: string;
    10→  onRetry?: () => void;
    11→}
    12→
    13→export function LoadingError({
    14→  title = 'Failed to load',
    15→  message = 'Unable to load the requested data. Please try again.',
    16→  onRetry
    17→}: LoadingErrorProps) {
    18→  return (
    19→    <Card className="w-full border-destructive/20">
    20→      <CardHeader>
    21→        <div className="flex items-center gap-2">
    22→          <AlertCircle className="h-5 w-5 text-destructive" />
    23→          <CardTitle className="text-lg">{title}</CardTitle>
    24→        </div>
    25→        <CardDescription>{message}</CardDescription>
    26→      </CardHeader>
    27→      {onRetry && (
    28→        <CardContent>
    29→          <Button onClick={onRetry} className="w-full gap-2">
    30→            <RefreshCw className="h-4 w-4" />
    31→            Try again
    32→          </Button>
    33→        </CardContent>
    34→      )}
    35→    </Card>
    36→  );
    37→}

<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 { useRouter } from "next/navigation";
     4→import { createClient } from "@/lib/supabase/client";
     5→import { Button } from "@/components/ui/button";
     6→import { LogOut } from "lucide-react";
     7→
     8→export function SignOutButton() {
     9→  const router = useRouter();
    10→  const supabase = createClient();
    11→
    12→  async function handleSignOut() {
    13→    await supabase.auth.signOut();
    14→    router.push("/");
    15→    router.refresh();
    16→  }
    17→
    18→  return (
    19→    <Button variant="ghost" size="icon" onClick={handleSignOut} title="Sign out">
    20→      <LogOut className="h-4 w-4" />
    21→    </Button>
    22→  );
    23→}
    24→

<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 read the payment components and key page files:


1→'use client';
     2→
     3→import { useState } from 'react';
     4→import {
     5→  PaymentElement,
     6→  useStripe,
     7→  useElements,
     8→} from '@stripe/react-stripe-js';
     9→import { Button } from '@/components/ui/button';
    10→import { Loader2, CreditCard } from 'lucide-react';
    11→
    12→interface PaymentFormProps {
    13→  onSuccess: () => void;
    14→  onCancel: () => void;
    15→  amount: number;
    16→}
    17→
    18→export function PaymentForm({ onSuccess, onCancel, amount }: PaymentFormProps) {
    19→  const stripe = useStripe();
    20→  const elements = useElements();
    21→  const [isLoading, setIsLoading] = useState(false);
    22→  const [errorMessage, setErrorMessage] = useState<string | null>(null);
    23→
    24→  const handleSubmit = async (e: React.FormEvent) => {
    25→    e.preventDefault();
    26→
    27→    if (!stripe || !elements) {
    28→      return;
    29→    }
    30→
    31→    setIsLoading(true);
    32→    setErrorMessage(null);
    33→
    34→    try {
    35→      const { error, paymentIntent } = await stripe.confirmPayment({
    36→        elements,
    37→        confirmParams: {
    38→          return_url: `${window.location.origin}/contractor/payment-success`,
    39→        },
    40→        redirect: 'if_required',
    41→      });
    42→
    43→      if (error) {
    44→        setErrorMessage(error.message || 'Payment failed. Please try again.');
    45→      } else if (paymentIntent && paymentIntent.status === 'succeeded') {
    46→        onSuccess();
    47→      } else if (paymentIntent && paymentIntent.status === 'requires_action') {
    48→        // 3D Secure or other authentication required - Stripe handles this automatically
    49→        // The redirect above will handle it
    50→      } else {
    51→        setErrorMessage('Payment processing. Please wait...');
    52→      }
    53→    } catch (error) {
    54→      console.error('Payment error:', error);
    55→      setErrorMessage('An unexpected error occurred. Please try again.');
    56→    } finally {
    57→      setIsLoading(false);
    58→    }
    59→  };
    60→
    61→  return (
    62→    <form onSubmit={handleSubmit} className="space-y-4">
    63→

    64→

    65→

    66→
Hire Fee

    67→
${amount.toFixed(2)} AUD

    68→

    69→          <CreditCard className="h-8 w-8 text-muted-foreground" />
    70→

    71→

    72→          One-time fee to hire this worker. No additional charges.
    73→

    74→

    75→
    76→

    77→        <PaymentElement
    78→          options={{
    79→            layout: {
    80→              type: 'tabs',
    81→              defaultCollapsed: false,
    82→            },
    83→          }}
    84→        />
    85→

    86→
    87→      {errorMessage && (
    88→

    89→          {errorMessage}
    90→

    91→      )}
    92→
    93→

    94→        <Button
    95→          type="button"
    96→          variant="outline"
    97→          onClick={onCancel}
    98→          disabled={isLoading}
    99→          className="flex-1"
   100→        >
   101→          Cancel
   102→        </Button>
   103→        <Button
   104→          type="submit"
   105→          disabled={!stripe || isLoading}
   106→          className="flex-1"
   107→        >
   108→          {isLoading ? (
   109→            <>
   110→              <Loader2 className="mr-2 h-4 w-4 animate-spin" />
   111→              Processing...
   112→            </>
   113→          ) : (
   114→            `Pay $${amount.toFixed(2)}`
   115→          )}
   116→        </Button>
   117→

   118→
   119→

   120→        Secured by Stripe. We never store your card details.
   121→

   122→    </form>
   123→  );
   124→}
   125→

<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, useCallback } from 'react';
     4→import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog';
     5→import { StripeProvider } from './StripeProvider';
     6→import { PaymentForm } from './PaymentForm';
     7→import { CheckCircle2, Loader2, AlertCircle } from 'lucide-react';
     8→import { createClient } from '@/lib/supabase/client';
     9→
    10→interface HirePaymentModalProps {
    11→  isOpen: boolean;
    12→  onClose: () => void;
    13→  onSuccess: () => void;
    14→  matchId: string;
    15→  companyId: string;
    16→  amount?: number;
    17→  workerName?: string;
    18→}
    19→
    20→type PaymentState =
    21→  | { type: 'initial' }
    22→  | { type: 'loading' }
    23→  | { type: 'ready'; clientSecret: string; publishableKey: string }
    24→  | { type: 'processing' }
    25→  | { type: 'success' }
    26→  | { type: 'error'; message: string };
    27→
    28→export function HirePaymentModal({
    29→  isOpen,
    30→  onClose,
    31→  onSuccess,
    32→  matchId,
    33→  companyId,
    34→  amount = 50.0,
    35→  workerName,
    36→}: HirePaymentModalProps) {
    37→  const [paymentState, setPaymentState] = useState<PaymentState>({ type: 'initial' });
    38→  const supabase = createClient();
    39→
    40→  const handleOpen = useCallback(async () => {
    41→    if (paymentState.type !== 'initial') return;
    42→
    43→    setPaymentState({ type: 'loading' });
    44→
    45→    try {
    46→      const response = await fetch('/api/payments/create', {
    47→        method: 'POST',
    48→        headers: { 'Content-Type': 'application/json' },
    49→        body: JSON.stringify({
    50→          match_id: matchId,
    51→          company_id: companyId,
    52→          amount,
    53→        }),
    54→      });
    55→
    56→      const data = await response.json();
    57→
    58→      if (!response.ok) {
    59→        throw new Error(data.error || 'Failed to initialize payment');
    60→      }
    61→
    62→      if (!data.clientSecret || !data.publishableKey) {
    63→        throw new Error('Invalid payment configuration');
    64→      }
    65→
    66→      setPaymentState({
    67→        type: 'ready',
    68→        clientSecret: data.clientSecret,
    69→        publishableKey: data.publishableKey,
    70→      });
    71→    } catch (error) {
    72→      console.error('Payment initialization error:', error);
    73→      setPaymentState({
    74→        type: 'error',
    75→        message: error instanceof Error ? error.message : 'Failed to initialize payment',
    76→      });
    77→    }
    78→  }, [matchId, companyId, amount, paymentState.type]);
    79→
    80→  // Initialize payment when modal opens
    81→  if (isOpen && paymentState.type === 'initial') {
    82→    handleOpen();
    83→  }
    84→
    85→  const handlePaymentSuccess = () => {
    86→    setPaymentState({ type: 'success' });
    87→    // Call the onSuccess callback after a brief delay to show success state
    88→    setTimeout(() => {
    89→      onSuccess();
    90→      // Reset state for next time
    91→      setPaymentState({ type: 'initial' });
    92→    }, 2000);
    93→  };
    94→
    95→  const handleCancel = () => {
    96→    onClose();
    97→    // Reset state after a delay
    98→    setTimeout(() => setPaymentState({ type: 'initial' }), 300);
    99→  };
   100→
   101→  const renderContent = () => {
   102→    switch (paymentState.type) {
   103→      case 'initial':
   104→      case 'loading':
   105→        return (
   106→

   107→            <Loader2 className="h-8 w-8 animate-spin text-primary" />
   108→
Initializing payment...

   109→

   110→        );
   111→
   112→      case 'ready':
   113→        return (
   114→          <StripeProvider
   115→            publishableKey={paymentState.publishableKey}
   116→            clientSecret={paymentState.clientSecret}
   117→          >
   118→            <PaymentForm
   119→              onSuccess={handlePaymentSuccess}
   120→              onCancel={handleCancel}
   121→              amount={amount}
   122→            />
   123→          </StripeProvider>
   124→        );
   125→
   126→      case 'processing':
   127→        return (
   128→

   129→            <Loader2 className="h-8 w-8 animate-spin text-primary" />
   130→
Processing payment...

   131→

   132→        );
   133→
   134→      case 'success':
   135→        return (
   136→

   137→

   138→              <CheckCircle2 className="h-12 w-12 text-green-600" />
   139→

   140→
Payment Successful!

   141→

   142→              The worker has been notified of your hire.
   143→

   144→

   145→        );
   146→
   147→      case 'error':
   148→        return (
   149→

   150→

   151→              <AlertCircle className="h-12 w-12 text-destructive" />
   152→

   153→
Payment Failed

   154→

   155→              {paymentState.message}
   156→

   157→            <button
   158→              onClick={() => setPaymentState({ type: 'initial' })}
   159→              className="mt-4 text-primary hover:underline"
   160→            >
   161→              Try again
   162→            </button>
   163→

   164→        );
   165→
   166→      default:
   167→        return null;
   168→    }
   169→  };
   170→
   171→  return (
   172→    <Dialog open={isOpen} onOpenChange={(open) => !open && handleCancel()}>
   173→      <DialogContent className="sm:max-w-md">
   174→        <DialogHeader>
   175→          <DialogTitle>
   176→            {paymentState.type === 'success'
   177→              ? 'Hire Confirmed'
   178→              : `Hire${workerName ? ` ${workerName}` : ''}`}
   179→          </DialogTitle>
   180→          {paymentState.type !== 'success' && paymentState.type !== 'error' && (
   181→            <DialogDescription>
   182→              Complete payment to confirm your hire.
   183→            </DialogDescription>
   184→          )}
   185→        </DialogHeader>
   186→        {renderContent()}
   187→      </DialogContent>
   188→    </Dialog>
   189→  );
   190→}
   191→

<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 { ReactNode } from 'react';
     4→import { loadStripe } from '@stripe/stripe-js';
     5→import { Elements } from '@stripe/react-stripe-js';
     6→
     7→// Load Stripe outside of component to avoid recreating on every render
     8→let stripePromise: ReturnType<typeof loadStripe> | null = null;
     9→
    10→export function getStripe(publishableKey: string) {
    11→  if (!stripePromise && publishableKey) {
    12→    stripePromise = loadStripe(publishableKey);
    13→  }
    14→  return stripePromise;
    15→}
    16→
    17→interface StripeProviderProps {
    18→  children: ReactNode;
    19→  publishableKey: string;
    20→  clientSecret: string;
    21→}
    22→
    23→export function StripeProvider({ children, publishableKey, clientSecret }: StripeProviderProps) {
    24→  const stripe = getStripe(publishableKey);
    25→
    26→  if (!stripe) {
    27→    return null;
    28→  }
    29→
    30→  const options = {
    31→    clientSecret,
    32→    appearance: {
    33→      theme: 'stripe' as const,
    34→      variables: {
    35→        colorPrimary: '#0f172a',
    36→        colorBackground: '#ffffff',
    37→        colorText: '#0f172a',
    38→        colorDanger: '#ef4444',
    39→        borderRadius: '8px',
    40→      },
    41→    },
    42→  };
    43→
    44→  return (
    45→    <Elements stripe={stripe} options={options}>
    46→      {children}
    47→    </Elements>
    48→  );
    49→}
    50→

<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 read the key pages for each flow:


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 { Input } from "@/components/ui/input";
     9→import { Label } from "@/components/ui/label";
    10→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
    11→
    12→export default function LoginPage() {
    13→  const [email, setEmail] = useState("");
    14→  const [password, setPassword] = useState("");
    15→  const [error, setError] = useState<string | null>(null);
    16→  const [loading, setLoading] = useState(false);
    17→  const router = useRouter();
    18→  const supabase = createClient();
    19→
    20→  async function handleLogin(e: React.FormEvent) {
    21→    e.preventDefault();
    22→    setLoading(true);
    23→    setError(null);
    24→
    25→    const { data, error } = await supabase.auth.signInWithPassword({
    26→      email,
    27→      password,
    28→    });
    29→
    30→    if (error) {
    31→      setError(error.message);
    32→      setLoading(false);
    33→      return;
    34→    }
    35→
    36→    // Check if email is verified before allowing dashboard access
    37→    if (data.user && !data.user.email_confirmed_at) {
    38→      router.push("/auth/verify-email");
    39→      router.refresh();
    40→      return;
    41→    }
    42→
    43→    router.push("/dashboard");
    44→    router.refresh();
    45→  }
    46→
    47→  return (
    48→

    49→      <Card className="w-full max-w-md">
    50→        <CardHeader className="text-center">
    51→          <CardTitle className="text-2xl font-bold">Welcome back</CardTitle>
    52→          <CardDescription>Sign in to your RateRight account</CardDescription>
    53→        </CardHeader>
    54→        <CardContent>
    55→          <form onSubmit={handleLogin} className="space-y-4">
    56→

    57→              Email
    58→              <Input
    59→                id="email"
    60→                type="email"
    61→                placeholder="you@example.com"
    62→                value={email}
    63→                onChange={(e) => setEmail(e.target.value)}
    64→                required
    65→                autoComplete="email"
    66→                className="h-12 text-base"
    67→              />
    68→

    69→

    70→              Password
    71→              <Input
    72→                id="password"
    73→                type="password"
    74→                placeholder="Your password"
    75→                value={password}
    76→                onChange={(e) => setPassword(e.target.value)}
    77→                required
    78→                autoComplete="current-password"
    79→                className="h-12 text-base"
    80→              />
    81→

    82→            {error && (
    83→

    84→
{error}

    85→

    86→            )}
    87→            <Button type="submit" className="w-full h-12 text-base font-semibold" disabled={loading}>
    88→              {loading ? "Signing in..." : "Sign in"}
    89→            </Button>
    90→          </form>
    91→

    92→            Don't have an account?{" "}
    93→            <Link href="/auth/signup" className="text-primary underline hover:no-underline">
    94→              Sign up
    95→            </Link>
    96→

    97→        </CardContent>
    98→      </Card>
    99→

   100→  );
   101→}
   102→

<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→import { useState } from "react";
     3→import { useRouter } from "next/navigation";
     4→import Link from "next/link";
     5→import { createClient } from "@/lib/supabase/client";
     6→import { Button } from "@/components/ui/button";
     7→import { Input } from "@/components/ui/input";
     8→import { Label } from "@/components/ui/label";
     9→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
    10→import { HardHat, Building2 } from "lucide-react";
    11→import type { UserType } from "@/lib/types";
    12→
    13→export default function SignupPage() {
    14→  const [email, setEmail] = useState("");
    15→  const [password, setPassword] = useState("");
    16→  const [userType, setUserType] = useState<UserType | null>(null);
    17→  const [agreedToTerms, setAgreedToTerms] = useState(false);
    18→  const [error, setError] = useState<string | null>(null);
    19→  const [loading, setLoading] = useState(false);
    20→  const router = useRouter();
    21→  const supabase = createClient();
    22→
    23→  async function handleSignup(e: React.FormEvent) {
    24→    e.preventDefault();
    25→    if (!userType) {
    26→      setError("Please select whether you're hiring or looking for work");
    27→      return;
    28→    }
    29→    if (!agreedToTerms) {
    30→      setError("Please agree to the Terms of Service and Privacy Policy");
    31→      return;
    32→    }
    33→
    34→    setLoading(true);
    35→    setError(null);
    36→
    37→    const { error } = await supabase.auth.signUp({
    38→      email,
    39→      password,
    40→      options: {
    41→        data: { type: userType },
    42→        emailRedirectTo: `${window.location.origin}/auth/callback`,
    43→      },
    44→    });
    45→
    46→    if (error) {
    47→      setError(error.message);
    48→      setLoading(false);
    49→      return;
    50→    }
    51→
    52→    // Redirect to email verification page instead of dashboard
    53→    router.push("/auth/verify-email");
    54→    router.refresh();
    55→  }
    56→
    57→  return (
    58→

    59→      <Card className="w-full max-w-md">
    60→        <CardHeader className="text-center">
    61→          <CardTitle className="text-2xl font-bold">Create your account</CardTitle>
    62→          <CardDescription>
    63→            Sign up to find work or hire workers
    64→          </CardDescription>
    65→        </CardHeader>
    66→        <CardContent>
    67→          <form onSubmit={handleSignup} className="space-y-4">
    68→

    69→

    70→                Email
    71→                <Input
    72→                  id="email"
    73→                  type="email"
    74→                  value={email}
    75→                  onChange={(e) => setEmail(e.target.value)}
    76→                  required
    77→                  placeholder="you@example.com"
    78→                  className="h-12 text-base"
    79→                />
    80→

    81→

    82→                Password
    83→                <Input
    84→                  id="password"
    85→                  type="password"
    86→                  value={password}
    87→                  onChange={(e) => setPassword(e.target.value)}
    88→                  required
    89→                  placeholder="Create a password"
    90→                  minLength={6}
    91→                  className="h-12 text-base"
    92→                />
    93→

    94→

    95→
    96→

    97→              I am a...
    98→

    99→                <Button
   100→                  type="button"
   101→                  variant={userType === "contractor" ? "default" : "outline"}
   102→                  onClick={() => setUserType("contractor")}
   103→                  className="h-24 flex-col gap-2"
   104→                >
   105→                  <Building2 className="h-6 w-6" />
   106→                  Contractor
   107→                  Hiring workers
   108→                </Button>
   109→                <Button
   110→                  type="button"
   111→                  variant={userType === "worker" ? "default" : "outline"}
   112→                  onClick={() => setUserType("worker")}
   113→                  className="h-24 flex-col gap-2"
   114→                >
   115→                  <HardHat className="h-6 w-6" />
   116→                  Worker
   117→                  Looking for work
   118→                </Button>
   119→

   120→

   121→
   122→

   123→              <input
   124→                type="checkbox"
   125→                id="terms"
   126→                checked={agreedToTerms}
   127→                onChange={(e) => setAgreedToTerms(e.target.checked)}
   128→                className="mt-1 h-4 w-4 rounded border-border accent-primary"
   129→              />
   130→
   131→                I agree to the{" "}
   132→                <Link href="/legal/terms-of-service" className="text-primary underline hover:no-underline" target="_blank">
   133→                  Terms of Service
   134→                </Link>{" "}
   135→                and{" "}
   136→                <Link href="/legal/privacy-policy" className="text-primary underline hover:no-underline" target="_blank">
   137→                  Privacy Policy
   138→                </Link>
   139→
   140→

   141→
   142→            {error && (
   143→

   144→
{error}

   145→

   146→            )}
   147→
   148→            <Button type="submit" className="w-full h-12 text-base font-semibold" disabled={loading || !agreedToTerms}>
   149→              {loading ? "Creating account..." : "Create account"}
   150→            </Button>
   151→
   152→

   153→              Already have an account?{" "}
   154→              <Link href="/auth/login" className="text-primary underline hover:no-underline">
   155→                Sign in
   156→              </Link>
   157→

   158→          </form>
   159→        </CardContent>
   160→      </Card>
   161→

   162→  );
   163→}
   164→

<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, useEffect } from "react";
     4→import { useRouter } from "next/navigation";
     5→import { createClient } from "@/lib/supabase/client";
     6→import { Button } from "@/components/ui/button";
     7→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
     8→import { Mail, CheckCircle2, Clock } from "lucide-react";
     9→
    10→export default function VerifyEmailPage() {
    11→  const [emailSent, setEmailSent] = useState(false);
    12→  const [loading, setLoading] = useState(false);
    13→  const [error, setError] = useState<string | null>(null);
    14→  const router = useRouter();
    15→  const supabase = createClient();
    16→
    17→  useEffect(() => {
    18→    // Check if user is already logged in and email is verified
    19→    checkEmailStatus();
    20→  }, []);
    21→
    22→  async function checkEmailStatus() {
    23→    const { data: { user } } = await supabase.auth.getUser();
    24→
    25→    if (user?.email_confirmed_at) {
    26→      // Email is already verified, redirect to dashboard
    27→      router.push('/dashboard');
    28→    }
    29→  }
    30→
    31→  async function resendVerificationEmail() {
    32→    setLoading(true);
    33→    setError(null);
    34→
    35→    try {
    36→      const { data: { user } } = await supabase.auth.getUser();
    37→
    38→      if (!user) {
    39→        setError("No user found. Please log in again.");
    40→        setLoading(false);
    41→        return;
    42→      }
    43→
    44→      const { error } = await supabase.auth.resend({
    45→        type: 'signup',
    46→        email: user.email as string,
    47→      });
    48→
    49→      if (error) {
    50→        setError(error.message);
    51→      } else {
    52→        setEmailSent(true);
    53→      }
    54→    } catch (error) {
    55→      setError("Failed to resend verification email.");
    56→    } finally {
    57→      setLoading(false);
    58→    }
    59→  }
    60→
    61→  return (
    62→

    63→      <Card className="w-full max-w-md">
    64→        <CardHeader className="text-center">
    65→

    66→            <Mail className="h-6 w-6 text-primary" />
    67→

    68→          <CardTitle className="text-2xl font-bold">
    69→            Check your email
    70→          </CardTitle>
    71→          <CardDescription className="mt-2">
    72→            We've sent a verification email to your address. Please check your inbox and click the link to verify.
    73→          </CardDescription>
    74→        </CardHeader>
    75→        <CardContent className="space-y-4">
    76→

    77→

    78→              <Clock className="h-5 w-5 text-primary shrink-0 mt-0.5" />
    79→

    80→
Didn't receive the email?

    81→

    82→                  Check your spam folder or click the button below to resend.
    83→

    84→

    85→

    86→

    87→
    88→          {error && (
    89→

    90→
{error}

    91→

    92→          )}
    93→
    94→          {emailSent && (
    95→

    96→

    97→                <CheckCircle2 className="h-4 w-4 text-green-600 shrink-0" />
    98→

    99→                  Verification email resent. Please check your inbox.
   100→

   101→

   102→

   103→          )}
   104→
   105→

   106→            <Button
   107→              onClick={resendVerificationEmail}
   108→              disabled={loading || emailSent}
   109→              className="w-full h-12 text-base font-semibold"
   110→              variant="outline"
   111→            >
   112→              {loading ? "Sending..." : emailSent ? "Email sent" : "Resend verification email"}
   113→            </Button>
   114→
   115→            <Button
   116→              onClick={() => router.push('/auth/login')}
   117→              variant="ghost"
   118→              className="w-full h-12 text-base"
   119→            >
   120→              Back to login
   121→            </Button>
   122→

   123→        </CardContent>
   124→      </Card>
   125→

   126→  );
   127→}
   128→

<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→import { redirect } from "next/navigation";
     2→import Link from "next/link";
     3→import { createClient } from "@/lib/supabase/server";
     4→import { Button } from "@/components/ui/button";
     5→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
     6→import { Badge } from "@/components/ui/badge";
     7→import { Building2, HardHat, Plus, Users } from "lucide-react";
     8→import { SignOutButton } from "@/components/sign-out-button";
     9→import { BottomNavWrapper } from "@/components/bottom-nav-wrapper";
    10→
    11→export default async function DashboardPage() {
    12→  const supabase = await createClient();
    13→
    14→  const {
    15→    data: { user },
    16→  } = await supabase.auth.getUser();
    17→
    18→  if (!user) {
    19→    redirect("/auth/login");
    20→  }
    21→
    22→  const { data: profile } = await supabase
    23→    .from("profiles")
    24→    .select("*")
    25→    .eq("id", user.id)
    26→    .single();
    27→
    28→  const isContractor = profile?.type === "contractor";
    29→
    30→  // Fetch stats
    31→  let jobCount = 0;
    32→  let matchCount = 0;
    33→
    34→  if (isContractor) {
    35→    const { data: company } = await supabase
    36→      .from("companies")
    37→      .select("id")
    38→      .eq("profile_id", user.id)
    39→      .single();
    40→
    41→    if (company) {
    42→      const { count } = await supabase
    43→        .from("jobs")
    44→        .select("*", { count: "exact", head: true })
    45→        .eq("company_id", company.id)
    46→        .eq("status", "active");
    47→      jobCount = count || 0;
    48→
    49→      const { count: mCount } = await supabase
    50→        .from("matches")
    51→        .select("*, jobs!inner(company_id)", { count: "exact", head: true })
    52→        .eq("jobs.company_id", company.id)
    53→        .in("status", ["applied", "suggested"]);
    54→      matchCount = mCount || 0;
    55→    }
    56→  }
    57→
    58→  return (
    59→

    60→
    61→

    62→          G'day{profile?.name ? `, ${profile.name}` : ""}!
    63→

    64→

    65→          {isContractor
    66→            ? "Ready to find your next worker?"
    67→            : "Let's get you on a job."}
    68→

    69→
    70→

    71→          {isContractor ? (
    72→            <>
    73→              {/* Quick stats */}
    74→

    75→                <Card>
    76→                  <CardContent className="flex flex-col items-center p-4">
    77→                    {jobCount}
    78→                    Active Jobs
    79→                  </CardContent>
    80→                </Card>
    81→                <Card>
    82→                  <CardContent className="flex flex-col items-center p-4">
    83→                    {matchCount}
    84→                    New Matches
    85→                  </CardContent>
    86→                </Card>
    87→

    88→
    89→              {/* Post a Job CTA */}
    90→              <Card className="border-primary/30 bg-primary/5">
    91→                <CardHeader className="pb-3">
    92→                  <CardTitle className="flex items-center gap-2 text-lg">
    93→                    <Plus className="h-6 w-6" />
    94→                    Post a Job
    95→                  </CardTitle>
    96→                  <CardDescription className="text-base">
    97→                    AI writes the post. You just tap.
    98→                  </CardDescription>
    99→                </CardHeader>
   100→                <CardContent>
   101→                  <Button asChild className="w-full h-16 text-lg font-semibold">
   102→                    <Link href="/contractor/post-job">Post a Job</Link>
   103→                  </Button>
   104→                </CardContent>
   105→              </Card>
   106→
   107→              {/* View Matches */}
   108→              {matchCount > 0 && (
   109→                <Card>
   110→                  <CardHeader className="pb-3">
   111→                    <CardTitle className="flex items-center gap-2 text-lg">
   112→                      <Users className="h-6 w-6" />
   113→                      {matchCount} Worker{matchCount > 1 ? "s" : ""} Matched
   114→                    </CardTitle>
   115→                    <CardDescription className="text-base">
   116→                      Workers have applied to your jobs
   117→                    </CardDescription>
   118→                  </CardHeader>
   119→                  <CardContent>
   120→                    <Button asChild variant="outline" className="w-full h-14 text-base font-semibold">
   121→                      <Link href="/contractor/matches">View Matches</Link>
   122→                    </Button>
   123→                  </CardContent>
   124→                </Card>
   125→              )}
   126→
   127→              {/* Company profile */}
   128→              <Card>
   129→                <CardHeader className="pb-3">
   130→                  <CardTitle className="flex items-center gap-2 text-lg">
   131→                    <Building2 className="h-6 w-6" />
   132→                    Company Profile
   133→                  </CardTitle>
   134→                  <CardDescription className="text-base">
   135→                    Manage your company details
   136→                  </CardDescription>
   137→                </CardHeader>
   138→                <CardContent>
   139→                  <Button asChild variant="outline" className="w-full h-14 text-base">
   140→                    <Link href="/contractor/signup">Edit Profile</Link>
   141→                  </Button>
   142→                </CardContent>
   143→              </Card>
   144→            </>
   145→          ) : (
   146→            <>
   147→              {/* Browse Jobs CTA */}
   148→              <Card className="border-primary/30 bg-primary/5">
   149→                <CardHeader className="pb-3">
   150→                  <CardTitle className="flex items-center gap-2 text-lg">
   151→                    <HardHat className="h-6 w-6" />
   152→                    Find Work
   153→                  </CardTitle>
   154→                  <CardDescription className="text-base">
   155→                    Browse available construction jobs near you
   156→                  </CardDescription>
   157→                </CardHeader>
   158→                <CardContent>
   159→                  <Button asChild className="w-full h-16 text-lg font-semibold">
   160→                    <Link href="/worker/jobs">Browse Jobs</Link>
   161→                  </Button>
   162→                </CardContent>
   163→              </Card>
   164→
   165→              {/* Profile */}
   166→              <Card>
   167→                <CardHeader className="pb-3">
   168→                  <CardTitle className="flex items-center gap-2 text-lg">
   169→                    <HardHat className="h-6 w-6" />
   170→                    Your Profile
   171→                  </CardTitle>
   172→                  <CardDescription className="text-base">
   173→                    A strong profile gets you more matches
   174→                  </CardDescription>
   175→                </CardHeader>
   176→                <CardContent>
   177→                  <Button asChild variant="outline" className="w-full h-14 text-base">
   178→                    <Link href="/worker/profile/build">Edit Profile</Link>
   179→                  </Button>
   180→                </CardContent>
   181→              </Card>
   182→            </>
   183→          )}
   184→

   185→
   186→
   187→      <BottomNavWrapper userType={profile?.type || "worker"} />
   188→

   189→  );
   190→}
   191→

<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, useEffect } from "react";
     4→import { useRouter } from "next/navigation";
     5→import Link from "next/link";
     6→import Image from "next/image";
     7→import toast from "react-hot-toast";
     8→import { createClient } from "@/lib/supabase/client";
     9→import { Button } from "@/components/ui/button";
    10→import { Input } from "@/components/ui/input";
    11→import { Label } from "@/components/ui/label";
    12→import { Card, CardContent } from "@/components/ui/card";
    13→import {
    14→  HardHat,
    15→  Keyboard,
    16→  Mic,
    17→  ArrowLeft,
    18→  ArrowRight,
    19→  Loader2,
    20→  CheckCircle2,
    21→  Sparkles,
    22→  MapPin,
    23→  Clock,
    24→  Shield,
    25→  Edit3,
    26→  Wrench,
    27→  Calendar,
    28→  Globe2,
    29→} from "lucide-react";
    30→
    31→type Step = "auth" | "method" | "trade" | "experience" | "location" | "availability" | "certs" | "review";
    32→
    33→const TRADES = [
    34→  "Labourer",
    35→  "Steel Fixer",
    36→  "Formworker",
    37→  "Carpenter",
    38→  "Electrician",
    39→  "Plumber",
    40→  "Concreter",
    41→  "Bricklayer",
    42→  "Painter",
    43→  "Roofer",
    44→  "Scaffolder",
    45→  "Tiler",
    46→  "Plasterer",
    47→  "Welder",
    48→  "Crane Operator",
    49→  "Excavator Operator",
    50→];
    51→
    52→const EXPERIENCE_OPTIONS = [
    53→  { label: "Less than 1 year", value: 0 },
    54→  { label: "1-3 years", value: 2 },
    55→  { label: "3-5 years", value: 4 },
    56→  { label: "5-10 years", value: 7 },
    57→  { label: "10+ years", value: 12 },
    58→];
    59→
    60→const AVAILABILITY_OPTIONS = [
    61→  { label: "Full-time", value: "full-time" as const, desc: "Mon-Fri, regular hours" },
    62→  { label: "Casual", value: "casual" as const, desc: "As needed, flexible" },
    63→  { label: "Weekends", value: "weekends" as const, desc: "Saturday & Sunday" },
    64→  { label: "Flexible", value: "flexible" as const, desc: "Any time, any days" },
    65→];
    66→
    67→const CERT_OPTIONS = [
    68→  "White Card",
    69→  "Forklift Licence",
    70→  "EWP (Elevated Work Platform)",
    71→  "Working at Heights",
    72→  "First Aid",
    73→  "Traffic Control",
    74→  "Rigging",
    75→  "Dogging",
    76→  "Confined Spaces",
    77→];
    78→
    79→interface WorkerData {
    80→  trades: string[];
    81→  experience: number;
    82→  suburb: string;
    83→  availability: "full-time" | "casual" | "weekends" | "flexible";
    84→  certifications: string[];
    85→}
    86→
    87→function generateBio(data: WorkerData): string {
    88→  const tradeStr = data.trades.join(" and ");
    89→  const expStr =
    90→    data.experience === 0
    91→      ? "under 1 year"
    92→      : data.experience >= 12
    93→        ? "10+ years"
    94→        : `${data.experience} years`;
    95→  const certStr =
    96→    data.certifications.length > 0
    97→      ? ` Holds ${data.certifications.join(", ")}.`
    98→      : "";
    99→
   100→  return `Experienced ${tradeStr.toLowerCase()} with ${expStr} in the construction industry. Based in ${data.suburb}, available for ${data.availability} work.${certStr}`;
   101→}
   102→
   103→export default function WorkerSignupPage() {
   104→  const [step, setStep] = useState<Step>("auth");
   105→  const [isAuthenticated, setIsAuthenticated] = useState(false);
   106→  const [loading, setLoading] = useState(true);
   107→
   108→  // Auth state
   109→  const [email, setEmail] = useState("");
   110→  const [password, setPassword] = useState("");
   111→  const [authError, setAuthError] = useState<string | null>(null);
   112→  const [authLoading, setAuthLoading] = useState(false);
   113→
   114→  // Worker data
   115→  const [selectedTrades, setSelectedTrades] = useState<string[]>([]);
   116→  const [experience, setExperience] = useState<number | null>(null);
   117→  const [suburb, setSuburb] = useState("");
   118→  const [availability, setAvailability] = useState<WorkerData["availability"] | null>(null);
   119→  const [certifications, setCertifications] = useState<string[]>([]);
   120→
   121→  const [saving, setSaving] = useState(false);
   122→
   123→  const router = useRouter();
   124→  const supabase = createClient();
   125→
   126→  useEffect(() => {
   127→    async function checkAuth() {
   128→      const {
   129→        data: { user },
   130→      } = await supabase.auth.getUser();
   131→      if (user) {
   132→        setIsAuthenticated(true);
   133→        setStep("method");
   134→      }
   135→      setLoading(false);
   136→    }
   137→    checkAuth();
   138→  }, [supabase.auth]);
   139→
   140→  async function handleAuth(e: React.FormEvent) {
   141→    e.preventDefault();
   142→    setAuthLoading(true);
   143→    setAuthError(null);
   144→
   145→    const { error } = await supabase.auth.signUp({
   146→      email,
   147→      password,
   148→      options: {
   149→        data: { type: "worker" },
   150→        emailRedirectTo: `${window.location.origin}/auth/callback`,
   151→      },
   152→    });
   153→
   154→    if (error) {
   155→      if (error.message.includes("already registered")) {
   156→        const { error: loginError } = await supabase.auth.signInWithPassword({
   157→          email,
   158→          password,
   159→        });
   160→        if (loginError) {
   161→          setAuthError(loginError.message);
   162→          setAuthLoading(false);
   163→          return;
   164→        }
   165→      } else {
   166→        setAuthError(error.message);
   167→        setAuthLoading(false);
   168→        return;
   169→      }
   170→    }
   171→
   172→    setIsAuthenticated(true);
   173→    setStep("method");
   174→    setAuthLoading(false);
   175→  }
   176→
   177→  function toggleTrade(trade: string) {
   178→    setSelectedTrades((prev) =>
   179→      prev.includes(trade) ? prev.filter((t) => t !== trade) : [...prev, trade]
   180→    );
   181→  }
   182→
   183→  function toggleCert(cert: string) {
   184→    setCertifications((prev) =>
   185→      prev.includes(cert) ? prev.filter((c) => c !== cert) : [...prev, cert]
   186→    );
   187→  }
   188→
   189→  async function handleConfirm() {
   190→    if (!availability || experience === null) return;
   191→    setSaving(true);
   192→
   193→    try {
   194→      const {
   195→        data: { user },
   196→      } = await supabase.auth.getUser();
   197→      if (!user) {
   198→        toast.error("Not logged in. Please sign in again.");
   199→        setSaving(false);
   200→        return;
   201→      }
   202→
   203→      const workerData: WorkerData = {
   204→        trades: selectedTrades,
   205→        experience,
   206→        suburb,
   207→        availability,
   208→        certifications,
   209→      };
   210→
   211→      const bio = generateBio(workerData);
   212→
   213→      console.log("Updating profile...", { userId: user.id, suburb });
   214→      const profileResult = await supabase.from("profiles").update({
   215→        name: user.email?.split("@")[0] ?? "",
   216→        suburb,
   217→        onboarding_completed: true,
   218→      }).eq("id", user.id);
   219→
   220→      if (profileResult.error) {
   221→        console.error("Profile update failed:", profileResult.error);
   222→        toast.error(`Profile update failed: ${profileResult.error.message}`);
   223→        setSaving(false);
   224→        return;
   225→      }
   226→
   227→      console.log("Inserting worker profile...", { profile_id: user.id });
   228→      const workerResult = await supabase.from("worker_profiles").insert({
   229→        profile_id: user.id,
   230→        trades: selectedTrades,
   231→        experience_years: experience,
   232→        availability,
   233→        certifications,
   234→        bio,
   235→        ai_extracted: {
   236→          source: "typed_answers",
   237→          created_at: new Date().toISOString(),
   238→        },
   239→      });
   240→
   241→      if (workerResult.error) {
   242→        console.error("Worker profile insert failed:", workerResult.error);
   243→        toast.error(`Worker profile creation failed: ${workerResult.error.message}`);
   244→        setSaving(false);
   245→        return;
   246→      }
   247→
   248→      console.log("Profile created successfully!");
   249→      router.push("/dashboard");
   250→      router.refresh();
   251→    } catch (error: any) {
   252→      console.error("Signup error:", error);
   253→      toast.error(`Something went wrong: ${error.message || "Unknown error"}`);
   254→      setSaving(false);
   255→    }
   256→  }
   257→
   258→  const workerData: WorkerData | null =
   259→    selectedTrades.length > 0 && experience !== null && suburb && availability
   260→      ? { trades: selectedTrades, experience, suburb, availability, certifications }
   261→      : null;
   262→
   263→  const stepOrder: Step[] = ["auth", "method", "trade", "experience", "location", "availability", "certs", "review"];
   264→  const activeSteps = isAuthenticated ? stepOrder.slice(1) : stepOrder;
   265→  const currentIndex = activeSteps.indexOf(step);
   266→  const totalSteps = activeSteps.length;
   267→
   268→  if (loading) {
   269→    return (
   270→

   271→        <Loader2 className="h-8 w-8 animate-spin text-primary" />
   272→

   273→    );
   274→  }
   275→
   276→  return (
   277→

   278→      {/* Header */}
   279→
   280→

   281→          <Link href="/" className="flex items-center gap-2">
   282→            <Image src="/logo_black.svg" alt="RateRight" width={120} height={34} className="h-7 w-auto" />
   283→          </Link>
   284→

   285→
   286→
   287→
   288→        {/* Progress bar */}
   289→

   290→          {Array.from({ length: totalSteps }, (_, i) => (
   291→            <div
   292→              key={i}
   293→              className={`h-1.5 flex-1 rounded-full transition-colors ${
   294→                i <= currentIndex ? "bg-primary" : "bg-muted"
   295→              }`}
   296→            />
   297→          ))}
   298→

   299→
   300→        {/* Step: Auth */}
   301→        {step === "auth" && (
   302→

   303→

   304→

   305→                <HardHat className="h-5 w-5" />
   306→

   307→

   308→
Create your account

   309→

   310→                  Always free for workers
   311→

   312→

   313→

   314→
   315→            <form onSubmit={handleAuth} className="mt-6 space-y-4">
   316→

   317→                Email
   318→                <Input
   319→                  id="email"
   320→                  type="email"
   321→                  placeholder="you@email.com"
   322→                  value={email}
   323→                  onChange={(e) => setEmail(e.target.value)}
   324→                  required
   325→                  autoComplete="email"
   326→                  className="h-12 text-base"
   327→                />
   328→

   329→

   330→                Password
   331→                <Input
   332→                  id="password"
   333→                  type="password"
   334→                  placeholder="Create a password"
   335→                  value={password}
   336→                  onChange={(e) => setPassword(e.target.value)}
   337→                  required
   338→                  minLength={6}
   339→                  autoComplete="new-password"
   340→                  className="h-12 text-base"
   341→                />
   342→

   343→
   344→              {authError && (
   345→
{authError}

   346→              )}
   347→
   348→              <Button
   349→                type="submit"
   350→                className="h-12 w-full gap-2 text-base font-semibold"
   351→                disabled={authLoading}
   352→              >
   353→                {authLoading ? (
   354→                  <>
   355→                    <Loader2 className="h-5 w-5 animate-spin" />
   356→                    Creating account...
   357→                  </>
   358→                ) : (
   359→                  <>
   360→                    Continue
   361→                    <ArrowRight className="h-5 w-5" />
   362→                  </>
   363→                )}
   364→              </Button>
   365→            </form>
   366→
   367→

   368→              Already have an account?{" "}
   369→              <Link
   370→                href="/auth/login"
   371→                className="text-primary underline hover:no-underline"
   372→              >
   373→                Sign in
   374→              </Link>
   375→

   376→

   377→        )}
   378→
   379→        {/* Step: Choose method */}
   380→        {step === "method" && (
   381→

   382→

   383→

   384→                <HardHat className="h-5 w-5" />
   385→

   386→

   387→
Build your profile

   388→

   389→                  How do you want to get started?
   390→

   391→

   392→

   393→
   394→

   395→              <button
   396→                onClick={() => setStep("trade")}
   397→                className="flex w-full items-center gap-4 rounded-xl border-2 border-primary bg-primary/5 p-5 text-left transition-colors hover:bg-primary/10"
   398→              >
   399→

   400→                  <Keyboard className="h-6 w-6" />
   401→

   402→

   403→
Type my answers

   404→

   405→                    5 quick questions, under 2 minutes
   406→

   407→

   408→                <ArrowRight className="ml-auto h-5 w-5 text-primary" />
   409→              </button>
   410→
   411→              <button
   412→                disabled
   413→                className="flex w-full items-center gap-4 rounded-xl border-2 border-muted p-5 text-left opacity-50"
   414→              >
   415→

   416→                  <Mic className="h-6 w-6" />
   417→

   418→

   419→
Send a voice note

   420→

   421→                    Coming soon — speak and AI builds your profile
   422→

   423→

   424→              </button>
   425→

   426→

   427→        )}
   428→
   429→        {/* Step: Select trade */}
   430→        {step === "trade" && (
   431→

   432→            <StepHeader
   433→              icon={<Wrench className="h-5 w-5" />}
   434→              title="What's your trade?"
   435→              subtitle="Select one or more"
   436→            />
   437→
   438→

   439→              {TRADES.map((trade) => (
   440→                <button
   441→                  key={trade}
   442→                  onClick={() => toggleTrade(trade)}
   443→                  className={`rounded-xl border-2 px-4 py-3 text-left text-sm font-medium transition-colors ${
   444→                    selectedTrades.includes(trade)
   445→                      ? "border-primary bg-primary/10 text-primary"
   446→                      : "border-border hover:border-primary/50"
   447→                  }`}
   448→                >
   449→                  {trade}
   450→                </button>
   451→              ))}
   452→

   453→
   454→

   455→              <Button
   456→                variant="outline"
   457→                onClick={() => setStep("method")}
   458→                className="h-12"
   459→              >
   460→                <ArrowLeft className="h-5 w-5" />
   461→              </Button>
   462→              <Button
   463→                onClick={() => setStep("experience")}
   464→                disabled={selectedTrades.length === 0}
   465→                className="h-12 flex-1 gap-2 text-base font-semibold"
   466→              >
   467→                Continue
   468→                <ArrowRight className="h-5 w-5" />
   469→              </Button>
   470→

   471→

   472→        )}
   473→
   474→        {/* Step: Experience */}
   475→        {step === "experience" && (
   476→

   477→            <StepHeader
   478→              icon={<Clock className="h-5 w-5" />}
   479→              title="How much experience?"
   480→              subtitle="In construction overall"
   481→            />
   482→
   483→

   484→              {EXPERIENCE_OPTIONS.map((opt) => (
   485→                <button
   486→                  key={opt.value}
   487→                  onClick={() => {
   488→                    setExperience(opt.value);
   489→                    setStep("location");
   490→                  }}
   491→                  className={`flex w-full items-center justify-between rounded-xl border-2 px-5 py-4 text-left text-base font-medium transition-colors ${
   492→                    experience === opt.value
   493→                      ? "border-primary bg-primary/10 text-primary"
   494→                      : "border-border hover:border-primary/50"
   495→                  }`}
   496→                >
   497→                  {opt.label}
   498→                  <ArrowRight className="h-4 w-4 text-muted-foreground" />
   499→                </button>
   500→              ))}
   501→

   502→
   503→            <button
   504→              onClick={() => setStep("trade")}
   505→              className="mt-4 flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground"
   506→            >
   507→              <ArrowLeft className="h-4 w-4" />
   508→              Back
   509→            </button>
   510→

   511→        )}
   512→
   513→        {/* Step: Location */}
   514→        {step === "location" && (
   515→

   516→            <StepHeader
   517→              icon={<MapPin className="h-5 w-5" />}
   518→              title="Where are you based?"
   519→              subtitle="Your suburb or area"
   520→            />
   521→
   522→            <form
   523→              onSubmit={(e) => {
   524→                e.preventDefault();
   525→                if (suburb.trim()) setStep("availability");
   526→              }}
   527→              className="mt-6 space-y-4"
   528→            >
   529→              <Input
   530→                placeholder="e.g. Parramatta, Blacktown, Liverpool"
   531→                value={suburb}
   532→                onChange={(e) => setSuburb(e.target.value)}
   533→                required
   534→                autoFocus
   535→                className="h-12 text-base"
   536→              />
   537→

   538→                We'll match you to jobs near this location.
   539→

   540→
   541→

   542→                <Button
   543→                  type="button"
   544→                  variant="outline"
   545→                  onClick={() => setStep("experience")}
   546→                  className="h-12"
   547→                >
   548→                  <ArrowLeft className="h-5 w-5" />
   549→                </Button>
   550→                <Button
   551→                  type="submit"
   552→                  disabled={!suburb.trim()}
   553→                  className="h-12 flex-1 gap-2 text-base font-semibold"
   554→                >
   555→                  Continue
   556→                  <ArrowRight className="h-5 w-5" />
   557→                </Button>
   558→

   559→            </form>
   560→

   561→        )}
   562→
   563→        {/* Step: Availability */}
   564→        {step === "availability" && (
   565→

   566→            <StepHeader
   567→              icon={<Calendar className="h-5 w-5" />}
   568→              title="Your availability?"
   569→              subtitle="When can you work?"
   570→            />
   571→
   572→

   573→              {AVAILABILITY_OPTIONS.map((opt) => (
   574→                <button
   575→                  key={opt.value}
   576→                  onClick={() => {
   577→                    setAvailability(opt.value);
   578→                    setStep("certs");
   579→                  }}
   580→                  className={`flex w-full items-center justify-between rounded-xl border-2 px-5 py-4 text-left transition-colors ${
   581→                    availability === opt.value
   582→                      ? "border-primary bg-primary/10"
   583→                      : "border-border hover:border-primary/50"
   584→                  }`}
   585→                >
   586→

   587→                    <p className={`text-base font-medium ${availability === opt.value ? "text-primary" : ""}`}>
   588→                      {opt.label}
   589→

   590→
{opt.desc}

   591→

   592→                  <ArrowRight className="h-4 w-4 shrink-0 text-muted-foreground" />
   593→                </button>
   594→              ))}
   595→

   596→
   597→            <button
   598→              onClick={() => setStep("location")}
   599→              className="mt-4 flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground"
   600→            >
   601→              <ArrowLeft className="h-4 w-4" />
   602→              Back
   603→            </button>
   604→

   605→        )}
   606→
   607→        {/* Step: Certifications */}
   608→        {step === "certs" && (
   609→

   610→            <StepHeader
   611→              icon={<Shield className="h-5 w-5" />}
   612→              title="Your certifications"
   613→              subtitle="Select all that apply (optional)"
   614→            />
   615→
   616→

   617→              {CERT_OPTIONS.map((cert) => (
   618→                <button
   619→                  key={cert}
   620→                  onClick={() => toggleCert(cert)}
   621→                  className={`flex w-full items-center gap-3 rounded-xl border-2 px-5 py-3.5 text-left text-sm font-medium transition-colors ${
   622→                    certifications.includes(cert)
   623→                      ? "border-primary bg-primary/10 text-primary"
   624→                      : "border-border hover:border-primary/50"
   625→                  }`}
   626→                >
   627→

   634→                    {certifications.includes(cert) && (
   635→                      <CheckCircle2 className="h-3.5 w-3.5" />
   636→                    )}
   637→

   638→                  {cert}
   639→                </button>
   640→              ))}
   641→

   642→
   643→

   644→              <Button
   645→                variant="outline"
   646→                onClick={() => setStep("availability")}
   647→                className="h-12"
   648→              >
   649→                <ArrowLeft className="h-5 w-5" />
   650→              </Button>
   651→              <Button
   652→                onClick={() => setStep("review")}
   653→                className="h-12 flex-1 gap-2 text-base font-semibold"
   654→              >
   655→                Review my profile
   656→                <ArrowRight className="h-5 w-5" />
   657→              </Button>
   658→

   659→

   660→        )}
   661→
   662→        {/* Step: Review */}
   663→        {step === "review" && workerData && (
   664→

   665→

   666→

   667→                <CheckCircle2 className="h-5 w-5" />
   668→

   669→

   670→
Your profile

   671→

   672→                  Review and confirm
   673→

   674→

   675→

   676→
   677→            <Card className="mt-6">
   678→              <CardContent className="space-y-4 pt-6">
   679→                <ReviewField
   680→                  icon={<Wrench className="h-4 w-4" />}
   681→                  label="Trade"
   682→                  value={workerData.trades.join(", ")}
   683→                  onEdit={() => setStep("trade")}
   684→                />
   685→                <ReviewField
   686→                  icon={<Clock className="h-4 w-4" />}
   687→                  label="Experience"
   688→                  value={
   689→                    workerData.experience === 0
   690→                      ? "Less than 1 year"
   691→                      : workerData.experience >= 12
   692→                        ? "10+ years"
   693→                        : `${workerData.experience} years`
   694→                  }
   695→                  onEdit={() => setStep("experience")}
   696→                />
   697→                <ReviewField
   698→                  icon={<MapPin className="h-4 w-4" />}
   699→                  label="Location"
   700→                  value={workerData.suburb}
   701→                  onEdit={() => setStep("location")}
   702→                />
   703→                <ReviewField
   704→                  icon={<Calendar className="h-4 w-4" />}
   705→                  label="Availability"
   706→                  value={
   707→                    AVAILABILITY_OPTIONS.find(
   708→                      (o) => o.value === workerData.availability
   709→                    )?.label ?? workerData.availability
   710→                  }
   711→                  onEdit={() => setStep("availability")}
   712→                />
   713→                <ReviewField
   714→                  icon={<Shield className="h-4 w-4" />}
   715→                  label="Certifications"
   716→                  value={
   717→                    workerData.certifications.length > 0
   718→                      ? workerData.certifications.join(", ")
   719→                      : "None added"
   720→                  }
   721→                  onEdit={() => setStep("certs")}
   722→                />
   723→              </CardContent>
   724→            </Card>
   725→
   726→            {/* AI-generated bio */}
   727→

   728→

   729→                <Sparkles className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
   730→

   731→

   732→                    AI-generated bio
   733→

   734→

   735→                    {generateBio(workerData)}
   736→

   737→

   738→

   739→

   740→
   741→            <Button
   742→              onClick={handleConfirm}
   743→              className="mt-6 h-14 w-full gap-2 text-base font-semibold"
   744→              disabled={saving}
   745→            >
   746→              {saving ? (
   747→                <>
   748→                  <Loader2 className="h-5 w-5 animate-spin" />
   749→                  Creating profile...
   750→                </>
   751→              ) : (
   752→                <>
   753→                  <CheckCircle2 className="h-5 w-5" />
   754→                  Looks good — create my profile
   755→                </>
   756→              )}
   757→            </Button>
   758→
   759→            <button
   760→              onClick={() => setStep("certs")}
   761→              className="mt-4 flex w-full items-center justify-center gap-1 text-sm text-muted-foreground hover:text-foreground"
   762→            >
   763→              <ArrowLeft className="h-4 w-4" />
   764→              Go back
   765→            </button>
   766→

   767→        )}
   768→
   769→

   770→  );
   771→}
   772→
   773→function StepHeader({
   774→  icon,
   775→  title,
   776→  subtitle,
   777→}: {
   778→  icon: React.ReactNode;
   779→  title: string;
   780→  subtitle: string;
   781→}) {
   782→  return (
   783→

   784→

   785→        {icon}
   786→

   787→

   788→
{title}

   789→
{subtitle}

   790→

   791→

   792→  );
   793→}
   794→
   795→function ReviewField({
   796→  icon,
   797→  label,
   798→  value,
   799→  onEdit,
   800→}: {
   801→  icon: React.ReactNode;
   802→  label: string;
   803→  value: string;
   804→  onEdit: () => void;
   805→}) {
   806→  return (
   807→    <button
   808→      onClick={onEdit}
   809→      className="flex w-full items-start gap-3 rounded-lg p-2 text-left transition-colors hover:bg-muted"
   810→    >
   811→
{icon}

   812→

   813→
{label}

   814→
{value}

   815→

   816→      <Edit3 className="mt-1 h-3.5 w-3.5 shrink-0 text-muted-foreground" />
   817→    </button>
   818→  );
   819→}
   820→

<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>