Deep audit of the auth/signup flow in /home/ccuser/rateright-v2. Read and report the FULL contents of:

1. All auth-related pages: search for signup, login, sign-in, sign-up, auth pages in src/app/
2. All Supabase client files (search for createClient, supabase)
3. Any auth callback route (src/app/auth/ or api/auth/)
4. The types.ts file for database types
5. supabase/schema.sql - full contents
6. Any auth-related components in src/components/
7. Any auth utility files

Report the COMPLETE file contents for each file found - do not summarize.


I'll conduct a comprehensive audit of the auth/signup flow. Let me search for all relevant files systematically.


/home/ccuser/rateright-v2/src/app/layout.tsx
/home/ccuser/rateright-v2/src/app/(authenticated)/layout.tsx


No files found


Now let me search more broadly for Supabase and auth utilities:


/home/ccuser/rateright-v2/src/lib/utils.ts
/home/ccuser/rateright-v2/src/lib/types.ts
/home/ccuser/rateright-v2/src/lib/abn-utils.ts
/home/ccuser/rateright-v2/src/lib/supabase/client.ts
/home/ccuser/rateright-v2/src/lib/supabase/middleware.ts
/home/ccuser/rateright-v2/src/lib/supabase/server.ts
/home/ccuser/rateright-v2/src/lib/rate-limit.ts
/home/ccuser/rateright-v2/src/lib/company-utils.ts


Bash

find /home/ccuser/rateright-v2 -name "*.ts" -o -name "*.tsx" | grep -E "(supabase|auth)" | head -20


/home/ccuser/rateright-v2/src/lib/utils.ts
/home/ccuser/rateright-v2/src/middleware.ts
/home/ccuser/rateright-v2/src/lib/types.ts
/home/ccuser/rateright-v2/src/app/api/ai/generate-profile/route.ts
/home/ccuser/rateright-v2/src/app/api/abn-lookup/route.ts
/home/ccuser/rateright-v2/src/app/auth/callback/route.ts
/home/ccuser/rateright-v2/src/app/api/auth/verify-email/route.ts
/home/ccuser/rateright-v2/src/app/api/company-logo/route.ts
/home/ccuser/rateright-v2/src/lib/abn-utils.ts
/home/ccuser/rateright-v2/src/lib/supabase/client.ts
/home/ccuser/rateright-v2/src/lib/supabase/middleware.ts
/home/ccuser/rateright-v2/src/lib/supabase/server.ts
/home/ccuser/rateright-v2/src/lib/rate-limit.ts
/home/ccuser/rateright-v2/src/lib/company-utils.ts


Perfect! Now let me read all the auth-related files. I'll start with the key files:


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-sm">
    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-5">
    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-14 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-14 text-base"
    80→              />
    81→

    82→            {error && (
    83→
{error}

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

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

    95→        </CardContent>
    96→      </Card>
    97→

    98→  );
    99→}
   100→

<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 [error, setError] = useState<string | null>(null);
    18→  const [loading, setLoading] = useState(false);
    19→  const router = useRouter();
    20→  const supabase = createClient();
    21→
    22→  async function handleSignup(e: React.FormEvent) {
    23→    e.preventDefault();
    24→    if (!userType) {
    25→      setError("Please select whether you're hiring or looking for work");
    26→      return;
    27→    }
    28→
    29→    setLoading(true);
    30→    setError(null);
    31→
    32→    const { error } = await supabase.auth.signUp({
    33→      email,
    34→      password,
    35→      options: {
    36→        data: { type: userType },
    37→        emailRedirectTo: `${window.location.origin}/auth/callback`,
    38→      },
    39→    });
    40→
    41→    if (error) {
    42→      setError(error.message);
    43→      setLoading(false);
    44→      return;
    45→    }
    46→
    47→    // Redirect to email verification page instead of dashboard
    48→    router.push("/auth/verify-email");
    49→    router.refresh();
    50→  }
    51→
    52→  return (
    53→

    54→      <Card className="w-full max-w-md">
    55→        <CardHeader>
    56→          <CardTitle className="text-2xl font-bold text-center">Create your account</CardTitle>
    57→          <CardDescription className="text-center">
    58→            Sign up to find work or hire workers
    59→          </CardDescription>
    60→        </CardHeader>
    61→        <CardContent>
    62→          <form onSubmit={handleSignup} className="space-y-6">
    63→

    64→

    65→                Email address
    66→                <Input
    67→                  id="email"
    68→                  type="email"
    69→                  value={email}
    70→                  onChange={(e) => setEmail(e.target.value)}
    71→                  required
    72→                  placeholder="you@example.com"
    73→                />
    74→

    75→

    76→                Password
    77→                <Input
    78→                  id="password"
    79→                  type="password"
    80→                  value={password}
    81→                  onChange={(e) => setPassword(e.target.value)}
    82→                  required
    83→                  placeholder="••••••••"
    84→                  minLength={6}
    85→                />
    86→

    87→

    88→
    89→

    90→              I am a...
    91→

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

   113→

   114→
   115→            {error && (
   116→

   117→
{error}

   118→

   119→            )}
   120→
   121→            <Button type="submit" className="w-full" disabled={loading}>
   122→              {loading ? "Creating account..." : "Create account"}
   123→            </Button>
   124→
   125→

   126→              Already have an account?{" "}
   127→              <Link href="/auth/login" className="font-medium text-blue-600 hover:text-blue-500">
   128→                Sign in
   129→              </Link>
   130→

   131→          </form>
   132→        </CardContent>
   133→      </Card>
   134→

   135→  );
   136→}
   137→

<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 { NextResponse } from "next/server";
     2→
     3→export async function GET(request: Request) {
     4→  const { searchParams, origin } = new URL(request.url);
     5→  const code = searchParams.get("code");
     6→  const next = searchParams.get("next") ?? "/dashboard";
     7→
     8→  if (code) {
     9→    // In production, exchange code for session
    10→    // For now, redirect to dashboard
    11→    return NextResponse.redirect(`${origin}${next}`);
    12→  }
    13→
    14→  return NextResponse.redirect(`${origin}/auth/login?error=Could+not+authenticate`);
    15→}
    16→

<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 appropriate dashboard
    27→      const userType = user.user_metadata?.type;
    28→      if (userType === 'contractor') {
    29→        router.push('/contractor/dashboard');
    30→      } else if (userType === 'worker') {
    31→        router.push('/worker/dashboard');
    32→      } else {
    33→        router.push('/dashboard');
    34→      }
    35→    }
    36→  }
    37→
    38→  async function resendVerificationEmail() {
    39→    setLoading(true);
    40→    setError(null);
    41→
    42→    try {
    43→      const { data: { user } } = await supabase.auth.getUser();
    44→
    45→      if (!user) {
    46→        setError("No user found. Please log in again.");
    47→        setLoading(false);
    48→        return;
    49→      }
    50→
    51→      const { error } = await supabase.auth.resend({
    52→        type: 'signup',
    53→        email: user.email as string,
    54→      });
    55→
    56→      if (error) {
    57→        setError(error.message);
    58→      } else {
    59→        setEmailSent(true);
    60→      }
    61→    } catch (error) {
    62→      setError("Failed to resend verification email.");
    63→    } finally {
    64→      setLoading(false);
    65→    }
    66→  }
    67→
    68→  return (
    69→

    70→      <Card className="w-full max-w-md">
    71→        <CardHeader className="text-center">
    72→

    73→            <Mail className="h-6 w-6 text-blue-600" />
    74→

    75→          <CardTitle className="mt-6 text-3xl font-extrabold text-gray-900">
    76→            Check your email
    77→          </CardTitle>
    78→          <CardDescription className="mt-2 text-sm text-gray-600">
    79→            We've sent a verification email to your address. Please check your inbox and click the link to verify your email.
    80→          </CardDescription>
    81→        </CardHeader>
    82→        <CardContent className="space-y-6">
    83→

    84→

    85→              <Clock className="h-5 w-5 text-blue-400 mt-0.5" />
    86→

    87→
Didn't receive the email?

    88→

    89→
Check your spam folder or click the button below to resend the verification email.

    90→

    91→

    92→

    93→

    94→
    95→          {error && (
    96→

    97→

    98→

    99→
Error

   100→

   101→
{error}

   102→

   103→

   104→

   105→

   106→          )}
   107→
   108→          {emailSent && (
   109→

   110→

   111→                <CheckCircle2 className="h-5 w-5 text-green-400" />
   112→

   113→
Email sent!

   114→

   115→
We've resent the verification email. Please check your inbox.

   116→

   117→

   118→

   119→

   120→          )}
   121→
   122→

   123→            <Button
   124→              onClick={resendVerificationEmail}
   125→              disabled={loading || emailSent}
   126→              className="w-full"
   127→              variant="outline"
   128→            >
   129→              {loading ? "Sending..." : emailSent ? "Email sent" : "Resend verification email"}
   130→            </Button>
   131→
   132→            <Button
   133→              onClick={() => router.push('/auth/login')}
   134→              variant="ghost"
   135→              className="w-full"
   136→            >
   137→              Back to login
   138→            </Button>
   139→

   140→        </CardContent>
   141→      </Card>
   142→

   143→  );
   144→}
   145→

<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→/**
     2→ * Client-side Supabase configuration with enhanced security
     3→ */
     4→
     5→import { createClient as createSupabaseClient } from '@supabase/supabase-js'
     6→
     7→const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
     8→const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
     9→
    10→export const supabase = createSupabaseClient(supabaseUrl, supabaseAnonKey, {
    11→  auth: {
    12→    persistSession: true,
    13→    autoRefreshToken: true,
    14→    detectSessionInUrl: true,
    15→    flowType: 'pkce',
    16→  },
    17→  global: {
    18→    headers: {
    19→      'X-Client-Info': 'rateright-v2/1.0.0',
    20→    },
    21→  },
    22→})
    23→
    24→// Export createClient for backward compatibility
    25→export const createClient = () => supabase
    26→

<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→/**
     2→ * Server-side Supabase client with admin privileges
     3→ * Use for server actions, API routes, and secure operations
     4→ */
     5→
     6→import { createClient as createSupabaseClient } from '@supabase/supabase-js'
     7→
     8→// Server-side environment variables (not exposed to client)
     9→const supabaseUrl = process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL!
    10→const supabaseServiceKey = process.env.SUPABASE_SERVICE_KEY!
    11→
    12→if (!supabaseServiceKey) {
    13→  throw new Error('SUPABASE_SERVICE_KEY is required for server-side operations')
    14→}
    15→
    16→export const supabaseAdmin = createSupabaseClient(supabaseUrl, supabaseServiceKey, {
    17→  auth: {
    18→    autoRefreshToken: false,
    19→    persistSession: false,
    20→  },
    21→})
    22→
    23→// Export createClient for backward compatibility
    24→export const createClient = () => supabaseAdmin
    25→
    26→/**
    27→ * Helper to verify email addresses
    28→ */
    29→export async function verifyEmail(userId: string) {
    30→  const { data, error } = await supabaseAdmin.auth.admin.updateUserById(
    31→    userId,
    32→    { email_confirm: true }
    33→  )
    34→
    35→  if (error) throw error
    36→  return data
    37→}
    38→
    39→/**
    40→ * Helper to check if user has verified their email
    41→ */
    42→export async function isEmailVerified(userId: string): Promise<boolean> {
    43→  const { data, error } = await supabaseAdmin.auth.admin.getUserById(userId)
    44→
    45→  if (error) return false
    46→  return !!data.user?.email_confirmed_at
    47→}
    48→

<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 { createServerClient } from "@supabase/ssr";
     2→import { NextResponse, type NextRequest } from "next/server";
     3→
     4→export async function updateSession(request: NextRequest) {
     5→  let supabaseResponse = NextResponse.next({
     6→    request,
     7→  });
     8→
     9→  const supabase = createServerClient(
    10→    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    11→    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    12→    {
    13→      cookies: {
    14→        getAll() {
    15→          return request.cookies.getAll();
    16→        },
    17→        setAll(cookiesToSet) {
    18→          cookiesToSet.forEach(({ name, value }) =>
    19→            request.cookies.set(name, value)
    20→          );
    21→          supabaseResponse = NextResponse.next({
    22→            request,
    23→          });
    24→          cookiesToSet.forEach(({ name, value, options }) =>
    25→            supabaseResponse.cookies.set(name, value, options)
    26→          );
    27→        },
    28→      },
    29→    }
    30→  );
    31→
    32→  // Use getUser() — the only safe server-side auth check (validates JWT with Supabase).
    33→  // Do NOT use getSession() here — it reads unverified cookie data.
    34→  const {
    35→    data: { user },
    36→  } = await supabase.auth.getUser();
    37→
    38→  const pathname = request.nextUrl.pathname;
    39→
    40→  // Route classification
    41→  const protectedPaths = ["/dashboard", "/contractor", "/worker", "/messages", "/profile"];
    42→  const isProtected = protectedPaths.some((p) => pathname.startsWith(p));
    43→  const isSignup = pathname.includes("/signup");
    44→  const isVerifyPage = pathname === "/auth/verify-email";
    45→  const isAuthPage = pathname.startsWith("/auth/login") || pathname.startsWith("/auth/signup");
    46→
    47→  // ── Not logged in ──
    48→  if (!user) {
    49→    // Redirect unauthenticated users away from protected routes (but allow signup pages)
    50→    if (isProtected && !isSignup) {
    51→      const url = request.nextUrl.clone();
    52→      url.pathname = "/auth/login";
    53→      return NextResponse.redirect(url);
    54→    }
    55→    return supabaseResponse;
    56→  }
    57→
    58→  // ── Logged in ──
    59→  const emailVerified = !!user.email_confirmed_at;
    60→
    61→  if (!emailVerified) {
    62→    // Unverified user trying to access protected routes → send to verify page
    63→    // (but let them stay on signup flows to finish onboarding after verification)
    64→    if (isProtected && !isSignup && !isVerifyPage) {
    65→      const url = request.nextUrl.clone();
    66→      url.pathname = "/auth/verify-email";
    67→      return NextResponse.redirect(url);
    68→    }
    69→  } else {
    70→    // Verified user on auth pages → send to dashboard
    71→    if (isAuthPage || isVerifyPage) {
    72→      const url = request.nextUrl.clone();
    73→      url.pathname = "/dashboard";
    74→      return NextResponse.redirect(url);
    75→    }
    76→  }
    77→
    78→  return supabaseResponse;
    79→}
    80→

<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 { type NextRequest } from "next/server";
     2→import { updateSession } from "@/lib/supabase/middleware";
     3→
     4→export async function middleware(request: NextRequest) {
     5→  return await updateSession(request);
     6→}
     7→
     8→export const config = {
     9→  matcher: [
    10→    "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
    11→  ],
    12→};
    13→

<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 remaining files:


     1→export type UserType = "worker" | "contractor";
     2→
     3→export type Availability = "full-time" | "casual" | "weekends" | "flexible";
     4→
     5→export type RateType = "hourly" | "daily";
     6→
     7→export type JobStatus = "draft" | "active" | "filled" | "closed";
     8→
     9→export type MatchStatus =
    10→  | "suggested"
    11→  | "applied"
    12→  | "accepted"
    13→  | "rejected"
    14→  | "hired";
    15→
    16→export type ActionStatus = "pending" | "accepted" | "rejected";
    17→
    18→export type PaymentStatus = "pending" | "charged" | "refunded";
    19→
    20→export type NotificationType = "match" | "hire_request" | "hire_confirmed" | "rating" | "job_posted" | "system";
    21→
    22→export interface Profile {
    23→  id: string;
    24→  type: UserType;
    25→  name: string | null;
    26→  phone: string | null;
    27→  email: string | null;
    28→  avatar_url: string | null;
    29→  location: { lat: number; lng: number } | null;
    30→  suburb: string | null;
    31→  verified: boolean;
    32→  onboarding_completed: boolean;
    33→  created_at: string;
    34→  updated_at: string;
    35→  ai_generated: boolean;
    36→}
    37→
    38→export interface Company {
    39→  id: string;
    40→  profile_id: string;
    41→  name: string;
    42→  abn: string | null;
    43→  address: string | null;
    44→  industry: string | null;
    45→  size: string | null;
    46→  website: string | null;
    47→  logo_url: string | null;
    48→  ai_research: Record<string, unknown> | null;
    49→}
    50→
    51→export interface WorkerProfile {
    52→  id: string;
    53→  profile_id: string;
    54→  trades: string[];
    55→  experience_years: number | null;
    56→  certifications: string[];
    57→  white_card_number: string | null;
    58→  white_card_verified: boolean;
    59→  availability: Availability | null;
    60→  languages: string[];
    61→  bio: string | null;
    62→  voice_note_url: string | null;
    63→  ai_extracted: Record<string, unknown> | null;
    64→}
    65→
    66→export interface Job {
    67→  id: string;
    68→  company_id: string;
    69→  title: string;
    70→  description: string | null;
    71→  trade: string;
    72→  location: { lat: number; lng: number } | null;
    73→  suburb: string | null;
    74→  site_address: string | null;
    75→  rate_min: number | null;
    76→  rate_max: number | null;
    77→  rate_type: RateType;
    78→  start_date: string | null;
    79→  duration: string | null;
    80→  workers_needed: number;
    81→  certifications_required: string[];
    82→  status: JobStatus;
    83→  ai_generated: boolean;
    84→  created_at: string;
    85→}
    86→
    87→export interface Match {
    88→  id: string;
    89→  job_id: string;
    90→  worker_id: string;
    91→  status: MatchStatus;
    92→  match_score: number | null;
    93→  ai_summary: string | null;
    94→  contractor_action: ActionStatus;
    95→  worker_action: ActionStatus;
    96→  hired_at: string | null;
    97→  fee_charged: boolean;
    98→  created_at: string;
    99→}
   100→
   101→export interface Rating {
   102→  id: string;
   103→  match_id: string;
   104→  from_id: string;
   105→  to_id: string;
   106→  score: number;
   107→  comment: string | null;
   108→  created_at: string;
   109→}
   110→
   111→export interface Payment {
   112→  id: string;
   113→  match_id: string;
   114→  company_id: string;
   115→  amount: number;
   116→  status: PaymentStatus;
   117→  stripe_payment_id: string | null;
   118→  created_at: string;
   119→}
   120→
   121→export interface Notification {
   122→  id: string;
   123→  profile_id: string;
   124→  type: NotificationType;
   125→  title: string;
   126→  body: string | null;
   127→  data: Record<string, unknown> | null;
   128→  read: boolean;
   129→  created_at: string;
   130→}
   131→

<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→-- RateRight v2 Database Schema
     2→-- Run this in the Supabase SQL Editor
     3→
     4→-- ============================================
     5→-- ENUMS
     6→-- ============================================
     7→
     8→CREATE TYPE user_type AS ENUM ('worker', 'contractor');
     9→CREATE TYPE availability_type AS ENUM ('full-time', 'casual', 'weekends', 'flexible');
    10→CREATE TYPE rate_type AS ENUM ('hourly', 'daily');
    11→CREATE TYPE job_status AS ENUM ('draft', 'active', 'filled', 'closed');
    12→CREATE TYPE match_status AS ENUM ('suggested', 'applied', 'accepted', 'rejected', 'hired');
    13→CREATE TYPE action_status AS ENUM ('pending', 'accepted', 'rejected');
    14→CREATE TYPE payment_status AS ENUM ('pending', 'charged', 'refunded');
    15→CREATE TYPE notification_type AS ENUM ('match', 'hire_request', 'hire_confirmed', 'rating', 'job_posted', 'system');
    16→
    17→-- ============================================
    18→-- HELPER FUNCTION: auto-update updated_at
    19→-- ============================================
    20→
    21→CREATE OR REPLACE FUNCTION update_updated_at()
    22→RETURNS trigger AS $$
    23→BEGIN
    24→  NEW.updated_at = now();
    25→  RETURN NEW;
    26→END;
    27→$$ LANGUAGE plpgsql;
    28→
    29→-- ============================================
    30→-- TABLES
    31→-- ============================================
    32→
    33→-- Profiles (extends Supabase Auth)
    34→CREATE TABLE profiles (
    35→  id uuid PRIMARY KEY REFERENCES auth.users ON DELETE CASCADE,
    36→  type user_type NOT NULL,
    37→  name text,
    38→  phone text,
    39→  email text,
    40→  avatar_url text,
    41→  location point,
    42→  suburb text,
    43→  verified boolean DEFAULT false,
    44→  onboarding_completed boolean DEFAULT false,
    45→  created_at timestamptz DEFAULT now(),
    46→  updated_at timestamptz DEFAULT now(),
    47→  ai_generated boolean DEFAULT false
    48→);
    49→
    50→CREATE TRIGGER profiles_updated_at
    51→  BEFORE UPDATE ON profiles
    52→  FOR EACH ROW EXECUTE FUNCTION update_updated_at();
    53→
    54→-- Companies (contractor-specific)
    55→CREATE TABLE companies (
    56→  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    57→  profile_id uuid NOT NULL UNIQUE REFERENCES profiles ON DELETE CASCADE,
    58→  name text NOT NULL,
    59→  abn text,
    60→  address text,
    61→  industry text,
    62→  size text,
    63→  website text,
    64→  logo_url text,
    65→  ai_research jsonb,
    66→  created_at timestamptz DEFAULT now(),
    67→  updated_at timestamptz DEFAULT now()
    68→);
    69→
    70→CREATE TRIGGER companies_updated_at
    71→  BEFORE UPDATE ON companies
    72→  FOR EACH ROW EXECUTE FUNCTION update_updated_at();
    73→
    74→-- Worker profiles (worker-specific)
    75→CREATE TABLE worker_profiles (
    76→  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    77→  profile_id uuid NOT NULL UNIQUE REFERENCES profiles ON DELETE CASCADE,
    78→  trades text[] DEFAULT '{}',
    79→  experience_years integer,
    80→  certifications text[] DEFAULT '{}',
    81→  white_card_number text,
    82→  white_card_verified boolean DEFAULT false,
    83→  availability availability_type,
    84→  languages text[] DEFAULT '{}',
    85→  bio text,
    86→  voice_note_url text,
    87→  ai_extracted jsonb,
    88→  created_at timestamptz DEFAULT now(),
    89→  updated_at timestamptz DEFAULT now()
    90→);
    91→
    92→CREATE TRIGGER worker_profiles_updated_at
    93→  BEFORE UPDATE ON worker_profiles
    94→  FOR EACH ROW EXECUTE FUNCTION update_updated_at();
    95→
    96→-- Jobs
    97→CREATE TABLE jobs (
    98→  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    99→  company_id uuid NOT NULL REFERENCES companies ON DELETE CASCADE,
   100→  title text NOT NULL,
   101→  description text,
   102→  trade text NOT NULL,
   103→  location point,
   104→  suburb text,
   105→  site_address text,
   106→  rate_min decimal,
   107→  rate_max decimal,
   108→  rate_type rate_type DEFAULT 'hourly',
   109→  start_date date,
   110→  duration text,
   111→  workers_needed integer DEFAULT 1,
   112→  certifications_required text[] DEFAULT '{}',
   113→  status job_status DEFAULT 'draft',
   114→  ai_generated boolean DEFAULT false,
   115→  created_at timestamptz DEFAULT now(),
   116→  updated_at timestamptz DEFAULT now()
   117→);
   118→
   119→CREATE TRIGGER jobs_updated_at
   120→  BEFORE UPDATE ON jobs
   121→  FOR EACH ROW EXECUTE FUNCTION update_updated_at();
   122→
   123→-- Matches
   124→CREATE TABLE matches (
   125→  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
   126→  job_id uuid NOT NULL REFERENCES jobs ON DELETE CASCADE,
   127→  worker_id uuid NOT NULL REFERENCES profiles ON DELETE CASCADE,
   128→  status match_status DEFAULT 'suggested',
   129→  match_score decimal,
   130→  ai_summary text,
   131→  contractor_action action_status DEFAULT 'pending',
   132→  worker_action action_status DEFAULT 'pending',
   133→  hired_at timestamptz,
   134→  fee_charged boolean DEFAULT false,
   135→  created_at timestamptz DEFAULT now(),
   136→  updated_at timestamptz DEFAULT now(),
   137→  UNIQUE (job_id, worker_id)
   138→);
   139→
   140→CREATE TRIGGER matches_updated_at
   141→  BEFORE UPDATE ON matches
   142→  FOR EACH ROW EXECUTE FUNCTION update_updated_at();
   143→
   144→-- Ratings
   145→CREATE TABLE ratings (
   146→  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
   147→  match_id uuid NOT NULL REFERENCES matches ON DELETE CASCADE,
   148→  from_id uuid NOT NULL REFERENCES profiles ON DELETE CASCADE,
   149→  to_id uuid NOT NULL REFERENCES profiles ON DELETE CASCADE,
   150→  score integer NOT NULL CHECK (score BETWEEN 1 AND 5),
   151→  comment text,
   152→  created_at timestamptz DEFAULT now(),
   153→  UNIQUE (match_id, from_id)
   154→);
   155→
   156→-- Payments
   157→CREATE TABLE payments (
   158→  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
   159→  match_id uuid NOT NULL REFERENCES matches ON DELETE CASCADE,
   160→  company_id uuid NOT NULL REFERENCES companies ON DELETE CASCADE,
   161→  amount decimal DEFAULT 50.00,
   162→  status payment_status DEFAULT 'pending',
   163→  stripe_payment_id text,
   164→  created_at timestamptz DEFAULT now()
   165→);
   166→
   167→-- Notifications
   168→CREATE TABLE notifications (
   169→  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
   170→  profile_id uuid NOT NULL REFERENCES profiles ON DELETE CASCADE,
   171→  type notification_type NOT NULL,
   172→  title text NOT NULL,
   173→  body text,
   174→  data jsonb,
   175→  read boolean DEFAULT false,
   176→  created_at timestamptz DEFAULT now()
   177→);
   178→
   179→-- ============================================
   180→-- INDEXES
   181→-- ============================================
   182→
   183→CREATE INDEX idx_profiles_type ON profiles (type);
   184→CREATE INDEX idx_profiles_suburb ON profiles (suburb);
   185→
   186→CREATE INDEX idx_companies_profile_id ON companies (profile_id);
   187→CREATE INDEX idx_companies_abn ON companies (abn);
   188→
   189→CREATE INDEX idx_worker_profiles_profile_id ON worker_profiles (profile_id);
   190→CREATE INDEX idx_worker_profiles_trades ON worker_profiles USING gin (trades);
   191→CREATE INDEX idx_worker_profiles_availability ON worker_profiles (availability);
   192→CREATE INDEX idx_worker_profiles_certifications ON worker_profiles USING gin (certifications);
   193→
   194→CREATE INDEX idx_jobs_company_id ON jobs (company_id);
   195→CREATE INDEX idx_jobs_status ON jobs (status);
   196→CREATE INDEX idx_jobs_trade ON jobs (trade);
   197→CREATE INDEX idx_jobs_created_at ON jobs (created_at DESC);
   198→CREATE INDEX idx_jobs_suburb ON jobs (suburb);
   199→
   200→CREATE INDEX idx_matches_job_id ON matches (job_id);
   201→CREATE INDEX idx_matches_worker_id ON matches (worker_id);
   202→CREATE INDEX idx_matches_status ON matches (status);
   203→
   204→CREATE INDEX idx_ratings_to_id ON ratings (to_id);
   205→CREATE INDEX idx_ratings_from_id ON ratings (from_id);
   206→
   207→CREATE INDEX idx_payments_match_id ON payments (match_id);
   208→CREATE INDEX idx_payments_company_id ON payments (company_id);
   209→
   210→CREATE INDEX idx_notifications_profile_id ON notifications (profile_id);
   211→CREATE INDEX idx_notifications_read ON notifications (profile_id, read);
   212→
   213→-- ============================================
   214→-- ROW LEVEL SECURITY
   215→-- ============================================
   216→
   217→ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
   218→ALTER TABLE companies ENABLE ROW LEVEL SECURITY;
   219→ALTER TABLE worker_profiles ENABLE ROW LEVEL SECURITY;
   220→ALTER TABLE jobs ENABLE ROW LEVEL SECURITY;
   221→ALTER TABLE matches ENABLE ROW LEVEL SECURITY;
   222→ALTER TABLE ratings ENABLE ROW LEVEL SECURITY;
   223→ALTER TABLE payments ENABLE ROW LEVEL SECURITY;
   224→ALTER TABLE notifications ENABLE ROW LEVEL SECURITY;
   225→
   226→-- Profiles: users can read all profiles, update their own
   227→CREATE POLICY "Public profiles are viewable by everyone"
   228→  ON profiles FOR SELECT
   229→  USING (true);
   230→
   231→CREATE POLICY "Users can update their own profile"
   232→  ON profiles FOR UPDATE
   233→  USING (auth.uid() = id);
   234→
   235→CREATE POLICY "Users can insert their own profile"
   236→  ON profiles FOR INSERT
   237→  WITH CHECK (auth.uid() = id);
   238→
   239→-- Companies: viewable by everyone, editable by owner
   240→CREATE POLICY "Companies are viewable by everyone"
   241→  ON companies FOR SELECT
   242→  USING (true);
   243→
   244→CREATE POLICY "Contractors can insert their own company"
   245→  ON companies FOR INSERT
   246→  WITH CHECK (profile_id = auth.uid());
   247→
   248→CREATE POLICY "Contractors can update their own company"
   249→  ON companies FOR UPDATE
   250→  USING (profile_id = auth.uid());
   251→
   252→-- Worker profiles: viewable by everyone, editable by owner
   253→CREATE POLICY "Worker profiles are viewable by everyone"
   254→  ON worker_profiles FOR SELECT
   255→  USING (true);
   256→
   257→CREATE POLICY "Workers can insert their own profile"
   258→  ON worker_profiles FOR INSERT
   259→  WITH CHECK (profile_id = auth.uid());
   260→
   261→CREATE POLICY "Workers can update their own profile"
   262→  ON worker_profiles FOR UPDATE
   263→  USING (profile_id = auth.uid());
   264→
   265→-- Jobs: viewable by everyone, editable by company owner
   266→CREATE POLICY "Jobs are viewable by everyone"
   267→  ON jobs FOR SELECT
   268→  USING (true);
   269→
   270→CREATE POLICY "Contractors can insert jobs for their company"
   271→  ON jobs FOR INSERT
   272→  WITH CHECK (
   273→    company_id IN (
   274→      SELECT id FROM companies WHERE profile_id = auth.uid()
   275→    )
   276→  );
   277→
   278→CREATE POLICY "Contractors can update their own jobs"
   279→  ON jobs FOR UPDATE
   280→  USING (
   281→    company_id IN (
   282→      SELECT id FROM companies WHERE profile_id = auth.uid()
   283→    )
   284→  );
   285→
   286→-- Matches: viewable by involved parties
   287→CREATE POLICY "Users can view their own matches"
   288→  ON matches FOR SELECT
   289→  USING (
   290→    worker_id = auth.uid() OR
   291→    job_id IN (
   292→      SELECT j.id FROM jobs j
   293→      JOIN companies c ON j.company_id = c.id
   294→      WHERE c.profile_id = auth.uid()
   295→    )
   296→  );
   297→
   298→CREATE POLICY "System can insert matches"
   299→  ON matches FOR INSERT
   300→  WITH CHECK (true);
   301→
   302→CREATE POLICY "Involved parties can update matches"
   303→  ON matches FOR UPDATE
   304→  USING (
   305→    worker_id = auth.uid() OR
   306→    job_id IN (
   307→      SELECT j.id FROM jobs j
   308→      JOIN companies c ON j.company_id = c.id
   309→      WHERE c.profile_id = auth.uid()
   310→    )
   311→  );
   312→
   313→-- Ratings: viewable by everyone, insertable by match participants
   314→CREATE POLICY "Ratings are viewable by everyone"
   315→  ON ratings FOR SELECT
   316→  USING (true);
   317→
   318→CREATE POLICY "Match participants can insert ratings"
   319→  ON ratings FOR INSERT
   320→  WITH CHECK (from_id = auth.uid());
   321→
   322→-- Payments: viewable by company owner
   323→CREATE POLICY "Company owners can view their payments"
   324→  ON payments FOR SELECT
   325→  USING (
   326→    company_id IN (
   327→      SELECT id FROM companies WHERE profile_id = auth.uid()
   328→    )
   329→  );
   330→
   331→CREATE POLICY "System can insert payments"
   332→  ON payments FOR INSERT
   333→  WITH CHECK (true);
   334→
   335→-- Notifications: users can only see their own
   336→CREATE POLICY "Users can view their own notifications"
   337→  ON notifications FOR SELECT
   338→  USING (profile_id = auth.uid());
   339→
   340→CREATE POLICY "System can insert notifications"
   341→  ON notifications FOR INSERT
   342→  WITH CHECK (true);
   343→
   344→CREATE POLICY "Users can update their own notifications"
   345→  ON notifications FOR UPDATE
   346→  USING (profile_id = auth.uid());
   347→
   348→-- ============================================
   349→-- FUNCTIONS
   350→-- ============================================
   351→
   352→-- Auto-create profile on signup
   353→CREATE OR REPLACE FUNCTION public.handle_new_user()
   354→RETURNS trigger AS $$
   355→BEGIN
   356→  INSERT INTO public.profiles (id, email, type)
   357→  VALUES (
   358→    NEW.id,
   359→    NEW.email,
   360→    COALESCE(
   361→      (NEW.raw_user_meta_data->>'type')::user_type,
   362→      'worker'
   363→    )
   364→  );
   365→  RETURN NEW;
   366→END;
   367→$$ LANGUAGE plpgsql SECURITY DEFINER;
   368→
   369→CREATE OR REPLACE TRIGGER on_auth_user_created
   370→  AFTER INSERT ON auth.users
   371→  FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
   372→
   373→-- Average rating helper
   374→CREATE OR REPLACE FUNCTION public.get_average_rating(user_id uuid)
   375→RETURNS decimal AS $$
   376→  SELECT COALESCE(AVG(score)::decimal, 0)
   377→  FROM ratings
   378→  WHERE to_id = user_id;
   379→$$ LANGUAGE sql STABLE;
   380→
   381→-- Rating count helper
   382→CREATE OR REPLACE FUNCTION public.get_rating_count(user_id uuid)
   383→RETURNS integer AS $$
   384→  SELECT COUNT(*)::integer
   385→  FROM ratings
   386→  WHERE to_id = user_id;
   387→$$ LANGUAGE sql STABLE;
   388→

<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 { createClient } from '@/lib/supabase/server'
     3→import Link from 'next/link'
     4→import { Home, Briefcase, MessageSquare, User } from 'lucide-react'
     5→
     6→export default async function AuthenticatedLayout({
     7→  children,
     8→}: {
     9→  children: React.ReactNode
    10→}) {
    11→  const supabase = await createClient()
    12→  const { data: { user } } = await supabase.auth.getUser()
    13→
    14→  if (!user) {
    15→    redirect('/auth/login')
    16→  }
    17→
    18→  return (
    19→

    20→      {/* Header */}
    21→
    22→

    23→          <Link href="/dashboard" className="text-xl font-bold text-blue-600">
    24→            RateRight
    25→          </Link>
    26→          {user.email}
    27→

    28→
    29→
    30→      {/* Main Content */}
    31→
    32→        {children}
    33→
    34→
    35→      {/* Bottom Navigation */}
    36→
    37→

    38→          <Link href="/dashboard" className="flex flex-col items-center p-2 text-gray-600 hover:text-blue-600">
    39→            <Home className="w-6 h-6" />
    40→            Home
    41→          </Link>
    42→          <Link href="/contractor/post-job" className="flex flex-col items-center p-2 text-gray-600 hover:text-blue-600">
    43→            <Briefcase className="w-6 h-6" />
    44→            Jobs
    45→          </Link>
    46→          <Link href="/messages" className="flex flex-col items-center p-2 text-gray-600 hover:text-blue-600">
    47→            <MessageSquare className="w-6 h-6" />
    48→            Messages
    49→          </Link>
    50→          <Link href="/worker/profile/build" className="flex flex-col items-center p-2 text-gray-600 hover:text-blue-600">
    51→            <User className="w-6 h-6" />
    52→            Profile
    53→          </Link>
    54→

    55→
    56→

    57→  )
    58→}
    59→

<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 { 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 } from "@/components/ui/card";
    11→import {
    12→  Building2,
    13→  Search,
    14→  Sparkles,
    15→  CheckCircle2,
    16→  ArrowLeft,
    17→  ArrowRight,
    18→  Loader2,
    19→  Edit3,
    20→  Globe,
    21→  MapPin,
    22→  Users,
    23→  FileText,
    24→  HardHat,
    25→} from "lucide-react";
    26→import { guessCompanyDomain } from "@/lib/company-utils";
    27→import { validateABN, formatABN } from "@/lib/abn-utils";
    28→
    29→type Step = "auth" | "company" | "researching" | "review";
    30→
    31→interface CompanyData {
    32→  name: string;
    33→  abn: string;
    34→  address: string;
    35→  industry: string;
    36→  size: string;
    37→  website: string;
    38→  logoUrl?: string | null;
    39→  logoSource?: 'clearbit' | 'google' | 'none';
    40→}
    41→
    42→async function lookupABN(abn: string): Promise<CompanyData> {
    43→  // Clean the ABN - remove spaces
    44→  const cleanAbn = abn.replace(/\s/g, '');
    45→
    46→  // Call the ABN lookup API
    47→  const response = await fetch(`/api/abn-lookup?abn=${cleanAbn}`);
    48→
    49→  if (!response.ok) {
    50→    const errData = await response.json().catch(() => null);
    51→    throw new Error(errData?.error || 'Failed to lookup ABN');
    52→  }
    53→
    54→  const result = await response.json();
    55→
    56→  if (!result.success || !result.data) {
    57→    throw new Error(result.error || 'Unable to lookup ABN');
    58→  }
    59→
    60→  const data = result.data;
    61→
    62→  // Map the ABR data to our CompanyData format
    63→  const companyData: CompanyData = {
    64→    name: data.name || 'Unknown Company',
    65→    abn: formatABN(data.abn || cleanAbn),
    66→    address: [data.postcode, data.state].filter(Boolean).join(' ') || 'Address not available',
    67→    industry: 'Construction',
    68→    size: 'Unknown',
    69→    website: '',
    70→  };
    71→
    72→  // Try to find company logo
    73→  await fetchCompanyLogo(companyData);
    74→
    75→  return companyData;
    76→}
    77→
    78→async function fetchCompanyLogo(companyData: CompanyData): Promise<void> {
    79→  try {
    80→    // If we have an explicit website, try that first
    81→    if (companyData.website) {
    82→      const domain = extractDomainFromUrl(companyData.website);
    83→      if (domain) {
    84→        const logoResponse = await fetch(`/api/company-logo?domain=${domain}`);
    85→        if (logoResponse.ok) {
    86→          const logoData = await logoResponse.json();
    87→          companyData.logoUrl = logoData.logoUrl;
    88→          companyData.logoSource = logoData.source;
    89→          return;
    90→        }
    91→      }
    92→    }
    93→
    94→    // Otherwise, try to guess domains from company name
    95→    const possibleDomains = guessCompanyDomain(companyData.name);
    96→
    97→    // Try each domain until we find a logo
    98→    for (const domain of possibleDomains) {
    99→      const logoResponse = await fetch(`/api/company-logo?domain=${domain}`);
   100→      if (logoResponse.ok) {
   101→        const logoData = await logoResponse.json();
   102→        if (logoData.logoUrl) {
   103→          companyData.logoUrl = logoData.logoUrl;
   104→          companyData.logoSource = logoData.source;
   105→          return;
   106→        }
   107→      }
   108→    }
   109→
   110→    // No logo found
   111→    companyData.logoUrl = null;
   112→    companyData.logoSource = 'none';
   113→  } catch (error) {
   114→    console.error('Logo fetch failed:', error);
   115→    companyData.logoUrl = null;
   116→    companyData.logoSource = 'none';
   117→  }
   118→}
   119→
   120→function extractDomainFromUrl(url: string): string | null {
   121→  try {
   122→    // Add protocol if missing
   123→    if (!url.match(/^https?:\/\//)) {
   124→      url = 'https://' + url;
   125→    }
   126→
   127→    const urlObj = new URL(url);
   128→    return urlObj.hostname.replace(/^www\./, '');
   129→  } catch {
   130→    return null;
   131→  }
   132→}
   133→
   134→export default function ContractorSignupPage() {
   135→  const [step, setStep] = useState<Step>("auth");
   136→  const [isAuthenticated, setIsAuthenticated] = useState(false);
   137→  const [loading, setLoading] = useState(true);
   138→
   139→  // Auth state
   140→  const [email, setEmail] = useState("");
   141→  const [password, setPassword] = useState("");
   142→  const [authError, setAuthError] = useState<string | null>(null);
   143→  const [authLoading, setAuthLoading] = useState(false);
   144→
   145→  // Company state
   146→  const [companyInput, setCompanyInput] = useState("");
   147→  const [companyData, setCompanyData] = useState<CompanyData | null>(null);
   148→  const [companyError, setCompanyError] = useState<string | null>(null);
   149→  const [editField, setEditField] = useState<string | null>(null);
   150→  const [saving, setSaving] = useState(false);
   151→
   152→  const router = useRouter();
   153→  const supabase = createClient();
   154→
   155→  useEffect(() => {
   156→    async function checkAuth() {
   157→      const {
   158→        data: { user },
   159→      } = await supabase.auth.getUser();
   160→      if (user) {
   161→        setIsAuthenticated(true);
   162→        setStep("company");
   163→      }
   164→      setLoading(false);
   165→    }
   166→    checkAuth();
   167→  }, [supabase.auth]);
   168→
   169→  async function handleAuth(e: React.FormEvent) {
   170→    e.preventDefault();
   171→    setAuthLoading(true);
   172→    setAuthError(null);
   173→
   174→    const { error } = await supabase.auth.signUp({
   175→      email,
   176→      password,
   177→      options: {
   178→        data: { type: "contractor" },
   179→        emailRedirectTo: `${window.location.origin}/auth/callback`,
   180→      },
   181→    });
   182→
   183→    if (error) {
   184→      if (error.message.includes("already registered")) {
   185→        const { error: loginError } = await supabase.auth.signInWithPassword({
   186→          email,
   187→          password,
   188→        });
   189→        if (loginError) {
   190→          setAuthError(loginError.message);
   191→          setAuthLoading(false);
   192→          return;
   193→        }
   194→        // If login successful, check if email is verified
   195→        const { data: { user } } = await supabase.auth.getUser();
   196→        if (user && !user.email_confirmed_at) {
   197→          // Email not verified, redirect to verification page
   198→          router.push("/auth/verify-email");
   199→          return;
   200→        }
   201→      } else {
   202→        setAuthError(error.message);
   203→        setAuthLoading(false);
   204→        return;
   205→      }
   206→    }
   207→
   208→    // Check if email verification is required
   209→    const { data: { user } } = await supabase.auth.getUser();
   210→    if (user && !user.email_confirmed_at) {
   211→      // Email not verified, redirect to verification page
   212→      router.push("/auth/verify-email");
   213→      return;
   214→    }
   215→
   216→    setIsAuthenticated(true);
   217→    setStep("company");
   218→    setAuthLoading(false);
   219→  }
   220→
   221→  async function handleCompanySearch(e: React.FormEvent) {
   222→    e.preventDefault();
   223→    if (!companyInput.trim()) return;
   224→    setCompanyError(null);
   225→
   226→    const input = companyInput.trim();
   227→
   228→    // Validate ABN checksum using the shared utility
   229→    if (!validateABN(input)) {
   230→      setCompanyError("Please enter a valid 11-digit ABN. Check the number and try again.");
   231→      return;
   232→    }
   233→
   234→    setStep("researching");
   235→
   236→    try {
   237→      const data = await lookupABN(input);
   238→      setCompanyData(data);
   239→      setStep("review");
   240→    } catch (error) {
   241→      setCompanyError(error instanceof Error ? error.message : 'Failed to lookup ABN');
   242→      setStep("company");
   243→    }
   244→  }
   245→
   246→  async function handleConfirm() {
   247→    if (!companyData) return;
   248→    setSaving(true);
   249→
   250→    const {
   251→      data: { user },
   252→    } = await supabase.auth.getUser();
   253→    if (!user) return;
   254→
   255→    await supabase.from("profiles").update({
   256→      name: companyData.name,
   257→      onboarding_completed: true,
   258→    }).eq("id", user.id);
   259→
   260→    await supabase.from("companies").insert({
   261→      profile_id: user.id,
   262→      name: companyData.name,
   263→      abn: companyData.abn,
   264→      address: companyData.address,
   265→      industry: companyData.industry,
   266→      size: companyData.size,
   267→      website: companyData.website,
   268→      abr_verified: {
   269→        source: "abr_verified",
   270→        abn: companyData.abn,
   271→        verified_at: new Date().toISOString(),
   272→      },
   273→    });
   274→
   275→    router.push("/dashboard");
   276→    router.refresh();
   277→  }
   278→
   279→  function updateCompanyField(field: keyof CompanyData, value: string) {
   280→    if (!companyData) return;
   281→    setCompanyData({ ...companyData, [field]: value });
   282→    setEditField(null);
   283→  }
   284→
   285→  const totalSteps = isAuthenticated ? 3 : 4;
   286→  const currentStep =
   287→    step === "auth" ? 0 : step === "company" ? 1 : step === "researching" ? 2 : 3;
   288→
   289→  if (loading) {
   290→    return (
   291→

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

   294→    );
   295→  }
   296→
   297→  return (
   298→

   299→      {/* Header */}
   300→
   301→

   302→          <Link href="/" className="flex items-center gap-2">
   303→

   304→              <HardHat className="h-5 w-5 text-primary-foreground" />
   305→

   306→            RateRight
   307→          </Link>
   308→

   309→
   310→
   311→
   312→        {/* Progress bar */}
   313→

   314→          {Array.from({ length: totalSteps }, (_, i) => (
   315→            <div
   316→              key={i}
   317→              className={`h-1.5 flex-1 rounded-full transition-colors ${
   318→                i <= currentStep - (isAuthenticated ? 1 : 0)
   319→                  ? "bg-primary"
   320→                  : "bg-muted"
   321→              }`}
   322→            />
   323→          ))}
   324→

   325→
   326→        {/* Step: Auth */}
   327→        {step === "auth" && (
   328→

   329→

   330→

   331→                <Building2 className="h-5 w-5" />
   332→

   333→

   334→
Create your account

   335→

   336→                  Quick signup to get started
   337→

   338→

   339→

   340→
   341→            <form onSubmit={handleAuth} className="mt-6 space-y-4">
   342→

   343→                Email
   344→                <Input
   345→                  id="email"
   346→                  type="email"
   347→                  placeholder="you@company.com.au"
   348→                  value={email}
   349→                  onChange={(e) => setEmail(e.target.value)}
   350→                  required
   351→                  autoComplete="email"
   352→                  className="h-12 text-base"
   353→                />
   354→

   355→

   356→                Password
   357→                <Input
   358→                  id="password"
   359→                  type="password"
   360→                  placeholder="Create a password"
   361→                  value={password}
   362→                  onChange={(e) => setPassword(e.target.value)}
   363→                  required
   364→                  minLength={6}
   365→                  autoComplete="new-password"
   366→                  className="h-12 text-base"
   367→                />
   368→

   369→
   370→              {authError && (
   371→
{authError}

   372→              )}
   373→
   374→              <Button
   375→                type="submit"
   376→                className="h-12 w-full gap-2 text-base font-semibold"
   377→                disabled={authLoading}
   378→              >
   379→                {authLoading ? (
   380→                  <>
   381→                    <Loader2 className="h-5 w-5 animate-spin" />
   382→                    Creating account...
   383→                  </>
   384→                ) : (
   385→                  <>
   386→                    Continue
   387→                    <ArrowRight className="h-5 w-5" />
   388→                  </>
   389→                )}
   390→              </Button>
   391→            </form>
   392→
   393→

   394→              Already have an account?{" "}
   395→              <Link
   396→                href="/auth/login"
   397→                className="text-primary underline hover:no-underline"
   398→              >
   399→                Sign in
   400→              </Link>
   401→

   402→

   403→        )}
   404→
   405→        {/* Step: Enter company name/ABN */}
   406→        {step === "company" && (
   407→

   408→

   409→

   410→                <Search className="h-5 w-5" />
   411→

   412→

   413→
Your company

   414→

   415→                  We'll verify it via ABR
   416→

   417→

   418→

   419→
   420→            <form onSubmit={handleCompanySearch} className="mt-6 space-y-4">
   421→

   422→                ABN
   423→                <Input
   424→                  id="company"
   425→                  type="text"
   426→                  placeholder="Enter your 11-digit ABN"
   427→                  value={companyInput}
   428→                  onChange={(e) => setCompanyInput(e.target.value)}
   429→                  required
   430→                  autoFocus
   431→                  className="h-12 text-base"
   432→                />
   433→

   434→                  Enter your 11-digit ABN for verification via the Australian Business Register.
   435→

   436→

   437→
   438→              {companyError && (
   439→
{companyError}

   440→              )}
   441→
   442→              <Button
   443→                type="submit"
   444→                className="h-12 w-full gap-2 text-base font-semibold"
   445→              >
   446→                <Search className="h-5 w-5" />
   447→                Verify my ABN
   448→              </Button>
   449→            </form>
   450→
   451→            {!isAuthenticated && (
   452→              <button
   453→                onClick={() => setStep("auth")}
   454→                className="mt-4 flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground"
   455→              >
   456→                <ArrowLeft className="h-4 w-4" />
   457→                Back
   458→              </button>
   459→            )}
   460→

   461→        )}
   462→
   463→        {/* Step: ABR Verification */}
   464→        {step === "researching" && (
   465→

   466→

   467→              <Sparkles className="h-10 w-10 animate-pulse text-primary" />
   468→

   469→
Verifying your business

   470→

   471→              Checking ABR for your business details...
   472→

   473→
   474→

   475→              <ResearchStep label="Checking ABR..." />
   476→              <ResearchStep label="Verifying company..." />
   477→              <ResearchStep label="Done" />
   478→

   479→

   480→        )}
   481→
   482→        {/* Step: Review profile */}
   483→        {step === "review" && companyData && (
   484→

   485→

   486→

   487→                <CheckCircle2 className="h-5 w-5" />
   488→

   489→

   490→
Confirm your profile

   491→

   492→                  Tap any field to edit
   493→

   494→

   495→

   496→
   497→            <Card className="mt-6">
   498→              <CardContent className="space-y-4 pt-6">
   499→                <EditableField
   500→                  icon={<Building2 className="h-4 w-4" />}
   501→                  label="Company name"
   502→                  value={companyData.name}
   503→                  fieldKey="name"
   504→                  editField={editField}
   505→                  setEditField={setEditField}
   506→                  onSave={(v) => updateCompanyField("name", v)}
   507→                  logoUrl={companyData.logoUrl}
   508→                />
   509→                <EditableField
   510→                  icon={<FileText className="h-4 w-4" />}
   511→                  label="ABN"
   512→                  value={companyData.abn}
   513→                  fieldKey="abn"
   514→                  editField={editField}
   515→                  setEditField={setEditField}
   516→                  onSave={(v) => updateCompanyField("abn", v)}
   517→                />
   518→                <EditableField
   519→                  icon={<MapPin className="h-4 w-4" />}
   520→                  label="Address"
   521→                  value={companyData.address}
   522→                  fieldKey="address"
   523→                  editField={editField}
   524→                  setEditField={setEditField}
   525→                  onSave={(v) => updateCompanyField("address", v)}
   526→                />
   527→                <EditableField
   528→                  icon={<Building2 className="h-4 w-4" />}
   529→                  label="Industry"
   530→                  value={companyData.industry}
   531→                  fieldKey="industry"
   532→                  editField={editField}
   533→                  setEditField={setEditField}
   534→                  onSave={(v) => updateCompanyField("industry", v)}
   535→                />
   536→                <EditableField
   537→                  icon={<Users className="h-4 w-4" />}
   538→                  label="Company size"
   539→                  value={companyData.size}
   540→                  fieldKey="size"
   541→                  editField={editField}
   542→                  setEditField={setEditField}
   543→                  onSave={(v) => updateCompanyField("size", v)}
   544→                />
   545→                <EditableField
   546→                  icon={<Globe className="h-4 w-4" />}
   547→                  label="Website"
   548→                  value={companyData.website}
   549→                  fieldKey="website"
   550→                  editField={editField}
   551→                  setEditField={setEditField}
   552→                  onSave={(v) => updateCompanyField("website", v)}
   553→                />
   554→              </CardContent>
   555→            </Card>
   556→
   557→

   558→

   559→                <CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0 text-green-600" />
   560→

   561→

   562→                    Verified via Australian Business Register
   563→

   564→

   565→                    Your ABN has been verified. Tap any field to correct it.
   566→

   567→

   568→

   569→

   570→
   571→            <Button
   572→              onClick={handleConfirm}
   573→              className="mt-6 h-14 w-full gap-2 text-base font-semibold"
   574→              disabled={saving}
   575→            >
   576→              {saving ? (
   577→                <>
   578→                  <Loader2 className="h-5 w-5 animate-spin" />
   579→                  Saving...
   580→                </>
   581→              ) : (
   582→                <>
   583→                  <CheckCircle2 className="h-5 w-5" />
   584→                  Looks good — create my profile
   585→                </>
   586→              )}
   587→            </Button>
   588→
   589→            <button
   590→              onClick={() => setStep("company")}
   591→              className="mt-4 flex w-full items-center justify-center gap-1 text-sm text-muted-foreground hover:text-foreground"
   592→            >
   593→              <ArrowLeft className="h-4 w-4" />
   594→              Search again
   595→            </button>
   596→

   597→        )}
   598→
   599→

   600→  );
   601→}
   602→
   603→function ResearchStep({ label }: { label: string }) {
   604→  return (
   605→

   606→      <Loader2 className="h-4 w-4 animate-spin text-primary" />
   607→      {label}
   608→

   609→  );
   610→}
   611→
   612→function EditableField({
   613→  icon,
   614→  label,
   615→  value,
   616→  fieldKey,
   617→  editField,
   618→  setEditField,
   619→  onSave,
   620→  logoUrl,
   621→}: {
   622→  icon: React.ReactNode;
   623→  label: string;
   624→  value: string;
   625→  fieldKey: string;
   626→  editField: string | null;
   627→  setEditField: (f: string | null) => void;
   628→  onSave: (value: string) => void;
   629→  logoUrl?: string | null;
   630→}) {
   631→  const [editValue, setEditValue] = useState(value);
   632→  const isEditing = editField === fieldKey;
   633→
   634→  if (isEditing) {
   635→    return (
   636→

   637→        {label}
   638→

   639→          {fieldKey === 'name' && logoUrl ? (
   640→

   641→              <img
   642→                src={logoUrl}
   643→                alt={`${value} logo`}
   644→                className="h-8 w-8 rounded-full object-cover"
   645→                onError={(e) => {
   646→                  e.currentTarget.style.display = 'none';
   647→                }}
   648→              />
   649→              <Input
   650→                value={editValue}
   651→                onChange={(e) => setEditValue(e.target.value)}
   652→                className="h-10 text-base"
   653→                autoFocus
   654→              />
   655→

   656→          ) : (
   657→            <Input
   658→              value={editValue}
   659→              onChange={(e) => setEditValue(e.target.value)}
   660→              className="h-10 text-base"
   661→              autoFocus
   662→            />
   663→          )}
   664→          <Button
   665→            size="sm"
   666→            onClick={() => onSave(editValue)}
   667→            className="h-10 shrink-0"
   668→          >
   669→            Save
   670→          </Button>
   671→

   672→

   673→    );
   674→  }
   675→
   676→  return (
   677→    <button
   678→      onClick={() => {
   679→        setEditValue(value);
   680→        setEditField(fieldKey);
   681→      }}
   682→      className="flex w-full items-start gap-3 rounded-lg p-2 text-left transition-colors hover:bg-muted"
   683→    >
   684→      {fieldKey === 'name' && logoUrl ? (
   685→

   686→          <img
   687→            src={logoUrl}
   688→            alt={`${value} logo`}
   689→            className="h-6 w-6 rounded-full object-cover"
   690→            onError={(e) => {
   691→              // If image fails to load, fall back to icon
   692→              e.currentTarget.style.display = 'none';
   693→            }}
   694→          />
   695→

   696→      ) : (
   697→
{icon}

   698→      )}
   699→

   700→
{label}

   701→
{value}

   702→

   703→      <Edit3 className="mt-1 h-3.5 w-3.5 shrink-0 text-muted-foreground" />
   704→    </button>
   705→  );
   706→}
   707→

<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→/**
     2→ * Email verification API endpoint
     3→ * Placeholder for email verification
     4→ */
     5→
     6→import { NextRequest, NextResponse } from 'next/server'
     7→
     8→export async function POST(request: NextRequest) {
     9→  try {
    10→    const { userId, token } = await request.json()
    11→
    12→    if (!userId || !token) {
    13→      return NextResponse.json({ error: 'Missing required fields' }, { status: 400 })
    14→    }
    15→
    16→    // In production, implement proper token validation
    17→    // For now, return success
    18→
    19→    return NextResponse.json({
    20→      success: true,
    21→      message: 'Email verification endpoint ready'
    22→    })
    23→
    24→  } catch (error) {
    25→    console.error('Email verification error:', error)
    26→    return NextResponse.json(
    27→      { error: 'Internal server error' },
    28→      { status: 500 }
    29→    )
    30→  }
    31→}
    32→

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

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

   271→    );
   272→  }
   273→
   274→  return (
   275→

   276→      {/* Header */}
   277→
   278→

   279→          <Link href="/" className="flex items-center gap-2">
   280→

   281→              <HardHat className="h-5 w-5 text-primary-foreground" />
   282→

   283→            RateRight
   284→          </Link>
   285→

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

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

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

   304→

   305→

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

   308→

   309→
Create your account

   310→

   311→                  Always free for workers
   312→

   313→

   314→

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

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

   330→

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

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

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

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

   377→

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

   383→

   384→

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

   387→

   388→
Build your profile

   389→

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

   392→

   393→

   394→
   395→

   396→              <button
   397→                onClick={() => setStep("trade")}
   398→                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"
   399→              >
   400→

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

   403→

   404→
Type my answers

   405→

   406→                    5 quick questions, under 2 minutes
   407→

   408→

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

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

   419→

   420→
Send a voice note

   421→

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

   424→

   425→              </button>
   426→

   427→

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

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

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

   454→
   455→

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

   472→

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

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

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

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

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

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

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

   541→
   542→

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

   560→            </form>
   561→

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

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

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

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

   591→
{opt.desc}

   592→

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

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

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

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

   618→              {CERT_OPTIONS.map((cert) => (
   619→                <button
   620→                  key={cert}
   621→                  onClick={() => toggleCert(cert)}
   622→                  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 ${
   623→                    certifications.includes(cert)
   624→                      ? "border-primary bg-primary/10 text-primary"
   625→                      : "border-border hover:border-primary/50"
   626→                  }`}
   627→                >
   628→

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

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

   643→
   644→

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

   660→

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

   666→

   667→

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

   670→

   671→
Your profile

   672→

   673→                  Review and confirm
   674→

   675→

   676→

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

   729→

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

   732→

   733→                    AI-generated bio
   734→

   735→

   736→                    {generateBio(workerData)}
   737→

   738→

   739→

   740→

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

   768→        )}
   769→
   770→

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

   785→

   786→        {icon}
   787→

   788→

   789→
{title}

   790→
{subtitle}

   791→

   792→

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

   813→

   814→
{label}

   815→
{value}

   816→

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

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


Perfect! Now let me get a few more files to complete the audit: