[IMP] dms: sibling-module extension seams (hero_chips slot, preview_action_registry, useDmsAccent)#10
Open
dnplkndll wants to merge 16 commits into
Open
[IMP] dms: sibling-module extension seams (hero_chips slot, preview_action_registry, useDmsAccent)#10dnplkndll wants to merge 16 commits into
dnplkndll wants to merge 16 commits into
Conversation
…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
- 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
5799f35 to
4c65ce7
Compare
…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
41843a7 to
fec90a4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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, anddms_field_auto_classificationwill 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 bothdms.fileanddms.directoryform heroes. Siblings inject chips with<xpath expr="//div[@name='hero_chips']" position="inside">. Without a named slot every sibling needs a fragilehasclassxpath that collides under co-installation.dms.preview_actionsregistry — mirrors the shape of the existingdms.preview_handlersregistry.FilePreviewPaneiterates 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_classificationwill register a "Re-classify" action here.useDmsAccent/getDmsAccent— JS utility that mirrorsdms_ext_palette.scss's extension→colour mapping. Lets sibling card components apply--dms-accentvia astylebinding 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).