Skip to content

[IMP] dms: sibling-module extension seams (hero_chips slot, preview_action_registry, useDmsAccent)#10

Open
dnplkndll wants to merge 16 commits into
19.0-imp-dms-uxfrom
19.0-imp-dms-ux-seams
Open

[IMP] dms: sibling-module extension seams (hero_chips slot, preview_action_registry, useDmsAccent)#10
dnplkndll wants to merge 16 commits into
19.0-imp-dms-uxfrom
19.0-imp-dms-ux-seams

Conversation

@dnplkndll
Copy link
Copy Markdown

Stacks on ledoent/dms 19.0-imp-dms-ux (→ OCA/dms #476). Will be retargeted to OCA/dms once the UX branch lands.

Adds three minimal extension seams that dms_user_role, dms_auto_classification, dms_field, and dms_field_auto_classification will consume when ported to 19.0. None change existing behaviour.

Non-mechanical adaptations worth flagging

name="hero_chips" div — inserted between the tag row and the stats row in both dms.file and dms.directory form heroes. Siblings inject chips with <xpath expr="//div[@name='hero_chips']" position="inside">. Without a named slot every sibling needs a fragile hasclass xpath that collides under co-installation.

dms.preview_actions registry — mirrors the shape of the existing dms.preview_handlers registry. FilePreviewPane iterates registered entries after the three built-in buttons (Download / Share / Open). Built-ins stay hardcoded so their position is stable; the registry is additive-only. dms_auto_classification will register a "Re-classify" action here.

useDmsAccent / getDmsAccent — JS utility that mirrors dms_ext_palette.scss's extension→colour mapping. Lets sibling card components apply --dms-accent via a style binding without requiring the .o_kanban_dms_card[data-ext="…"] DOM selector on their own element.

Hoot tests lock down the registry contract (score ordering, match() filtering, null-file guard) and the accent mapping (case-insensitivity, filename parsing, unknown-extension null return).

dnplkndll added 9 commits May 25, 2026 21:14
…review + form heroes + portal grid + test coverage)

Comprehensive UX upgrade for OCA dms on 19.0, stacked on the in-flight MIG.
Fork-only PR for runboat preview + stakeholder review. See PR description for
the full breakdown; this is a single squash of 50 iteration commits.

Phases shipped:
  • Phase 1 — kanban density tiers (comfortable / compact / list) with toggle.
  • Phase 2 — directories landing dashboard: stat bar + live 30-day daily
    sparklines (line for files, bar for storage) + 24-hour line for "new today"
    + week-deltas. All data via _read_group over indexed create_date.
  • Phase 3 — side-pane file preview with extensible handler registry
    (dms.preview_handlers) + Download / Share / Open form toolbar. Built-in
    handlers for image, PDF, audio, video; office-format fallback with
    download + Google Viewer affordance.
  • Phase 8 — visual identity: per-extension accent palette (PDF crimson, code
    teal, docs blue, etc.) extracted to a shared SCSS partial used by both
    backend kanban and frontend portal; animated dropzone with marching dashed
    border; hash-bucketed initials chip on directory kanban cards; condensed
    form heroes for directory + file (icon + title + tag chips + path + stats
    grid + 2-tab notebook replacing the upstream loose-fields layout); portal
    card grid mirroring backend accents.
  • Phase 11 — late mockup gaps: kanban directory subtitle + extension pill;
    search facet pill styling scoped to dms surfaces.
  • Phase 12 — test coverage: 61 Hoot + Python test cases. Hoot suites for
    DmsStatBar, FilePreviewPane, kanban + list renderer routing, preview
    handler URL builders + dispatch; mount-view regression tests pinning the
    two browser-only Owl quirks (regex-literal tokenizer crash, boolean attr
    serialization). Python tests for get_dashboard_stats + _compute_path.
  • Phase 13 — preview routing consolidation: kanban file-icon click
    routes through the side-pane registry (dropping a redundant detour into
    Odoo's built-in `fileViewer` modal that bypassed the handler chain).
    New TextPreview handler covers text/*, application/json,
    application/xml, application/javascript via the same `/web/content`
    iframe pattern PdfPreview uses — browsers render the source natively.
    Form binary-field readable_types extended so the preview button shows
    for text mimetypes too.
  • CI — workflow_dispatch added to tests workflow so we can manually trigger
    when GitHub's anti-abuse heuristic throttles fork PR runs.

Architecture notes:
  • Preview-handler registry (dms.preview_handlers) is the extension point for
    downstream modules; the always-last DownloadFallback at score=-100 means
    every mimetype gets some handler.
  • path_names compute uses direct field assignment (OCA idiom).
  • Sparkline geometry is pure inline-SVG in dms_stat_bar.esm.js; no chart
    library dependency. Geometry pinned by Hoot tests.
  • Owl quirks documented + pinned by mount-view tests: QWeb t-* expressions
    can't tokenize regex literals; t-att-boolean serializes as empty string
    unless coerced to "true"/"false".

Signed-off-by: Don Kendall <dkendall@ledoweb.com>
Fork-only — diagnostics-stage iteration on top of the base UX modernisation
in this PR. To be split / squashed appropriately before any upstream OCA
submission.

OWL 19.0 readiness audit (per oca-review U/T pattern catalog)
- U7 (HIGH): delete dead static/src/js/views/file_kanban_controller.{xml,esm.js}.
  The XML defined dms.FileKanbanView.Buttons via t-inherit="web.KanbanView.Buttons"
  — the exact pattern that crashes at OWL render against 19.0's emptied
  template. Not wired anywhere; the view uses the self-contained
  dms.KanbanButtons template.
- U5 (MEDIUM): coerce boolean t-att-* attributes to explicit 'true'/'false'
  strings. Owl serializes truthy booleans as a presence flag (<div data-loading>),
  so CSS selectors [data-loading="true"] and tour selectors
  [aria-pressed="true"] silently never matched. Applied to data-loading
  on the stat bar and to aria-pressed on both density and preview toggles.
- U2 (style): convert 10 OWL 1 prototype-assignment idioms
  (Foo.template = "x"; Foo.props = {...}) to OWL 2 static class fields
  across 7 component classes. Cross-file `Renderer.template = "..."`
  assignments lifted into the renderer class declarations where they belong.

View toolbar collapse (overlap fix)
- Both renderers were stacking four absolute-positioned floaters in the
  top-right corner: preview-toggle pill (kanban + list), density toggle.
  Now collapse to icon-only buttons below Bootstrap lg (991.98px) so they
  share the row without overlapping the view controls.
Footer creator avatar in the directory kanban was rendering as a square
because core's oe_kanban_avatar doesn't apply border-radius in this
context. Add a scoped rule under .mk_directory_kanban_view so the
20x20 avatar matches the circular treatment already used on file cards.
Live audit on the PR #8 runboat (2026-05-26) surfaced these defects:

B1 — Sample.md rendered as raw text in the side pane. libmagic returns
text/plain for .md (no magic signature distinguishes it from prose), so
_effectiveMimetype() returned text/plain and TextPreview won over
MarkdownPreview. Add text/plain to _STORED_OVERRIDABLE so the extension
mapping wins for .md / .markdown / .json / .xml / .csv / etc.

B2 / B3 / B4 — Files list view polish:
- Add a leading mime-icon column (icon_url + image_url widget, 24×24)
  so list view matches kanban's scannability.
- Replace the raw mimetype column ("application/vnd.oasis.ope…") with
  the short extension field; rename column header to "Type".
- Set explicit widths on name (320), extension (90), human_size (100),
  write_date (180) so name no longer truncates ("Document…").

B5 — Form hero "TYPE" stat showed lowercase ("svg") while kanban cards
use uppercase ("SVG"). Add &--uppercase modifier on .o_dms_form_hero
__stat_value and apply it to the extension stat.

B9 — Standardise the modified-date column label across list ("Modified")
and form hero ("Modified"); upstream had "Last Updated on" in list.

B10 — Hide the Subdirectories / Files table in the directory form when
empty, reclaiming ~150px of blank rows on leaf directories. The
hero-stat tiles still expose the "+ Add" affordance.
B7 — Chromium's PDF viewer shows a dark grey backdrop during the
brief render window. Looked broken in screenshots / screen shares /
demos even though the PDF was loading fine. Force a white background
on .o_dms_preview__iframe so the loading paint is clean.

B8 — Empty preview pane consumed 40% of the viewport even when
nothing was selected. Add a .o_dms_preview_pane--empty modifier
(toggled when state.file is null and we're not loading/erroring)
that collapses the pane to 240px. The listing reclaims ~200px of
horizontal space; the eye-icon empty-state stays visible at the
narrower width. Pane snaps back to 40% the moment a file is
selected. Matches the macOS Finder / GNOME Files pattern.
Sibling addons (dms_field, dms_auto_classification, dms_libreoffice_preview,
future dms_onlyoffice / dms_ocr / dms_annotation) need the same
localStorage-backed UI state + side-pane state shape that the base file
renderers ship. Without an extraction they would each re-implement the
readStored/writeStored ceremony and the toggle/close/select methods.

useStoredState(key, default, {serializer, deserializer})
  - useState that auto-loads from localStorage on setup and auto-persists
    every change via useEffect. Templates read state.value; assignments
    to state.value flow through to writeStored.
  - serializer/deserializer hooks support booleans, JSON-shaped state,
    or any custom encoding without forcing a String(...) at the call
    site.

useDmsPreviewState(storageKey)
  - Encapsulates the open / recordId / toggle / close / select machinery
    that was duplicated across file_kanban_renderer + file_list_renderer.
  - recordId is intentionally non-persisted — it's tied to the current
    view's record set, not the user's hide/show preference.

Both renderers refactored to use the new hooks. Template-facing API
(previewState.open, previewState.recordId, togglePreview(), closePreview())
preserved via shim methods, so file_kanban_renderer.xml + file_list_renderer.xml
need no changes.

Lines of state machinery in the two renderers: ~85 → ~30.
Today, renaming a file means: click card → form opens → click name field
→ type → click save → back to kanban. Five clicks per rename. With
hundreds of files in a typical DMS, that's painful.

Add inline-rename to the file kanban card, matching the macOS Finder /
GNOME Files / Google Drive idiom:

- Double-click the filename on a card.
- The label swaps for a focused <input> at the same flow slot (no card
  geometry shift).
- Enter commits via record.update + record.save.
- Esc cancels (no write).
- Blur commits if the draft changed, cancels otherwise.

FileKanbanRecord gains a useState({active, draft}). The template
branches on renameState.active to render either the static label
(with dblclick handler) or the input (with input/keydown/blur
handlers). onGlobalClick swallows clicks while renaming so the input
doesn't accidentally trigger the side-pane preview routing.

SCSS adds an .o_kanban_dms_card__name_input rule with a primary-border
focus ring so the editable state is visually distinct from the read
state.

Also: extend .gitignore with session-local artefact patterns
(*-PLAN.md, SESSION-HANDOFF-*.md, DMS-UX-AUDIT-*.md, screenshots/,
dms-ux-prototype.html) so future commits don't accidentally pull in
work-in-progress audit material.
"Recent (7 days)" filter on the file search view, alongside "My Files"
and "Modification Date". Domain: write_date >= context_today() - 7d.

The audit doc proposed injecting a "🕒 Recent" pseudo-node into the
search-panel directory tree, which would require overriding
search_panel_select_range AND wiring the frontend to interpret the
synthetic entry as a date filter rather than a directory_id filter
— ~3 hours of work. This commit ships the 80% UX value (one click
to filter to recent files) for ~2 minutes of code. The search-panel
sliver injection remains a follow-up if the search-bar filter
proves discoverable enough; if so, we don't need to do it.
…stry, useDmsAccent)

- Add `name="hero_chips"` named div to both dms.file and dms.directory
  form heroes so siblings can inject chips without xpath collisions
- New `preview_action_registry.esm.js`: `dms.preview_actions` registry
  for extra FilePreviewPane action buttons; FilePreviewPane iterates
  registered entries after Download/Share/Open
- New `use_dms_accent.esm.js`: getDmsAccent/getDmsAccentFromName helpers
  + useDmsAccent OWL composable matching the dms_ext_palette.scss mapping
- Hoot tests for both new utilities
dnplkndll added 4 commits May 26, 2026 20:59
- Prettier: capitalise inline comments in use_dms_accent.esm.js; reflow
  _file helper and long assertion in preview_action_registry.test.js;
  collapse single-item import in use_dms_accent.test.js
- Remove t-on-input/keydown/blur/click/dblclick from kanban rename input
  in arch XML (19.0 view validator rejects t-on-* event directives in arch);
  wire all five handlers imperatively via useEffect(() => [renameInput.el])
  in FileKanbanRecord.setup() — same behaviour, validator-clean arch
…rrors

19.0 kanban arch validator rejects ALL OWL-specific directives (t-on-*, t-ref,
etc.) — not just t-on-input. The F3 inline rename used both t-ref and t-on-*,
making it impossible to keep in arch without a full widget-component extraction.

- Strip the renameState/startRename/commitRename/onRenameKeydown block from
  FileKanbanRecord; keep only the onGlobalClick override (preview pane routing)
- Remove the t-if/t-else rename block from dms_file.xml kanban arch; name div
  is now a plain static element (rename still available via Open form dialog)
- Replace () => {} noop callbacks in preview_action_registry.test.js with
  () => undefined to satisfy no-empty-function ESLint error rule
- sort-imports: reorder import declarations by member-syntax tier then
  first-member alphabetically (multiple before single, none first)
- no-implicit-coercion: replace unary + on toFixed() results with Number()
- no-negated-condition: flip !== null ternary to positive condition
- no-inline-comments: remove inline comment on code line
- capitalized-comments: capitalize section comments in _ACCENT_MAP
@dnplkndll dnplkndll force-pushed the 19.0-imp-dms-ux-seams branch from 5799f35 to 4c65ce7 Compare May 27, 2026 14:37
dnplkndll added 3 commits May 28, 2026 15:57
…r guard

Colour map sync (use_dms_accent.esm.js ↔ dms_ext_palette.scss):
- Add css/scss/sass/less → #7048e8 to JS map (were in SCSS, missing from JS)
- Add bz2/7z to SCSS archive block + portal variants (were in JS, missing from SCSS)
- Add m4a to SCSS media block + portal variant (were in JS, missing from SCSS)
- Add portal variants for the full web/data/styles block in SCSS
- Add assertions in use_dms_accent.test.js for all newly synced extensions

FilePreviewPane extraActions integration (file_preview_pane.test.js):
- Add 5 tests in describe("extra actions") covering: no-file guard, empty
  registry, score ordering, onClick(file, services) call contract, and
  match() predicate filtering by mimetype

Stat bar RPC error guard (dms_directory_kanban_renderer):
- Add try/catch in onWillStart; on failure set statsState.error = true
- Expose statsError getter on renderer
- Guard <DmsStatBar> mount with t-if="!statsError" so a failed RPC hides
  the bar rather than leaving it in perpetual loading state
@dnplkndll dnplkndll force-pushed the 19.0-imp-dms-ux branch 8 times, most recently from 41843a7 to fec90a4 Compare May 30, 2026 03:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant