Read and report the FULL contents of every page.tsx and layout.tsx file in /home/ccuser/rateright-v2/src/app/. Also read any loading.tsx, error.tsx, not-found.tsx files. Report complete file contents for each file found. Additionally, list all component files in src/components/ with their full contents.


I'll search for all the layout, page, loading, error, and not-found files in the app directory, and then list all component files with their full contents.


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


No files found


Now let me read all these files:


     1→import type { Metadata, Viewport } from "next";
     2→import { Geist, Geist_Mono } from "next/font/google";
     3→import "./globals.css";
     4→
     5→const geistSans = Geist({
     6→  variable: "--font-geist-sans",
     7→  subsets: ["latin"],
     8→});
     9→
    10→const geistMono = Geist_Mono({
    11→  variable: "--font-geist-mono",
    12→  subsets: ["latin"],
    13→});
    14→
    15→export const metadata: Metadata = {
    16→  title: "RateRight — Hire Construction Workers Fast",
    17→  description:
    18→    "The simplest way to hire construction workers in Australia. AI does the work, you just tap. $50 flat fee per hire.",
    19→  keywords: [
    20→    "construction hiring",
    21→    "hire labourers",
    22→    "construction workers Australia",
    23→    "hire tradies",
    24→    "construction jobs Sydney",
    25→  ],
    26→  manifest: "/manifest.json",
    27→  appleWebApp: {
    28→    capable: true,
    29→    statusBarStyle: "default",
    30→    title: "RateRight",
    31→  },
    32→  other: {
    33→    "mobile-web-app-capable": "yes",
    34→    "apple-mobile-web-app-capable": "yes",
    35→    "application-name": "RateRight",
    36→    "msapplication-starturl": "/",
    37→    "msapplication-TileColor": "#2563eb",
    38→    "theme-color": "#2563eb",
    39→  },
    40→};
    41→
    42→export const viewport: Viewport = {
    43→  width: "device-width",
    44→  initialScale: 1,
    45→  maximumScale: 1,
    46→  themeColor: "#2563eb",
    47→};
    48→
    49→export default function RootLayout({
    50→  children,
    51→}: Readonly<{
    52→  children: React.ReactNode;
    53→}>) {
    54→  return (
    55→    <html lang="en-AU">
    56→      <head>
    57→        <link rel="manifest" href="/manifest.json" />
    58→        <link rel="apple-touch-icon" href="/apple-touch-icon.svg" />
    59→        <link rel="icon" type="image/svg+xml" href="/icon-192x192.svg" />
    60→        <meta name="apple-mobile-web-app-status-bar-style" content="default" />
    61→        <meta name="apple-mobile-web-app-title" content="RateRight" />
    62→        <meta name="mobile-web-app-capable" content="yes" />
    63→        <meta name="apple-mobile-web-app-capable" content="yes" />
    64→        <meta name="application-name" content="RateRight" />
    65→        <meta name="msapplication-starturl" content="/" />
    66→        <meta name="msapplication-TileColor" content="#2563eb" />
    67→        <meta name="theme-color" content="#2563eb" />
    68→      </head>
    69→      <body
    70→        className={`${geistSans.variable} ${geistMono.variable} font-sans antialiased`}
    71→      >
    72→        {children}
    73→        <script
    74→          dangerouslySetInnerHTML={{
    75→            __html: `
    76→              if ('serviceWorker' in navigator) {
    77→                window.addEventListener('load', () => {
    78→                  navigator.serviceWorker.register('/sw.js').then(
    79→                    (registration) => {
    80→                      console.log('SW registered:', registration);
    81→                    },
    82→                    (err) => {
    83→                      console.log('SW registration failed:', err);
    84→                    }
    85→                  );
    86→                });
    87→              }
    88→            `,
    89→          }}
    90→        />
    91→      </body>
    92→    </html>
    93→  );
    94→}
    95→

<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 Link from "next/link";
     2→import { Button } from "@/components/ui/button";
     3→import { Card, CardContent } from "@/components/ui/card";
     4→import {
     5→  Building2,
     6→  HardHat,
     7→  Zap,
     8→  DollarSign,
     9→  Shield,
    10→  CheckCircle2,
    11→  Search,
    12→  ArrowRight,
    13→  Sparkles,
    14→  MapPin,
    15→  Clock,
    16→  Users,
    17→} from "lucide-react";
    18→
    19→export default function LandingPage() {
    20→  return (
    21→

    22→      {/* Orange accent bar */}
    23→

    24→
    25→      {/* Header */}
    26→
    27→

    28→          <Link href="/" className="flex items-center gap-2">
    29→

    30→              <HardHat className="h-5 w-5 text-primary-foreground" />
    31→

    32→            RateRight
    33→          </Link>
    34→          <Link href="/auth/login">
    35→            <Button variant="ghost" size="sm">
    36→              Sign in
    37→            </Button>
    38→          </Link>
    39→

    40→
    41→
    42→      {/* Hero */}
    43→

    44→

    45→

    46→            <MapPin className="h-3.5 w-3.5" />
    47→            Now live in Sydney
    48→

    49→
    50→

    51→            Hire construction workers{" "}
    52→            in seconds
    53→

    54→
    55→

    56→            AI builds profiles and finds matches.
    57→            You just tap hire. $50 flat fee per hire.
    58→

    59→
    60→          {/* CTA Buttons - BIG touch targets */}
    61→

    62→            <Link href="/contractor/signup">
    63→              <Button className="h-14 w-full gap-3 text-base font-semibold shadow-lg shadow-primary/25">
    64→                <Building2 className="h-6 w-6" />
    65→                I'm Hiring
    66→                <ArrowRight className="ml-auto h-5 w-5" />
    67→              </Button>
    68→            </Link>
    69→            <Link href="/worker/signup">
    70→              <Button
    71→                variant="outline"
    72→                className="h-14 w-full gap-3 border-2 text-base font-semibold"
    73→              >
    74→                <HardHat className="h-6 w-6" />
    75→                I'm Looking For Work
    76→                <ArrowRight className="ml-auto h-5 w-5" />
    77→              </Button>
    78→            </Link>
    79→

    80→
    81→

    82→            Free to sign up. Workers always free.
    83→

    84→

    85→

    86→
    87→      {/* How it works */}
    88→

    89→

    90→

    91→            How it works
    92→

    93→

    94→            Three steps. Under two minutes.
    95→

    96→
    97→

    98→            <Step
    99→              number="1"
   100→              icon={<Search className="h-5 w-5" />}
   101→              title="Tell us your company"
   102→              description="Enter your company name or ABN. AI researches your business and builds your profile automatically."
   103→            />
   104→            <Step
   105→              number="2"
   106→              icon={<Sparkles className="h-5 w-5" />}
   107→              title="AI finds the best matches"
   108→              description="Post a job in 60 seconds. Smart matching ranks workers by distance, experience, and ratings."
   109→            />
   110→            <Step
   111→              number="3"
   112→              icon={<CheckCircle2 className="h-5 w-5" />}
   113→              title="One tap to hire"
   114→              description="See top matches, tap hire, worker accepts. $50 flat fee — only charged when you hire."
   115→            />
   116→

   117→

   118→

   119→
   120→      {/* For Contractors */}
   121→

   122→

   123→

   124→            <Building2 className="h-5 w-5 text-primary" />
   125→
For Contractors

   126→

   127→

   128→            Stop wasting time hiring
   129→

   130→
   131→

   132→            <FeatureCard
   133→              icon={<Zap className="h-5 w-5 text-primary" />}
   134→              title="AI does the work"
   135→              description="Company research, job descriptions, worker matching — all automated."
   136→            />
   137→            <FeatureCard
   138→              icon={<DollarSign className="h-5 w-5 text-primary" />}
   139→              title="$50 flat fee per hire"
   140→              description="No subscriptions, no percentages. Free to post. Only pay when you hire."
   141→            />
   142→            <FeatureCard
   143→              icon={<Shield className="h-5 w-5 text-primary" />}
   144→              title="Verified workers"
   145→              description="White Card verification, ratings, and real experience on every profile."
   146→            />
   147→            <FeatureCard
   148→              icon={<Clock className="h-5 w-5 text-primary" />}
   149→              title="Hire by tomorrow"
   150→              description="Post a job now, get matched in minutes. Workers on site tomorrow."
   151→            />
   152→

   153→

   154→

   155→
   156→      {/* For Workers */}
   157→

   158→

   159→

   160→            <HardHat className="h-5 w-5 text-primary" />
   161→
For Workers

   162→

   163→

   164→            Get on a job fast
   165→

   166→

   167→            Sign up free. Get matched to jobs near you. Know what you'll earn.
   168→

   169→
   170→

   171→            <Benefit
   172→              icon={<DollarSign className="h-4 w-4" />}
   173→              text="Always free — $0 to sign up, $0 to get hired"
   174→            />
   175→            <Benefit
   176→              icon={<Clock className="h-4 w-4" />}
   177→              text="Answer 5 questions, your profile is done in 2 minutes"
   178→            />
   179→            <Benefit
   180→              icon={<MapPin className="h-4 w-4" />}
   181→              text="Get matched to jobs within kilometres of you"
   182→            />
   183→            <Benefit
   184→              icon={<Users className="h-4 w-4" />}
   185→              text="See the pay rate upfront — no guessing"
   186→            />
   187→

   188→
   189→          <Link href="/worker/signup">
   190→            <Button className="mt-8 h-12 w-full gap-2 text-base font-semibold">
   191→              <HardHat className="h-5 w-5" />
   192→              Sign up free
   193→            </Button>
   194→          </Link>
   195→

   196→

   197→
   198→      {/* Pricing */}
   199→

   200→

   201→
Simple pricing

   202→

   203→            No hidden fees. No lock-in. No percentages.
   204→

   205→
   206→          <Card className="mt-6 border-2 border-primary/20">
   207→            <CardContent className="pt-8 pb-8">
   208→
$50

   209→

   210→                per hire — that's it
   211→

   212→

   213→                <BenefitCheck text="Free to post jobs" />
   214→                <BenefitCheck text="Free to browse workers" />
   215→                <BenefitCheck text="Free for workers — always" />
   216→                <BenefitCheck text="Only pay when you confirm a hire" />
   217→                <BenefitCheck text="No monthly subscriptions" />
   218→

   219→            </CardContent>
   220→          </Card>
   221→

   222→

   223→
   224→      {/* Final CTA */}
   225→

   226→

   227→
Ready to hire smarter?

   228→

   229→            Join Sydney contractors already using RateRight.
   230→

   231→

   232→            <Link href="/contractor/signup" className="w-full sm:w-auto">
   233→              <Button
   234→                variant="secondary"
   235→                className="h-12 w-full gap-2 text-base font-semibold"
   236→              >
   237→                <Building2 className="h-5 w-5" />
   238→                Start hiring
   239→              </Button>
   240→            </Link>
   241→            <Link href="/worker/signup" className="w-full sm:w-auto">
   242→              <Button
   243→                variant="secondary"
   244→                className="h-12 w-full gap-2 text-base font-semibold"
   245→              >
   246→                <HardHat className="h-5 w-5" />
   247→                Find work
   248→              </Button>
   249→            </Link>
   250→

   251→

   252→

   253→
   254→      {/* Footer */}
   255→
   256→

   257→

   258→

   259→              <HardHat className="h-4 w-4 text-primary-foreground" />
   260→

   261→            RateRight
   262→

   263→

   264→            The simplest way to hire construction workers in Australia.
   265→

   266→

   267→            © {new Date().getFullYear()} RateRight Pty Ltd. All rights
   268→            reserved.
   269→

   270→

   271→
   272→

   273→  );
   274→}
   275→
   276→function Step({
   277→  number,
   278→  icon,
   279→  title,
   280→  description,
   281→}: {
   282→  number: string;
   283→  icon: React.ReactNode;
   284→  title: string;
   285→  description: string;
   286→}) {
   287→  return (
   288→

   289→

   290→        {number}
   291→

   292→

   293→

   294→          {icon}
   295→
{title}

   296→

   297→

   298→          {description}
   299→

   300→

   301→

   302→  );
   303→}
   304→
   305→function FeatureCard({
   306→  icon,
   307→  title,
   308→  description,
   309→}: {
   310→  icon: React.ReactNode;
   311→  title: string;
   312→  description: string;
   313→}) {
   314→  return (
   315→

   316→
{icon}

   317→

   318→
{title}

   319→
{description}

   320→

   321→

   322→  );
   323→}
   324→
   325→function Benefit({
   326→  icon,
   327→  text,
   328→}: {
   329→  icon: React.ReactNode;
   330→  text: string;
   331→}) {
   332→  return (
   333→

   334→
{icon}

   335→      {text}
   336→

   337→  );
   338→}
   339→
   340→function BenefitCheck({ text }: { text: string }) {
   341→  return (
   342→

   343→      <CheckCircle2 className="h-4 w-4 shrink-0 text-primary" />
   344→      {text}
   345→

   346→  );
   347→}
   348→

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

    60→      {/* Header */}
    61→
    62→

    63→          <Link href="/dashboard" className="text-xl font-bold tracking-tight">
    64→            RateRight
    65→          </Link>
    66→

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

    72→

    73→
    74→
    75→
    76→

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

    79→

    80→          {isContractor
    81→            ? "Ready to find your next worker?"
    82→            : "Let's get you on a job."}
    83→

    84→
    85→

    86→          {isContractor ? (
    87→            <>
    88→              {/* Quick stats */}
    89→

    90→                <Card>
    91→                  <CardContent className="flex flex-col items-center p-4">
    92→                    {jobCount}
    93→                    Active Jobs
    94→                  </CardContent>
    95→                </Card>
    96→                <Card>
    97→                  <CardContent className="flex flex-col items-center p-4">
    98→                    {matchCount}
    99→                    New Matches
   100→                  </CardContent>
   101→                </Card>
   102→

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

   200→
   201→
   202→      <BottomNavWrapper userType={profile?.type || "worker"} />
   203→

   204→  );
   205→}
   206→

<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→"use client";
     2→
     3→import { useState } from "react";
     4→import { useRouter } from "next/navigation";
     5→import Link from "next/link";
     6→import { createClient } from "@/lib/supabase/client";
     7→import { Button } from "@/components/ui/button";
     8→import { Card, CardContent } from "@/components/ui/card";
     9→import { Input } from "@/components/ui/input";
    10→import { Label } from "@/components/ui/label";
    11→import { Badge } from "@/components/ui/badge";
    12→import {
    13→  ArrowLeft,
    14→  ArrowRight,
    15→  Loader2,
    16→  Sparkles,
    17→  Check,
    18→  MapPin,
    19→  Calendar,
    20→  Users,
    21→  DollarSign,
    22→  HardHat,
    23→  Wrench,
    24→  Hammer,
    25→  Blocks,
    26→  Fence,
    27→  CircleDot,
    28→  Shovel,
    29→} from "lucide-react";
    30→
    31→const TRADE_CATEGORIES = [
    32→  { id: "steel-fixer", label: "Steel Fixer", icon: Wrench, color: "bg-blue-500/10 border-blue-500/30 text-blue-700" },
    33→  { id: "formworker", label: "Formworker", icon: Blocks, color: "bg-amber-500/10 border-amber-500/30 text-amber-700" },
    34→  { id: "labourer", label: "Labourer", icon: Shovel, color: "bg-green-500/10 border-green-500/30 text-green-700" },
    35→  { id: "concreter", label: "Concreter", icon: CircleDot, color: "bg-gray-500/10 border-gray-500/30 text-gray-700" },
    36→  { id: "carpenter", label: "Carpenter", icon: Hammer, color: "bg-orange-500/10 border-orange-500/30 text-orange-700" },
    37→  { id: "scaffolder", label: "Scaffolder", icon: Fence, color: "bg-purple-500/10 border-purple-500/30 text-purple-700" },
    38→  { id: "other", label: "Other", icon: HardHat, color: "bg-muted border-border text-foreground" },
    39→];
    40→
    41→const AI_SUGGESTIONS: Record<string, { title: string; description: string; certs: string[]; rateMin: number; rateMax: number; rateType: "hourly" | "daily" }> = {
    42→  "steel-fixer": {
    43→    title: "Steel Fixer Needed",
    44→    description: "Experienced steel fixer required for commercial construction project. Must be able to read drawings, tie rebar, and work with mesh. PPE required on site at all times. Early starts — 6:30am.",
    45→    certs: ["White Card", "Working at Heights"],
    46→    rateMin: 45,
    47→    rateMax: 65,
    48→    rateType: "hourly",
    49→  },
    50→  "formworker": {
    51→    title: "Formworker Required",
    52→    description: "Skilled formworker needed for concrete pours on a residential build. Experience with timber and steel formwork systems. Must be comfortable with early starts and physical work.",
    53→    certs: ["White Card"],
    54→    rateMin: 42,
    55→    rateMax: 58,
    56→    rateType: "hourly",
    57→  },
    58→  "labourer": {
    59→    title: "General Labourer Needed",
    60→    description: "Reliable labourer needed for site clean-up, material handling, and general duties. No specific trade experience needed but must be fit and punctual. PPE and boots required.",
    61→    certs: ["White Card"],
    62→    rateMin: 32,
    63→    rateMax: 42,
    64→    rateType: "hourly",
    65→  },
    66→  "concreter": {
    67→    title: "Concreter Required",
    68→    description: "Experienced concreter needed for slab pours and finishing work. Must know how to screed, float, and finish concrete to a high standard. Early morning pours expected.",
    69→    certs: ["White Card"],
    70→    rateMin: 45,
    71→    rateMax: 65,
    72→    rateType: "hourly",
    73→  },
    74→  "carpenter": {
    75→    title: "Carpenter Needed",
    76→    description: "Qualified carpenter required for framing and fit-out work. Must have own basic tools. Experience with residential or commercial builds preferred.",
    77→    certs: ["White Card", "Trade Certificate"],
    78→    rateMin: 45,
    79→    rateMax: 65,
    80→    rateType: "hourly",
    81→  },
    82→  "scaffolder": {
    83→    title: "Scaffolder Required",
    84→    description: "Licensed scaffolder needed for erection and dismantling of scaffolding systems. Must hold current scaffolding licence. Safety-first attitude essential.",
    85→    certs: ["White Card", "Scaffolding Licence", "Working at Heights"],
    86→    rateMin: 48,
    87→    rateMax: 68,
    88→    rateType: "hourly",
    89→  },
    90→  "other": {
    91→    title: "Construction Worker Needed",
    92→    description: "Worker needed for construction project. Please specify the role and requirements in the description.",
    93→    certs: ["White Card"],
    94→    rateMin: 35,
    95→    rateMax: 55,
    96→    rateType: "hourly",
    97→  },
    98→};
    99→
   100→export default function PostJobPage() {
   101→  const router = useRouter();
   102→  const supabase = createClient();
   103→
   104→  const [step, setStep] = useState(1);
   105→  const [loading, setLoading] = useState(false);
   106→  const [aiLoading, setAiLoading] = useState(false);
   107→  const [error, setError] = useState("");
   108→
   109→  // Step 1: Trade
   110→  const [selectedTrade, setSelectedTrade] = useState("");
   111→
   112→  // Step 2: AI-filled job details + location/date/workers (COMBINED)
   113→  const [title, setTitle] = useState("");
   114→  const [description, setDescription] = useState("");
   115→  const [certs, setCerts] = useState<string[]>([]);
   116→  const [rateMin, setRateMin] = useState("");
   117→  const [rateMax, setRateMax] = useState("");
   118→  const [rateType, setRateType] = useState<"hourly" | "daily">("hourly");
   119→  const [suburb, setSuburb] = useState("");
   120→  const [siteAddress, setSiteAddress] = useState("");
   121→  const [startDate, setStartDate] = useState("");
   122→  const [duration, setDuration] = useState("");
   123→  const [workersNeeded, setWorkersNeeded] = useState("1");
   124→
   125→  async function handleTradeSelect(tradeId: string) {
   126→    setSelectedTrade(tradeId);
   127→    setAiLoading(true);
   128→    setStep(2);
   129→
   130→    // Simulate AI generating job details
   131→    await new Promise((r) => setTimeout(r, 1500));
   132→
   133→    const suggestion = AI_SUGGESTIONS[tradeId];
   134→    if (suggestion) {
   135→      setTitle(suggestion.title);
   136→      setDescription(suggestion.description);
   137→      setCerts(suggestion.certs);
   138→      setRateMin(String(suggestion.rateMin));
   139→      setRateMax(String(suggestion.rateMax));
   140→      setRateType(suggestion.rateType);
   141→    }
   142→    setAiLoading(false);
   143→  }
   144→
   145→  async function handlePost() {
   146→    setLoading(true);
   147→    setError("");
   148→
   149→    try {
   150→      const { data: { user } } = await supabase.auth.getUser();
   151→      if (!user) {
   152→        router.push("/auth/login");
   153→        return;
   154→      }
   155→
   156→      // Get contractor's company
   157→      const { data: company } = await supabase
   158→        .from("companies")
   159→        .select("id")
   160→        .eq("profile_id", user.id)
   161→        .single();
   162→
   163→      if (!company) {
   164→        setError("Please complete your company profile first.");
   165→        setLoading(false);
   166→        return;
   167→      }
   168→
   169→      const { error: insertError } = await supabase.from("jobs").insert({
   170→        company_id: company.id,
   171→        title,
   172→        description,
   173→        trade: selectedTrade,
   174→        suburb,
   175→        site_address: siteAddress,
   176→        rate_min: parseFloat(rateMin) || null,
   177→        rate_max: parseFloat(rateMax) || null,
   178→        rate_type: rateType,
   179→        start_date: startDate || null,
   180→        duration: duration || null,
   181→        workers_needed: parseInt(workersNeeded) || 1,
   182→        certifications_required: certs,
   183→        status: "active",
   184→        ai_generated: true,
   185→      });
   186→
   187→      if (insertError) {
   188→        setError(insertError.message);
   189→        setLoading(false);
   190→        return;
   191→      }
   192→
   193→      setStep(3); // Success (was step 5, now step 3)
   194→    } catch {
   195→      setError("Something went wrong. Please try again.");
   196→      setLoading(false);
   197→    }
   198→  }
   199→
   200→  return (
   201→

   202→      {/* Header */}
   203→
   204→

   205→          {step > 1 && step < 3 ? (
   206→            <button
   207→              onClick={() => setStep(step - 1)}
   208→              className="mr-3 flex h-12 w-12 items-center justify-center rounded-full active:bg-muted"
   209→            >
   210→              <ArrowLeft className="h-6 w-6" />
   211→            </button>
   212→          ) : (
   213→            <Link
   214→              href="/dashboard"
   215→              className="mr-3 flex h-12 w-12 items-center justify-center rounded-full active:bg-muted"
   216→            >
   217→              <ArrowLeft className="h-6 w-6" />
   218→            </Link>
   219→          )}
   220→
Post a Job

   221→          {step < 3 && (
   222→
   223→              Step {step}/2
   224→
   225→          )}
   226→

   227→        {/* Progress bar */}
   228→        {step < 3 && (
   229→

   230→            <div
   231→              className="h-1 bg-primary transition-all duration-300"
   232→              style={{ width: `${(step / 2) * 100}%` }}
   233→            />
   234→

   235→        )}
   236→
   237→
   238→
   239→        {/* STEP 1: Select Trade */}
   240→        {step === 1 && (
   241→

   242→
What trade do you need?

   243→

   244→              Tap to select. AI will write the job post for you.
   245→

   246→

   247→              {TRADE_CATEGORIES.map((trade) => {
   248→                const Icon = trade.icon;
   249→                return (
   250→                  <button
   251→                    key={trade.id}
   252→                    onClick={() => handleTradeSelect(trade.id)}
   253→                    className={`flex flex-col items-center justify-center gap-3 rounded-2xl border-2 p-6 min-h-[120px] text-center transition-all active:scale-95 ${trade.color}`}
   254→                  >
   255→                    <Icon className="h-10 w-10" />
   256→                    {trade.label}
   257→                  </button>
   258→                );
   259→              })}
   260→

   261→

   262→        )}
   263→
   264→        {/* STEP 2: AI-filled Job Details + Location/Date/Workers (COMBINED) */}
   265→        {step === 2 && (
   266→

   267→            {aiLoading ? (
   268→

   269→

   270→                  <Sparkles className="h-12 w-12 text-primary animate-pulse" />
   271→

   272→
AI writing your job post...

   273→

   274→                  Based on market rates and common requirements
   275→

   276→

   277→            ) : (
   278→

   279→

   280→                  <Sparkles className="h-5 w-5 text-primary" />
   281→
Job Details

   282→

   283→

   284→                  AI pre-filled these. Edit anything you like.
   285→

   286→
   287→

   288→                  {/* Job Details Section */}
   289→

   290→                    Job Title
   291→                    <Input
   292→                      id="title"
   293→                      value={title}
   294→                      onChange={(e) => setTitle(e.target.value)}
   295→                      className="mt-2 h-14 text-base"
   296→                    />
   297→

   298→
   299→

   300→                    Description
   301→                    <textarea
   302→                      id="description"
   303→                      value={description}
   304→                      onChange={(e) => setDescription(e.target.value)}
   305→                      rows={5}
   306→                      className="mt-2 w-full rounded-md border border-input bg-background px-4 py-3 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
   307→                    />
   308→

   309→
   310→

   311→                    Pay Rate (AUD)
   312→

   313→

   314→                        <DollarSign className="absolute left-4 top-1/2 h-5 w-5 -translate-y-1/2 text-muted-foreground" />
   315→                        <Input
   316→                          type="number"
   317→                          value={rateMin}
   318→                          onChange={(e) => setRateMin(e.target.value)}
   319→                          placeholder="Min"
   320→                          className="h-14 pl-10 text-base"
   321→                        />
   322→

   323→                      to
   324→

   325→                        <DollarSign className="absolute left-4 top-1/2 h-5 w-5 -translate-y-1/2 text-muted-foreground" />
   326→                        <Input
   327→                          type="number"
   328→                          value={rateMax}
   329→                          onChange={(e) => setRateMax(e.target.value)}
   330→                          placeholder="Max"
   331→                          className="h-14 pl-10 text-base"
   332→                        />
   333→

   334→

   335→

   336→                      <button
   337→                        onClick={() => setRateType("hourly")}
   338→                        className={`rounded-xl border-2 py-3.5 text-base font-semibold transition-colors ${
   339→                          rateType === "hourly"
   340→                            ? "border-primary bg-primary/10 text-primary"
   341→                            : "border-border text-muted-foreground"
   342→                        }`}
   343→                      >
   344→                        Per Hour
   345→                      </button>
   346→                      <button
   347→                        onClick={() => setRateType("daily")}
   348→                        className={`rounded-xl border-2 py-3.5 text-base font-semibold transition-colors ${
   349→                          rateType === "daily"
   350→                            ? "border-primary bg-primary/10 text-primary"
   351→                            : "border-border text-muted-foreground"
   352→                        }`}
   353→                      >
   354→                        Per Day
   355→                      </button>
   356→

   357→

   358→
   359→                  {/* Location Section */}
   360→

   361→

   362→                      <MapPin className="h-5 w-5" />
   363→                      Location
   364→

   365→
   366→

   367→

   368→                        Site Address
   369→                        <Input
   370→                          id="siteAddress"
   371→                          value={siteAddress}
   372→                          onChange={(e) => setSiteAddress(e.target.value)}
   373→                          placeholder="e.g. 123 George St, Sydney"
   374→                          className="mt-2 h-14 text-base"
   375→                        />
   376→

   377→
   378→

   379→                        Suburb
   380→                        <Input
   381→                          id="suburb"
   382→                          value={suburb}
   383→                          onChange={(e) => setSuburb(e.target.value)}
   384→                          placeholder="e.g. Parramatta"
   385→                          className="mt-2 h-14 text-base"
   386→                        />
   387→

   388→

   389→

   390→
   391→                  {/* Date & Workers Section */}
   392→

   393→

   394→                      <Calendar className="h-5 w-5" />
   395→                      Schedule
   396→

   397→
   398→

   399→

   400→                        Start Date
   401→                        <Input
   402→                          id="startDate"
   403→                          type="date"
   404→                          value={startDate}
   405→                          onChange={(e) => setStartDate(e.target.value)}
   406→                          className="mt-2 h-14 text-base"
   407→                        />
   408→

   409→
   410→

   411→                        Duration
   412→                        <Input
   413→                          id="duration"
   414→                          value={duration}
   415→                          onChange={(e) => setDuration(e.target.value)}
   416→                          placeholder="e.g. 2 weeks, ongoing"
   417→                          className="mt-2 h-14 text-base"
   418→                        />
   419→

   420→
   421→

   422→
   423→                          <Users className="h-5 w-5" />
   424→                          Workers Needed
   425→
   426→

   427→                          <button
   428→                            onClick={() =>
   429→                              setWorkersNeeded(String(Math.max(1, parseInt(workersNeeded) - 1)))
   430→                            }
   431→                            className="flex h-16 w-16 items-center justify-center rounded-xl border-2 text-2xl font-bold active:scale-95 active:bg-muted transition-all"
   432→                          >
   433→                            -
   434→                          </button>
   435→
   436→                            {workersNeeded}
   437→
   438→                          <button
   439→                            onClick={() =>
   440→                              setWorkersNeeded(String(parseInt(workersNeeded) + 1))
   441→                            }
   442→                            className="flex h-16 w-16 items-center justify-center rounded-xl border-2 text-2xl font-bold active:scale-95 active:bg-muted transition-all"
   443→                          >
   444→                            +
   445→                          </button>
   446→

   447→

   448→

   449→

   450→
   451→                  {/* Required Certifications */}
   452→                  {certs.length > 0 && (
   453→

   454→                      Required Certifications
   455→

   456→                        {certs.map((cert) => (
   457→                          <Badge key={cert} variant="secondary" className="text-sm py-1.5 px-3">
   458→                            {cert}
   459→                            <button
   460→                              onClick={() => setCerts(certs.filter((c) => c !== cert))}
   461→                              className="ml-2 text-muted-foreground hover:text-foreground"
   462→                            >
   463→                              ×
   464→                            </button>
   465→                          </Badge>
   466→                        ))}
   467→

   468→

   469→                  )}
   470→

   471→

   472→            )}
   473→

   474→        )}
   475→
   476→        {/* STEP 3: Success */}
   477→        {step === 3 && (
   478→

   479→

   480→              <Check className="h-12 w-12 text-green-600" />
   481→

   482→
Job Posted!

   483→

   484→              We're matching you with the best workers now.
   485→              You'll get notified when we find matches.
   486→

   487→

   488→              <Button
   489→                onClick={() => router.push("/contractor/matches")}
   490→                className="w-full h-16 text-lg font-semibold"
   491→              >
   492→                View Matches
   493→              </Button>
   494→              <Button
   495→                onClick={() => router.push("/dashboard")}
   496→                variant="outline"
   497→                className="w-full h-16 text-lg"
   498→              >
   499→                Back to Dashboard
   500→              </Button>
   501→

   502→

   503→        )}
   504→
   505→
   506→      {/* Sticky Bottom CTA (Step 2 only) */}
   507→      {step === 2 && !aiLoading && (
   508→

   509→

   510→            {error && (
   511→
{error}

   512→            )}
   513→            <Button
   514→              onClick={handlePost}
   515→              className="w-full h-16 text-lg font-semibold"
   516→              disabled={loading || !title || !suburb}
   517→            >
   518→              {loading ? (
   519→                <>
   520→                  <Loader2 className="mr-2 h-6 w-6 animate-spin" />
   521→                  Posting...
   522→                </>
   523→              ) : (
   524→                <>
   525→                  Post Job Now
   526→                  <Check className="ml-2 h-6 w-6" />
   527→                </>
   528→              )}
   529→            </Button>
   530→

   531→              Free to post. You only pay $50 when you hire someone.
   532→

   533→

   534→

   535→      )}
   536→

   537→  );
   538→}
   539→

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