## 1. Database & Models

- [x] 1.1 Create migration `create_unions_and_villages_tables`: `unions` (bigint id, unique name(100), timestamps) and `villages` (bigint id, name(100), `union_id` FK constrained cascade-on-delete, unique(union_id, name), timestamps)
- [x] 1.2 Fix `0001_01_01_000000_create_users_table.php`: remove `district_id` FK, `union_name`, `village_name`; add `foreignId('union_id')->nullable()->constrained('unions')->nullOnDelete()` and `foreignId('village_id')->nullable()->constrained('villages')->nullOnDelete()`; update indexes to `['union_id','village_id']` and `['blood_group','is_available','union_id']`
- [x] 1.3 Create `app/Models/Union.php` (fillable name, `villages()` hasMany) and `app/Models/Village.php` (fillable name/union_id, `union()` belongsTo); delete `app/Models/District.php`
- [x] 1.4 Update `app/Models/User.php`: replace `district()` with `union()` / `village()` belongsTo relations, update `$fillable` (union_id, village_id instead of district_id/union_name/village_name), add `getAuthPassword()` returning `password_hash`
- [x] 1.5 Write `UnionSeeder` / `VillageSeeder` (unions + their villages from the mockup's locale) and extend `DatabaseSeeder` to seed unions, villages, ~9 sample donors with varied blood groups/availability/donations (donations update `last_donation_date`)
- [x] 1.6 Run `php artisan migrate:fresh --seed` and verify it completes clean

## 2. Auth (Fortify override, phone/username)

- [x] 2.1 Register `authenticateUsing` callback: look up user by `phone` OR `username`, verify `password_hash`; wire it in the Fortify service provider
- [x] 2.2 Implement custom `CreateNewUser` action: validate name, phone (unique), username (unique), password, blood_group, union_id, optional village_id (exists within chosen union), is_available; create user with hashed password into `password_hash`
- [x] 2.3 Adapt Fortify login/register views: login uses single `identifier` field; register form fields match the mockup sections (Personal / Location / Blood / Donor Status)
- [x] 2.4 Add `GET /username-check` guest route + controller endpoint returning `{status: short|taken|available}`
- [x] 2.5 Verify rate limiting still applies to login attempts after the override

## 3. Controllers → Inertia

- [x] 3.1 Rewrite `AuthController` for Inertia: welcome page render (redirect authed → `/donors`), username check endpoint; remove token/JSON register/login/logout (Fortify owns them)
- [x] 3.2 Rewrite `DonorController::index`: search (name, username, blood_group, union name, village name — driver-safe LIKE), filters (blood_group, availability, union_id, village_id, donation_status never/recent/before via `last_donation_date`), exclude self, order availability desc then name, paginate 15 `withQueryString()`, map to public shape (no phone/reason), compute total + available stats, render `Donors/Index` with unions prop
- [x] 3.3 Rewrite `DonorController::show`: public profile props (name, username, blood_group, available, village/union names, donations count, last donation date, phone for call dialog); own-id request redirects to `/profile`
- [x] 3.4 Rewrite `ProfileController::show/update`: `$request->validate()` with Inertia error bags, unique rules excluding self, village-in-union validation; update via `$request->user()->update()` with validated fields only
- [x] 3.5 Rewrite `ProfileController::toggleAvailability`: validate is_available + optional unavailable_reason; clear reason when becoming available; redirect back with flash
- [x] 3.6 Rewrite `ProfileController::donations/storeDonation`: history listing (newest first), store with validation (date not in future, location, type in Whole Blood/Platelets/Plasma, optional hospital/notes, status Completed), refresh cached `last_donation_date` when newer
- [x] 3.7 Update `routes/web.php` with all donor routes under `auth` middleware (`/donors`, `/donors/{user}`, `/profile`, `/profile/availability`, `/profile/donations`, `/settings`), dashboard redirect → `/donors`; regenerate Wayfinder routes

## 4. Frontend — theme & layout

- [x] 4.1 Port the blood-red palette + Hanken Grotesk font into `resources/css/app.css` via Tailwind v4 `@theme` tokens (blood, blood-deep, blood-tint, ink, line, bg, good…)
- [x] 4.2 Build `donor-app-layout.tsx`: 430px mobile shell (max-width, rounded on desktop), sticky headers with back buttons, bottom nav (Donors / Find / Donations / Profile) that hides on auth pages
- [x] 4.3 Build shared components: `donor-card` (availability accent bar), `blood-group-picker` (4-col grid), `location-selects` (union → village cascading, resets village on union change), `call-confirm-dialog` (tel: link), toast wiring via sonner

## 5. Frontend — pages

- [x] 5.1 `welcome.tsx` splash page: logo, pulse ring, tagline, "Become a Donor" + Login buttons
- [x] 5.2 `auth/login.tsx`: identifier + password form (Inertia `<Form>`), errors, links to register
- [x] 5.3 `auth/register.tsx`: mockup sections (Personal Info, Location with cascading selects, Blood Group grid, Donor Status cards), debounced username availability feedback (available/taken), submit → directory
- [x] 5.4 `donors/index.tsx`: search input, blood-group chip row (All + 8 groups), filter bottom-sheet (blood group, availability, union, village, donation status), active-filter chips with individual removal + active count badge, donor cards (View Profile / Call Donor), skeleton loading, empty state with Clear Filters, pagination, stats line (X Donors · Y Available)
- [x] 5.5 `donors/show.tsx`: public donor profile per mockup (avatar initials, blood pill, availability card, location card, donation stats, Call Donor with confirm, respect notice)
- [x] 5.6 `profile/index.tsx`: my profile (stats tiles, availability toggle switch + private reason select, links to history/add-donation/settings, contact & location card, logout with confirm)
- [x] 5.7 `profile/edit.tsx`: edit form mirroring register (name, phone, username, blood group, union/village, availability), field errors, success toast
- [x] 5.8 `profile/donations.tsx`: summary tile (total + last date), timeline list, empty state, add-donation bottom-sheet (date, location, type grid, hospital, notes)
- [x] 5.9 `settings.tsx`: account/donation/about groups per mockup (stub rows hidden or disabled), logout
- [x] 5.10 Update `HandleInertiaRequests` shared props if needed (auth.user shape incl. blood_group, union/village names, availability)

## 6. Verification

- [x] 6.1 Full manual flow: fresh migrate+seed → register (with username availability feedback) → auto-login → directory browse/search/filter → view donor → call confirm → my profile → toggle availability with reason → add donation → history updates → edit profile → logout → login with phone, then with username
- [x] 6.2 Verify privacy: directory/profile responses contain no phone in lists, no unavailable_reason outside own profile
- [x] 6.3 Verify filters persist across pagination and clear correctly (Clear All)
- [x] 6.4 Run `php artisan wayfinder:generate`, `pnpm build` (or `npm run build`), Pint/PHPStan if configured; fix fallout
