# RFID Page Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Build a reviewable `/rfid` UI matching the RFID and Group RFID views in `tiar-dashboard`, backed only by fictional mock records.

**Architecture:** A client-side feature view owns the mock records and local CRUD state. Shared query and table primitives drive URL-backed tabs, search, filters, sorting, and pagination. The page does not make backend requests or persist changes.

**Tech Stack:** Next.js App Router 16, React 19, TypeScript, Tailwind CSS v4, existing shared UI components, URL search parameters.

**Spec:** `docs/superpowers/specs/2026-10-08-rfid-page-design.md`

## Global Constraints

- This stage is a UI preview with mock data only; it must not call the backend or persist mutations.
- Match the reference page's two tabs: **RFID** and **Group RFID**.
- Create/edit/delete actions update only in-memory records; tab, search, filters, sorting, and pagination use URL query parameters.
- Keep the existing CSMS visual system and responsive behavior while matching the reference layout and density.
- Exclude a separate group-detail route.
- Refresh resets in-memory records to their initial mock state and clears list query parameters.
- No document is included in any commit or push.
- Do not add dependencies.

## Review Focus

- A filter that returns no records should show the table's empty state and allow clearing filters.
- A page number beyond the available pages should be clamped to a valid page after filtering or deleting a record.
- Records without an optional parent group, description, or expiry should render a readable placeholder.
- Closing or cancelling a create/edit dialog should not mutate mock records.
- Refresh should restore both tables to their original mock records and reset list query parameters.

## File Structure

- `src/features/rfid/types.ts` — RFID card, group, filter, sort, and pagination types.
- `src/features/rfid/mock-data.ts` — deterministic fictional preview records and form options.
- `src/features/rfid/query.ts` — URL-state parsing and pure filtering, sorting, and pagination helpers.
- `src/features/rfid/rfid-view.tsx` — page-level tab state, in-memory records, reset, and mutation callbacks.
- `src/features/rfid/rfid-card-tab.tsx` — RFID toolbar, table, filters, export, and row actions.
- `src/features/rfid/rfid-group-tab.tsx` — Group RFID toolbar, table, filters, and row actions.
- `src/features/rfid/rfid-dialogs.tsx` — create/edit dialogs for cards and groups, plus delete confirmations.
- `src/app/(dashboard)/rfid/page.tsx` — page metadata and feature composition.
- `src/components/layout/navigation.ts` — add `/rfid` to the existing RFID Cards item.
- `src/components/shared/table-toolbar.tsx` — optional `onRefresh` callback while preserving the existing default `router.refresh()` behavior.

---

### Task 1: Define mock RFID records and list queries

**Files:**
- Create: `src/features/rfid/types.ts`
- Create: `src/features/rfid/mock-data.ts`
- Create: `src/features/rfid/query.ts`

**Interfaces:**
- `RfidCardRecord`: `id`, `organizationCode`, `userId`, `userName`, `name`, `description`, `active`, `rfidCode`, optional `groupId`, optional `expiresAt`, `createdAt`, `updatedAt`.
- `RfidGroupRecord`: `id`, `organizationCode`, `groupName`, `description`, `active`, `createdAt`, `updatedAt`.
- `RfidCardRow`: `RfidCardRecord` plus `parentGroupName` for display and sorting.
- `RfidGroupRow`: `RfidGroupRecord` plus `rfidCount` for display and sorting.
- `RfidFormOptions`: `organizations: { code: string; name: string }[]` and `users: { id: string; name: string }[]`.
- `RfidCardFormValues`: `organizationCode`, `userId`, `name`, `description`, `active`, `rfidCode`, optional `groupId`, optional `expiresAt`.
- `RfidGroupFormValues`: `organizationCode`, `groupName`, `description`, and `active`.
- `RfidListState`: `search`, `organizationCode`, `name`, `userId`, `rfidCode`, `groupId`, `groupName`, `active` (`"" | "active" | "inactive"`), `createdFrom`, `createdTo`, `sortBy` (`RfidCardSortKey | RfidGroupSortKey`), `sortOrder` (`"asc" | "desc"`), `page`, and `pageSize`.
- `RfidCardSortKey`: `"name" | "rfid_code" | "parent_group" | "active" | "user_id" | "created_at"`.
- `RfidGroupSortKey`: `"group_name" | "description" | "active" | "rfid_count" | "created_at" | "updated_at"`.
- `PaginatedRecords<T>`: `{ items: T[] } & Pagination` from `@/lib/list-query`, where `Pagination` contains `page`, `pages`, `limit`, `total`, `start`, and `end`.
- `parseRfidListState(params: URLSearchParams): { tab: "rfid" | "group"; list: RfidListState }` reads `tab`, `query`, `organization_code`, `name`, `user_id`, `rfid_code`, `parent_rfid_id`, `group_name`, `active`, `createdAt[$gte]`, `createdAt[$lte]`, `sort_by`, `sort_order`, `paginate_page`, and `paginate_limit`; it uses defaults `tab="rfid"`, `sort_by="created_at"`, `sort_order="desc"`, `paginate_page=1`, and `paginate_limit=25`.
- `selectRfidCards(records: readonly RfidCardRecord[], groups: readonly RfidGroupRecord[], state: RfidListState): RfidCardRow[]` and `selectRfidGroups(records: readonly RfidGroupRecord[], cards: readonly RfidCardRecord[], state: RfidListState): RfidGroupRow[]` return related display rows after filtering and sorting.
- `paginateRfidRecords<T>(records: readonly T[], page: number, pageSize: number): PaginatedRecords<T>` clamps the page and returns visible rows plus display counts.
- Fixtures contain at least six clearly fictional cards, four groups, and local organization/user options used by forms.

- [ ] Create the types and fixture arrays; keep fixture identifiers stable and use invented names, emails, and codes.
- [ ] Implement query parsing for `tab`, `query`, feature-specific filters, `sort_by`, `sort_order`, `paginate_page`, and `paginate_limit`.
- [ ] Implement stable text/status/date filtering and sorting, then clamp pagination after filters.
- [ ] Manually check the default RFID and group states, a no-match search, and an out-of-range page value.

### Task 2: Build RFID card table and local CRUD dialogs

**Files:**
- Create: `src/features/rfid/rfid-card-tab.tsx`
- Create: `src/features/rfid/rfid-dialogs.tsx`
- Modify: `src/components/shared/table-toolbar.tsx`

**Interfaces:**
- `RFIDCardTab` props are `{ records: RfidCardRecord[]; groups: RfidGroupRecord[]; options: RfidFormOptions; onCreate(values: RfidCardFormValues): void; onUpdate(id: string, updates: Partial<RfidCardFormValues>): void; onDelete(id: string): void; onRefresh(): void }`.
- RFID card create form uses organization, user, name, RFID code, active, optional parent group, description, and expiry fields.
- RFID card edit form exposes the reference's editable name, RFID code, and active fields.
- `TableToolbar` accepts optional `onRefresh?: () => void`; when supplied it calls the callback, otherwise it retains `router.refresh()`.

- [ ] Add the optional refresh callback to `TableToolbar` without changing default behavior for existing pages.
- [ ] Build the RFID toolbar with search, filter dialog, download, refresh, and create actions using existing shared controls.
- [ ] Render sortable columns for Name, RFID Code, Parent RFID / Group, Active, User, Created, and Actions.
- [ ] Implement create/edit/delete dialogs; local callbacks update only the feature's in-memory records and announce the preview action accessibly.
- [ ] Manually verify filtering, sorting, pagination after a delete, export, cancelled dialogs, optional-field placeholders, and reset-to-seed behavior for the RFID tab.

### Task 3: Build Group RFID table and local CRUD dialogs

**Files:**
- Create: `src/features/rfid/rfid-group-tab.tsx`
- Modify: `src/features/rfid/rfid-dialogs.tsx`

**Interfaces:**
- `RFIDGroupTab` props are `{ records: RfidGroupRecord[]; cards: RfidCardRecord[]; organizations: { code: string; name: string }[]; onCreate(values: RfidGroupFormValues): void; onUpdate(id: string, updates: Partial<RfidGroupFormValues>): void; onDelete(id: string): void; onRefresh(): void }`.
- Group create/edit fields are group name, description, and active status.

- [ ] Build the Group RFID toolbar with search, filter, refresh, and create actions.
- [ ] Render sortable columns for Group Name, Description, Status, RFID Count, Created, Updated, and Actions.
- [ ] Implement local group create/edit/delete flows and calculate RFID counts from each card's optional `groupId`.
- [ ] Manually verify group filters, empty state, counts after assigning/removing a card group, cancelled dialogs, and reset-to-seed behavior.

### Task 4: Wire `/rfid` into the dashboard and review the UI

**Files:**
- Create: `src/features/rfid/rfid-view.tsx`
- Create: `src/app/(dashboard)/rfid/page.tsx`
- Modify: `src/components/layout/navigation.ts`

**Interfaces:**
- `RFIDView(): JSX.Element` initializes card/group state from the fixtures, switches tabs through `QueryTabs`, and supplies mutation/reset callbacks to both tabs.
- `/rfid` renders a `PageHeader` titled `RFID` and `RFIDView`.
- Navigation item `RFID Cards` uses `href: "/rfid"`.

- [ ] Compose both tabs and ensure switching tabs preserves relevant URL state while resetting the page number.
- [ ] Connect the RFID Cards navigation item to `/rfid` and set page metadata.
- [ ] Confirm no fetch or route handler for RFID was introduced.
- [ ] Run `npm run lint`, `npm run typecheck`, and `npm run build`; manually review desktop and narrow viewport layouts in the browser.
- [ ] Inspect the browser Network panel to confirm no `/rfids` or `/parent-rfids` request is issued.

## Scope Coverage Review

- Both tabs, column sets, search/filter/sort/pagination, local CRUD, export, and refresh are covered by Tasks 1–4.
- Mock-only behavior and no persistence are covered by Tasks 1–4.
- Responsive behavior, route/navigation, and no backend requests are covered by Task 4.
- The separate group-detail route remains excluded as specified.
