v0.4.0
-
@lowdefy/modules-mongodb-companies: ### Minor Changes
-
#42
cb7b574Thanks @JohannMoller! - Add opt-in parent/child hierarchy to thecompaniesmodule. Companies form a directed acyclic graph (DAG) over a new top-levelparent_ids: string[]field — each company can have multiple parents and multiple children. Gated by a singlehierarchy.enabledvar (defaultfalse); when disabled, the module behaves exactly as today and theparent_idsfield is omitted from new documents.What enabling
hierarchy.enabled: trueadds:- Edit form — a new "Parent {label_plural}" multi-select section appended to the form. Self is filtered out of the options entirely; descendants render as disabled options with a "(child of this company)" suffix so users see why they can't be picked. The selector reads
state.cycle_check_self_idandstate.cycle_check_ids, populated by the page'sset_stateaction afterget_descendant_company_idsresolves on mount. - View page — a new "Company Hierarchy" sidebar tile with two stacked sections (Parents above, Children below) rendered as inline anchor links. Empty sections collapse; the whole tile self-hides when there's nothing to show. Soft-deleted parents are filtered out via the
$lookupsub-pipeline; soft-deleted children are filtered out via the children request. - Cycle prevention —
update-companyruns a$graphLookup-based pre-check (walks upward from each candidate parent throughparent_ids). If self appears anywhere in the ancestor closure,:reject:aborts the routine with the message "Selected parents would create a cycle in the company hierarchy." — surfaces to the calling form'sonErrorhandler.
New module vars (under
hierarchy):enabled(bool, defaultfalse) — master flag.parent_label(string, optional) — override for the parent multi-select label and parents heading.children_label(string, optional) — override for the children heading.max_depth(number, default20) — defensive cap on every$graphLookupin the module's pipelines (descendants resolution + cycle check). Backstops runaway traversal in the unlikely case a cycle leaks past the API check.
New collection field:
parent_ids: string[](top-level, only emitted whenhierarchy.enabled: true). No data migration needed — existing companies without the field behave as roots (no parents) under MongoDB multikey index semantics.New module exports:
parent_selectorcomponent —MultipleSelectorwrapper used on the edit form (no ownonMount; the consuming page sequences the options fetch).tile_hierarchycomponent — referenced internally by the view page's sidebar.get_descendant_company_idsrequest — shared by edit form (cycle-check exclusion list) and the deferred list filter; reads_state.filter.parent_scopewith fallback to_state._idso one request file serves both consumers.get_company_childrenrequest — direct-children-only multikey$matchfor the view-page tile.
Cleanup bundled in this release (verified against
@lowdefy/blocks-antd@4.7.1schemas):- Dropped the vestigial
optionConfigblock fromcompany-selector.yaml— not inSelector/schema.json, not consumed by any plugin inplugins/. The schema's option shape ({ label, value, disabled, ... }) already matches the projection's output natively. - Switched
label: <string>totitle: <string>oncompany-selector.yaml—label:is an object on the antd schema (label-area styling),title:is the string-typed displayed label.
MongoDB version requirement. The new
$lookupinget_company.yamluses thelocalField + foreignField + pipelinecombination, which requires MongoDB 5.0+. Apps on older MongoDB versions need to upgrade before deploying this version.Out of scope for this release: hierarchy filter on the list page (
tasks/10-list-filter.mdspec retained for a future implementation when needed); cross-module hierarchy roll-ups (e.g. "all contacts under any descendant of X"); hierarchical permissions; bulk re-parent / drag-and-drop graph editor.The list filter and the related no-op Atlas Search soft-delete cleanup (
mustNot exists path: removed.timestampinget_all_companies.yamlandget_company_excel_data.yaml) are documented in the design's "Related cleanup" section and remain pending. - Edit form — a new "Parent {label_plural}" multi-select section appended to the form. Self is filtered out of the options entirely; descendants render as disabled options with a "(child of this company)" suffix so users see why they can't be picked. The selector reads
-
#43
6167087Thanks @JohannMoller! - Add ashort_nametop-level field to the companies module for narrow display contexts (reports, chart axes, dense tables). The field is required on the create/edit form and is surfaced on the view-page core descriptions, the list table (between Name and Description), the Excel export, and the create/update API payloads.Toggled by a new
short_name.enabledvar (defaulttrue, opt-out). When set tofalse, every surface referencingshort_name— form input, view row, table column, Excel column, API payload — is omitted at build time and the field is absent from new documents. Existing documents that already carryshort_namekeep the value on disk but won't render or be written until re-enabled.Apps that want
short_nameto drive selectors, table titles, and event templates can additionally setname_field: short_nameon the module entry — the existing escape hatch already supports it. -
#45
d64b5d2Thanks @Yianni99! - Changeevent_displaydefaulting and override semantics across all event-emitting modules. The default (no override) now renders titles under the consumer'sapp_nameinstead of a literaldefaultkey, and an override fully replaces the defaults instead of merging with them.Behavior changes (potentially breaking for consumers):
- Override fully replaces, no merge. Whatever you write under
event_displayis exactly what's stored on the event document. Consumers that previously relied on partial overrides being merged with the module's defaults must now list every app and event type they want rendered. - Defaults file shape changed.
modules/{name}/defaults/event_display.yamlis now a flat{ event-type: template }map. The previous top-leveldefault:wrapper is gone — the build wraps the flat map under the consumer'sapp_namevar. Consumers that_refthe defaults file directly will see the new shape. companiesnow requiresapp_name. Every event-emitting module declares its app context the same way contacts/user-admin/user-account already did. Companies consumers must addapp_nameto their module vars (typically wired fromapp_config.yaml).
Migration:
- If you didn't override
event_display, no action needed beyond settingapp_nameon companies. - If you overrode
event_display, list every app and event type you want stored — defaults no longer fill the gaps. The override shape stays{ [app_name]: { [event-type]: template } }.
See
docs/idioms.md#event-displayfor the updated reference. - Override fully replaces, no merge. Whatever you write under
-
@lowdefy/modules-mongodb-contacts: ### Minor Changes
-
#45
d64b5d2Thanks @Yianni99! - Changeevent_displaydefaulting and override semantics across all event-emitting modules. The default (no override) now renders titles under the consumer'sapp_nameinstead of a literaldefaultkey, and an override fully replaces the defaults instead of merging with them.Behavior changes (potentially breaking for consumers):
- Override fully replaces, no merge. Whatever you write under
event_displayis exactly what's stored on the event document. Consumers that previously relied on partial overrides being merged with the module's defaults must now list every app and event type they want rendered. - Defaults file shape changed.
modules/{name}/defaults/event_display.yamlis now a flat{ event-type: template }map. The previous top-leveldefault:wrapper is gone — the build wraps the flat map under the consumer'sapp_namevar. Consumers that_refthe defaults file directly will see the new shape. companiesnow requiresapp_name. Every event-emitting module declares its app context the same way contacts/user-admin/user-account already did. Companies consumers must addapp_nameto their module vars (typically wired fromapp_config.yaml).
Migration:
- If you didn't override
event_display, no action needed beyond settingapp_nameon companies. - If you overrode
event_display, list every app and event type you want stored — defaults no longer fill the gaps. The override shape stays{ [app_name]: { [event-type]: template } }.
See
docs/idioms.md#event-displayfor the updated reference. - Override fully replaces, no merge. Whatever you write under
-
@lowdefy/modules-mongodb-user-account: ### Minor Changes
-
#45
d64b5d2Thanks @Yianni99! - Changeevent_displaydefaulting and override semantics across all event-emitting modules. The default (no override) now renders titles under the consumer'sapp_nameinstead of a literaldefaultkey, and an override fully replaces the defaults instead of merging with them.Behavior changes (potentially breaking for consumers):
- Override fully replaces, no merge. Whatever you write under
event_displayis exactly what's stored on the event document. Consumers that previously relied on partial overrides being merged with the module's defaults must now list every app and event type they want rendered. - Defaults file shape changed.
modules/{name}/defaults/event_display.yamlis now a flat{ event-type: template }map. The previous top-leveldefault:wrapper is gone — the build wraps the flat map under the consumer'sapp_namevar. Consumers that_refthe defaults file directly will see the new shape. companiesnow requiresapp_name. Every event-emitting module declares its app context the same way contacts/user-admin/user-account already did. Companies consumers must addapp_nameto their module vars (typically wired fromapp_config.yaml).
Migration:
- If you didn't override
event_display, no action needed beyond settingapp_nameon companies. - If you overrode
event_display, list every app and event type you want stored — defaults no longer fill the gaps. The override shape stays{ [app_name]: { [event-type]: template } }.
See
docs/idioms.md#event-displayfor the updated reference. - Override fully replaces, no merge. Whatever you write under
-
@lowdefy/modules-mongodb-user-admin: ### Minor Changes
-
#45
d64b5d2Thanks @Yianni99! - Changeevent_displaydefaulting and override semantics across all event-emitting modules. The default (no override) now renders titles under the consumer'sapp_nameinstead of a literaldefaultkey, and an override fully replaces the defaults instead of merging with them.Behavior changes (potentially breaking for consumers):
- Override fully replaces, no merge. Whatever you write under
event_displayis exactly what's stored on the event document. Consumers that previously relied on partial overrides being merged with the module's defaults must now list every app and event type they want rendered. - Defaults file shape changed.
modules/{name}/defaults/event_display.yamlis now a flat{ event-type: template }map. The previous top-leveldefault:wrapper is gone — the build wraps the flat map under the consumer'sapp_namevar. Consumers that_refthe defaults file directly will see the new shape. companiesnow requiresapp_name. Every event-emitting module declares its app context the same way contacts/user-admin/user-account already did. Companies consumers must addapp_nameto their module vars (typically wired fromapp_config.yaml).
Migration:
- If you didn't override
event_display, no action needed beyond settingapp_nameon companies. - If you overrode
event_display, list every app and event type you want stored — defaults no longer fill the gaps. The override shape stays{ [app_name]: { [event-type]: template } }.
See
docs/idioms.md#event-displayfor the updated reference. - Override fully replaces, no merge. Whatever you write under