---
phase: 01-foundation
plan: 02
type: execute
wave: 2
depends_on:
  - "01-01"
files_modified:
  - src/app/agent/login/page.tsx
  - src/app/agent/dashboard/page.tsx
  - src/app/agent/layout.tsx
  - src/components/ui/LogoutButton.tsx
  - src/app/layout.tsx
  - src/app/page.tsx
  - public/red.jpg
autonomous: true
requirements:
  - AUTH-01
  - AUTH-02
  - AUTH-03
  - AUTH-04

must_haves:
  truths:
    - "Visiting /agent/login displays a branded form with email, password (with show/hide toggle), and a submit button"
    - "Submitting invalid credentials shows 'Invalid email or password' without hinting which field is wrong"
    - "Submitting valid credentials redirects to /agent/dashboard"
    - "After login, the agent's email is visible somewhere on the dashboard"
    - "The dashboard has a logout button that, when clicked, redirects to /agent/login"
    - "Visiting /agent/login after logout shows 'You've been signed out' confirmation message"
    - "Visiting /agent/dashboard without a session redirects to /agent/login (enforced by middleware from Plan 01)"
  artifacts:
    - path: "src/app/agent/login/page.tsx"
      provides: "Branded login page with email/password form, password toggle, error display, signed-out confirmation"
      exports: ["default (LoginPage)"]
    - path: "src/app/agent/dashboard/page.tsx"
      provides: "Protected blank dashboard — server component that calls auth() and shows agent email + logout button"
      exports: ["default (DashboardPage)"]
    - path: "src/app/agent/layout.tsx"
      provides: "Agent portal layout wrapper — defense-in-depth session check, shared nav shell"
      exports: ["default (AgentLayout)"]
    - path: "src/components/ui/LogoutButton.tsx"
      provides: "Client component with logout form that calls signOut server action"
      exports: ["LogoutButton"]
  key_links:
    - from: "src/app/agent/login/page.tsx"
      to: "src/lib/auth.ts"
      via: "loginAction() calls signIn('credentials', ...) from @/lib/auth"
      pattern: "signIn.*credentials"
    - from: "src/components/ui/LogoutButton.tsx"
      to: "src/lib/auth.ts"
      via: "logoutAction() calls signOut({ redirectTo: '/agent/login?signed_out=1' })"
      pattern: "signOut.*signed_out"
    - from: "src/app/agent/dashboard/page.tsx"
      to: "src/lib/auth.ts"
      via: "const session = await auth() — defense-in-depth beyond middleware"
      pattern: "await auth\\(\\)"
---

<objective>
Build the two agent-facing pages (login and dashboard) and the logout mechanism. The login page is fully branded with Teressa's photo and implements all user-specified behaviors. The dashboard is a blank stub that confirms the session is valid and provides a logout button. Together these pages satisfy all four AUTH requirements.

Purpose: These pages are the visible proof that Phase 1 works. The middleware from Plan 01 protects routes — these pages are what the agent actually sees.
Output: /agent/login (branded form) and /agent/dashboard (blank, protected, with logout).
</objective>

<execution_context>
@/Users/ccopeland/.claude/get-shit-done/workflows/execute-plan.md
@/Users/ccopeland/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/01-foundation/01-CONTEXT.md
@.planning/phases/01-foundation/01-RESEARCH.md
@.planning/phases/01-foundation/01-01-SUMMARY.md

<interfaces>
<!-- Contracts from Plan 01 that this plan consumes. -->
<!-- Do not import from other paths — these are the established contracts. -->

From src/lib/auth.ts (created in Plan 01):
```typescript
export const { handlers, auth, signIn, signOut } = NextAuth({ ... });
// auth() — call in server components to get session
// signIn("credentials", { email, password, redirectTo }) — call in server actions
// signOut({ redirectTo }) — call in server actions
```

From src/lib/db/schema.ts (created in Plan 01):
```typescript
export const users = pgTable("users", { ... });
// Not needed directly in UI — session comes from auth()
```

Key behaviors locked by user decisions (CONTEXT.md):
- Login route: /agent/login
- Post-login redirect: /agent/dashboard
- Error message: "Invalid email or password" (generic — do not vary by field)
- Password visibility toggle: included
- Session behavior: 7-day rolling (handled by auth.ts — no code needed here)
- Logout: signOut({ redirectTo: "/agent/login?signed_out=1" })
- Logout confirmation: "You've been signed out" (shown when ?signed_out=1 is in URL)
- No forgot-password link (per deferred decisions — do NOT add it)

Brand assets:
- Agent photo: /Users/ccopeland/Downloads/red.jpg — copy to public/red.jpg
- Brand colors/logo: Use discretion — create CSS custom properties for easy swap later
  - Suggested color palette: warm gold (#C9A84C) for accent, deep navy (#1B2B4B) for primary, off-white (#FAF9F7) for background
  - Login page should feel like a premium real estate brand, not a generic SaaS admin
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Build the branded login page with password toggle and error handling</name>
  <files>src/app/agent/login/page.tsx, public/red.jpg</files>
  <action>
Copy the agent photo from its source path to the public directory:
```bash
cp /Users/ccopeland/Downloads/red.jpg /path/to/project/public/red.jpg
```
If the file does not exist at that path, use a placeholder image (create a simple Next.js Image with a colored background placeholder) and note it in the SUMMARY.md.

Create `src/app/agent/login/page.tsx` as a **server component** (no "use client") that:

1. Reads `searchParams` to detect `?error=invalid` and `?signed_out=1`
2. Contains an inline `loginAction` server action that calls signIn
3. Contains an inline `SignedOutMessage` and `ErrorMessage` based on searchParams
4. Renders a full-page branded layout with a left/right split or centered card

The login page structure:
- Page fills the full viewport height (min-h-screen)
- Left side (hidden on mobile): Teressa's photo (`/red.jpg`) as a cover image, overlaid with a subtle dark gradient and brand name text
- Right side (or centered on mobile): Login form card
  - "Agent Portal" heading (h1, per user decision)
  - If `?signed_out=1`: green/teal banner — "You've been signed out."
  - If `?error=invalid`: red banner — "Invalid email or password."
  - Email input: type="email", name="email", required, label="Email address"
  - Password input: type="password" by default, name="password", required, label="Password"
  - Password toggle button (show/hide) — this is a client interaction, so wrap just the password field and toggle button in a small "use client" sub-component called `PasswordField`
  - Submit button: "Sign in" text, full-width, shows loading state during submission
  - Brand accent color on the submit button and focus rings

The `loginAction` server action (inside the page file):
```typescript
async function loginAction(formData: FormData) {
  "use server";
  try {
    await signIn("credentials", {
      email: formData.get("email") as string,
      password: formData.get("password") as string,
      redirectTo: "/agent/dashboard",
    });
  } catch (error) {
    // CRITICAL: Only catch AuthError. Everything else (including NEXT_REDIRECT) must re-throw.
    if (error instanceof AuthError) {
      redirect("/agent/login?error=invalid");
    }
    throw error; // Re-throws NEXT_REDIRECT — allows the redirect to fire
  }
}
```

The `PasswordField` sub-component (can live in same file or separate file, use client):
- Manages `showPassword` boolean state
- Renders `<input type={showPassword ? "text" : "password"} ... />`
- Renders a toggle button (eye/eye-off icon or text "Show"/"Hide")
- Use Tailwind for all styling — no additional UI libraries

Brand color guide (CSS custom properties or Tailwind classes):
- Primary background: #FAF9F7 (warm off-white)
- Primary text: #1B2B4B (deep navy)
- Accent (buttons, focus rings): #C9A84C (warm gold)
- Error background: #FEF2F2, error text: #991B1B
- Success background: #F0FDF4, success text: #166534

Do NOT add a "Forgot password?" link — this is explicitly deferred per user decisions.
  </action>
  <verify>
Navigate to /agent/login in browser (or run: curl -s http://localhost:3000/agent/login | grep -i "Agent Portal"). Verify:
1. Page renders without errors
2. Submitting wrong credentials reloads with "Invalid email or password" banner
3. Password toggle switches input type between password/text
4. npm run build passes with no TypeScript errors in this file
  </verify>
  <done>Login page renders at /agent/login with branded design, password toggle works, error displays on failed login, signed-out message displays when ?signed_out=1, no TypeScript errors</done>
</task>

<task type="auto">
  <name>Task 2: Build the agent portal layout, dashboard stub, and logout mechanism</name>
  <files>src/app/agent/layout.tsx, src/app/agent/dashboard/page.tsx, src/components/ui/LogoutButton.tsx, src/app/page.tsx</files>
  <action>
Create `src/components/ui/LogoutButton.tsx` as a client component with a server action for logout:
```typescript
// src/components/ui/LogoutButton.tsx
import { signOut } from "@/lib/auth";

async function logoutAction() {
  "use server";
  await signOut({ redirectTo: "/agent/login?signed_out=1" });
}

export function LogoutButton() {
  return (
    <form action={logoutAction}>
      <button
        type="submit"
        className="text-sm text-gray-600 hover:text-gray-900 underline"
      >
        Sign out
      </button>
    </form>
  );
}
```

Note: signOut() server action must NOT be caught — it throws NEXT_REDIRECT which must bubble up. The form action pattern avoids the issue.

Create `src/app/agent/layout.tsx` — the shared agent portal layout:
```typescript
import { auth } from "@/lib/auth";
import { redirect } from "next/navigation";
import { LogoutButton } from "@/components/ui/LogoutButton";

export default async function AgentLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  // Defense-in-depth: middleware handles most cases, this catches edge cases
  const session = await auth();
  if (!session) redirect("/agent/login");

  return (
    <div className="min-h-screen bg-gray-50">
      <header className="bg-white border-b border-gray-200 px-6 py-4 flex items-center justify-between">
        <span className="text-sm font-medium text-gray-700">Agent Portal</span>
        <div className="flex items-center gap-4">
          <span className="text-sm text-gray-500">{session.user?.email}</span>
          <LogoutButton />
        </div>
      </header>
      <main className="p-6">{children}</main>
    </div>
  );
}
```

Create `src/app/agent/dashboard/page.tsx` — blank dashboard stub:
```typescript
import { auth } from "@/lib/auth";
import { redirect } from "next/navigation";

export default async function DashboardPage() {
  // Defense-in-depth session check (layout also checks, this is belt-and-suspenders)
  const session = await auth();
  if (!session) redirect("/agent/login");

  return (
    <div>
      <h1 className="text-2xl font-semibold text-gray-900">Dashboard</h1>
      <p className="mt-2 text-gray-500">
        Welcome back, {session.user?.email}. Portal content coming in Phase 3.
      </p>
    </div>
  );
}
```

Update `src/app/page.tsx` (public homepage — placeholder for Phase 2):
```typescript
export default function HomePage() {
  return (
    <main className="min-h-screen flex items-center justify-center">
      <div className="text-center">
        <h1 className="text-4xl font-bold text-gray-900">Teressa Copeland Homes</h1>
        <p className="mt-4 text-gray-600">Real estate expertise for Utah home buyers and sellers.</p>
        <p className="mt-2 text-sm text-gray-400">Marketing site coming in Phase 2.</p>
      </div>
    </main>
  );
}
```

AUTH-04 verification: The LogoutButton must be accessible from the dashboard (it is in the agent layout header). Clicking it calls logoutAction which calls signOut() with ?signed_out=1, which redirects to /agent/login, which reads the query param and shows "You've been signed out."

AUTH-02 verification: The 7-day rolling JWT session is handled entirely in auth.ts (Plan 01). No additional code needed here — the httpOnly encrypted cookie persists across browser restarts by default. Do not add any sessionStorage usage.

AUTH-03 verification: The middleware from Plan 01 handles unauthenticated redirects. The layout's auth() check is defense-in-depth — not the primary enforcement mechanism. Both layers must be in place.
  </action>
  <verify>
1. Run npm run build — no TypeScript errors
2. Verify agent layout exists: ls src/app/agent/layout.tsx
3. Verify dashboard exists: ls src/app/agent/dashboard/page.tsx
4. Verify LogoutButton exists: ls src/components/ui/LogoutButton.tsx
5. TypeScript check: npx tsc --noEmit
  </verify>
  <done>AgentLayout renders portal header with agent email and sign-out button; DashboardPage shows welcome message with agent email; LogoutButton calls signOut({ redirectTo: "/agent/login?signed_out=1" }); npm run build and npx tsc --noEmit pass with no errors</done>
</task>

</tasks>

<verification>
Full auth flow verification (requires running dev server):

```bash
npm run dev
```

Manual verification checklist:
1. Visit http://localhost:3000/agent/dashboard — should redirect to /agent/login (AUTH-03)
2. Submit wrong credentials on /agent/login — should show "Invalid email or password" (AUTH-01)
3. Submit correct credentials — should redirect to /agent/dashboard with agent email visible (AUTH-01)
4. Refresh /agent/dashboard — should stay logged in, not redirect (AUTH-02 partial — session persists across refresh)
5. Click "Sign out" — should redirect to /agent/login with "You've been signed out" message (AUTH-04)
6. Visit /agent/dashboard while logged out — should redirect to /agent/login (AUTH-03)

Build verification:
```bash
npm run build && echo "BUILD OK"
npx tsc --noEmit && echo "TYPECHECK OK"
```
</verification>

<success_criteria>
- /agent/login renders with branded design — photo, "Agent Portal" heading, email/password form
- Password show/hide toggle works
- Invalid login shows "Invalid email or password" (not a more specific error)
- Valid login redirects to /agent/dashboard
- /agent/dashboard shows agent email and Sign out button
- Signing out redirects to /agent/login with "You've been signed out" message
- /agent/dashboard (and all future /agent/* routes) redirect to /agent/login when unauthenticated
- npm run build passes with no errors
- No forgot-password link exists on any page (deferred per user decisions)
</success_criteria>

<output>
After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md` using the summary template.

Include in the summary:
- Confirmation that the agent photo was copied from /Users/ccopeland/Downloads/red.jpg (or note if placeholder used)
- Confirmation of the brand colors used (or CSS custom properties defined)
- Manual verification results for the full auth flow (items 1-6 above)
- Any deviations from planned patterns and why
</output>
