# Peejeezet — Bug Fixes, Real Dashboard, Public Site & UI/UX Polish

**Date:** 2026-06-27
**Status:** Approved design (pending spec review)
**Scope:** Whole app (Dashboard, public Welcome, Members, Fees, Auth, Settings) — refine the existing
dark/glass Flux theme, fix fake/broken content, add real data, raise consistency & accessibility, and
add per-record fee payment-detail capture.

---

## 1. Background & problem

Peejeezet (PGZ / programerz.my) is a single-tenant membership **fee tracker** for a gamer-founded
community organization. See [`.ai/agents/`](../../../.ai/agents/) for the full system map.

Problems found during analysis:

1. **The Dashboard is entirely fake mockup data.** `resources/views/dashboard.blade.php` is a
   `Route::view` with hardcoded, domain-irrelevant content (Open Requests, Budget Utilization,
   Upcoming Events, "Department performance", Retention/Attendance/Volunteer Hours, a fake activity
   feed, fake member names, "Updated Feb 20, 2026"). It is wired to nothing. **This is the app's
   landing screen and it is misleading.**
2. **The public Welcome page shows invented statistics** (`128`, `74%`, `19`) as if real.
3. **Hero filler**: Members/Fees pages bury real data under verbose decorative copy (e.g. the Members
   "notes" cards that re-explain the search box).
4. **Dead code**: commented-out blocks in `resources/views/pages/fee/index.blade.php`.
5. **Unused capability**: `member_fee.payment_method`, `reference_no`, `remarks` columns exist but no
   UI writes them.

The **design system is already strong** (`resources/css/theme/tokens.css`, `components/ui.css`,
`layout/shell.css`): full token set, light/dark, glass surfaces, status colors, reusable components.
So the work is **truth + consistency + accessibility + targeted features**, not a redesign.

## 2. Goals

- Replace the fake dashboard with one driven by real data.
- Turn the public landing page into an honest site for the PGZ organization (no fake stats, no member
  data).
- Polish UI/UX consistently across all pages while keeping the current dark/glass aesthetic.
- Add per-record fee payment-detail capture (method, reference, remarks) using existing columns.
- Keep all behavior correct (status `lated` handling, manual `MemberFee` timestamps, deactivate-not-
  delete) and all tests green.

## 3. Non-goals

- No new design language (no wholesale liquid-glass/Material rebuild — refine what exists).
- No schema/DB-structure changes (Phase 3 reuses existing columns; **no migration**).
- No roles/permissions, no programs/registration modules, no API.
- No dependency changes without explicit approval.

## 4. Decisions (from brainstorming)

| Decision | Choice |
|----------|--------|
| Scope | All pages |
| Design direction | Refine current dark theme (keep Flux + glass accents) |
| Risk tolerance | Open to bigger changes |
| Welcome page | Real public **organization landing site** for Peejeezet/PGZ (programerz.my), a gamer community running activities beyond gaming. No fake stats, no member data. |
| Phase 3 | Include payment-detail capture |

---

## 5. Design

### Phase 1 — Real Dashboard

**Routing:** Replace `Route::view('dashboard', 'dashboard')` in `routes/web.php` with
`Route::get('dashboard', [DashboardController::class, 'index'])->name('dashboard')`, inside the
existing `auth,verified` group (keep the `dashboard` route name).

**Controller:** new `app/Http/Controllers/DashboardController.php` (thin; injects repository, returns
`view('dashboard', $data)`).

**Data layer:** add a `getDashboardData(): array` method. Place it in a new
`app/Repositories/DashboardRepository.php` that composes `MemberRepository`/`FeeRepository` data, OR
add focused methods to the existing repositories. (Implementation plan will pick one; default:
`DashboardRepository` to keep existing repos focused.)

**Real metrics (current year unless noted):**
- Total members, active members, inactive members (counts).
- Coverage % = paid tracked months ÷ tracked months (reuse `FeeRepository::buildSummary` logic).
- Total paid amount (RM) = `SUM(member_fee.amount_paid)`.
- Outstanding months = count of `late` + `unpaid` cells (current year).

**Real panels:**
- KPI `metric-card` row (the four metrics above).
- "Members with outstanding fees (this year)" — `list-card` list (member name + count of late/unpaid
  months); empty state when none.
- "Recently joined members" — last N by `joined_at`/`id`; empty state.
- "This year's coverage" — `metric-card__bar`/progress bar.

**Removed:** all invented concepts. No fake activity feed, departments, budgets, events.

**Empty states:** if there are no members, the dashboard shows a clear "Add your first member" CTA
(links `members.create`).

### Phase 1b — Public landing page (Peejeezet / PGZ)

Rebuild `resources/views/welcome.blade.php` as a public organization site. It stays public (the
`home` route, no auth). Reuses tokens + glass aesthetic; `resources/css/pages/welcome.css` refreshed.

**Sections (truthful, generic, easy to edit — no invented numbers):**
1. **Top bar**: PGZ brand mark + "Member login" (→ `login`) / "Dashboard" (if authenticated).
2. **Hero**: "Peejeezet (PGZ)" / programerz.my, tagline about a gamer-founded community organization,
   primary CTA (Member login) + secondary (learn more / contact).
3. **About**: who PGZ is — a community that started in gaming and runs activities beyond it.
4. **What we do**: 3–4 cards (community & gaming, events/activities, member organization, etc.).
5. **Community / values**: short section.
6. **CTA / footer**: get involved / contact + member login; year + org name.

**Constraints:** no member data, no fake statistics. Copy is placeholder-but-honest and clearly
editable. Dark-first to match the app; remains responsive.

### Phase 2 — UI/UX polish (all pages)

- **Consistent page header**: factor the repeated eyebrow + title + description hero into one
  consistent pattern; reduce vertical space before real data.
- **Trim filler**: remove the Members "notes" `list-card`s and similarly redundant decorative copy;
  keep genuinely useful guidance only.
- **Fees grid readability** (`pages/fee/index` + `pages/fee/manage`): sticky member/name column on
  horizontal scroll; subtle highlight of the current month column; verify legend clarity.
- **Accessibility**:
  - `:focus-visible` ring on all interactive elements (buttons, inputs, links, table actions).
  - `@media (prefers-reduced-motion: reduce)` to disable the `translateY`/transform hover animations.
  - Contrast pass on status tones (`member-status--*`, pills) for WCAG AA on dark surfaces.
- **Remove dead code**: commented blocks in `pages/fee/index.blade.php`.
- **Auth & Settings**: confirm they consume the same tokens/components; fix any drift. No behavioral
  change to Fortify/Livewire flows.

### Phase 3 — Per-record fee payment details

Columns already exist (`payment_method varchar(30)`, `reference_no varchar(100)`, `remarks text`) —
**no migration**.

**UX**: the bulk manage grid stays for fast status toggling. Detailed capture happens per record via a
**single-fee editor**:
- Route: `GET fees/{memberFee}/edit` → `fees.record.edit`; `PUT fees/{memberFee}` → `fees.record.update`
  (inside `auth,verified`). Implicit binding on `MemberFee`.
- Entry point: an "Edit details" action on each fee row of the **member show** page
  (`pages/member/show`), and/or a row action in the fee views.
- Form fields: status (`paid|late|unpaid|null`), `paid_at`, `payment_method`, `reference_no`,
  `remarks`.
- Controller method on `FeeController` (`editRecord`/`updateRecord`) delegating to a repository method
  that writes via the **denormalized** status (`late→lated`), sets `amount_paid`/`paid_at`
  consistent with current paid-logic, and sets `updated_at` manually (`MemberFee::$timestamps=false`).

**Validation:**
```
status         => ['nullable', Rule::in(['paid','late','unpaid','null'])]
paid_at        => ['nullable','date']
payment_method => ['nullable','string','max:30']
reference_no   => ['nullable','string','max:100']
remarks        => ['nullable','string']
```

Display the captured details on the member show page's recent-fees list.

## 6. Data flow (unchanged pattern)

`Request → thin Controller (validate) → Repository (queries + status normalize/denormalize) →
Eloquent → Blade view`. New code follows this exactly. All fee writes route through
`denormalizeStatus()`; all reads through `normalizeStatus()`.

## 7. Testing

New/updated Pest/PHPUnit feature tests:
- `DashboardTest`: authed dashboard renders and shows real aggregates (seed members+fees, assert
  counts/coverage strings); empty-state path with zero members.
- Landing page: guest can load `/` (200), shows org content, no fake stat assertions; authed link to
  dashboard present.
- Single-fee editor: updates persist `payment_method`/`reference_no`/`remarks`; status stored as
  `lated` for `late`; `amount_paid`/`paid_at` follow paid logic; `updated_at` set.
- Regression: existing Member/Fee/Auth/Settings tests stay green.

DB assertions use **stored** values (`lated`, `null`). Member/MemberFee created via
`Model::query()->create([...])` (no factories; set fee timestamps manually). Time-sensitive tests use
`Carbon::setTestNow()` and reset.

## 8. Risks & mitigations

| Risk | Mitigation |
|------|------------|
| Dashboard aggregation perf with many members | Reuse existing summary logic; select only needed columns; current data set is small (≈13 members). |
| Breaking the `lated`/`late` contract in the new fee editor | Route all writes through `denormalizeStatus()`; assert `lated` in tests. |
| Forgetting manual timestamps on `MemberFee` writes | Repository helper sets `updated_at`/`created_at` explicitly; covered by test. |
| Public landing exposing data | Static/honest copy only; no queries for member data on `/`. |
| Polish drifting into a redesign | Stick to existing tokens/components; changes are additive/subtractive, not a new language. |
| CSS scope regressions | Keep changes within existing `@layer components`; verify Members/Fees/Auth/Settings after. |

## 9. Affected files (anticipated)

- `routes/web.php` (dashboard route, fee record routes).
- `app/Http/Controllers/DashboardController.php` (new), `FeeController.php` (record edit/update).
- `app/Repositories/DashboardRepository.php` (new) + possibly small additions to existing repos.
- `resources/views/dashboard.blade.php` (rewrite), `welcome.blade.php` (rewrite),
  `pages/fee/index.blade.php`, `pages/fee/manage.blade.php`, `pages/member/show.blade.php`,
  `pages/member/index.blade.php`, plus a new `pages/fee/record.blade.php` (single-fee editor).
- `resources/css/`: `theme/tokens.css` (focus ring, reduced-motion), `components/ui.css`,
  `components/tables.css`, `layout/shell.css`, `pages/welcome.css`, `pages/dashboard.css`,
  `pages/fees.css`, `pages/members.css` (polish only).
- `tests/Feature/`: `DashboardTest.php` (rewrite), new fee-record test, landing test.
- Docs: `.ai/agents/*` + `.github/ai/*` updated after implementation; `.ai/agents/progress-log.md`
  entry.

## 10. Execution order

1. Phase 1 (dashboard backend + view + test) — fixes the worst bug first.
2. Phase 1b (landing page).
3. Phase 3 (fee record editor) — backend + view + test.
4. Phase 2 (cross-cutting UI/UX polish + accessibility + dead-code removal).
5. Run full test suite + Pint; update docs.

Each phase: implement → test → `vendor/bin/pint --dirty --format agent` → verify before moving on.
