Skip to main content

webscheduler-frontend

WebScheduler frontend contract — Vite entry points, SPA lifecycle and navigation, dark mode (FOUC prevention + Tailwind class strategy), shared fetch layer, profile surface, avatar/initials system, design system (`xs-card`, `xs-btn`, `xs-actions-container`), `layouts/app` layout rules, and appointments toolbar layout. Use whenever you're working in `resources/js`, `resources/scss`, `app/Views/`, the SPA layer, Vite config, theme toggles, view layouts, avatars, or any styling/layout decision. Triggers on phrases like "view", "layout", "SPA", "Vite", "dark mode", "theme", "Tailwind", "SCSS", "avatar", "initials", "xs-card", "xs-btn", "appointments view", "toolbar", "scripts section", "DOMContentLoaded", "spa:navigated", or any JS/CSS/template change.

Informations de source

Dépôt
niloc95/xscheduler_ci4
Dernière activité de la source
21 juillet 2026 à 17:18
Langue détectée de SKILL.md
anglais
Étoiles
1
Forks
0

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
webscheduler-frontend
description
WebScheduler frontend contract — Vite entry points, SPA lifecycle and navigation, dark mode (FOUC prevention + Tailwind class strategy), shared fetch layer, profile surface, avatar/initials system, design system (`xs-card`, `xs-btn`, `xs-actions-container`), `layouts/app` layout rules, and appointments toolbar layout. Use whenever you're working in `resources/js`, `resources/scss`, `app/Views/`, the SPA layer, Vite config, theme toggles, view layouts, avatars, or any styling/layout decision. Triggers on phrases like "view", "layout", "SPA", "Vite", "dark mode", "theme", "Tailwind", "SCSS", "avatar", "initials", "xs-card", "xs-btn", "appointments view", "toolbar", "scripts section", "DOMContentLoaded", "spa:navigated", or any JS/CSS/template change.
# WebScheduler — Frontend Contract ## 1. Canonical Entry Points (Vite) Defined in `vite.config.js` `rollupOptions.input`: | Key | File | | --- | --- | | `main` | `resources/js/app.js` | | `style` | `resources/scss/app-consolidated.scss` | | `spa` | `resources/js/spa.js` | | `dark-mode` | `resources/js/dark-mode.js` | | `theme-bootstrap` | `resources/js/theme-bootstrap.js` | | `app-layout-init` | `resources/js/layout/app-layout-init.js` | | `public-booking` | `resources/js/public-booking.js` | | `public-booking-bootstrap` | `resources/js/public-booking-bootstrap.js` | | `unified-sidebar` | `resources/js/unified-sidebar.js` | | `charts` | `resources/js/charts.js` | | `setup` | `resources/js/setup.js` | `resources/js/material-web.js` was removed — the `@material/web` bundle was unused (zero `<md-*>` elements in production views). **Non-entry utility modules** (import only, not Vite entry points): - `resources/js/currency.js` — currency formatting, loads settings from `/api/v1/settings/localization` - `resources/js/utils/avatar.js` — avatar initials helper (see §6 below) ## 2. Lifecycle Contract Core lifecycle for authenticated surfaces: 1. `app.js` loads 2. `spa.js` intercepts links/forms 3. `initializeComponents()` runs on load and after `spa:navigated` 4. View code uses `xsRegisterViewInit(fn)` 5. Initializers **must be idempotent** via dataset guards Body-level overlays or modals created by JS (outside `#spa-content`) must explicitly clean themselves up on `spa:leaving`. Hiding them is not sufficient when the DOM node survives SPA swaps; teardown must remove stale nodes, listeners, and pending timers so the next initializer run can recreate fresh state. **Do not add bare `DOMContentLoaded`-only logic for app surfaces that can SPA-navigate.** ## 3. SPA Navigation Contract `spa.js` behavior: - Swaps `#spa-content` - Emits `spa:navigated` after successful swap - Supports `data-no-spa` and `no-spa` opt-outs - Supports `forceReload` semantics for same-URL post-mutation refresh If a controller action redirects back to the current page, the JSON response **must** include a `redirect` key so SPA navigation can force-reload (see `api-contract` skill). ## 4. Dark Mode Contract (Owner Section) **Tailwind configuration:** `darkMode: 'class'` in `tailwind.config.js`. All Tailwind `dark:` utilities require `.dark` on `<html>`. **FOUC prevention — inline blocking script:** The script is **literally inlined** in `<head>` — NOT a `<script src>` or `type="module"`. All three layouts (`app.php`, `public.php`, `auth.php`) and the standalone `booking.php` carry the same one-liner. It runs synchronously during HTML parsing, before any CSS file loads: ```html <script {csp-script-nonce}>!function(){var t=localStorage.getItem('xs-theme')||(window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light');document.documentElement.setAttribute('data-theme',t);document.documentElement.classList.toggle('dark',t==='dark');document.documentElement.style.colorScheme=t;if(t==='dark')document.documentElement.style.backgroundColor='#111827';document.documentElement.classList.add('xs-no-transition')}();</script> ``` > **CSP nonce required:** Every inline `<script>` in CI4 layouts must carry `{csp-script-nonce}`. CI4 replaces this placeholder with `nonce="…"` when `app.CSPEnabled = true`. Omitting it causes a CSP violation and the script is silently blocked in production. What it does (in order): 1. Reads `xs-theme` from `localStorage`; falls back to `prefers-color-scheme` 2. Sets `data-theme` attribute + toggles `.dark` class on `<html>` 3. Sets `style.colorScheme = t` — system UI (scrollbars, native inputs) renders in the right scheme 4. For dark mode: sets `style.backgroundColor = '#111827'` on `<html>` — covers viewport before CSS loads 5. Adds `.xs-no-transition` class — suppresses all `transition-colors` when the CSS file first applies **`xs-no-transition` CSS rule** lives in `resources/scss/base/_reset.scss`: ```scss .xs-no-transition, .xs-no-transition * { transition: none !important; } ``` This prevents the 200ms animated fade that would otherwise occur when `dark:bg-gray-900` first applies to `<body>`. **FOUC cleanup:** The guards must be removed after the first paint to restore normal transitions for user-triggered theme switches. - **Layouts that load `app-layout-init.js`:** cleanup runs in the double `requestAnimationFrame` inside `onDomReady()`. - **Standalone pages (e.g. `booking.php`):** cleanup is a bare inline script at end of `<body>`: ```html <script {csp-script-nonce}>requestAnimationFrame(function(){requestAnimationFrame(function(){document.documentElement.classList.remove('xs-no-transition');document.documentElement.style.backgroundColor='';document.documentElement.style.colorScheme=''})});</script> ``` **Dark background values by page:** - Authenticated app (`layouts/app.php`, `auth.php`, `public.php`): `#111827` (Tailwind `gray-900` = `dark:bg-gray-900` on `<body>`) - Public booking SPA (`booking.php`): `#0f172a` (Tailwind `slate-900` = `dark:bg-slate-900` on `<body>`) **`<html>` tag:** must NOT carry `transition-colors duration-200` — it has no `background-color` rule. `transition-colors` lives on `<body>` only, for user-triggered theme switches. **`theme-bootstrap` Vite entry:** still exists in `vite.config.js` and builds `resources/js/theme-bootstrap.js`, but **no layout loads it via `<script src>`**. The content is inlined directly. Do not re-introduce the module src load — it would defer execution and break FOUC prevention. **Runtime management:** `DarkModeManager` in `resources/js/dark-mode.js` handles the full lifecycle: - On every `applyTheme()` call: sets `data-theme` attribute, `.dark` class, AND `style.colorScheme` - Persists to `localStorage` key `xs-theme` - Dispatches `xs:theme-changed` custom event for downstream components - Re-wires `[data-theme-toggle]` buttons after each `spa:navigated` event **SCSS:** Dark overrides use `.dark { ... }` selector in `resources/scss/abstracts/_custom-properties.scss`, **not** `[data-theme="dark"]`. **Summary:** `data-theme` attribute is always kept in sync (for external consumers), but `.dark` class is the canonical trigger for Tailwind utilities and SCSS dark overrides. See `ui-ux` skill for dark mode styling rules (color tokens, xs-no-transition usage). ## 5. Styling Contract - Tailwind + consolidated SCSS only - **No inline `style` attributes** in app-facing templates — caught by pre-merge grep check ## 6. Avatar System Contract (Owner Section) All avatar rendering — profile images and initials fallback — has a **single source of truth** on both PHP and JS sides. ### 6.1 PHP Helpers (`app/Helpers/app_helper.php`) Four canonical helpers, loaded via `helper('app')`: | Helper | Purpose | | --- | --- | | `avatar_initials(string $name, string $default = 'U'): string` | Derives 1-2 letter initials from a display name. Strips titles (Dr., Prof., Mr., Mrs., Ms., Rev.) and suffixes (MD, PhD, DDS, DO, RN, NP, PA, DVM, Jr., Sr., II, III, IV). Multi-word → first + last initial. Single-word → first 2 chars. Empty → `$default`. | | `avatar_display_name(array $user, string $fallback = 'User'): string` | Prefers `$user['name']`; falls back to `first_name` + `last_name` concatenation. | | `avatar_profile_image_url(array $user): ?string` | Resolves profile image URL. Paths starting with `assets/` map to `FCPATH`; paths starting with `uploads/` or `writable/` map to `WRITEPATH`. Returns `null` if no usable path. | | `avatar_data(array $user, string $defaultInitial = 'U'): array` | Returns `['imageUrl' => ?string, 'initials' => string, 'displayName' => string]`. Use this in views for image-first rendering with initials fallback. | `ProfilePageService::buildProfileImageUrl()` and `buildProfileInitials()` delegate to these helpers. ### 6.2 JS Utility (`resources/js/utils/avatar.js`) Single ESM module — import directly: ```js import { getAvatarInitials, getDisplayName } from '../../utils/avatar.js'; ``` | Export | Signature | Behaviour | | --- | --- | --- | | `getDisplayName(entity, fallback)` | `(object, string) → string` | Prefers `entity.name`; falls back to `first_name` + `last_name`. | | `getAvatarInitials(name, options)` | `(string, { defaultInitial? }) → string` | Same normalization rules as PHP: strip titles/suffixes, multi-word → first+last initial, single-word → first 2 chars. `options.defaultInitial` defaults to `'U'`. | For inline `<script>` blocks in PHP views (which cannot use ESM imports), use the globals exposed by `resources/js/app.js`: ```js window.xsGetAvatarInitials(name, defaultInitial) window.xsGetDisplayName(entity, fallback) ``` ### 6.3 Canonical Default Initials by Context | Context | Default | | --- | --- | | User / staff / header | `'U'` | | Customer | `'C'` | | Staff assignment widget | `'S'` | | Provider assignment widget | `'P'` | | Scheduler provider chip | `'?'` | ### 6.4 Covered Surfaces - `app/Views/layouts/app.php` — header user avatar (image-first via `avatar_data()`) - `app/Views/user-management/index.php` — PHP rows + JS `userRow()` via `window.xsGetAvatarInitials` - `app/Views/customer-management/index.php` — `avatar_data()` with `defaultInitial: 'C'` - `app/Views/customer-management/history.php` — large customer header avatar - `app/Views/appointments/form.php` — customer avatar placeholder (`'C'`) - `app/Views/user-management/components/provider-staff.php` — server PHP + JS `renderStaff()` widget - `app/Views/user-management/components/staff-providers.php` — server PHP + JS `renderProviders()` widget - `resources/js/modules/scheduler/appointment-colors.js` — `getProviderInitials()` delegates to `getAvatarInitials` - `resources/js/modules/customer-management/customer-search.js` - `resources/js/modules/appointments/appointments-form.js` ### 6.5 Do Not Duplicate **Do not reimplement initials logic in any view, controller, or JS module.** Always call the shared helper. A one-letter initial in a completed surface is a regression against this contract. ## 7. View Layout & Design System Contract > **Design system component tokens (`xs-card`, `xs-btn`, `xs-actions-container`) are owned by the `ui-ux` skill.** This section covers layout-level contracts only. ### 7.1 Canonical Layout All authenticated views **must** extend `layouts/app`. The legacy `layouts/dashboard` layout is **deprecated**; do not use it for new views or when migrating existing views. | Layout | Status | Sections | | --- | --- | --- | | `layouts/app` | **Active standard** | `sidebar`, `header_title`, `header_subtitle`, `header_primary_action`, `header_controls`, `content`, `scripts`, `modals` | | `layouts/dashboard` | **Deprecated** | `dashboard_stats`, `dashboard_actions`, `dashboard_filters`, `dashboard_content_top`, `dashboard_content` | To suppress the header's built-in "New Appointment" CTA for a page that provides its own CTA in `content`: ```php <?= $this->section('header_primary_action') ?>hidden<?= $this->endSection() ?> ``` ### 7.2 Design System Component Classes | Token | Element | Notes | | --- | --- | --- | | `xs-card` | Card container | Replaces `card card-spacious` | | `xs-card-header` | Card header bar | Use with `xs-card-header-content` (left) + `xs-card-actions` (right) | | `xs-card-header-content` | Left slot of card header | Contains title + subtitle | | `xs-card-title` | Title inside `xs-card-header-content` | | | `xs-card-subtitle` | Subtitle inside `xs-card-header-content` | | | `xs-card-actions` | Right slot of card header | Icon buttons, secondary controls | | `xs-card-body` | Card content area | Add `p-0` when the card contains a full-bleed table |
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub