# Admin Portal Shell Migration — Epic 14A1

## Scope

Epic 14A1 migrates only the authenticated Admin shell to the shared navigation system. Admin page content, tables, forms, cards, modals, business rules, routes, and backend middleware remain unchanged. The Agent shell introduced in Epic 13A1 is not altered.

## Shell architecture

At `lg` and above, Admin uses shared `Sidebar` with local collapsed state, shared `Topbar`, and a flexible full-width content area. Below `lg`, Topbar opens shared `MobileSidebar`, which provides overlay/Escape/close dismissal, body-scroll lock, independent navigation scrolling, and close-after-navigation events.

The shell preserves existing header/default page slots instead of requiring any Admin page rewrite. It has no global narrow content maximum, so leads, support, round-robin, website settings, and other operational tables retain their page-level layout and horizontal-scroll behavior.

The existing notification bell/list and account `Dropdown` are retained in Topbar slots. Existing unread count, mark-read behavior, notification navigation, Profile, and Logout continue unchanged.

## Navigation information architecture

| Group | Items |
| --- | --- |
| Main | Dashboard |
| Operations | Registrations, Agents & Partners, Leads, Premium Lead Approvals, Support, Round Robin Monitor, Lead Credit Monitor |
| Website | Website Settings, Coupons |
| Partner & Finance | Partner Withdrawals, Partner Receipt Settings, Payments, Affiliate Commissions |
| System | Agent Onboarding Settings |

Profile and Logout remain in the sidebar footer and account menu. Membership/partner-financial settings are still accessed through their existing Admin Dashboard controls because the backend exposes no standalone settings page for them.

## Admin and Agent separation

`AuthenticatedLayout` selects the Admin composition only when the existing `auth.user.is_admin` capability is true. It does not add shared props, client queries, or data reads for normal Agents/Partners. Server-side `admin` middleware remains the authorization boundary. The legacy Admin menu is disabled, preventing double navigation; the legacy Agent branch remains separate.

## Responsive and accessibility behavior

- Desktop sidebar is expanded by default, collapsible locally, and independently scrollable for the larger Admin IA.
- Collapsed items keep accessible labels and native title hints; active routes use the shared active state.
- Mobile/tablet displays one Topbar menu trigger and one drawer; navigation closes after an item is selected.
- Topbar account and notification controls retain keyboard access and visible focus.
- Route patterns keep nested Admin detail/editor pages active under their parent list/settings item.

## Deferred work

No Admin dashboard/page redesign, individual table/form/card migration, Agent page work, public landing polish, notification redesign, or product feature was introduced in this shell-only phase.

## Dashboard Mission Control — Epic 14B1

The Admin Dashboard now follows a calm Mission Control hierarchy: PageHeader, four existing lead/distribution KPIs (Leads Today, Leads This Month, Assigned Leads, New Pipeline Leads), a restrained distribution-attention alert, operational health/quick-access cards, operator breakdown, bounded recent lead activity, and the existing membership/Partner settings forms. It uses no new dashboard queries or analytics.

Pending, failed, and closed assignment counts are shown with text-bearing semantic badges. Quick actions reuse existing Registrations, Premium Lead Approvals, Support, Leads, Round Robin Monitor, and Website Settings routes. The KPI grid progresses from one column on mobile to four at wide desktop, while the operator table retains its responsive table container. Existing backend props remain authoritative; registrations, payments, membership health, and Support workload are not shown because this dashboard read model does not provide them.

## Registration Review — Epic 14B2

Registrations now presents the existing server-filtered, oldest-first pending-review queue with labelled search/status filters, semantic status badges, responsive table pagination, and a focused review action. Registration detail separates review-safe applicant information from status/timestamps/reviewer context, with visible resubmission and rejection context where already supplied. Only pending-admin-approval registrations show approve/reject controls; rejection remains a required server-validated plain-text reason. No lifecycle, email, resubmission, applicant-data, or approval logic changed.

## Leads Listing — Epic 14B3A

The Admin Leads workspace now uses a PageHeader, protected current-filter CSV export, filtered-result KPI, responsive filter card, compact operational table, and shared pagination. It preserves the existing search, operator, distribution status, pipeline status, assigned agent, repeat submission, credit charged, from-date, and to-date query parameters. Pending and failed assignments use text-bearing semantic badges; existing pending diagnostic messages remain compact secondary context. Lead detail and retry actions remain deferred.

## Lead Detail — Epic 14B3B

The Admin Lead Detail view is now a responsive two-column operational workspace: a PageHeader with a clear return to Leads and text-bearing assignment/pipeline status, a primary review column for prospect/quotation context, assignment and pipeline activity, and internal notes, plus a supporting rail for assignment ownership/status, repeat-submission and lead-credit context, the existing pending-assignment diagnostic, and bounded reminder history. It collapses to one calm reading order on mobile without horizontal overflow.

The view reuses only the existing safe detail read model. It intentionally preserves the controller's newest-first history order and its limits of 50 activities, 20 notes, and 20 reminders; no additional activity, diagnostic, credit, or prospect data is queried or exposed. The existing retry route and form submission are preserved solely inside the pending diagnostic card. Assignment pipeline, diagnostics, retry behavior, notes, reminders, credit handling, and route behavior remain unchanged.

Cards use SectionHeader, semantic StatusBadge, Alert, EmptyState, and Button primitives. The page presents no IC numbers, identity metadata, wallet balances, payment instruments, internal IDs, or diagnostic detail beyond the existing authorized props.

## Lead Retry and Diagnostic Action — Epic 14B3C

Pending Lead assignments present one clear diagnostic hierarchy in the existing Lead Detail sidebar: pending status, a human-readable reason, the existing safe diagnostic message, and the action. Retry remains visible only when the server-provided `retry_url` is present; the UI does not recreate retry eligibility rules and no listing retry action was added.

Selecting **Retry Distribution** opens the shared default ConfirmDialog. Its high-level copy explains that the retry uses current eligibility and Lead Credit rules without promising an assignment. The shared Button prevents duplicate submission while processing, and the dialog cannot be dismissed during the request. The existing redirect refreshes the server props after completion. Existing controlled retry outcomes render through Alert: successful assignment is success-styled; an assignment that remains pending or is no longer retryable is warning-styled. Unexpected request errors use a generic danger Alert and never expose raw error data.

No distribution, credit, same-day reuse, round-robin, diagnostic, retry-history, status, route, authorization, or backend behavior changed. Admin middleware remains the authority boundary, and no polling or new queries were introduced.

## Premium Lead Approvals — Epic 14B4

Premium Lead Approvals is now a labelled, newest-first review queue with the existing search, status, source operator, selected operator, screening-reason, and Malaysia date-range filters. Its responsive table keeps prospect contact, submitted time, original operator selection, human-readable screening labels, status, and a direct review action together; it retains the existing paginator and adds no count query or bulk workflow.

The review page separates safe submission information and quality-review context from the original operator snapshot and approval decision. Screening reasons remain Admin-only operational labels, presented as text-bearing warning badges and alerts rather than raw screening metadata. Approval and rejection controls appear only when their existing server-provided URLs are present. Both use shared confirmation dialogs: approval describes conversion into normal distribution using the original submitted time and existing rules; rejection clearly identifies permanent removal. Terminal records expose no duplicate decision controls and existing redirect/flash feedback remains authoritative.

No quality-gate, quarantine, approval conversion, original-submitted-time, same-day reuse, distribution, credit, ordering, public response, route, authorization, or query behavior changed. No internal quality implementation, IDs, credit data, or public disclosure was added.

## Support — Epic 14B5

Admin Support now presents the existing paginated support queue with labelled search, category, and status filters. The table prioritizes subject, safe agent identity and MTA number, backend-provided category labels, text-bearing local status badges, update time, and a direct ticket view. It preserves the server-side query strings, status values, category set, ordering, and pagination without exposing listing email data.

Ticket detail is a responsive conversation-and-operations workspace. It preserves the backend's oldest-first message order, displays only sender role, timestamp, and plain-text body, and keeps safe ticket metadata and existing status control in the supporting rail. Reply remains unavailable for closed tickets; status updates remain the existing server-authorized action, including any already-supported status transition behavior. No new workflow rules are imposed in the browser.

Balance Inquiry remains a normal Support category. When the existing safe detail prop shows a completed refund, the page presents its amount and timestamp rather than a second action. Otherwise, the current balance-refund form accepts the existing MYR amount and optional note, then uses a shared confirmation dialog before invoking the unchanged Admin route. The dialog makes the financial E-Wallet credit and automatic ticket resolution explicit without exposing ledger, wallet balance, payment, or provider data. Refund accounting, audit rows, reply creation, ticket resolution, notifications, exactly-once handling, and authorization remain backend-owned. No new queries, attachments, live chat, or notification changes were added.

## Coupons — Epic 14B6

Coupons is now a Membership promotions workspace with the existing code/description search, newest-first aggregate listing, responsive table, and pagination. Each row presents the curated human-readable discount (percentage or MYR), Membership-only applicability, first-purchase context, validity window, aggregate usage against total and per-user limits, active/inactive status, and existing edit/toggle actions. Open-ended validity and unlimited limits are stated as such; no redemption identities or extra redemption queries are exposed.

Create and edit share one labelled form hierarchy for the current Coupon fields: code, optional description, discount type and human entry value, Membership applicability, validity, usage limits, first-paid-membership flag, and active state. The server retains code normalization, validation, percentage/fixed conversion, defaulting, and all Coupon rules. On edit, the code is visibly immutable and the form explains that changes affect future validation only; historical Membership order snapshots remain unchanged.

The existing reversible activate/deactivate endpoint is presented with a shared default confirmation dialog and loading protection. There is no delete control, code cloning, stacking, Lead Credit scope, campaign analytics, bulk action, checkout change, or modification to eligibility, referral restrictions, economics, redemption limits, or historical data.

## Website Settings Workspace — Epic 14B7A

Website Settings is now a route-authoritative CMS operations workspace: PageHeader and scoped live-site link, compact responsive operator selector, existing five-page selector, selected-page/draft context, theme summary, and authoritative published-section overview. Operator and page links continue to drive the existing server read model; no parallel client-side CMS state or route layer was added.

The operator selector keeps the nine websites compact while showing active state, slug, locale, and small theme swatches. The theme summary shows current safe colour values alongside swatches and a public-safe logo when configured; every colour also retains its textual hex value. It is display-only in this phase. Published sections remain in their existing order and show label/type, locale, published revision/time, draft indicator, and concise field summaries rather than raw JSON. `quotation_form` is explicitly marked System Controlled with user-facing application-control guidance. Draft content is not shown in the published summary.

Existing section Edit, History, Draft Publish, Draft Preview, and scoped live-site links remain available without redesigning their detailed workflows. Section editor, asset upload, preview/publish UX, revision history/rollback, theme editing, reordering, visibility controls, locale switching, and Public website presentation are deferred. No CMS lifecycle, content, query, security, or publication behavior changed.

## CMS Section Editor and Asset Upload — Epic 14B7B

The CMS Section Editor now establishes operator, page, section, locale, published revision, and current-draft context before the schema-driven form. It clearly distinguishes an existing Draft from the first save that creates the next revision, and repeatedly states that **Save Draft does not publish**. The existing Draft save route, redirect, flash feedback, and secondary existing Publish Draft action are retained without changing the lifecycle.

Fields are rendered from the existing CMS schema as labelled short text, long text, CTA, image, and structured-list controls. Structured list content is shown as individual existing rows and fields rather than a free-form JSON editor; no new schema fields or generic content model were introduced. Schema-approved top-level image fields show a constrained preview, native JPG/JPEG/PNG/WebP chooser, clear uploading state, and safe error feedback. The current upload endpoint remains authoritative for actual-image/MIME checks, 5 MB maximum, approved context, operator-scoped generated public path, and the rule that upload only updates the Draft form until it is saved and later published.

Preview/Publish UX, History/Rollback, Theme Editing, Public pages, media-library behavior, drag/drop, cropping, asset deletion, reordering, visibility controls, and locale switching remain deferred.

## CMS Draft Preview and Section Publish — Epic 14B7C

Draft Preview is now named explicitly at both existing admin entry points: the Website Settings workspace presents **Preview Draft** only when the server reports a page-level Draft, and an editable section editor presents the same action when that section has an active Draft. The existing admin-only preview route remains the authority; preview stays a pure read and no share token or public preview path was added. The preview banner is persistent while scrolling, states **Draft Preview** and **Not Live**, identifies the operator, page, and locale, and offers the existing route-authoritative return to Website Settings plus a separately labelled View Live Website link.

The quotation page continues to use its production form renderer in preview. In the existing preview context the form is visibly read-only and its fieldset and submit button are disabled, while normal public quotation behavior remains unchanged. No CMS edits can be made from a preview page.

Publishing remains available only for editable sections that already have an active Draft; system-controlled quotation content still has no publish action. The workspace and editor now use the shared ConfirmDialog before the existing publish route. Its copy scopes publication to the current Draft section and explicitly says other Draft sections on the page remain unpublished. It includes the existing Draft revision number where the editor/read model already provides it, prevents duplicate submissions while loading, and preserves the server redirect and flash status as the post-publish source of truth. No revision lifecycle, rollback/history UI, theme editing, renderer data, public landing design, route, authorization, or CMS business behavior changed.

## CMS Revision History and Rollback — Epic 14B7D

Revision History now starts with the section’s operator, page, section, and locale context, direct return paths to Website Settings and the section editor, plus a current-state summary. The summary distinguishes the current Published revision from an active Draft using text-bearing semantic badges and explicitly explains that Published content is live while a Draft is not. The existing locale-scoped, newest-first revision list is shown in a responsive shared DataTable with revision number, stored status, readable creation/publication dates, safe creator display name, concise structured-content summaries, and an accessible action label. It does not show raw JSON, user IDs, emails, or add any history query or pagination behavior.

Only server-designated Archived revisions expose **Restore to Draft**. The shared confirmation dialog states that restoring copies the selected archived content into Draft and that the live website remains unchanged until the existing Publish Section flow is used. When an active Draft exists, it warns that the current Draft will be replaced; otherwise it states that a new Draft will be created. Loading prevents duplicate restore submissions, the existing redirect refreshes history/current state, and the existing flash is shown through Alert. A Preview Draft link appears only when the existing revision list reports an active Draft. `quotation_form` remains server-denied and receives no history or rollback UI path.

The underlying rollback architecture is unchanged: an archived revision updates the active Draft if present, otherwise creates the next Draft revision; it never changes Published content directly. Direct-live restore, restore-and-publish, revision diffs, bulk/page-wide rollback, theme/asset history, locale switching, theme editing, and Public visual changes remain deferred.

## CMS Theme Editor — Epic 14B7E

The Theme editor now identifies the selected operator website before any controls and makes its immediate-live model explicit: CMS content follows **Draft → Preview → Publish**, while theme colors and logo follow **Save → Live immediately** and never create content revisions. The page is scoped to one operator website by its name and public slug; it does not imply any global theme or affect other operators.

The existing five-color theme schema remains the only editable surface: primary, secondary, accent, background, and text colors. Each is presented with a named swatch, an accessible textual `#RRGGBB` input, field-specific helper text, and the existing server validation feedback. A restrained color-context panel reflects the currently entered semantic values without creating a simulated landing-page preview. Server-side uppercase normalization and rejection of malformed colors, CSS values, URLs, and unknown fields remain authoritative.

The existing `theme_logo` upload remains a native JPG/JPEG/PNG/WebP (5 MB) upload using the current approved asset context and server-generated public path. The editor shows the public current logo but never exposes the raw path or arbitrary `theme_config`. Upload feedback and the immediate-live warning distinguish it from section-image uploads, which belong to a Draft until published. Theme drafts, history, rollback, presets, arbitrary CSS/JSON, font controls, media-library features, cropping, and Public landing visual changes remain intentionally absent.

With this phase, the current Admin Website Settings/CMS surfaces have been migrated: workspace, section editor, approved asset upload presentation, Draft Preview, section publish, revision history, rollback, and Theme editor. The existing backend models, transactional theme save, content-revision lifecycle, preview/publish routes, security boundaries, and Public rendering remain unchanged.

## Lead Credit Monitor — Epic 15A2

Lead Credit Monitor now follows the Admin operational hierarchy: PageHeader, four existing aggregate KPI cards, labelled server-side filters, the existing lowest-balance-first agent table, and existing operator aggregate context. It uses no new queries or analytics. The original search, operator, eligibility, account state, Membership state, and Lead Credit state filters remain server-authoritative, and the existing paginator keeps its query string behavior.

Each agent row keeps the current safe monitor fields only: name, MTA number, operator, account/Membership state, Lead Credit balance, existing eligibility state/reason, and latest Lead Credit transaction type/date. Account, Membership, and eligibility display text-bearing local StatusBadge mappings; the browser does not recompute eligibility. Lead Credit values are labelled as credits rather than money, and no E-Wallet, Partner Wallet, transaction IDs/references/metadata, payment-provider data, IC data, or other financial data is added.

The page exposes no grant/debit action because the existing monitor read model and route do not provide one; no mutation route or UI was introduced. DataTable, Pagination, EmptyState, Card, KpiCard, PageHeader, SectionHeader, FormField, Input, Select, Button, and StatusBadge now provide the shared responsive, focus, spacing, and table-density behavior. Existing monitor business rules, ordering, bounded query behavior, and authorization remain unchanged.

## Agents & Partners — Epic 15A3

Agents & Partners now uses the shared Admin workspace hierarchy: PageHeader, the existing server-side search-only filter, a responsive DataTable, shared Pagination, and EmptyState. The listing keeps the existing safe directory context—agent name/MTA number, operator, latest loaded Membership status, Partner flag, account state, and Lead Credit balance—without adding counts, filters, queries, financial data, or agent-side behavior. Each status is text-bearing through local StatusBadge mappings, and the existing Manage route remains the sole table action.

Agent detail now separates safe account/Lead Credit context, current Membership state, Partner management, and the existing Lead Credit adjustment form into shared Cards. Existing routes and services remain authoritative for Partner assignment, allocation validation, Partner removal, Membership activation/renewal, and Lead Credit grant/debit validation. Partner removal and the existing credit adjustment now use shared ConfirmDialog with loading protection; the credit confirmation states that server validation and balance protections still apply. No new action, permission, Partner-financial data, E-Wallet data, lifecycle, search behavior, or business rule was introduced.

## Partner Withdrawals — Epic 15A4

Partner Withdrawals now uses a shared payout-management hierarchy: PageHeader, labelled existing search/status/request-date filters, responsive DataTable, Pagination, EmptyState, and local text-bearing status badges. The paginated listing preserves the existing newest-request-first order and current safe Partner/MTA, MYR amount, request date, status, approved/paid/rejected context, and direct detail action. It adds no query, filter, wallet ID, ledger reference, or financial data.

Withdrawal detail now presents the existing safe Partner identity, requested amount, status, dates, payment reference, existing Admin Commission Wallet total/reserved/available context, lifecycle guidance, and receipt availability using shared Cards, KpiCard, Alert, and StatusBadge. Pending requests expose the existing approve/reject routes through ConfirmDialog; approval explicitly retains the reservation, while rejection explicitly releases it. Approved requests expose only the existing Mark Paid form, including required payment reference and optional short internal note, followed by a high-consequence confirmation. Paid requests expose the existing protected receipt download and no state-changing actions. The server remains authoritative for all transition conflicts, row locking, reservation handling, permanent debit, exactly-once ledger entry, receipt snapshot creation, and validation.

## Payments / Billing — Epic 15A5

The Admin Payments workspace now uses PageHeader, labelled existing search/order-type/payment-method filters, responsive DataTable, Pagination, StatusBadge, and EmptyState. The existing newest-first paginated listing retains Membership and Lead Credit order types, safe buyer identity, amount, payment method/provider, payment status, paid time, and fulfilment time. Payment and fulfilment are intentionally shown separately: a paid order without `fulfilled_at` is labelled as pending fulfilment rather than implied complete. A single existing SQL join now always selects the safe buyer name/email for the Admin list; it adds no query or financial data.

The shared order-detail component now renders the Design System workspace only for the existing Admin route; the Agent Billing branch remains unchanged. Admin detail presents current safe order/buyer context, MYR amount, payment method/provider, payment and fulfilment status, timestamps, and up to the existing bounded ten safe payment attempts with state/timing. Failed and paid-but-unfulfilled states receive restrained safe alerts. Provider references, checkout URLs, callback bodies, metadata, headers, signatures, secrets, wallet ledger IDs, commission/Partner economics, and any reconciliation action remain absent. No payment, callback, fulfilment, E-Wallet, Membership/Credit, provider, query, or authorization behavior changed.
