docs: prompt réutilisable de refactor UX (UX_REFACTORING_PROMPT.md)
Réécriture vérifiée du brouillon initial — 43 corrections issues d'un review
multi-agents contre le code réel @ 718698f :
- 16 règles UX calibrées au repo (chrome-only, CTA desktop top-right,
Function-props, fr-CA, raccourcis clavier, états async, LTE, 44px, tokens)
- tableau des primitives canoniques (OverflowMenu, DisclosureSection, HelpHint…)
- audit anti-patterns avec colonnes Status+Anchor (repérage par nom d'état,
pas par ligne) ; lignes déjà résolues marquées (T2, TechTasks, PL4/PL7)
- plan de décomposition PlanificationPage réaliste (décomposition #4 reconnue,
vagues 1-2, règle script-mapping, pas de composable fourre-tout)
- Definition of Done (lint/build/preview + invariants DnD/SSE/MapLibre/Publier)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
718698f0d0
commit
8e7838d1fb
259
docs/UX_REFACTORING_PROMPT.md
Normal file
259
docs/UX_REFACTORING_PROMPT.md
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
# UX Refactoring Prompt — Gigafibre FSM
|
||||
|
||||
> **Audited: 2026-07-14 @ commit `718698f`.** Line numbers below are hints only — they drift with every commit. ALWAYS locate code by the **Anchor** string (state/component/label name); if the anchor isn't where the table says, search the file and update this doc before refactoring.
|
||||
|
||||
## Project Context
|
||||
|
||||
Gigafibre FSM is the operations platform for a fiber ISP in Quebec. Stack: Vue 3 / Quasar v2 / Pinia / Vite. The app has two audiences:
|
||||
|
||||
- **Ops staff** (CSRs, dispatchers) on desktop/tablet → `apps/ops/src/pages/` — Quasar components, light theme, `--ops-*` tokens.
|
||||
- **Field technicians** on mobile (often one-handed, gloves, spotty LTE) → `apps/ops/src/modules/tech/pages/` — **its own design system**: `tg-*` classes from `src/css/tech.scss`, native `<button>`, NOT q-btn. Never inject Quasar desktop patterns into the tech module.
|
||||
|
||||
All strings are French (fr-CA). ERPNext v16 is the backend (REST API). The hub (`targo-hub`) is a Node.js API gateway.
|
||||
|
||||
---
|
||||
|
||||
## Existing Code — REUSE, never rebuild (standing team rule)
|
||||
|
||||
Before creating ANY new file, state which existing file you checked and why it doesn't fit.
|
||||
|
||||
- **Pinia stores** (`src/stores/`): `useAuthStore`, `useDispatchStore`, `useOfflineStore`. Extend these; do NOT create new stores without asking.
|
||||
- **Composables** (`src/composables/`, ~60): check here FIRST — `useDragDrop`, `useSSE`, `useJobPool`, `useScheduler`, `useMediaQuery`, `useUndo`, `useSelection`, `usePeriodNavigation` already exist.
|
||||
- **Shared components** (`src/components/shared/`, ~48): see the Canonical Primitives table below.
|
||||
- **`src/components/planif/` already contains 8 extracted components** (décomposition #4): `JobPool`, `LeaveDialog`, `MonthOverview`, `PlanifCellMenu`, `ShiftTypesDialog`, `SkillCadenceTable`, `TechScheduleDialog`, `TechSyncDialog`. New Planification extractions go here and must not duplicate them.
|
||||
|
||||
**Where extracted components go:** PlanificationPage pieces → `src/components/planif/` · ClientDetailPage pieces → `src/components/customer/` · reused across pages → `src/components/shared/` (check the ~48 existing first) · tech (mobile) module pieces → `src/modules/tech/components/` (currently only `JobCard.vue`), styled with `tg-*` classes.
|
||||
|
||||
### Canonical primitives
|
||||
|
||||
All in `src/components/shared/` unless noted. Import these; do NOT rebuild an inferior equivalent.
|
||||
|
||||
| Need | Use |
|
||||
|---|---|
|
||||
| ⋮ overflow of secondary actions | `OverflowMenu.vue` (`:groups="[{label, items:[{icon,label,caption?,action|to,disabled?}]}]"` or flat `:items`) |
|
||||
| Collapsible « ▾ Options avancées » / glance→detail | `DisclosureSection.vue` (`title`, `count`, `persist-key` = per-user persisted open state, slots `#summary`/`#actions`) |
|
||||
| Rare actions removed from a toolbar | register in `composables/useCommandPalette.js` (⌘K) so they stay reachable |
|
||||
| ⓘ contextual help | `HelpHint.vue` (title/text or slot) — global behavior in `boot/tooltip-ux.js` (click = hide, never 2 bubbles) |
|
||||
| Tables & async states | `DataTable.vue` · `EmptyState.vue` · `TableSkeleton.vue` |
|
||||
| Page top bar / record detail | `PageHeader.vue` · `DetailModal.vue` + `detail-sections/` (incl. `modules/JobThread.vue`, `JobReplyBox.vue`) |
|
||||
| Stats / avatars | `StatCard.vue` · `UserAvatar.vue` (canonical identity avatar) |
|
||||
| Skill chips & editor | `TagEditor.vue` · `components/planif/SkillCadenceTable.vue` |
|
||||
| Maps / occupancy | `RouteMap.vue` · `OccupancyStrip.vue` · `OccupancyBands.vue` |
|
||||
| Responsive breakpoints | `composables/useMediaQuery.js` |
|
||||
|
||||
---
|
||||
|
||||
## UX Rules (enforce these on every review)
|
||||
|
||||
1. **MAX 5 interactive CONTROLS per chrome zone** (toolbar, header, footer, dialog action row). Data cells/rows/chips inside grids, tables, calendars and kanban lanes do NOT count — a planning grid legitimately renders dozens of interactive cells. If a chrome zone exceeds 5 controls, group them behind a single trigger using `shared/OverflowMenu.vue` — never hand-roll a q-menu of secondary actions.
|
||||
2. **Progressive disclosure**: show only what the CURRENT step needs. Hide rarely-used options with `shared/DisclosureSection.vue` (« ▾ Options avancées ») or an OverflowMenu. Actions removed from a toolbar must be registered in `useCommandPalette` (⌘K) so they stay reachable — PlanificationPage already does this.
|
||||
3. **Every form field must justify its presence** — if it has a sensible default or is rarely edited, hide it. Only expose what the user needs to touch >50% of the time.
|
||||
4. **No menu-in-menu**: if a dropdown item opens a submenu, extract it into its own dialog.
|
||||
5. **Mobile-first for mobile surfaces**: on the tech module (`modules/tech/`) and on mobile breakpoints (`lt-md`) of ops pages, the primary CTA must be in thumb reach (bottom 1/3) — reuse the floating CTA pill pattern (`.tg-fab-cta`, TechJobDetailPage). On desktop (`gt-sm`) ops pages, the primary CTA belongs top-right of the header toolbar, visually dominant — do NOT relocate desktop CTAs to the bottom of the viewport. No hover-only affordances anywhere (desktop included).
|
||||
6. **No `.vue` file > 800 lines** — decompose into child components with focused responsibilities.
|
||||
7. **Props carry data/config; behaviour goes through emits or a shared composable/Pinia store.** HARD flag: any Function-typed prop used as a parent callback (see C1: LocationCard has 32 props, 26 of them Functions — the real anti-pattern). SOFT flag: >10 props (config-heavy shared components like DetailModal at 12 declarative props are acceptable).
|
||||
8. **One primary action per view state**. If the user has to choose between 3+ equally prominent buttons, you've failed.
|
||||
9. **Status feedback**: every mutation shows visible feedback within 200ms — using the app's existing mechanisms ONLY: `$q.notify` for results/errors (see the `err()` helper pattern), `:loading` / `:disabled="saving"` on the triggering button while pending, the changeLog journal + revert for grid edits, and OutboxPanel-style delayed send with « Tout annuler » for bulk/destructive sends. Do NOT introduce a new toast system. "Never silently `catch()`" applies to user-initiated mutations; background best-effort loads may catch silently but must carry a comment (existing convention: `catch (e) { /* … best-effort */ }`).
|
||||
10. **Dev-only UI hidden in production**: gate ONLY on `import.meta.env.DEV` (compile-time, stripped from prod builds) — NEVER on a custom `VITE_*` flag: `.env.local` VITE_ vars are bundled even in prod builds (documented leak, `src/config/hub.js`). Canonical pattern: the `canTerminateDirect` computed in TechJobDetailPage.vue.
|
||||
11. **French only (fr-CA)**: every user-facing string — labels, tooltips, toasts, aria-labels, empty states — including in NEW elements you create. Use « guillemets » and the existing domain vocabulary (quart, garde, tournée, « À assigner », « Publier »). Any English string in output is an automatic P1 review finding.
|
||||
12. **Preserve ALL keyboard shortcuts.** Existing: ⌘K/Ctrl+K → CommandPalette (window listener in MainLayout); Planification grid: Ctrl/⌘+Z / ⇧Z undo-redo, Ctrl/⌘+C/V copy-paste cells, Delete/Backspace clear, `A` = absent, `G` = garde (document-level `onKey` listener, which skips events from INPUT/TEXTAREA/SELECT/contenteditable — preserve that guard). Any extraction must re-wire document/window-level key listeners explicitly and list the component's shortcuts in its top docblock.
|
||||
13. **Every async view has three states**: loading → `shared/TableSkeleton.vue` (or q-skeleton), empty → `shared/EmptyState.vue` (icon + phrase FR + CTA), error → message + bouton « Réessayer ». When splitting a component, carry all three branches into the child. Never rebuild an equivalent skeleton/empty-state.
|
||||
14. **Spotty-LTE resilience (tech module)**: every mutation button shows a pending state and is disabled while pending (`:disabled="saving"`, cf. TechJobDetailPage). Offline-sensitive mutations go through the IndexedDB queue in `src/stores/offline.js` — never fire-and-forget fetch. Queued work must be visible: count + tap-to-retry chip (cf. TechScanPage « N scans en attente · toucher pour réessayer »). Never gate messaging on `navigator.onLine` alone — it reports true on unusable 2-bar LTE (documented in `stores/offline.js`).
|
||||
15. **Touch & focus (tech module)**: every tappable element ≥ 44×44 px (existing convention: `min-height: 44px` in TechJobDetailPage.vue). Keep visible `:focus-visible` states; never encode state by colour alone (add icon or label); use the tokens in `css/tech.scss` (`--ink-*`, `--t-*`) — no hardcoded colours.
|
||||
16. **Design tokens**: any new or moved style uses the `--ops-*` variables from `src/css/app.scss` (`--ops-accent/-success/-warning/-danger/-info/-bg/-bg-hover/-bg-light/-surface/-border/-text/-text-muted`) — no new hardcoded hex. Use each extraction as the opportunity to convert copied colours. All pages are LIGHT theme; `DispatchPage` is the sole dark exception — never introduce dark styling elsewhere.
|
||||
|
||||
### Expert-density surfaces (declutter rules relaxed)
|
||||
|
||||
On these surfaces, Rules 1–3 and 8 apply ONLY to chrome (toolbars, dialogs), never to board/grid content: **Planification grid/day/month/routes views** (PlanificationPage) and **Dispatch timeline & calendars** (`modules/dispatch/components/`). Information density is a feature for dispatchers: do not reduce data shown per cell, collapse columns, or add clicks between the dispatcher and job details. Dispatch keeps its dark theme (`pages/dispatch-styles.scss`).
|
||||
|
||||
---
|
||||
|
||||
## Known Anti-Patterns (audited snapshot)
|
||||
|
||||
> ⚠️ This audit is a snapshot (2026-07-14 @ `718698f`) and remediation is ongoing — the code marks applied fixes with `#N` comments. Before acting on any row, VERIFY the pattern still exists at the anchor; if already fixed, say so and move on.
|
||||
|
||||
### PlanificationPage.vue (6,559 lines) — Priority: CRITICAL
|
||||
|
||||
File layout: template L1–1768 · script L1770–6033 (~4,264 lines — the real bulk) · style L6035–6559.
|
||||
|
||||
| ID | Anti-pattern | Anchor | Status | Problem |
|
||||
|---|---|---|---|---|
|
||||
| PL1 | WALL_OF_OPTIONS | desktop header rows (comment « Rangée 1 : actions principales ») ~L32-154 | open | 20+ interactive controls in one toolbar |
|
||||
| PL2 | WALL_OF_OPTIONS | `label="Outils"` dropdown ~L71-125 | open | 13 items in 3 sections + nested submenu |
|
||||
| PL3 | WALL_OF_OPTIONS | q-dialog `jobSheet.open` ~L281-356 | open | ~15 interactive zones on a phone screen. Reuse `detail-sections/modules/JobThread.vue` + `JobReplyBox.vue` instead of the hand-rolled pm-postbox composer |
|
||||
| PL4 | HIDDEN_PRIMARY | `label` contains « Publier », split q-btn-dropdown ~L131-147 | **partially fixed** | Publier is now a unified split q-btn-dropdown (color=positive, dirty-count, outline-when-clean). Remaining: the row still has ~10 sibling controls (see PL1) |
|
||||
| PL5 | MENU_IN_MENU | « Modèles de semaine » ~L85-97 | open | Submenu inside a dropdown |
|
||||
| PL6 | ORPHAN_FIELD | `label="Max h/sem"` ~L159 | open | Floating numeric input with no context |
|
||||
| PL7 | STATUS_MYSTERY | autosave journal ~L48-61 | **largely fixed** | Icon now swaps to cloud_sync + primary color while saving (persistent feedback); popover holds the change journal + per-change revert by design. Verify before changing |
|
||||
| PL8 | MONOLITH | entire file | open | 6,559 lines = unmaintainable (see decomposition plan) |
|
||||
|
||||
### TechJobDetailPage.vue (679 lines) — Priority: LOW
|
||||
|
||||
| ID | Anti-pattern | Anchor | Status | Problem |
|
||||
|---|---|---|---|---|
|
||||
| T1 | WALL_OF_OPTIONS | "Détails" section ~L60-104 | open | Type, Priority, Time, Duration always visible — tech rarely edits these |
|
||||
| T2 | ~~DEAD_CLICK~~ | « Terminer directement (test) » ~L160-166 | **RESOLVED** | Already gated by `import.meta.env.DEV` (`canTerminateDirect`, L296-298) — verify only, do not re-fix |
|
||||
| T3 | ORPHAN_FIELD | "Créer un équipement" dialog ~L237-241 | open | 5 fields visible, only Serial is required |
|
||||
|
||||
### TechTasksPage.vue (748 lines) — Priority: NONE
|
||||
|
||||
No open issues — the dev test button (~L213-218) is already DEV-gated via `canTerminateDirect` (L332-335). Do not modify this page.
|
||||
|
||||
### ClientDetailPage.vue (1,728 lines) — Priority: MEDIUM
|
||||
|
||||
| ID | Anti-pattern | Anchor | Status | Problem |
|
||||
|---|---|---|---|---|
|
||||
| C1 | PROP_OVERLOAD | `<LocationCard` bindings ~L74-107 → `components/customer/LocationCard.vue` (defineProps L299-334) | open | **32 props, 0 emits** — 6 data props + 26 Function props (10 selectors drilled from the parent's `useSubscriptionGroups` instance + 16 action callbacks). The anti-pattern is callbacks-as-props. Fix: instantiate `useSubscriptionGroups` inside LocationCard (or provide/inject the instance), keep the data props, convert action callbacks to emits (Rule 7) |
|
||||
|
||||
---
|
||||
|
||||
## Decomposition plan — PlanificationPage.vue
|
||||
|
||||
**`components/planif/` ALREADY CONTAINS (décomposition #4 — do NOT re-create, extend these):**
|
||||
`JobPool.vue` (pool « à assigner » unifié : variants floating/docked/mobile — the mobile swipe/tap-sheet logic lives HERE) · `LeaveDialog.vue` · `ShiftTypesDialog.vue` · `TechSyncDialog.vue` · `TechScheduleDialog.vue` · `MonthOverview.vue` · `PlanifCellMenu.vue` · `SkillCadenceTable.vue` (SHARED skill+cadence editor).
|
||||
|
||||
New extractions must follow the same props/emits conventions as ShiftTypesDialog/LeaveDialog.
|
||||
|
||||
**Still to extract (anchor by state/component name — line numbers drift):**
|
||||
|
||||
```
|
||||
Wave 1
|
||||
├── PlanifHeader.vue ← desktop toolbar + filters + skills chips, L32-187
|
||||
│ (anchor: q-btn-toggle `boardView` → OverflowMenu).
|
||||
│ Keep existing OverflowMenu / HelpHint / useCommandPalette wiring.
|
||||
│ Mobile toolbar (`pm-toolbar`, ~L3-30): include in PlanifHeader
|
||||
│ (render both breakpoints) or extract as PlanifMobileBar.vue —
|
||||
│ decide before extracting.
|
||||
├── PlanifGrid.vue ← week grid table
|
||||
├── PlanifKanban.vue ← day view (already renders <JobPool variant="docked">)
|
||||
├── PlanifRoutes.vue ← routes view (uses shared RouteMap.vue + OccupancyStrip.vue)
|
||||
├── JobOptionsSheet.vue ← q-dialog `jobSheet.open` ONLY (the `pm-sheet` assignment overlay
|
||||
│ and swipe/tap stay coupled to JobPool.vue — coordinate via its emits)
|
||||
├── SkillEditorPopover.vue ← q-menu `skillMenuShown` (WRAPS existing SkillCadenceTable — keep it)
|
||||
├── GardeDialog.vue ← q-dialog `showGarde` (keep its HelpHint)
|
||||
└── TeamEditorDialog.vue ← q-dialog `showTeamEditor` (wraps SkillCadenceTable per tech)
|
||||
|
||||
Wave 2 (template L789-1768, currently unmapped)
|
||||
├── SuggestReviewDialog.vue ← « Suggérer » review (q-dialog `suggestDlg.open`)
|
||||
├── TagManagerDialog.vue ← `showTagManager` + `skillImpactOpen`
|
||||
├── JobPoolPanel.vue ← floating unassigned-jobs panel + pins map (`assignPanel`)
|
||||
├── LegacyPushDialog.vue ← `legacyPush.open` (write-back osTicket)
|
||||
├── LocPickerDialog.vue ← `locPicker.open`
|
||||
└── (also unmapped: notify-contacts block ~L1259, legacy job-detail dialog ~L1288)
|
||||
```
|
||||
|
||||
**Script-mapping rule:** for each extracted component, move every function/ref referenced ONLY by the extracted template into the child (e.g. `kb*`/`kanban*` → PlanifKanban, `cell*`/`onCellDrop`/`onDown` → PlanifGrid, `garde*` → GardeDialog). State shared across views stays in the parent or a per-domain composable.
|
||||
|
||||
**Composables:** do NOT create a catch-all `usePlanification.js`. `useJobPool.js` already covers pool filter/sort/group (3 instances owned by the page — keep that ownership). Extract PER DOMAIN only when a child needs it: `useGardeRules.js` (rule CRUD + preview), `useWeekTemplates.js`, `usePlanifBoard.js` (cells/occupancy/capacity). Before writing a composable, check `src/composables/` (~60 existing).
|
||||
|
||||
**CSS:** Vue scoped styles do NOT reach into child components. Each extraction must copy the scoped CSS rules matching its markup (`.pm-*`, `.assign-*`, `.garde-*`…) into the new component's own `<style scoped>`. Do not rename classes.
|
||||
|
||||
**Target:** PlanificationPage.vue < 800 lines (Rule 6); ~400 lines only after ALL rows above are extracted. Extract top-down, ONE row per session.
|
||||
|
||||
---
|
||||
|
||||
## How to Use This Prompt
|
||||
|
||||
Text in `[square brackets]` is a placeholder — replace it with your actual target (anti-pattern IDs, component name, line range) before sending.
|
||||
|
||||
### Option A: Full-page refactor
|
||||
Paste this file + the full `.vue` file content, then say:
|
||||
|
||||
```
|
||||
Refactor this component following the UX rules above. Address anti-patterns
|
||||
[PL1, PL2, PL3] specifically. Keep all v-model bindings, event names AND
|
||||
keyboard shortcuts intact. Output ONE unified diff against the pasted file,
|
||||
covering <template> AND any <script> changes the fix requires (new refs/handlers
|
||||
for grouped menus, etc.). If a rule can only be satisfied in <script> (rules 7,
|
||||
9, 10, 12) and the script was not pasted, do not guess: emit a `// TODO(script):`
|
||||
comment in the diff describing the exact change needed. All new UI strings in
|
||||
French (fr-CA).
|
||||
```
|
||||
|
||||
### Option B: Extract a child component
|
||||
Paste this file + the relevant template section of the parent **+ the script portions that move with it + the matching scoped CSS**, then say:
|
||||
|
||||
```
|
||||
Extract the section anchored at [jobSheet.open] into a new `[JobOptionsSheet].vue`
|
||||
component in [src/components/planif/]. The parent should pass only the job object
|
||||
and emit events for mutations. Apply progressive disclosure: show Status + Reply
|
||||
visible, rest under DisclosureSection « ▾ Options avancées ». Copy the scoped CSS
|
||||
rules this markup uses into the new component. Output both the new component and
|
||||
the parent's updated template section. All new UI strings in French (fr-CA).
|
||||
```
|
||||
|
||||
### Option C: Review a new component
|
||||
Paste this file + the new component, then say:
|
||||
|
||||
```
|
||||
Review this component for cognitive load following the UX rules above.
|
||||
For each issue found, output:
|
||||
- ANCHOR: the element (state/label name) + approximate line range
|
||||
- PROBLEM: what causes overload
|
||||
- FIX: concrete code change (not just advice)
|
||||
- PRIORITY: P0 (confusing) / P1 (cluttered) / P2 (polish)
|
||||
|
||||
Do NOT suggest adding features. Only simplify.
|
||||
```
|
||||
|
||||
### Option D: Decompose a monolith
|
||||
Paste this file + the full monolith (or the target section + its script), then say:
|
||||
|
||||
```
|
||||
Decompose this monolith following the decomposition plan in the prompt.
|
||||
Start with [PlanifHeader.vue]. Output:
|
||||
1. The new child component (complete, working — template + script + scoped CSS)
|
||||
2. The updated parent template section (replacing the extracted lines)
|
||||
3. The props/emits interface between them
|
||||
|
||||
Keep all existing functionality. Do not rename CSS classes. Do not change any
|
||||
API call, URL, payload shape, or emitted event name — refactors are UI-structure
|
||||
only. Target: parent < 800 lines; ~400 only as the end-state after all
|
||||
extractions (one extraction per session).
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Definition of Done (every session)
|
||||
|
||||
A refactor is complete only when:
|
||||
|
||||
1. `npm run lint` and `npm run build` pass in `apps/ops` (paste errors back if not).
|
||||
2. Manually verified in `quasar dev` (port 9001): the page renders, no new console errors.
|
||||
3. These invariants still work (PlanificationPage): **drag-and-drop** of jobs onto grid cells and kanban lanes (incl. drop preview badge, SortableJS day editor), **SSE live updates** (`legacy-update` listener), **the 4 MapLibre maps** (job-detail, location picker, assign panel, day route), **keyboard shortcuts** (Rule 12), and the **Publier** flow.
|
||||
4. No behavior change: same API calls, same payloads, same emitted events.
|
||||
|
||||
If you (the LLM) cannot run these, end your output with a checklist of exactly what the human must verify.
|
||||
|
||||
---
|
||||
|
||||
## Token Optimization Tips
|
||||
|
||||
| Technique | Savings | How / caveats |
|
||||
|---|---|---|
|
||||
| Send only `<template>` section | 58–73% (template is 27–42% of these files) | ONLY for layout-only reviews (rules 1–5, 8). Include `<script setup>` whenever the task touches rules 7/9/10/12, extracts a component (Options B/D), or reviews error handling — key handlers and gating live in script |
|
||||
| One component at a time | ~70% | Don't paste 5 files; review sequentially |
|
||||
| Specify viewport | Response tokens | Add `"Target: 375×667 iPhone SE"` for tech-module work to avoid desktop suggestions |
|
||||
| Ask for diff, not full file | ~60% response | `"Output changes as a unified diff, not the full file"` |
|
||||
| "Do NOT suggest adding features" | ~30% response | Prevents AI from adding complexity |
|
||||
| Strip `<style>` section | 8–26% (only ~8% on PlanificationPage/ClientDetailPage) | ONLY for reviews (Option C). NEVER for extractions (Options B/D): scoped styles don't reach child components — the extracted markup needs its CSS copied along |
|
||||
|
||||
**Section boundaries (at `718698f`)** — to slice without opening the file: PlanificationPage template L1–1768 / script L1770–6033 / style L6035–6559.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference: Quasar Components Used
|
||||
|
||||
| Component | Purpose | Notes |
|
||||
|---|---|---|
|
||||
| `q-btn` / `q-btn-group` / `q-btn-toggle` / `q-btn-dropdown` | Buttons | Split buttons = `q-btn-dropdown split` (cf. « Publier ») |
|
||||
| `q-dialog` | Modal dialogs | `position="bottom"` = mobile bottom-sheet |
|
||||
| `q-menu` | Dropdown/context menus | `context-menu` = right-click; `auto-close`. For secondary-action overflow, use `shared/OverflowMenu.vue` instead |
|
||||
| `q-expansion-item` | Collapsible sections | Prefer `shared/DisclosureSection.vue` (persisted open state); `group` prop for accordion |
|
||||
| `q-chip` | Tags, filters, status badges | `clickable` makes it interactive |
|
||||
| `q-input` / `q-select` | Form fields | `dense outlined` is the standard style |
|
||||
| `q-list` / `q-item` | Lists inside menus/dialogs | `q-item-section avatar` for icons |
|
||||
| `q-tooltip` | Hover help text | Overused — not a substitute for clear labels. For contextual help use `shared/HelpHint.vue` (ⓘ with title/rich text); the global behavior (click = hide, never 2 bubbles) lives in `boot/tooltip-ux.js` — don't fix tooltips one by one |
|
||||
| `q-banner` | Alerts, notifications | Status banners (offline, warnings) |
|
||||
|
||||
— End of prompt —
|
||||
Loading…
Reference in New Issue
Block a user