# KamTeswa Dashboard UI Guide

> Phase 2.1 established the opt-in design-system foundation. Phase 2.2 applied it to
> the authenticated admin shell. Phase 2.3 applies it to the admin login view and auth
> layout only. Phase 2.4 rebuilds the dashboard home around real KamTeswa operations,
> Phase 2.4.1 adds translation hardening, and Phase 2.4.2 performs the dashboard and
> authenticated-shell visual recovery. Phase 2.4.3 replaces the undersized home
> composition with the premium visual acceptance contract. Phase 2.4.4 locks the
> composition and replaces observer-based reveals with deterministic first-paint motion.
> Business module pages remain on legacy markup until their approved migration phases.

## Design Direction

- Premium SaaS administration UI with complete Light, Dark, and System modes.
- RTL-first: use logical properties and verify Arabic before LTR.
- Comfortable density for long sessions; restrained elevation and motion.
- Solid component surfaces; the Dashboard Home hero may use one restrained
  primary/accent light treatment for hierarchy.
- No Tailwind, new package, external design font, or new CDN dependency.

Core palette:

| Role | Value |
|---|---|
| Primary | `#344D70` |
| Accent | `#FF9F34` |
| Background | `#F4F7FB` |
| Surface | `#FFFFFF` |
| Text | `#0F172A` |
| Muted | `#64748B` |
| Border | `#E2E8F0` |
| Success | `#16A34A` |
| Warning | `#F59E0B` |
| Danger | `#DC2626` |
| Info | `#2563EB` |

## Assets and Load Order

The active admin shell loads:

1. Existing vendor and `--zt-*` theme CSS.
2. `/admin/assets/css/design-system.css`
3. `/admin/assets/css/components.css`
4. `/admin/assets/css/animations.css`
5. Page-injected EJS styles.

Shared authenticated-shell JS loads `admin-ui.js` after the existing vendor/theme
runtime and before page-injected scripts.

The login layout independently loads the design-system CSS, local `login.css`,
the existing FormValidation scripts, `admin-ui.js`, and local `login.js`. It does not
load jQuery, Toastr, Google Fonts, Lucide, or the legacy `--zt-*` theme runtime.

Legacy CSS and JS remain available. New work should use the `--kt-*`, `.kt-*`, and
`data-kt-*` namespace; do not add more global overrides.

### Production theme system

Phase 2.4.21.9 supports `light`, `dark`, and `system` throughout the authenticated
dashboard. The chosen mode is stored under `kamteswa.theme`; missing or invalid values
fall back to `system`. A small inline bootstrap runs before CSS, resolves the system
preference with `prefers-color-scheme`, and sets both `data-theme` (effective palette)
and `data-theme-mode` (the user's choice) to prevent a wrong-theme flash.

`admin-ui.js` owns the same contract after load, keeps the navbar's three
`menuitemradio` options synchronized, persists changes immediately, and listens for OS
preference changes while System mode is selected. It reasserts KamTeswa state after
load. The older Zafirra runtime, its pre-CSS bootstrap, and the vendor
`TemplateCustomizer` instance are explicitly bypassed when the KamTeswa bootstrap is
present, so only one runtime may write the theme attributes.

The production palette is token-driven. Light preserves the approved marketplace
dashboard. Dark uses slate/navy surfaces (`#081225`, `#0E1A2B`, `#102033`,
`#13263D`), readable text, `#344D70` / `#8FA9D0` primary roles, and `#FF9F34`
accents. Components consume one shared `--kt-*` surface contract; the Dark selector
changes token values rather than mounting a second override skin. Required legacy
`--zt-*` and Bootstrap variables reference that same contract, so old template purple
does not control authenticated UI.

The vendor `.layout-page::before` blur band is disabled inside the authenticated shell,
and the dashboard navbar no longer draws a page-wide decorative pseudo-element. This
removes the reported horizontal artifacts at their sources while preserving contained
pipeline and table separators. The theme dropdown uses three RTL-aware radio menu
items with a selected check, keyboard navigation, Escape/click-outside dismissal, and
theme-aware surfaces.

## Authenticated Admin Shell

The active `views/admin/layouts/layout.ejs` shell keeps the legacy Bootstrap/template
classes and adds an opt-in KamTeswa layer:

| Area | Contract |
|---|---|
| Root shell | `.kt-shell` |
| Sidebar | `.kt-shell-sidebar`, `[data-kt-sidebar]` |
| Sidebar navigation | `.kt-shell-nav`, existing `.menu-item.active` / `.open` |
| Navbar | `.kt-shell-navbar` |
| Page context | `.kt-shell-navbar__context` |
| Breadcrumb | `.kt-breadcrumb` |
| Legacy content boundary | `#kt-main-content.kt-shell__main` |
| Mobile overlay | `[data-kt-sidebar-dismiss]` |

The `.kt-ui` typography/color foundation is attached to the sidebar, navbar, and
breadcrumb rather than the full body. This prevents unmigrated page content from
inheriting new component styling. Existing `body`, `style`, `script`, `title`,
`currentMenu`, `user`, and `i18n` layout variables remain supported.

All existing sidebar route links and `currentMenu` expressions are authoritative.
Do not rename or remove them during a visual migration. The current shared sidebar
does not contain permission-condition blocks; future permission work must remain
separate from UI migration.

### Responsive sidebar

Below `1200px`, controls marked with `[data-kt-sidebar-toggle]` open the sidebar and
the shared overlay closes it. `admin-ui.js` synchronizes `aria-expanded`,
`aria-hidden`, Escape handling, and the `kt-shell-sidebar-open` root state. Existing
`layout-menu-toggle` classes remain present so the vendor menu runtime stays
compatible. At desktop widths, the custom mobile state is removed and the vendor
layout remains in control.

### Flash-to-toast bridge

`flashToasts.ejs` is optional and emits no markup when no supported flash local is
present. It accepts the first available source:

1. `flashMessages`
2. `flash`
3. an object-valued `messages`

Supported tone keys are `info`, `success`, `warning`, and `danger`; `error` maps to
`danger`, and `warn` maps to `warning`. Values may be strings, arrays, or objects with
`message`, `title`, and `tone`/`type`.

The bridge serializes server values with `<` and Unicode line separators escaped.
`KamTeswaUI.showToast` inserts content with `textContent`. If `KamTeswaUI` is missing,
the partial renders the same safe text into a temporary toast DOM node. Do not put
passwords, tokens, OTPs, cookies, or other secrets in flash messages.

Authenticated layouts provide one fixed `.kt-toast-region`; it sits 84px below the
desktop shell top and 32px from the logical end edge (left in Arabic RTL), has a compact
320px maximum width, and sits 76px below the compact mobile header with 16px side
spacing. Toasts use one premium markup contract:
`.kt-toast__accent`, `.kt-toast__icon`, `.kt-toast__content`,
`.kt-toast__title`, `.kt-toast__message`, and `.kt-toast__close`.

Missing titles receive localized semantic defaults such as `تم بنجاح` and
`حدث خطأ`. Success/info use polite status announcements; warning/danger use assertive
alerts. The default 3.5-second dismiss timer pauses while the toast is hovered or
contains keyboard focus. Enter and exit motion uses only opacity and transform for
240ms, and is fully disabled when reduced motion is requested.

## Dashboard Home

Phase 2.4 is isolated to `/dashboard/index` and uses these files:

- `src/services/admin/dashboardHomeService.js`
- `src/controllers/dashboard/homeController/homeController.js`
- `views/admin/dashboard/index.ejs`
- `/admin/assets/css/pages/dashboard-home.css`
- `/admin/assets/js/pages/dashboard-home.js`

The route remains unchanged. The controller is a thin rendering adapter and delegates
all dashboard counts and recent-list queries to `dashboardHomeService`.

Phase 2.4.1 treats dashboard copy as part of the safe view model. The service supplies
the selected `home.dashboard` Arabic or English dictionary, while the EJS page retains
an explicit bilingual fallback for a stale application process or unavailable runtime
catalog. Dashboard translation lookup must never emit its raw key: unresolved copy
falls back to approved page text rather than rendering `home.dashboard.*`.

The service uses `Promise.all` and `countDocuments` for real KPIs. Latest orders,
pending service providers, active auctions, and latest products are limited to five
records and use narrow aggregation `$lookup`/`$project` stages. This intentionally
avoids the legacy models' `pre(/^find/)` population hooks. Only the safe dashboard view
model reaches EJS; it contains display strings, numbers, status keys/tones, and internal
dashboard action URLs—not raw Mongoose documents, passwords, activation codes, tokens,
phone numbers, or other credentials.

The home layout consists of:

1. a light welcome card with current admin/date metadata and four quick actions;
2. ten real business KPI cards;
3. order, provider, product, and auction operations summaries;
4. latest orders, pending providers, active auctions, and latest products tables;
5. explicit professional empty states with an icon, title, and explanatory sentence
   for every recent-data section.

The current visual-QA baseline uses 34–40px welcome titles, 22–26px section titles,
40–48px KPI values, 14–17px body copy, 24px card padding, 20px card gaps, and 20–24px
card radii. Desktop KPI grids use five columns at 1440px and wider and four below that,
then two columns on tablet and one on mobile.

Product departments are the current category source because no `Category` model exists.
Featured-product counts use the declared `Product.isPremium` field. No trend is displayed
because the current data layer does not provide a trustworthy historical comparison.

Home presentation is scoped under `.kt-dashboard-premium`. Phase 2.4.4 removes every
rendered `.kt-home-*` class, including the former `.kt-dashboard-home` motion hook, so
historical dashboard rules no longer participate in the cascade.

Phase 2.4.2 makes the shell treatment decisive over the legacy RTL template:

- the sidebar is a 280px white surface with five visual groups, a local `KT` mark,
  44–48px navigation rows, and a high-specificity active rule that explicitly removes
  the legacy purple gradient and pseudo-element;
- active navigation uses `rgb(52 77 112 / 8%)`, `#344D70` text/icons, and a
  `#FF9F34` logical-start rail;
- the navbar is a flush 68px white surface with a bottom border instead of the
  detached-template card, and its user summary and controls share one sizing system;
- the breadcrumb is transparent and secondary rather than another white card;
- desktop content uses up to 1440px with 32px horizontal padding;
- home KPI cards use a structured header, 34–38px value, helper/action footer, and a
  five-column grid at 1440px and above;
- empty activity states are capped at a compact 168px rather than creating large blank
  panels.

The Phase 2.4.2 page assets use the `v=2.4.2` cache key. Sidebar links, routes, auth
behavior, login, and inner module pages remain unchanged.

Phase 2.4.3 supersedes the Phase 2.4.2 home-page sizing:

- `.kt-dashboard-premium` is the required page root and scope boundary;
- `.kt-dashboard-hero` is a 248px command area with a 34–40px title, operational
  metadata, and four high-priority action cards;
- `.kt-kpi-card` is at least 232px tall with 24px padding, a 52px icon, a 40–48px
  value, helper text, a details action, and a semantic accent rail;
- `.kt-operations-grid` keeps four large status-summary cards in one row on wide
  desktop;
- latest orders and products remain full width, while providers and auctions share
  the two-column `.kt-activity-grid`;
- activity empty states remain compact at 172px and always include a title and
  explanatory sentence;
- shared shell rows are 46–50px tall, navbar height remains 68px, and the toast is
  exactly 320px wide at 84px from the top and 32px from the RTL left edge.

The Phase 2.4.3 page assets use the `v=2.4.3` cache key. This phase does not alter
sidebar URLs, controller routes, authentication behavior, the login view, or inner
module pages.

Phase 2.4.4 supersedes the Phase 2.4.3 rendered class and motion contracts:

- the root renders as `.kt-dashboard-premium.kt-dashboard-preload`, owns literal
  32px desktop padding and gap, and expands through the parent shell padding without
  narrowing its maximum 1440px canvas;
- the hero is at least 260px high with 32px padding, 28px radius, 38px title, a
  60/40 content/action split, and four 74px action tiles;
- ten `.kt-dashboard-kpi-card` elements use five columns at 1440px+, three on laptop,
  two on tablet, and one on mobile; values remain 44px and icons 52px;
- operation cards are at least 250px high and keep four columns on wide desktop;
- activity cards use 24px headers and bodies, 24px radius, and empty states capped at
  180px with 48px icons, 16px titles, and 14px descriptions;
- sidebar branding uses the local 44px `KT` mark and `Admin Portal`, 48px/14px-radius
  navigation rows, and the primary/accent active treatment;
- navbar controls are 36px tall and the breadcrumb is capped at 48px.

The deferred page runtime waits for two `requestAnimationFrame` callbacks before adding
`html.kt-dashboard-mounted`. Hero motion runs for 700ms from
`translateY(28px) scale(.985)`. KPI cards run for 500ms with an 80ms per-card delay;
operations and activity follow with 550ms transitions. The full visible sequence ends
at approximately 1400ms, after which the preload class is removed so normal hover
transitions have no stagger delay.

`?ktMotionDebug=1` adds a safe, fixed badge reading
`Dashboard motion: mounted`. It contains no account or request data. Reduced-motion
preference mounts immediately, removes the preload state, and disables animation and
transitions.

The Phase 2.4.4 page assets use the `v=2.4.4` cache key. Routes, authentication, login,
models, and other module pages remain unchanged.

## Admin Login

The Phase 2.3 login is isolated to:

- `views/admin/layouts/layoutAuth.ejs`
- `views/admin/auth/auth-login.ejs`
- `/admin/assets/css/pages/login.css`
- `/admin/assets/js/pages/login.js`

The preserved request contract is:

```text
POST /dashboard/login
_csrf, deviceId, email, password, rememberMe
```

Do not rename these fields, change the method, or bypass server validation. The client
keeps the prior validation rules: email is required and must be valid; password is
required and has a minimum length of six.

The login JavaScript submits URL-encoded form data with same-origin credentials and
`X-Requested-With: XMLHttpRequest`, matching the prior AJAX contract. During the
request, `KamTeswaUI.setLoading` disables the submit button. Network and API error
messages are inserted into the alert using `textContent`; non-string/raw responses and
stack traces are replaced with the localized generic error.

Initial server-rendered login errors may be supplied through a string `errorMessage`,
or an `error`/`danger` value inside `flashMessages` or `flash`. Only string values are
rendered and EJS escapes them.

The auth layout does not include the authenticated shell's `flashToasts.ejs` bridge.
The login runtime also never calls `KamTeswaUI.showToast`: successful authentication
keeps the submit button loading and redirects immediately, while failed authentication
restores the button and renders the safe message only in `.kt-login-error`. This policy
is isolated to `/dashboard/login`; authenticated layouts continue to include and use
the shared flash-to-toast bridge.

Successful authentication stores one `success` flash in the admin session before
returning the existing redirect URL. The authenticated dashboard layout reads that
flash through `flashToasts.ejs`, after `admin-ui.js` is available, and displays
`مرحبًا بك في لوحة التحكم` with the standard success toast. Calling `req.flash()` while
rendering consumes the queue, so refresh does not repeat the welcome message; a later
successful login creates a new one. The login page never includes or consumes this
bridge.

The page uses the local Cairo asset and local Tabler icons. The existing Firebase
messaging scripts remain temporarily because they populate the preserved `deviceId`
field before login; removing or replacing that notification integration requires a
separate behavior review. Because `public` is mounted at the site root, the browser
client loads from `/notification/firebase.js` and registers
`/notification/firebase-messaging-sw.js`; neither URL includes a `/public` prefix.
Unsupported browsers, denied notification permission, and Firebase failures leave the
existing `deviceId=default` fallback intact. Permission state, device tokens, and
Firebase errors are never written to the browser console.

### Login QA and motion

Phase 2.3.1 uses a text-based `KT` mark with the visible `KamTeswa` wordmark because
the former upload URL had no matching local file and the available image logos belong
to older templates/products. Do not restore an image until an approved KamTeswa asset
exists at a stable local path.

Validation styling is gated by `.is-validation-visible`. Login fields receive that
class only after a form submit attempt; focus, typing, and blur alone do not reveal
red borders or validation messages. Server authentication errors continue to use the
separate `.kt-login-error` alert.

Phase 2.3.2 adds a browser-visible mount boundary. The login wrapper is rendered with
`.kt-login-preload`; `login.js` waits for two `requestAnimationFrame` callbacks, adds
`.kt-login-mounted`, and removes the preload class. This guarantees that Chrome paints
the initial transform/opacity state before the CSS keyframes begin. The two
page-specific assets use the static cache version `v=2.3.2`; increment it only when a
later approved login phase changes either asset.

Login entrance motion uses CSS keyframes after that small JavaScript class toggle:

- `kt-login-fade-up` for the card, brand panel, and staggered form groups
- `kt-login-hero-in` for the brand panel's slight horizontal entrance
- `kt-login-fade-in` for quiet supporting content
- `kt-login-soft-scale` for the submit button
- `kt-login-error-in` for the alert
- `kt-login-orb-drift` for one-time background-shape entrance

The final stagger completes within about `980ms`; interaction is never blocked. Keyframes
animate only `opacity` and `transform`. Under `prefers-reduced-motion: reduce`, the
login page disables all animations, transitions, and smooth scrolling.

The password button remains `type="button"` and controls the input referenced by its
`aria-controls` value. Each click prevents the button's default action, switches the
input between `password` and `text`, updates `aria-pressed`, icon, accessible label,
and title, then restores focus to the password input. The behavior is dependency-free
and direction-neutral.

### Phase 2.3.5 premium opening animation

Phase 2.3.5 adds a full-screen opening layer before the login form. It contains the
local text-based `KT` mark, `KamTeswa Admin` title, and an accent shimmer line. The
overlay is immediately visible, remains active for `700ms`, and then fades for
`420ms`. The page reveal starts as the overlay fades, and its last staggered element
finishes no later than `1400ms` after initial load.

The guaranteed intro bootstrap lives in `layoutAuth.ejs`. This is intentional: the
layout engine extracts scripts declared inside the login view and emits them later, so
a view-level bootstrap is not guaranteed to execute beside the overlay markup. The
bootstrap runs before the external validation/runtime scripts and owns this lifecycle:

```text
data-kt-login-intro="running"
→ html.kt-login-mounted + data-kt-login-mounted="true"
→ data-kt-login-intro="done"
→ overlay removed after its fade transition
```

The mounted content uses only opacity and transform:

- login card: `translateY(36px) scale(.96)` to its final state;
- hero panel: `translateX(-32px)` to its final state;
- title and form fields: `translateY(20px)` with `80ms`, `180ms`, and `280ms` delays;
- remember option, submit, and footer: `360ms`, `460ms`, and `540ms` delays;
- decorative orbs: one-time soft drift.

The CSS also contains a `1400ms` no-JavaScript visibility fallback so the overlay can
never trap the page. With `prefers-reduced-motion: reduce`, the page mounts immediately,
the overlay is hidden and removed quickly, and all animations, transitions, and smooth
scrolling are disabled.

Password clicks remain handled through one guarded document-level delegated listener
in the capture phase, including clicks originating from the eye icon. Each click
prevents form submission, switches between `password` and `text`, updates the icon and
accessible state, and returns focus to the password field. Duplicate script evaluation
cannot bind a second handler.

The page CSS remains at `v=2.3.5`; the login runtime uses `v=2.3.6` after the
login-only toast suppression. Open `/dashboard/login?ktDebug=1` to display the safe
bottom-left QA badge. During the opening, `Login intro` reads `running`; after the
reveal it must show:

```text
Login intro: done
Login mounted: true
Password toggle: ready
```

The badge contains no form values or credentials and remains hidden on the normal
`/dashboard/login` URL. It also reports the handled toggle-click count and current
password input type. A successful two-click test changes the count from `0` to `2` and
the type from `password` to `text` and back to `password`.

## Tokens

Use semantic variables rather than hard-coded colors:

```css
.example {
  padding: var(--kt-space-4);
  border: 1px solid var(--kt-color-border);
  border-radius: var(--kt-radius-lg);
  background: var(--kt-color-surface);
  color: var(--kt-color-text);
  box-shadow: var(--kt-shadow-xs);
}
```

Token groups cover palette, typography, spacing, radii, shadows, controls, layout,
motion, and z-index layers. All new components use a local/system font stack.

## Component Primitives

Wrap newly migrated sections in `.kt-ui` for the foundation font, sizing, and box model.
Existing pages do not need that wrapper until their approved migration phase.

### Buttons

```html
<button class="kt-btn kt-btn--primary" type="button">Save</button>
<button class="kt-btn kt-btn--secondary" type="button">Cancel</button>
<button class="kt-btn kt-btn--danger kt-btn--sm" type="button">Delete</button>
```

Variants: `primary`, `accent`, `secondary`, `ghost`, `danger`; sizes: `sm`, default,
`lg`, and `icon`.

### Fields and selects

```html
<div class="kt-field">
  <label class="kt-label" for="status">Status</label>
  <select class="kt-select" id="status" name="status">
    <option value="">Choose</option>
  </select>
  <p class="kt-field__hint">Choose the current operational state.</p>
</div>
```

Use `.is-invalid` on `.kt-field`, or `aria-invalid="true"` on a control. Link errors
with `aria-describedby`.

### Cards, badges, and status

```html
<section class="kt-card kt-hover-lift">
  <header class="kt-card__header">
    <h2 class="kt-card__title">Orders</h2>
    <span class="kt-status kt-status--success">Active</span>
  </header>
  <div class="kt-card__body">…</div>
</section>
```

Semantic badge/status modifiers: `primary`, `success`, `warning`, `danger`, `info`,
and `neutral`.

### Tables and filters

Use `.kt-table-card > .kt-table-responsive > .kt-table` so narrow screens scroll
horizontally. Use `.kt-filters`, `.kt-filters__grid`, and `.kt-filters__actions` for
filter panels. Do not remove server-side validation or existing filter behavior during
a visual migration.

### Pagination

Use `.kt-pagination` with `.kt-page-link`. Mark the active item with
`aria-current="page"` and unavailable items with `aria-disabled="true"`.

### Empty and loading states

Use `.kt-empty` with title, description, and optional actions. Skeleton modifiers are
`kt-skeleton--text`, `--title`, `--avatar`, and `--button`. Skeletons must not replace
real accessible labels.

## Modal and Toast Helpers

Open an opt-in modal:

```html
<button data-kt-modal-open="example-modal">Open</button>
<div class="kt-modal" id="example-modal" aria-hidden="true" hidden>
  <section class="kt-modal__dialog" role="dialog" aria-modal="true"
           aria-labelledby="example-title">
    <header class="kt-modal__header">
      <h2 class="kt-modal__title" id="example-title">Title</h2>
      <button class="kt-modal__close" data-kt-modal-close aria-label="Close">×</button>
    </header>
    <div class="kt-modal__body">…</div>
  </section>
</div>
```

JavaScript API:

```js
KamTeswaUI.showToast({
  tone: 'success',
  title: 'Saved',
  message: 'Changes were saved successfully.'
});

KamTeswaUI.setLoading(document.querySelector('#save-button'), true);
KamTeswaUI.openModal('example-modal');
KamTeswaUI.closeModal('example-modal');
```

Messages are inserted with `textContent`, not HTML. Existing Bootstrap modals and
Toastr calls remain supported and are not automatically converted.

## Motion and Accessibility

Utilities:

- `.kt-animate-fade-in`
- `.kt-animate-slide-up`
- `.kt-hover-lift`
- skeleton shimmer through `.kt-skeleton`
- modal enter through `.kt-modal.is-open`
- toast slide through `.kt-toast.is-visible`

`prefers-reduced-motion: reduce` disables these animations and removes lift transforms.
Interactive primitives have visible keyboard focus. Modals contain keyboard focus,
restore trigger focus after closing, and support Escape. Continue to supply localized
`aria-label` text in EJS.

## Adoption Rules for Phase 2.4+

1. Migrate one approved page or shared partial at a time.
2. Prefer `.kt-*` primitives; do not overwrite Bootstrap globally.
3. Keep route names, input names, CSRF fields, AJAX hooks, IDs, and response behavior.
4. Verify Arabic RTL, English LTR, keyboard focus, mobile table scrolling, and reduced
   motion.
5. Do not remove legacy assets until repository usage and visual regression checks prove
   they are unused.
