@mmlogic/components 0.5.16 → 0.5.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/dist/cjs/loader.cjs.js +1 -1
  2. package/dist/cjs/mosterdcomponents.cjs.js +1 -1
  3. package/dist/cjs/mrd-boolean-field_28.cjs.entry.js +244 -21
  4. package/dist/collection/collection-manifest.json +2 -2
  5. package/dist/collection/components/documents/mrd-document-container/mrd-document-container.js +31 -4
  6. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.js +196 -11
  7. package/dist/collection/components/memo/mrd-memo-container/mrd-memo-container.js +28 -4
  8. package/dist/collection/components/table/mrd-table/mrd-table.js +193 -4
  9. package/dist/collection/dev/api.js +19 -0
  10. package/dist/collection/dev/app.js +75 -0
  11. package/dist/components/mrd-document-container2.js +1 -1
  12. package/dist/components/mrd-layout-section.js +1 -1
  13. package/dist/components/mrd-memo-container2.js +1 -1
  14. package/dist/components/mrd-table2.js +1 -1
  15. package/dist/esm/loader.js +1 -1
  16. package/dist/esm/mosterdcomponents.js +1 -1
  17. package/dist/esm/mrd-boolean-field_28.entry.js +244 -21
  18. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
  19. package/dist/mosterdcomponents/p-29d6dcbc.entry.js +3 -0
  20. package/dist/types/components/documents/mrd-document-container/mrd-document-container.d.ts +7 -0
  21. package/dist/types/components/layout/mrd-layout-section/mrd-layout-section.d.ts +69 -0
  22. package/dist/types/components/memo/mrd-memo-container/mrd-memo-container.d.ts +4 -0
  23. package/dist/types/components/table/mrd-table/mrd-table.d.ts +75 -0
  24. package/dist/types/components.d.ts +79 -0
  25. package/package.json +1 -1
  26. package/dist/mosterdcomponents/p-8a7fa81d.entry.js +0 -3
@@ -367,6 +367,13 @@ const MrdDocumentContainer = class {
367
367
  * (with a tooltip explaining why) and the embedded table/list get it passed
368
368
  * through so their own create/upload/drag-drop paths are disabled too. */
369
369
  this.readOnly = false;
370
+ /** Whether the current user may create a document here — set by mrd-layout-section from
371
+ * its generic capability check (TASK-0299/FINDING-0155), the same answer the embedded
372
+ * table gets in list mode. Named `mayCreate` (not `canCreate`) to avoid colliding with
373
+ * the existing `canCreate` state below, which tracks something unrelated: whether the
374
+ * folder tree currently has a valid drop target selected. Defaults to false (hidden)
375
+ * until confirmed, same as mrd-table. */
376
+ this.mayCreate = false;
370
377
  /** Active body: folder tree or flat table. */
371
378
  this.mode = 'tree';
372
379
  /** Whether the folder tree currently has a valid target for "New folder". */
@@ -381,7 +388,7 @@ const MrdDocumentContainer = class {
381
388
  };
382
389
  this.pickFile = () => {
383
390
  var _a;
384
- if (this.readOnly)
391
+ if (this.readOnly || !this.mayCreate)
385
392
  return;
386
393
  (_a = this.fileInputEl) === null || _a === void 0 ? void 0 : _a.click();
387
394
  };
@@ -419,7 +426,7 @@ const MrdDocumentContainer = class {
419
426
  }
420
427
  // ── Toolbar ────────────────────────────────────────────────────────────────
421
428
  renderToolbar() {
422
- return (index.h("div", { class: "mrd-document-container__toolbar" }, this.mode === 'tree' && (index.h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? cellRenderer.t('readonly_access', this.locale) : undefined, onClick: this.createFolder }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), index.h("path", { d: "M12 11v4M10 13h4" })), cellRenderer.t('new_folder', this.locale))), this.mode === 'tree' && (index.h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? cellRenderer.t('readonly_access', this.locale) : (this.targetLabel ? `${cellRenderer.t('doc_upload_to', this.locale)} ${this.targetLabel}` : undefined), onClick: this.pickFile }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), index.h("path", { d: "M12 3v13M7 8l5-5 5 5" })), cellRenderer.t('doc_upload', this.locale))), index.h("input", { type: "file", class: "mrd-document-container__file-input", ref: el => (this.fileInputEl = el), onChange: this.onFilePicked }), index.h("div", { class: "mrd-document-container__seg", role: "group", "aria-label": cellRenderer.t('doc_view_switch', this.locale) }, index.h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'tree'), title: cellRenderer.t('doc_view_tree', this.locale), "aria-label": cellRenderer.t('doc_view_tree', this.locale), onClick: () => this.setMode('tree') }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M3 5h6M3 12h9M3 19h6" }), index.h("circle", { cx: "18", cy: "5", r: "1.6" }), index.h("circle", { cx: "18", cy: "19", r: "1.6" }), index.h("path", { d: "M16 12h4" }))), index.h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'list'), title: cellRenderer.t('doc_view_list', this.locale), "aria-label": cellRenderer.t('doc_view_list', this.locale), onClick: () => this.setMode('list') }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
429
+ return (index.h("div", { class: "mrd-document-container__toolbar" }, this.mode === 'tree' && this.mayCreate && (index.h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? cellRenderer.t('readonly_access', this.locale) : undefined, onClick: this.createFolder }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), index.h("path", { d: "M12 11v4M10 13h4" })), cellRenderer.t('new_folder', this.locale))), this.mode === 'tree' && this.mayCreate && (index.h("button", { type: "button", class: "mrd-document-container__action", disabled: this.readOnly || !this.canCreate, title: this.readOnly ? cellRenderer.t('readonly_access', this.locale) : (this.targetLabel ? `${cellRenderer.t('doc_upload_to', this.locale)} ${this.targetLabel}` : undefined), onClick: this.pickFile }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), index.h("path", { d: "M12 3v13M7 8l5-5 5 5" })), cellRenderer.t('doc_upload', this.locale))), index.h("input", { type: "file", class: "mrd-document-container__file-input", ref: el => (this.fileInputEl = el), onChange: this.onFilePicked }), index.h("div", { class: "mrd-document-container__seg", role: "group", "aria-label": cellRenderer.t('doc_view_switch', this.locale) }, index.h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'tree'), title: cellRenderer.t('doc_view_tree', this.locale), "aria-label": cellRenderer.t('doc_view_tree', this.locale), onClick: () => this.setMode('tree') }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M3 5h6M3 12h9M3 19h6" }), index.h("circle", { cx: "18", cy: "5", r: "1.6" }), index.h("circle", { cx: "18", cy: "19", r: "1.6" }), index.h("path", { d: "M16 12h4" }))), index.h("button", { class: "mrd-document-container__seg-btn", "aria-pressed": String(this.mode === 'list'), title: cellRenderer.t('doc_view_list', this.locale), "aria-label": cellRenderer.t('doc_view_list', this.locale), onClick: () => this.setMode('list') }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
423
430
  }
424
431
  // ── Bodies ───────────────────────────────────────────────────────────────
425
432
  renderTree() {
@@ -450,7 +457,7 @@ const MrdDocumentContainer = class {
450
457
  } }));
451
458
  }
452
459
  renderList() {
453
- return (index.h("mrd-table", { "data-view": this.viewKey, ref: el => (this.tableEl = el), item: this.item, parentId: this.parentId, locale: this.locale, readOnly: this.readOnly, onMrdLoadPage: (e) => {
460
+ return (index.h("mrd-table", { "data-view": this.viewKey, ref: el => (this.tableEl = el), item: this.item, parentId: this.parentId, locale: this.locale, readOnly: this.readOnly, canCreate: this.mayCreate, onMrdLoadPage: (e) => {
454
461
  e.stopPropagation();
455
462
  this.mrdLoadPage.emit(e.detail);
456
463
  }, onMrdLoadAggregations: (e) => {
@@ -474,7 +481,7 @@ const MrdDocumentContainer = class {
474
481
  }
475
482
  render() {
476
483
  const treeMode = this.mode === 'tree';
477
- return (index.h(index.Host, { key: '6792d49f08dd19f6c51e4b656df06bbd8bc14f3e' }, this.renderToolbar(), index.h("div", { key: '702f4c288f8c40bd2c5999beb392b9dfd022b0bc', class: {
484
+ return (index.h(index.Host, { key: '4b674a508ac7ddf6af80a40836b72dc65f41c741' }, this.renderToolbar(), index.h("div", { key: '19968916f685a2e3cd075dabf170f67fb104251c', class: {
478
485
  'mrd-document-container__body': true,
479
486
  'mrd-document-container__body--tree': treeMode,
480
487
  }, style: treeMode ? { height: `${this.height}px`, overflowY: 'auto' } : {} }, treeMode ? this.renderTree() : this.renderList())));
@@ -5443,6 +5450,7 @@ const MrdLayoutSection = class {
5443
5450
  this.mrdViewAction = index.createEvent(this, "mrdViewAction");
5444
5451
  this.mrdLoadViewAggregations = index.createEvent(this, "mrdLoadViewAggregations");
5445
5452
  this.mrdLoadViewDistinct = index.createEvent(this, "mrdLoadViewDistinct");
5453
+ this.mrdViewCheckCapabilities = index.createEvent(this, "mrdViewCheckCapabilities");
5446
5454
  this.mrdUpdateObject = index.createEvent(this, "mrdUpdateObject");
5447
5455
  this.mrdDeleteObject = index.createEvent(this, "mrdDeleteObject");
5448
5456
  this.mrdViewUpload = index.createEvent(this, "mrdViewUpload");
@@ -5463,6 +5471,12 @@ const MrdLayoutSection = class {
5463
5471
  * create ("+") action and the documents view's New folder / Upload are disabled
5464
5472
  * with a tooltip explaining why, so it's clear at a glance the user can't mutate data. */
5465
5473
  this.readOnly = false;
5474
+ /** When false, skips the mrdViewCheckCapabilities round trip for every VIEW/RELATED_VIEW
5475
+ * binding and hands mrd-table/mrd-memo-container/mrd-document-container their create
5476
+ * affordance as allowed immediately — for a tenant with no access control configured,
5477
+ * the answer would always be mayCreate: true anyway (FINDING-0171 / TASK-0322). Default
5478
+ * `true` keeps today's ask-and-hide behaviour unchanged. */
5479
+ this.accessControlEnabled = true;
5466
5480
  /** The active account's linked person record (userProfile.links.person), if any.
5467
5481
  * Threaded to mrd-memo-container so a top-level memo VIEW (no parent record to
5468
5482
  * attach a new memo to) can still offer "New memo" by falling back to the
@@ -5473,7 +5487,22 @@ const MrdLayoutSection = class {
5473
5487
  this.imagePreviewUrl = null;
5474
5488
  this.imagePreviews = {};
5475
5489
  this.openHistoryField = null;
5490
+ /** Answers to mrdViewCheckCapabilities, keyed by view key — see emitCapabilitiesChecks().
5491
+ * Passed straight down as a `canCreate`/`mayCreate` prop to whichever body (mrd-table,
5492
+ * mrd-memo-container, mrd-document-container) is mounted for that key; a missing entry
5493
+ * means "not answered yet" and is treated as false (hidden), same default as mrd-table's
5494
+ * own standalone behaviour (TASK-0299). */
5495
+ this.canCreateMap = {};
5496
+ /** Companion to canCreateMap — concrete types the mrdViewCheckCapabilities answer allows,
5497
+ * when the API reports them; passed to mrd-table's `allowedTypes` prop to narrow its
5498
+ * create-type picker down to types a create POST would actually accept (FINDING-0176 /
5499
+ * TASK-0314). A missing entry means "not answered yet / not reported" — mrd-table then
5500
+ * leaves `item.createTypes` unfiltered, same as before this existed. */
5501
+ this.allowedTypesMap = {};
5476
5502
  this.historyClickOutside = null;
5503
+ /** Signature last asked per view key (see emitCapabilitiesChecks()) — avoids re-emitting
5504
+ * mrdViewCheckCapabilities on every data/items update when the binding hasn't changed. */
5505
+ this.capabilitiesAsked = new Map();
5477
5506
  this.searchTimers = {};
5478
5507
  this.handleViewLoadPage = (e, name) => {
5479
5508
  e.stopPropagation();
@@ -5493,6 +5522,10 @@ const MrdLayoutSection = class {
5493
5522
  };
5494
5523
  }
5495
5524
  componentDidLoad() {
5525
+ // No DOM/child-hydration dependency, unlike initEmbeddedTables()/emitLoadImages() below —
5526
+ // run it immediately rather than deferring, so the host's answer (setViewCanCreate) can
5527
+ // land before the child bodies even mount.
5528
+ this.emitCapabilitiesChecks();
5496
5529
  setTimeout(() => {
5497
5530
  this.initEmbeddedTables();
5498
5531
  this.emitLoadImages();
@@ -5511,9 +5544,15 @@ const MrdLayoutSection = class {
5511
5544
  }
5512
5545
  dataChanged(newVal) {
5513
5546
  if (newVal && Object.keys(newVal).length > 0) {
5547
+ this.emitCapabilitiesChecks();
5514
5548
  setTimeout(() => this.initEmbeddedTables(), 0);
5515
5549
  }
5516
5550
  }
5551
+ /** A host can swap `items` (e.g. a different dashboard layout) without `data` changing —
5552
+ * dataChanged() alone would miss that binding change. */
5553
+ itemsChanged() {
5554
+ this.emitCapabilitiesChecks();
5555
+ }
5517
5556
  async initEmbeddedTables() {
5518
5557
  const tables = this.el.querySelectorAll('mrd-table[data-view]');
5519
5558
  for (const table of Array.from(tables)) {
@@ -5541,6 +5580,70 @@ const MrdLayoutSection = class {
5541
5580
  }
5542
5581
  }
5543
5582
  }
5583
+ /** Parent record id extracted from `data._links.self.href` — the same value every
5584
+ * VIEW/RELATED_VIEW item on this record shares, used for RELATED_VIEW path construction
5585
+ * (renderRelatedView) and capability checks (emitCapabilitiesChecks). */
5586
+ computeParentId() {
5587
+ var _a, _b, _c, _d, _e;
5588
+ const selfHref = (_d = (_c = (_b = (_a = this.data) === null || _a === void 0 ? void 0 : _a._links) === null || _b === void 0 ? void 0 : _b.self) === null || _c === void 0 ? void 0 : _c.href) !== null && _d !== void 0 ? _d : '';
5589
+ return (_e = selfHref.split('/').filter(Boolean).pop()) !== null && _e !== void 0 ? _e : '';
5590
+ }
5591
+ /** Asks, once per VIEW/RELATED_VIEW binding, whether the current user may create a record
5592
+ * there — the single generic place for this check (TASK-0299/FINDING-0155), so mrd-table,
5593
+ * mrd-memo-container and mrd-document-container all get the answer the same way: as a plain
5594
+ * `canCreate`/`mayCreate` prop from this component, computed here and handed down in
5595
+ * renderTable()/renderMemoContainer()/renderDocumentContainer() — none of the three leaf
5596
+ * components need to ask on their own when rendered through mrd-layout-section (mrd-table's
5597
+ * own self-ask only kicks in for standalone use, i.e. its `canCreate` prop left undefined).
5598
+ *
5599
+ * The route/path variables mirror what each renderer already uses for its own rows —
5600
+ * `type` = `dataClass`, `refType`/`refId` = `fromClass`/parentId for a RELATED_VIEW, and
5601
+ * `filterClass` when the view is scoped to one subtype (the same value sent as `?type=` on
5602
+ * the row fetch). A signature (not just the key) is tracked per binding so a genuine route
5603
+ * change re-asks and hides again, without re-asking on every unrelated data/items update. */
5604
+ emitCapabilitiesChecks() {
5605
+ var _a, _b, _c, _d;
5606
+ if (!this.accessControlEnabled)
5607
+ return;
5608
+ const parentId = this.computeParentId();
5609
+ for (const item of flattenItems(this.items)) {
5610
+ if (item.type !== clientLayout.ClientLayoutItemType.VIEW && item.type !== clientLayout.ClientLayoutItemType.RELATED_VIEW)
5611
+ continue;
5612
+ const key = viewKeyFor(item);
5613
+ if (!key || !item.view)
5614
+ continue;
5615
+ const isRelatedView = item.type === clientLayout.ClientLayoutItemType.RELATED_VIEW;
5616
+ const type = (_b = (_a = item.dataClass) !== null && _a !== void 0 ? _a : item.relatedClass) !== null && _b !== void 0 ? _b : key;
5617
+ const refType = isRelatedView ? ((_c = item.fromClass) !== null && _c !== void 0 ? _c : '') : undefined;
5618
+ const refId = isRelatedView ? parentId : undefined;
5619
+ const filterClass = (_d = item.filterClass) !== null && _d !== void 0 ? _d : undefined;
5620
+ // A host binding `items` and `data` as separate properties (e.g. Angular's
5621
+ // `[items]="…" [data]="…"` on <mrd-layout-section>) can apply them in either
5622
+ // order — itemsChanged() can fire while `data` still holds its default `{}`,
5623
+ // before the real record (and its self href) has landed. Asking here would
5624
+ // emit refId: '' for a RELATED_VIEW, which a host's `refType && refId` check
5625
+ // reads as "no parent" and answers via the plain, unscoped capabilities route
5626
+ // instead of the parent-scoped one (FINDING-0175 / TASK-0316). Skip without
5627
+ // marking capabilitiesAsked, so the real ask fires once dataChanged() lands.
5628
+ if (isRelatedView && !refId)
5629
+ continue;
5630
+ const signature = JSON.stringify([type, refType, refId, filterClass]);
5631
+ if (this.capabilitiesAsked.get(key) === signature)
5632
+ continue;
5633
+ this.capabilitiesAsked.set(key, signature);
5634
+ if (key in this.canCreateMap) {
5635
+ const next = Object.assign({}, this.canCreateMap);
5636
+ delete next[key];
5637
+ this.canCreateMap = next;
5638
+ }
5639
+ if (key in this.allowedTypesMap) {
5640
+ const next = Object.assign({}, this.allowedTypesMap);
5641
+ delete next[key];
5642
+ this.allowedTypesMap = next;
5643
+ }
5644
+ this.mrdViewCheckCapabilities.emit(Object.assign(Object.assign({ name: key, type }, (refType !== undefined ? { refType, refId } : {})), (filterClass ? { filterClass } : {})));
5645
+ }
5646
+ }
5544
5647
  /** Inject search results. Pass dataClass to target a specific SEARCH item; omit when there is only one. */
5545
5648
  async setSearchResults(results, dataClass) {
5546
5649
  const key = dataClass !== null && dataClass !== void 0 ? dataClass : this.resolveSearchKey();
@@ -5582,6 +5685,28 @@ const MrdLayoutSection = class {
5582
5685
  return;
5583
5686
  await table.setAggregations(data);
5584
5687
  }
5688
+ /** Host answer to mrdViewCheckCapabilities — whether the current user may create a record
5689
+ * for the given view's bound route. `name` is the view key from the event detail. Stored in
5690
+ * canCreateMap and passed down as a plain prop to whichever body is mounted for that key
5691
+ * (mrd-table, mrd-memo-container or mrd-document-container) — no DOM lookup needed, unlike
5692
+ * setViewPage/setViewAggregations, since the prop flows through the next render. */
5693
+ async setViewCanCreate(name, value, allowedTypes) {
5694
+ this.canCreateMap = Object.assign(Object.assign({}, this.canCreateMap), { [name]: value });
5695
+ this.allowedTypesMap = Object.assign(Object.assign({}, this.allowedTypesMap), { [name]: allowedTypes });
5696
+ }
5697
+ /** `canCreateMap[key]` when access control is configured (default); always allowed
5698
+ * when `accessControlEnabled` is false, since emitCapabilitiesChecks() never asked
5699
+ * and canCreateMap[key] would otherwise stay permanently unanswered. */
5700
+ effectiveCanCreate(key) {
5701
+ var _a;
5702
+ return this.accessControlEnabled ? ((_a = this.canCreateMap[key]) !== null && _a !== void 0 ? _a : false) : true;
5703
+ }
5704
+ /** `allowedTypesMap[key]` when access control is configured; `undefined` (don't filter)
5705
+ * when disabled, since emitCapabilitiesChecks() never asked and the map entry would
5706
+ * otherwise stay permanently unanswered — see effectiveCanCreate(). */
5707
+ effectiveAllowedTypes(key) {
5708
+ return this.accessControlEnabled ? this.allowedTypesMap[key] : undefined;
5709
+ }
5585
5710
  /** Inject distinct values into an embedded mrd-document-list for a document view. */
5586
5711
  async setViewDistinct(name, nodeId, values) {
5587
5712
  const list = this.el.querySelector(`mrd-document-list[data-doclist="${name}"]`);
@@ -5669,20 +5794,19 @@ const MrdLayoutSection = class {
5669
5794
  return (index.h("div", { class: "mrd-layout-section__search", key: `search-${dataClass}` }, index.h("div", { class: "mrd-layout-section__search-wrap" }, index.h("svg", { class: "mrd-layout-section__search-icon", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true" }, index.h("path", { "fill-rule": "evenodd", d: "M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z", "clip-rule": "evenodd" })), index.h("input", { class: "mrd-layout-section__search-input", type: "text", value: query, placeholder: (_c = item.label) !== null && _c !== void 0 ? _c : '', onInput: e => this.handleSearchInput(dataClass, e.target.value) })), results.length > 0 && (index.h("ul", { class: "mrd-layout-section__search-results" }, results.map(r => (index.h("li", { key: r.id, class: "mrd-layout-section__search-result" }, index.h("button", { class: "mrd-layout-section__search-result-btn", onClick: () => this.mrdNavigate.emit({ href: r.id, label: r.label }) }, index.h("span", { class: "mrd-layout-section__search-result-label" }, r.label), r.description && index.h("span", { class: "mrd-layout-section__search-result-desc" }, r.description)))))))));
5670
5795
  }
5671
5796
  renderRelatedView(item) {
5672
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
5797
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
5673
5798
  const key = viewKeyFor(item);
5674
5799
  if (!key)
5675
5800
  return null;
5676
5801
  if (!item.view)
5677
5802
  return null;
5678
5803
  const showTitle = (_a = item.showTitle) !== null && _a !== void 0 ? _a : false;
5679
- // Extract parentId from data._links.self.href for RELATED_VIEW path construction
5680
5804
  const selfHref = (_e = (_d = (_c = (_b = this.data) === null || _b === void 0 ? void 0 : _b._links) === null || _c === void 0 ? void 0 : _c.self) === null || _d === void 0 ? void 0 : _d.href) !== null && _e !== void 0 ? _e : '';
5681
- const parentId = (_f = selfHref.split('/').filter(Boolean).pop()) !== null && _f !== void 0 ? _f : '';
5805
+ const parentId = this.computeParentId();
5682
5806
  // The API publishes `archetypes` on the VIEW/RELATED_VIEW item itself
5683
5807
  // (sibling of `view`), not inside `view`.
5684
- const docArchetype = clientLayout.resolveArchetype((_g = item.archetypes) !== null && _g !== void 0 ? _g : (_h = item.view) === null || _h === void 0 ? void 0 : _h.archetypes, clientLayout.ARCHETYPE_DOCUMENT);
5685
- const memoArchetype = !docArchetype ? clientLayout.resolveArchetype((_j = item.archetypes) !== null && _j !== void 0 ? _j : (_k = item.view) === null || _k === void 0 ? void 0 : _k.archetypes, clientLayout.ARCHETYPE_MEMO) : undefined;
5808
+ const docArchetype = clientLayout.resolveArchetype((_f = item.archetypes) !== null && _f !== void 0 ? _f : (_g = item.view) === null || _g === void 0 ? void 0 : _g.archetypes, clientLayout.ARCHETYPE_DOCUMENT);
5809
+ const memoArchetype = !docArchetype ? clientLayout.resolveArchetype((_h = item.archetypes) !== null && _h !== void 0 ? _h : (_j = item.view) === null || _j === void 0 ? void 0 : _j.archetypes, clientLayout.ARCHETYPE_MEMO) : undefined;
5686
5810
  // Canonical parent href for single-container seeding: use the RELATED_VIEW's
5687
5811
  // fromClass (base class) rather than the route class, so polymorphic subtypes
5688
5812
  // resolve to the same container the documents actually reference.
@@ -5693,7 +5817,7 @@ const MrdLayoutSection = class {
5693
5817
  // back to this parent; only when it matches the container slot does the
5694
5818
  // parent's own href double as the memo's container.
5695
5819
  const memoContainerRelated = item.type !== clientLayout.ClientLayoutItemType.RELATED_VIEW
5696
- || item.inverseRelation === ((_l = memoArchetype === null || memoArchetype === void 0 ? void 0 : memoArchetype.slots) === null || _l === void 0 ? void 0 : _l.container);
5820
+ || item.inverseRelation === ((_k = memoArchetype === null || memoArchetype === void 0 ? void 0 : memoArchetype.slots) === null || _k === void 0 ? void 0 : _k.container);
5697
5821
  const memoContainerHref = memoContainerRelated ? containerHref : '';
5698
5822
  // Distinct from memoContainerRelated: only a genuine container-scoped
5699
5823
  // RELATED_VIEW (a company's own memo tab) makes every card's container
@@ -5706,7 +5830,7 @@ const MrdLayoutSection = class {
5706
5830
  * swaps between the folder tree and the flat table. Events are re-emitted to
5707
5831
  * the host with the view key, exactly as the raw list/table events were. */
5708
5832
  renderDocumentContainer(item, key, parentId, archetype, containerHref) {
5709
- return (index.h("mrd-document-container", { item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, viewKey: key, locale: this.locale, readOnly: this.readOnly, onMrdLoadDistinct: (e) => {
5833
+ return (index.h("mrd-document-container", { item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, viewKey: key, locale: this.locale, readOnly: this.readOnly, mayCreate: this.effectiveCanCreate(key), onMrdLoadDistinct: (e) => {
5710
5834
  e.stopPropagation();
5711
5835
  this.mrdLoadViewDistinct.emit(Object.assign({ name: key }, e.detail));
5712
5836
  }, onMrdLoadPage: (e) => {
@@ -5743,7 +5867,7 @@ const MrdLayoutSection = class {
5743
5867
  renderMemoContainer(item, key, parentId, archetype, containerHref, containerRelated, showContainerLink) {
5744
5868
  var _a, _b, _c, _d;
5745
5869
  const labelsListItems = (_d = (_c = (_b = (_a = item.view) === null || _a === void 0 ? void 0 : _a.values) === null || _b === void 0 ? void 0 : _b.find(v => { var _a; return v.name === ((_a = archetype.slots) === null || _a === void 0 ? void 0 : _a.labels); })) === null || _c === void 0 ? void 0 : _c.listItems) !== null && _d !== void 0 ? _d : null;
5746
- return (index.h("mrd-memo-container", { "data-view": key, item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, containerRelated: containerRelated, showContainerLink: showContainerLink, viewKey: key, locale: this.locale, readOnly: this.readOnly, me: this.me, labelsListItems: labelsListItems, onMrdNavigate: (e) => {
5870
+ return (index.h("mrd-memo-container", { "data-view": key, item: item, archetype: archetype, parentId: parentId, containerHref: containerHref, containerRelated: containerRelated, showContainerLink: showContainerLink, viewKey: key, locale: this.locale, readOnly: this.readOnly, canCreate: this.effectiveCanCreate(key), me: this.me, labelsListItems: labelsListItems, onMrdNavigate: (e) => {
5747
5871
  var _a;
5748
5872
  e.stopPropagation();
5749
5873
  this.mrdNavigate.emit(Object.assign(Object.assign({}, e.detail), { navigateBehaviour: (_a = item.navigateBehaviour) !== null && _a !== void 0 ? _a : clientLayout.ClientLayoutNavigateBehaviour.PAGE }));
@@ -5770,7 +5894,7 @@ const MrdLayoutSection = class {
5770
5894
  } }));
5771
5895
  }
5772
5896
  renderTable(item, key, parentId) {
5773
- return (index.h("mrd-table", { "data-view": key, item: item, parentId: parentId, locale: this.locale, readOnly: this.readOnly, onMrdLoadPage: (e) => this.handleViewLoadPage(e, key), onMrdLoadAggregations: (e) => {
5897
+ return (index.h("mrd-table", { "data-view": key, item: item, parentId: parentId, locale: this.locale, readOnly: this.readOnly, canCreate: this.effectiveCanCreate(key), allowedTypes: this.effectiveAllowedTypes(key), accessControlEnabled: this.accessControlEnabled, onMrdLoadPage: (e) => this.handleViewLoadPage(e, key), onMrdLoadAggregations: (e) => {
5774
5898
  var _a;
5775
5899
  e.stopPropagation();
5776
5900
  this.mrdLoadViewAggregations.emit(Object.assign({ name: key, dataClass: (_a = item.dataClass) !== null && _a !== void 0 ? _a : key }, e.detail));
@@ -5908,12 +6032,15 @@ const MrdLayoutSection = class {
5908
6032
  const emailArchetype = !docArchetype && !invoiceArchetype ? clientLayout.resolveArchetype(this.archetypes, clientLayout.ARCHETYPE_EMAIL) : undefined;
5909
6033
  const memoArchetype = !docArchetype && !invoiceArchetype && !emailArchetype ? clientLayout.resolveArchetype(this.archetypes, clientLayout.ARCHETYPE_MEMO) : undefined;
5910
6034
  const anyArchetype = docArchetype || invoiceArchetype || emailArchetype || memoArchetype;
5911
- return (index.h(index.Host, { key: '057d145da172e68f84b6ff34f71ae649d6d6bf92' }, index.h("div", { key: 'e03a1761dc6bcd9ff54b264fee56130270bddc2f', class: "mrd-layout-section" }, docArchetype && this.renderDocumentObject(docArchetype), invoiceArchetype && this.renderInvoiceObject(invoiceArchetype), emailArchetype && this.renderEmailObject(emailArchetype), memoArchetype && this.renderMemoObject(memoArchetype), !anyArchetype && this.items.map((item, i) => this.renderItem(item, this.items, i))), this.renderImageModal()));
6035
+ return (index.h(index.Host, { key: '97351c57865614662e888b3c0278a61d866a74e1' }, index.h("div", { key: '43e8f0fda8fcd3300b879d05ac692ce331c896d5', class: "mrd-layout-section" }, docArchetype && this.renderDocumentObject(docArchetype), invoiceArchetype && this.renderInvoiceObject(invoiceArchetype), emailArchetype && this.renderEmailObject(emailArchetype), memoArchetype && this.renderMemoObject(memoArchetype), !anyArchetype && this.items.map((item, i) => this.renderItem(item, this.items, i))), this.renderImageModal()));
5912
6036
  }
5913
6037
  get el() { return index.getElement(this); }
5914
6038
  static get watchers() { return {
5915
6039
  "data": [{
5916
6040
  "dataChanged": 0
6041
+ }],
6042
+ "items": [{
6043
+ "itemsChanged": 0
5917
6044
  }]
5918
6045
  }; }
5919
6046
  };
@@ -6075,6 +6202,10 @@ const MrdMemoContainer = class {
6075
6202
  * tooltip, cards/rows are not clickable, and the embedded table's own
6076
6203
  * create action is disabled too. */
6077
6204
  this.readOnly = false;
6205
+ /** Whether the current user may create a memo here — set by mrd-layout-section from its
6206
+ * generic capability check (TASK-0299/FINDING-0155), the same answer the embedded table
6207
+ * gets in table mode. Defaults to false (hidden) until confirmed, same as mrd-table. */
6208
+ this.canCreate = false;
6078
6209
  /** The active account's linked person record (userProfile.links.person), if any.
6079
6210
  * Used both to gate "New memo" (see effectiveContainerHref) and, on submit, to
6080
6211
  * fill the author slot. */
@@ -6307,7 +6438,7 @@ const MrdMemoContainer = class {
6307
6438
  * the dialog), so without it there is nothing valid to create — regardless
6308
6439
  * of whether a parent record already supplies a container. */
6309
6440
  get canAdd() {
6310
- return !this.readOnly && !!this.meHref() && !!this.effectiveContainerHref;
6441
+ return this.canCreate && !this.readOnly && !!this.meHref() && !!this.effectiveContainerHref;
6311
6442
  }
6312
6443
  /** Path for creating a new memo: `/{dataClass}` for a top-level VIEW, or
6313
6444
  * `/{fromClass}/{parentId}/{dataClass}` when nested under a parent record. */
@@ -6321,7 +6452,7 @@ const MrdMemoContainer = class {
6321
6452
  });
6322
6453
  }
6323
6454
  renderToolbar() {
6324
- return (index.h("div", { class: "mrd-memo-container__toolbar" }, this.mode === 'cards' && (index.h("button", { type: "button", class: "mrd-memo-container__action", disabled: !this.canAdd, title: this.readOnly ? cellRenderer.t('readonly_access', this.locale) : undefined, onClick: this.openCreate }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M12 5v14M5 12h14" })), cellRenderer.t('memo_new', this.locale))), index.h("div", { class: "mrd-memo-container__seg", role: "group", "aria-label": cellRenderer.t('doc_view_switch', this.locale) }, index.h("button", { class: "mrd-memo-container__seg-btn", "aria-pressed": String(this.mode === 'cards'), title: cellRenderer.t('memo_view_cards', this.locale), "aria-label": cellRenderer.t('memo_view_cards', this.locale), onClick: () => this.setMode('cards') }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("rect", { x: "3", y: "4", width: "7", height: "7", rx: "1" }), index.h("rect", { x: "14", y: "4", width: "7", height: "7", rx: "1" }), index.h("rect", { x: "3", y: "15", width: "7", height: "5", rx: "1" }), index.h("rect", { x: "14", y: "15", width: "7", height: "5", rx: "1" }))), index.h("button", { class: "mrd-memo-container__seg-btn", "aria-pressed": String(this.mode === 'table'), title: cellRenderer.t('memo_view_table', this.locale), "aria-label": cellRenderer.t('memo_view_table', this.locale), onClick: () => this.setMode('table') }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
6455
+ return (index.h("div", { class: "mrd-memo-container__toolbar" }, this.mode === 'cards' && this.canCreate && (index.h("button", { type: "button", class: "mrd-memo-container__action", disabled: !this.canAdd, title: this.readOnly ? cellRenderer.t('readonly_access', this.locale) : undefined, onClick: this.openCreate }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M12 5v14M5 12h14" })), cellRenderer.t('memo_new', this.locale))), index.h("div", { class: "mrd-memo-container__seg", role: "group", "aria-label": cellRenderer.t('doc_view_switch', this.locale) }, index.h("button", { class: "mrd-memo-container__seg-btn", "aria-pressed": String(this.mode === 'cards'), title: cellRenderer.t('memo_view_cards', this.locale), "aria-label": cellRenderer.t('memo_view_cards', this.locale), onClick: () => this.setMode('cards') }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("rect", { x: "3", y: "4", width: "7", height: "7", rx: "1" }), index.h("rect", { x: "14", y: "4", width: "7", height: "7", rx: "1" }), index.h("rect", { x: "3", y: "15", width: "7", height: "5", rx: "1" }), index.h("rect", { x: "14", y: "15", width: "7", height: "5", rx: "1" }))), index.h("button", { class: "mrd-memo-container__seg-btn", "aria-pressed": String(this.mode === 'table'), title: cellRenderer.t('memo_view_table', this.locale), "aria-label": cellRenderer.t('memo_view_table', this.locale), onClick: () => this.setMode('table') }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, index.h("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }))))));
6325
6456
  }
6326
6457
  renderDeleteConfirm() {
6327
6458
  return (index.h("div", null, index.h("h2", { class: "mrd-memo-container__modal-title" }, cellRenderer.t('memo_delete_confirm_title', this.locale)), index.h("p", { class: "mrd-memo-container__modal-warning" }, cellRenderer.t('memo_delete_confirm_body', this.locale)), index.h("div", { class: "mrd-memo-container__modal-actions" }, index.h("button", { type: "button", class: "mrd-memo-container__modal-cancel", onClick: this.cancelDeleteConfirm }, cellRenderer.t('cancel', this.locale)), index.h("button", { type: "button", class: "mrd-memo-container__modal-delete-confirm", onClick: this.confirmDelete }, cellRenderer.t('delete', this.locale)))));
@@ -6358,7 +6489,7 @@ const MrdMemoContainer = class {
6358
6489
  } }));
6359
6490
  }
6360
6491
  renderTable() {
6361
- return (index.h("mrd-table", { "data-view": this.viewKey, ref: el => (this.tableEl = el), item: this.item, parentId: this.parentId, locale: this.locale, readOnly: this.readOnly, onMrdLoadPage: (e) => {
6492
+ return (index.h("mrd-table", { "data-view": this.viewKey, ref: el => (this.tableEl = el), item: this.item, parentId: this.parentId, locale: this.locale, readOnly: this.readOnly, canCreate: this.canCreate, onMrdLoadPage: (e) => {
6362
6493
  e.stopPropagation();
6363
6494
  this.mrdLoadPage.emit(e.detail);
6364
6495
  }, onMrdLoadAggregations: (e) => {
@@ -6373,7 +6504,7 @@ const MrdMemoContainer = class {
6373
6504
  } }));
6374
6505
  }
6375
6506
  render() {
6376
- return (index.h(index.Host, { key: 'a000221db6b1ba12351f38619f319b277b791f68' }, this.renderToolbar(), index.h("div", { key: '712951c8e391ed693bf6e4263c4f1076172b0451', class: "mrd-memo-container__body" }, this.mode === 'cards' ? this.renderCards() : this.renderTable()), this.renderDialog()));
6507
+ return (index.h(index.Host, { key: 'b0479aad6dc49935442183fcbdf30c07c2c490ff' }, this.renderToolbar(), index.h("div", { key: '40d0fb882186001ad4984c1ba64c5a5c529f4d76', class: "mrd-memo-container__body" }, this.mode === 'cards' ? this.renderCards() : this.renderTable()), this.renderDialog()));
6377
6508
  }
6378
6509
  static get watchers() { return {
6379
6510
  "dialogOpen": [{
@@ -7505,6 +7636,7 @@ const MrdTable = class {
7505
7636
  this.mrdAction = index.createEvent(this, "mrdAction");
7506
7637
  this.mrdDownload = index.createEvent(this, "mrdDownload");
7507
7638
  this.mrdLoadAggregations = index.createEvent(this, "mrdLoadAggregations");
7639
+ this.mrdCheckCapabilities = index.createEvent(this, "mrdCheckCapabilities");
7508
7640
  // ── Non-state internals ────────────────────────────────────────────────────
7509
7641
  this.pendingPages = new Set();
7510
7642
  this.debounceTimer = null;
@@ -7566,6 +7698,12 @@ const MrdTable = class {
7566
7698
  * explaining why) so a viewer-role user can see at a glance that this dashboard
7567
7699
  * is read-only. Sorting, filtering and export stay available — those don't mutate data. */
7568
7700
  this.readOnly = false;
7701
+ /** When false, skips the standalone mrdCheckCapabilities round trip entirely and renders
7702
+ * the create action immediately as allowed — for a tenant that has no access control
7703
+ * configured, the answer would always be true anyway (FINDING-0171 / TASK-0322). Ignored
7704
+ * while `canCreate` is set explicitly (the host already controls the answer directly in
7705
+ * that mode). Default `true` keeps today's ask-and-hide behaviour unchanged. */
7706
+ this.accessControlEnabled = true;
7569
7707
  // ── Internal state ─────────────────────────────────────────────────────────
7570
7708
  /** Index into allViews[] for the currently displayed view. 0 = primary, 1+ = alternatives. */
7571
7709
  this.activeViewIdx = 0;
@@ -7610,6 +7748,13 @@ const MrdTable = class {
7610
7748
  this.aggregationsPending = false;
7611
7749
  /** Lower bound on total derived from setPage() hasNext info; grows as pages load. */
7612
7750
  this.minKnownTotal = 0;
7751
+ /** Self-asked answer for standalone use (the `canCreate` prop is undefined) — defaults to
7752
+ * hidden (TASK-0299) until the host answers mrdCheckCapabilities via setCanCreate(). Ignored
7753
+ * when the host controls `canCreate` directly; see effectiveCanCreate(). */
7754
+ this.selfCanCreate = false;
7755
+ /** Self-asked companion to selfCanCreate — the concrete types setCanCreate() reported allowed,
7756
+ * or `undefined` while unanswered / when the host's mrdCheckCapabilities answer omitted it. */
7757
+ this.selfAllowedTypes = undefined;
7613
7758
  this.handleScroll = (e) => {
7614
7759
  const scroller = e.currentTarget;
7615
7760
  const { renderStart, renderEnd } = computeWindow(scroller.scrollTop, this.rowHeight, this.tableHeight, this.baseTotal);
@@ -7644,6 +7789,14 @@ const MrdTable = class {
7644
7789
  this.searchOpen = false;
7645
7790
  this.searchQuery = '';
7646
7791
  this.stashedFilters = null;
7792
+ this.emitCapabilitiesCheck();
7793
+ }
7794
+ /** A parent-record navigation can reuse the same `item` reference while only `parentId`
7795
+ * changes (e.g. clicking a different dossier for the same RELATED_VIEW layout) — itemChanged()
7796
+ * would not fire for that. Re-ask so a stale canCreate answer from the previous parent never
7797
+ * leaks into the new one. */
7798
+ parentIdChanged() {
7799
+ this.emitCapabilitiesCheck();
7647
7800
  }
7648
7801
  // ── Lifecycle ──────────────────────────────────────────────────────────────
7649
7802
  componentWillLoad() {
@@ -7651,6 +7804,7 @@ const MrdTable = class {
7651
7804
  this.applyDefaultSort((_c = (_b = (_a = this.item) === null || _a === void 0 ? void 0 : _a.view) === null || _b === void 0 ? void 0 : _b.defaultSort) !== null && _c !== void 0 ? _c : '');
7652
7805
  }
7653
7806
  componentDidLoad() {
7807
+ this.emitCapabilitiesCheck();
7654
7808
  this.staleCheckTimer = setInterval(() => this.checkStalePages(), STALE_CHECK_INTERVAL_MS);
7655
7809
  // A table inside a hidden tab pane renders and loads page 0 without ever having a box, so
7656
7810
  // the width freeze cannot measure anything. ResizeObserver fires the moment the pane is
@@ -7769,6 +7923,15 @@ const MrdTable = class {
7769
7923
  // Fresh totals can be wider than the ones the columns were checked against.
7770
7924
  this.fixedContentMeasured = false;
7771
7925
  }
7926
+ /** Host answer to mrdCheckCapabilities — whether the current user may create a record for
7927
+ * the table's currently bound route (standalone use only; ignored while the `canCreate`
7928
+ * prop is set, since the host controls it directly in that mode). Call it any time after
7929
+ * the event fires; a stale answer for an old binding is discarded automatically
7930
+ * (selfCanCreate is reset to false before every new mrdCheckCapabilities emission). */
7931
+ async setCanCreate(value, allowedTypes) {
7932
+ this.selfCanCreate = value;
7933
+ this.selfAllowedTypes = allowedTypes;
7934
+ }
7772
7935
  // ── Lifecycle ──────────────────────────────────────────────────────────────
7773
7936
  disconnectedCallback() {
7774
7937
  if (this.resizeObserver) {
@@ -8128,6 +8291,33 @@ const MrdTable = class {
8128
8291
  dataClass: v.dataClass,
8129
8292
  });
8130
8293
  }
8294
+ /** Asks the host whether creating is allowed for the table's current binding (item, parent
8295
+ * and active view) — only in standalone use (`canCreate` prop undefined). A host that
8296
+ * controls `canCreate` directly already knows the answer, so the table never asks on its
8297
+ * own in that mode. Resets selfCanCreate to false first so the add button stays hidden for
8298
+ * the new binding until the host answers — it never inherits the previous binding's answer. */
8299
+ emitCapabilitiesCheck() {
8300
+ var _a, _b;
8301
+ if (this.canCreate !== undefined)
8302
+ return;
8303
+ if (!this.accessControlEnabled)
8304
+ return;
8305
+ this.selfCanCreate = false;
8306
+ this.selfAllowedTypes = undefined;
8307
+ const v = this.allViews[this.activeViewIdx];
8308
+ if (!(v === null || v === void 0 ? void 0 : v.dataClass))
8309
+ return;
8310
+ const isRelatedView = ((_a = this.item) === null || _a === void 0 ? void 0 : _a.type) === 'RELATED_VIEW';
8311
+ // A host binding `item` and `parentId` as separate properties (e.g. Angular) can apply
8312
+ // them in either order — this can run while `parentId` still holds its default `''`,
8313
+ // before the real parent record has landed. Asking here would emit refId: '' for a
8314
+ // RELATED_VIEW, which a host's `refType && refId` check reads as "no parent" and answers
8315
+ // via the plain, unscoped capabilities route instead of the parent-scoped one
8316
+ // (FINDING-0175 / TASK-0316). Skip; parentIdChanged() re-asks once it lands.
8317
+ if (isRelatedView && !this.parentId)
8318
+ return;
8319
+ this.mrdCheckCapabilities.emit(Object.assign(Object.assign({ type: v.dataClass }, (isRelatedView ? { refType: (_b = v.fromClass) !== null && _b !== void 0 ? _b : '', refId: this.parentId } : {})), (v.filterClass ? { filterClass: v.filterClass } : {})));
8320
+ }
8131
8321
  /** Build query params for a page request from current sort, view filters, filterClass and active column filters.
8132
8322
  *
8133
8323
  * `q` and column filters are mutually exclusive on the backend — combining them breaks
@@ -8162,11 +8352,41 @@ const MrdTable = class {
8162
8352
  var _a, _b, _c;
8163
8353
  return ((_c = (_b = (_a = this.allViews[this.activeViewIdx]) === null || _a === void 0 ? void 0 : _a.view) === null || _b === void 0 ? void 0 : _b.values) !== null && _c !== void 0 ? _c : []);
8164
8354
  }
8355
+ /** `canCreate` when the host controls it directly; else, when access control is disabled,
8356
+ * always allowed; else the table's own self-asked answer. */
8357
+ get effectiveCanCreate() {
8358
+ if (this.canCreate !== undefined)
8359
+ return this.canCreate;
8360
+ if (!this.accessControlEnabled)
8361
+ return true;
8362
+ return this.selfCanCreate;
8363
+ }
8364
+ /** `allowedTypes` when the host controls it directly, else the table's own self-asked
8365
+ * answer. `undefined` means "unknown — don't filter", not "nothing allowed". */
8366
+ get effectiveAllowedTypes() {
8367
+ if (this.allowedTypes !== undefined)
8368
+ return this.allowedTypes;
8369
+ return this.selfAllowedTypes;
8370
+ }
8371
+ /** `item.createTypes` intersected with effectiveAllowedTypes when that is known, so the
8372
+ * create-type picker only offers concrete types a create POST would actually accept
8373
+ * (FINDING-0176 / TASK-0314). Unfiltered when the answer isn't known (undefined) — an
8374
+ * older host/API that never reports allowedTypes keeps today's unfiltered behaviour. */
8375
+ get effectiveCreateTypes() {
8376
+ var _a;
8377
+ const all = (_a = this.item) === null || _a === void 0 ? void 0 : _a.createTypes;
8378
+ const allowed = this.effectiveAllowedTypes;
8379
+ if (!all || allowed == null)
8380
+ return all;
8381
+ return all.filter(ct => allowed.includes(ct.type));
8382
+ }
8165
8383
  get tableActions() {
8166
8384
  var _a, _b;
8167
8385
  const raw = (_b = (_a = this.item) === null || _a === void 0 ? void 0 : _a.actions) !== null && _b !== void 0 ? _b : [];
8168
8386
  return (raw !== null && raw !== void 0 ? raw : []).reduce((acc, a) => {
8169
- if (a === 'NEW')
8387
+ // Hidden (not just disabled) until creating is confirmed allowed for this binding —
8388
+ // see effectiveCanCreate() / emitCapabilitiesCheck() (TASK-0299).
8389
+ if (a === 'NEW' && this.effectiveCanCreate)
8170
8390
  acc.push({ action: 'create', label: cellRenderer.t('table_new_record', this.locale), icon: 'assets/sprites.svg#icon-plus', variant: 'primary', disabled: this.readOnly });
8171
8391
  if (a === 'EXPORT')
8172
8392
  acc.push({ action: 'export', label: cellRenderer.t('table_export_excel', this.locale), icon: 'assets/sprites.svg#icon-file-excel' });
@@ -8574,6 +8794,7 @@ const MrdTable = class {
8574
8794
  this.searchOpen = false;
8575
8795
  this.searchQuery = '';
8576
8796
  this.stashedFilters = null;
8797
+ this.emitCapabilitiesCheck();
8577
8798
  this.init();
8578
8799
  }
8579
8800
  toggleViewPopover(e) {
@@ -8595,8 +8816,7 @@ const MrdTable = class {
8595
8816
  }
8596
8817
  // ── Render: toolbar ────────────────────────────────────────────────────────
8597
8818
  renderToolbar() {
8598
- var _a;
8599
- return (index.h(Toolbar, { locale: this.locale, filterCount: this.activeFilters.size, actions: this.tableActions, views: this.allViews, activeViewIdx: this.activeViewIdx, viewPopoverOpen: this.viewPopoverOpen, createPickerOpen: this.createPickerOpen, createTypes: (_a = this.item) === null || _a === void 0 ? void 0 : _a.createTypes, readOnly: this.readOnly, searchEnabled: this.searchEnabled, searchOpen: this.searchOpen, searchQuery: this.searchQuery, cb: {
8819
+ return (index.h(Toolbar, { locale: this.locale, filterCount: this.activeFilters.size, actions: this.tableActions, views: this.allViews, activeViewIdx: this.activeViewIdx, viewPopoverOpen: this.viewPopoverOpen, createPickerOpen: this.createPickerOpen, createTypes: this.effectiveCreateTypes, readOnly: this.readOnly, searchEnabled: this.searchEnabled, searchOpen: this.searchOpen, searchQuery: this.searchQuery, cb: {
8600
8820
  onClearAllFilters: () => this.clearAllFilters(),
8601
8821
  onViewSwitch: idx => this.handleViewSwitch(idx),
8602
8822
  onToggleViewPopover: e => this.toggleViewPopover(e),
@@ -8751,6 +8971,9 @@ const MrdTable = class {
8751
8971
  }],
8752
8972
  "item": [{
8753
8973
  "itemChanged": 0
8974
+ }],
8975
+ "parentId": [{
8976
+ "parentIdChanged": 0
8754
8977
  }]
8755
8978
  }; }
8756
8979
  };
@@ -2,6 +2,7 @@
2
2
  "entries": [
3
3
  "components/documents/mrd-document-container/mrd-document-container.js",
4
4
  "components/documents/mrd-document-list/mrd-document-list.js",
5
+ "components/table/mrd-table/mrd-table.js",
5
6
  "components/documents/mrd-document-view/mrd-document-view.js",
6
7
  "components/email/mrd-email-view/mrd-email-view.js",
7
8
  "components/fields/mrd-boolean-field/mrd-boolean-field.js",
@@ -27,8 +28,7 @@
27
28
  "components/memo/mrd-memo-container/mrd-memo-container.js",
28
29
  "components/memo/mrd-memo-list/mrd-memo-list.js",
29
30
  "components/memo/mrd-memo-view/mrd-memo-view.js",
30
- "components/merge/mrd-merge-view/mrd-merge-view.js",
31
- "components/table/mrd-table/mrd-table.js"
31
+ "components/merge/mrd-merge-view/mrd-merge-view.js"
32
32
  ],
33
33
  "mixins": [],
34
34
  "compiler": {