@mmlogic/components 0.5.1 → 0.5.3

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 (113) hide show
  1. package/dist/cjs/cell-renderer-7KX9Qbml.js +983 -0
  2. package/dist/cjs/{client-layout-RZ3Ix-4a.js → client-layout-CqVuJumR.js} +9 -0
  3. package/dist/cjs/{index-DzRfoAPz.js → index-DVbdkYO3.js} +468 -6
  4. package/dist/cjs/index.cjs.js +4 -1
  5. package/dist/cjs/loader.cjs.js +2 -2
  6. package/dist/cjs/mosterdcomponents.cjs.js +2 -2
  7. package/dist/cjs/{mrd-boolean-field_23.cjs.entry.js → mrd-boolean-field_25.cjs.entry.js} +3017 -951
  8. package/dist/cjs/mrd-merge-view.cjs.entry.js +273 -0
  9. package/dist/collection/collection-manifest.json +3 -0
  10. package/dist/collection/components/documents/mrd-document-view/mrd-document-view.js +7 -8
  11. package/dist/collection/components/email/mrd-email-view/mrd-email-view.css +126 -0
  12. package/dist/collection/components/email/mrd-email-view/mrd-email-view.js +247 -0
  13. package/dist/collection/components/fields/file-upload-common.js +23 -7
  14. package/dist/collection/components/fields/mrd-boolean-field/mrd-boolean-field.js +1 -1
  15. package/dist/collection/components/fields/mrd-currency-field/mrd-currency-field.js +1 -1
  16. package/dist/collection/components/fields/mrd-date-field/mrd-date-field.js +1 -1
  17. package/dist/collection/components/fields/mrd-datetime-field/mrd-datetime-field.js +1 -1
  18. package/dist/collection/components/fields/mrd-email-field/mrd-email-field.js +1 -1
  19. package/dist/collection/components/fields/mrd-file-field/mrd-file-field.css +20 -1
  20. package/dist/collection/components/fields/mrd-file-field/mrd-file-field.js +108 -47
  21. package/dist/collection/components/fields/mrd-image-field/mrd-image-field.css +44 -0
  22. package/dist/collection/components/fields/mrd-image-field/mrd-image-field.js +126 -58
  23. package/dist/collection/components/fields/mrd-longtext-field/mrd-longtext-field.js +1 -1
  24. package/dist/collection/components/fields/mrd-number-field/mrd-number-field.js +1 -1
  25. package/dist/collection/components/fields/mrd-secret-field/mrd-secret-field.js +2 -2
  26. package/dist/collection/components/fields/mrd-text-field/mrd-text-field.js +1 -1
  27. package/dist/collection/components/fields/mrd-textarea-field/mrd-textarea-field.js +9 -5
  28. package/dist/collection/components/fields/mrd-time-field/mrd-time-field.js +1 -1
  29. package/dist/collection/components/form/mrd-field/mrd-field.js +5 -5
  30. package/dist/collection/components/form/mrd-form/mrd-form.js +55 -6
  31. package/dist/collection/components/invoice/mrd-invoice-view/mrd-invoice-view.css +145 -0
  32. package/dist/collection/components/invoice/mrd-invoice-view/mrd-invoice-view.js +281 -0
  33. package/dist/collection/components/layout/mrd-layout-section/field-value.js +5 -1
  34. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.css +10 -0
  35. package/dist/collection/components/layout/mrd-layout-section/mrd-layout-section.js +38 -8
  36. package/dist/collection/components/merge/mrd-merge-view/merge-diff.js +99 -0
  37. package/dist/collection/components/merge/mrd-merge-view/merge-parts.js +44 -0
  38. package/dist/collection/components/merge/mrd-merge-view/mrd-merge-view.css +244 -0
  39. package/dist/collection/components/merge/mrd-merge-view/mrd-merge-view.js +397 -0
  40. package/dist/collection/dev/api.js +41 -2
  41. package/dist/collection/dev/app.js +135 -17
  42. package/dist/collection/dev/example-data.js +126 -2
  43. package/dist/collection/types/client-layout.js +6 -0
  44. package/dist/collection/utils/cell-renderer.js +6 -1
  45. package/dist/collection/utils/document-attachments.js +27 -14
  46. package/dist/collection/utils/i18n.js +224 -0
  47. package/dist/components/cell-renderer.js +1 -0
  48. package/dist/components/client-layout.js +1 -1
  49. package/dist/components/document-attachments.js +1 -1
  50. package/dist/components/file-upload-common.js +1 -1
  51. package/dist/components/format.js +1 -1
  52. package/dist/components/i18n.js +1 -1
  53. package/dist/components/index.js +1 -1
  54. package/dist/components/mrd-boolean-field2.js +1 -1
  55. package/dist/components/mrd-currency-field2.js +1 -1
  56. package/dist/components/mrd-date-field2.js +1 -1
  57. package/dist/components/mrd-datetime-field2.js +1 -1
  58. package/dist/components/mrd-document-view2.js +1 -1
  59. package/dist/components/mrd-email-field2.js +1 -1
  60. package/dist/components/mrd-email-view.d.ts +11 -0
  61. package/dist/components/mrd-email-view.js +1 -0
  62. package/dist/components/mrd-email-view2.js +1 -0
  63. package/dist/components/mrd-field2.js +1 -1
  64. package/dist/components/mrd-file-field2.js +1 -1
  65. package/dist/components/mrd-form.js +1 -1
  66. package/dist/components/mrd-image-field2.js +1 -1
  67. package/dist/components/mrd-invoice-view.d.ts +11 -0
  68. package/dist/components/mrd-invoice-view.js +1 -0
  69. package/dist/components/mrd-invoice-view2.js +1 -0
  70. package/dist/components/mrd-layout-section.js +1 -1
  71. package/dist/components/mrd-longtext-field2.js +1 -1
  72. package/dist/components/mrd-merge-view.d.ts +11 -0
  73. package/dist/components/mrd-merge-view.js +1 -0
  74. package/dist/components/mrd-number-field2.js +1 -1
  75. package/dist/components/mrd-secret-field2.js +1 -1
  76. package/dist/components/mrd-table2.js +1 -1
  77. package/dist/components/mrd-text-field2.js +1 -1
  78. package/dist/components/mrd-textarea-field2.js +1 -1
  79. package/dist/components/mrd-time-field2.js +1 -1
  80. package/dist/components/purify.es.js +2 -0
  81. package/dist/esm/cell-renderer-DIsNX9D7.js +974 -0
  82. package/dist/esm/{client-layout-DCOAAH92.js → client-layout-Bc42CfJB.js} +7 -1
  83. package/dist/esm/{index-DiSe6vjv.js → index-CDiBvvJU.js} +468 -6
  84. package/dist/esm/index.js +1 -1
  85. package/dist/esm/loader.js +3 -3
  86. package/dist/esm/mosterdcomponents.js +3 -3
  87. package/dist/esm/{mrd-boolean-field_23.entry.js → mrd-boolean-field_25.entry.js} +2949 -885
  88. package/dist/esm/mrd-merge-view.entry.js +271 -0
  89. package/dist/mosterdcomponents/index.esm.js +1 -1
  90. package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
  91. package/dist/mosterdcomponents/p-4e061416.entry.js +1 -0
  92. package/dist/mosterdcomponents/p-910ebfee.entry.js +3 -0
  93. package/dist/mosterdcomponents/{p-DCOAAH92.js → p-Bc42CfJB.js} +1 -1
  94. package/dist/mosterdcomponents/p-CDiBvvJU.js +2 -0
  95. package/dist/mosterdcomponents/p-Ce2QDq0t.js +1 -0
  96. package/dist/types/components/documents/mrd-document-view/mrd-document-view.d.ts +2 -2
  97. package/dist/types/components/email/mrd-email-view/mrd-email-view.d.ts +52 -0
  98. package/dist/types/components/fields/file-upload-common.d.ts +19 -1
  99. package/dist/types/components/fields/mrd-file-field/mrd-file-field.d.ts +24 -10
  100. package/dist/types/components/fields/mrd-image-field/mrd-image-field.d.ts +27 -11
  101. package/dist/types/components/fields/mrd-textarea-field/mrd-textarea-field.d.ts +1 -0
  102. package/dist/types/components/form/mrd-form/mrd-form.d.ts +16 -1
  103. package/dist/types/components/invoice/mrd-invoice-view/mrd-invoice-view.d.ts +60 -0
  104. package/dist/types/components/layout/mrd-layout-section/mrd-layout-section.d.ts +13 -0
  105. package/dist/types/components/merge/mrd-merge-view/merge-diff.d.ts +43 -0
  106. package/dist/types/components/merge/mrd-merge-view/merge-parts.d.ts +37 -0
  107. package/dist/types/components/merge/mrd-merge-view/mrd-merge-view.d.ts +68 -0
  108. package/dist/types/components.d.ts +445 -19
  109. package/dist/types/types/client-layout.d.ts +6 -0
  110. package/dist/types/utils/document-attachments.d.ts +12 -5
  111. package/package.json +2 -1
  112. package/dist/mosterdcomponents/p-DiSe6vjv.js +0 -2
  113. package/dist/mosterdcomponents/p-cdecc698.entry.js +0 -1
@@ -0,0 +1,145 @@
1
+ :host {
2
+ display: block;
3
+ }
4
+
5
+ .mrd-invoice-view {
6
+ max-width: 720px;
7
+ display: flex;
8
+ flex-direction: column;
9
+ gap: var(--mrd-space-6);
10
+ }
11
+ .mrd-invoice-view__title-row {
12
+ display: flex;
13
+ align-items: center;
14
+ gap: var(--mrd-space-3);
15
+ flex-wrap: wrap;
16
+ }
17
+ .mrd-invoice-view__title {
18
+ font-size: var(--mrd-font-size-2xl);
19
+ font-weight: var(--mrd-font-weight-bold);
20
+ color: var(--mrd-color-neutral-900);
21
+ letter-spacing: -0.02em;
22
+ margin: 0;
23
+ }
24
+ .mrd-invoice-view__badge {
25
+ display: inline-block;
26
+ font-size: var(--mrd-font-size-xs);
27
+ font-weight: var(--mrd-font-weight-medium);
28
+ padding: 2px var(--mrd-space-3);
29
+ border-radius: var(--mrd-border-radius-full);
30
+ }
31
+ .mrd-invoice-view__badge--plain {
32
+ color: var(--mrd-color-neutral-600);
33
+ background: var(--mrd-color-neutral-100);
34
+ }
35
+ .mrd-invoice-view__dates {
36
+ display: flex;
37
+ gap: var(--mrd-space-4);
38
+ font-size: var(--mrd-font-size-sm);
39
+ color: var(--mrd-color-neutral-400);
40
+ font-variant-numeric: tabular-nums;
41
+ margin-top: var(--mrd-space-1);
42
+ }
43
+ .mrd-invoice-view__container-link {
44
+ display: inline-block;
45
+ margin-top: var(--mrd-space-3);
46
+ background: none;
47
+ border: none;
48
+ padding: 0;
49
+ font-family: var(--mrd-font-family);
50
+ font-size: var(--mrd-font-size-sm);
51
+ font-weight: var(--mrd-font-weight-semibold);
52
+ color: var(--mrd-color-primary);
53
+ cursor: pointer;
54
+ text-align: left;
55
+ }
56
+ .mrd-invoice-view__container-link:hover {
57
+ text-decoration: underline;
58
+ }
59
+ .mrd-invoice-view__parties {
60
+ display: flex;
61
+ gap: var(--mrd-space-8);
62
+ flex-wrap: wrap;
63
+ }
64
+ .mrd-invoice-view__party {
65
+ display: flex;
66
+ flex-direction: column;
67
+ gap: var(--mrd-space-1);
68
+ }
69
+ .mrd-invoice-view__party-label {
70
+ font-size: var(--mrd-font-size-xs);
71
+ font-weight: var(--mrd-font-weight-medium);
72
+ color: var(--mrd-color-neutral-400);
73
+ text-transform: uppercase;
74
+ letter-spacing: 0.04em;
75
+ }
76
+ .mrd-invoice-view__link {
77
+ background: none;
78
+ border: none;
79
+ padding: 0;
80
+ font-family: var(--mrd-font-family);
81
+ font-size: var(--mrd-font-size-sm);
82
+ font-weight: var(--mrd-font-weight-semibold);
83
+ color: var(--mrd-color-primary);
84
+ cursor: pointer;
85
+ text-align: left;
86
+ }
87
+ .mrd-invoice-view__link:hover {
88
+ text-decoration: underline;
89
+ }
90
+ .mrd-invoice-view__lines {
91
+ border-top: var(--mrd-border-width) solid var(--mrd-color-neutral-200);
92
+ border-bottom: var(--mrd-border-width) solid var(--mrd-color-neutral-200);
93
+ padding: var(--mrd-space-4) 0;
94
+ }
95
+ .mrd-invoice-view__amounts {
96
+ align-self: flex-end;
97
+ display: flex;
98
+ flex-direction: column;
99
+ gap: var(--mrd-space-2);
100
+ min-width: 16rem;
101
+ }
102
+ .mrd-invoice-view__amount-row {
103
+ display: flex;
104
+ justify-content: space-between;
105
+ gap: var(--mrd-space-6);
106
+ font-size: var(--mrd-font-size-sm);
107
+ color: var(--mrd-color-neutral-700);
108
+ }
109
+ .mrd-invoice-view__amount-row--total {
110
+ padding-top: var(--mrd-space-2);
111
+ border-top: var(--mrd-border-width) solid var(--mrd-color-neutral-200);
112
+ font-size: var(--mrd-font-size-base);
113
+ font-weight: var(--mrd-font-weight-bold);
114
+ color: var(--mrd-color-neutral-900);
115
+ }
116
+ .mrd-invoice-view__attachments {
117
+ display: flex;
118
+ flex-direction: column;
119
+ gap: var(--mrd-space-2);
120
+ }
121
+ .mrd-invoice-view__attachment {
122
+ display: inline-flex;
123
+ align-items: center;
124
+ gap: var(--mrd-space-2);
125
+ align-self: flex-start;
126
+ height: 2rem;
127
+ padding: 0 var(--mrd-space-3);
128
+ background: var(--mrd-color-white);
129
+ border: 1px solid var(--mrd-color-neutral-300);
130
+ border-radius: var(--mrd-border-radius);
131
+ font-family: var(--mrd-font-family);
132
+ font-size: var(--mrd-font-size-sm);
133
+ font-weight: var(--mrd-font-weight-medium);
134
+ color: var(--mrd-color-neutral-700);
135
+ cursor: pointer;
136
+ }
137
+ .mrd-invoice-view__attachment:hover {
138
+ background: var(--mrd-color-neutral-100);
139
+ color: var(--mrd-color-neutral-900);
140
+ }
141
+ .mrd-invoice-view__attachment-icon {
142
+ width: 0.9375rem;
143
+ height: 0.9375rem;
144
+ flex-shrink: 0;
145
+ }
@@ -0,0 +1,281 @@
1
+ import { Host, h } from "@stencil/core";
2
+ import { t } from "../../../utils/i18n";
3
+ import { formatCurrency, formatDate } from "../../../utils/format";
4
+ import { attachmentsFromRecord, attachmentsKey } from "../../../utils/document-attachments";
5
+ /**
6
+ * Archetype-driven single-invoice view. Given a `commons.invoice` binding and a data
7
+ * record, renders a clear, recognisable invoice summary — number, status, dates,
8
+ * customer/supplier, totals and attachments — instead of a generic field form.
9
+ *
10
+ * The line items (`lines` slot, the `commons.invoiceLine` sub-archetype) are NOT
11
+ * rendered here: this component only reserves a `slot="lines"` at the right spot in
12
+ * the layout. The host (`mrd-layout-section`) still owns all the actual RELATED_VIEW
13
+ * table data-loading/event wiring — via its existing `renderRelatedView()` — and
14
+ * projects the resulting `<mrd-table>` into that slot, so line editing keeps using
15
+ * the same generic create/edit-navigation mechanism every other relation already has.
16
+ *
17
+ * Like `mrd-document-view`, this component never resolves file hrefs itself — it only
18
+ * exposes the attachment references via `mrdAttachments` (now possibly more than one)
19
+ * and a plain download button; the host decides how/whether to render them.
20
+ */
21
+ export class MrdInvoiceView {
22
+ constructor() {
23
+ /** The data record from the /data endpoint. Fields keyed by name; relations under `_links`. */
24
+ this.data = {};
25
+ this.locale = navigator.language;
26
+ /** listItems of the FIELD bound to the `status` slot, resolved by mrd-layout-section
27
+ * from its own items array (archetype metadata does not publish listItems), so the
28
+ * status badge can get a real label/color instead of the raw stored key. */
29
+ this.statusListItems = null;
30
+ /** Tracks the key attachments were last emitted for, so componentDidLoad and the
31
+ * data watcher don't emit duplicate events for the same set of files. */
32
+ this.lastAttachmentKey = null;
33
+ }
34
+ componentDidLoad() {
35
+ this.emitAttachmentsIfNeeded();
36
+ }
37
+ dataChanged() {
38
+ this.emitAttachmentsIfNeeded();
39
+ }
40
+ emitAttachmentsIfNeeded() {
41
+ const attachments = attachmentsFromRecord(this.archetype, this.data, 'attachments');
42
+ const key = attachmentsKey(attachments);
43
+ if (key === this.lastAttachmentKey)
44
+ return;
45
+ this.lastAttachmentKey = key;
46
+ this.mrdAttachments.emit({ attachments });
47
+ }
48
+ /** Value of a value-slot: reads data[field] via the slot→field map. */
49
+ slotValue(slot) {
50
+ var _a, _b;
51
+ const field = (_b = (_a = this.archetype) === null || _a === void 0 ? void 0 : _a.slots) === null || _b === void 0 ? void 0 : _b[slot];
52
+ if (!field)
53
+ return undefined;
54
+ return this.data[field];
55
+ }
56
+ /** Relation link for a relation-slot: reads data._links[field]. */
57
+ slotLink(slot) {
58
+ var _a, _b, _c, _d;
59
+ const field = (_b = (_a = this.archetype) === null || _a === void 0 ? void 0 : _a.slots) === null || _b === void 0 ? void 0 : _b[slot];
60
+ if (!field)
61
+ return undefined;
62
+ const links = ((_d = (_c = this.data) === null || _c === void 0 ? void 0 : _c._links) !== null && _d !== void 0 ? _d : {});
63
+ return links[field];
64
+ }
65
+ renderPartyLink(slot, label) {
66
+ const link = this.slotLink(slot);
67
+ if (!(link === null || link === void 0 ? void 0 : link.href) || !link.name)
68
+ return null;
69
+ return (h("div", { class: "mrd-invoice-view__party" }, h("span", { class: "mrd-invoice-view__party-label" }, label), h("button", { type: "button", class: "mrd-invoice-view__link", onClick: () => this.mrdNavigate.emit({ href: link.href, label: link.name }) }, link.name)));
70
+ }
71
+ renderContainer() {
72
+ const link = this.slotLink('container');
73
+ if (!(link === null || link === void 0 ? void 0 : link.href) || !link.name)
74
+ return null;
75
+ return (h("button", { type: "button", class: "mrd-invoice-view__container-link", onClick: () => this.mrdNavigate.emit({ href: link.href, label: link.name }) }, link.name));
76
+ }
77
+ renderStatus() {
78
+ var _a, _b;
79
+ const statusRaw = this.slotValue('status');
80
+ if (statusRaw == null || statusRaw === '')
81
+ return null;
82
+ const listItem = ((_a = this.statusListItems) !== null && _a !== void 0 ? _a : []).find(li => li.key === String(statusRaw));
83
+ const label = (_b = listItem === null || listItem === void 0 ? void 0 : listItem.label) !== null && _b !== void 0 ? _b : String(statusRaw);
84
+ if ((listItem === null || listItem === void 0 ? void 0 : listItem.color) && (listItem === null || listItem === void 0 ? void 0 : listItem.backgroundColor)) {
85
+ return (h("span", { class: "mrd-invoice-view__badge", style: { color: listItem.color, backgroundColor: listItem.backgroundColor } }, label));
86
+ }
87
+ return h("span", { class: "mrd-invoice-view__badge mrd-invoice-view__badge--plain" }, label);
88
+ }
89
+ renderAmounts() {
90
+ const net = this.slotValue('netAmount');
91
+ const tax = this.slotValue('taxAmount');
92
+ const total = this.slotValue('amount');
93
+ if ((net === null || net === void 0 ? void 0 : net.amount) == null && (tax === null || tax === void 0 ? void 0 : tax.amount) == null && (total === null || total === void 0 ? void 0 : total.amount) == null)
94
+ return null;
95
+ return (h("div", { class: "mrd-invoice-view__amounts" }, (net === null || net === void 0 ? void 0 : net.amount) != null && (h("div", { class: "mrd-invoice-view__amount-row" }, h("span", null, t('invoice_subtotal', this.locale)), h("span", null, formatCurrency(net.amount, net.currency, this.locale)))), (tax === null || tax === void 0 ? void 0 : tax.amount) != null && (h("div", { class: "mrd-invoice-view__amount-row" }, h("span", null, t('invoice_tax', this.locale)), h("span", null, formatCurrency(tax.amount, tax.currency, this.locale)))), (total === null || total === void 0 ? void 0 : total.amount) != null && (h("div", { class: "mrd-invoice-view__amount-row mrd-invoice-view__amount-row--total" }, h("span", null, t('invoice_total', this.locale)), h("span", null, formatCurrency(total.amount, total.currency, this.locale))))));
96
+ }
97
+ renderAttachments() {
98
+ const attachments = attachmentsFromRecord(this.archetype, this.data, 'attachments');
99
+ if (!attachments.length)
100
+ return null;
101
+ return (h("div", { class: "mrd-invoice-view__attachments" }, attachments.map(a => (h("button", { type: "button", key: a.href, class: "mrd-invoice-view__attachment", onClick: () => this.mrdDownload.emit({ href: a.href, fileName: a.fileName }) }, h("svg", { class: "mrd-invoice-view__attachment-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M21.44 11.05l-9.19 9.19a5 5 0 01-7.07-7.07l9.19-9.19a3 3 0 014.24 4.24l-9.2 9.19a1 1 0 01-1.41-1.41l8.49-8.48" })), a.fileName)))));
102
+ }
103
+ render() {
104
+ var _a, _b;
105
+ const invoiceNumberRaw = this.slotValue('invoiceNumber');
106
+ const invoiceNumber = invoiceNumberRaw != null && invoiceNumberRaw !== '' ? String(invoiceNumberRaw) : null;
107
+ const invoiceDateRaw = this.slotValue('invoiceDate');
108
+ const dueDateRaw = this.slotValue('dueDate');
109
+ const hasLines = !!((_b = (_a = this.archetype) === null || _a === void 0 ? void 0 : _a.slots) === null || _b === void 0 ? void 0 : _b['lines']);
110
+ return (h(Host, { key: 'a97c51e53e39ace913c902ad2ba6813eb85e4561' }, h("div", { key: '6938267848bceed5b28b56c3cdd6d6693e57cdbf', class: "mrd-invoice-view" }, h("div", { key: '7a0b3346232abfc1e35805e4e92c19d35698f881', class: "mrd-invoice-view__header" }, h("div", { key: '5c0471dd8ebd94dd9b374897268c7c03bec66ec7', class: "mrd-invoice-view__title-row" }, invoiceNumber && h("h1", { key: '86b4c0ebd0a5b83ae03b731b7e41394645aae954', class: "mrd-invoice-view__title" }, t('invoice_title', this.locale), " ", invoiceNumber), this.renderStatus()), h("div", { key: 'd209bc34d989146b5f510f2bcdc4c484d723378c', class: "mrd-invoice-view__dates" }, invoiceDateRaw != null && invoiceDateRaw !== '' && (h("span", { key: '8fffcd1ab10e511c9d939c351ea93e4d8a902688' }, t('invoice_date', this.locale), ": ", formatDate(invoiceDateRaw, this.locale))), dueDateRaw != null && dueDateRaw !== '' && (h("span", { key: '8d13c6660798108cc2f54137c8415173326d5bf2' }, t('invoice_due_date', this.locale), ": ", formatDate(dueDateRaw, this.locale)))), this.renderContainer()), h("div", { key: 'cb08c6fd7e6b46a46a69f11aafa960ab5cb35544', class: "mrd-invoice-view__parties" }, this.renderPartyLink('customer', t('invoice_customer', this.locale)), this.renderPartyLink('supplier', t('invoice_supplier', this.locale))), hasLines && (h("div", { key: '8321e74171fe933a4e10fc202b66cd3720ba959e', class: "mrd-invoice-view__lines" }, h("slot", { key: 'eb31d97080173413e74e5b0024984a4b06aa5bdd', name: "lines" }))), this.renderAmounts(), this.renderAttachments())));
111
+ }
112
+ static get is() { return "mrd-invoice-view"; }
113
+ static get encapsulation() { return "scoped"; }
114
+ static get originalStyleUrls() {
115
+ return {
116
+ "$": ["mrd-invoice-view.scss"]
117
+ };
118
+ }
119
+ static get styleUrls() {
120
+ return {
121
+ "$": ["mrd-invoice-view.css"]
122
+ };
123
+ }
124
+ static get properties() {
125
+ return {
126
+ "archetype": {
127
+ "type": "unknown",
128
+ "mutable": false,
129
+ "complexType": {
130
+ "original": "ClientArchetype",
131
+ "resolved": "ClientArchetype",
132
+ "references": {
133
+ "ClientArchetype": {
134
+ "location": "import",
135
+ "path": "../../../types/client-layout",
136
+ "id": "src/types/client-layout.ts::ClientArchetype",
137
+ "referenceLocation": "ClientArchetype"
138
+ }
139
+ }
140
+ },
141
+ "required": true,
142
+ "optional": false,
143
+ "docs": {
144
+ "tags": [],
145
+ "text": "Resolved `commons.invoice` binding: slot name \u2192 concrete field/relation name."
146
+ },
147
+ "getter": false,
148
+ "setter": false
149
+ },
150
+ "data": {
151
+ "type": "unknown",
152
+ "mutable": false,
153
+ "complexType": {
154
+ "original": "Record<string, unknown>",
155
+ "resolved": "string | unknown",
156
+ "references": {
157
+ "Record": {
158
+ "location": "global",
159
+ "id": "global::Record"
160
+ }
161
+ }
162
+ },
163
+ "required": false,
164
+ "optional": false,
165
+ "docs": {
166
+ "tags": [],
167
+ "text": "The data record from the /data endpoint. Fields keyed by name; relations under `_links`."
168
+ },
169
+ "getter": false,
170
+ "setter": false,
171
+ "defaultValue": "{}"
172
+ },
173
+ "locale": {
174
+ "type": "string",
175
+ "mutable": false,
176
+ "complexType": {
177
+ "original": "string",
178
+ "resolved": "string",
179
+ "references": {}
180
+ },
181
+ "required": false,
182
+ "optional": false,
183
+ "docs": {
184
+ "tags": [],
185
+ "text": ""
186
+ },
187
+ "getter": false,
188
+ "setter": false,
189
+ "reflect": false,
190
+ "attribute": "locale",
191
+ "defaultValue": "navigator.language"
192
+ },
193
+ "statusListItems": {
194
+ "type": "unknown",
195
+ "mutable": false,
196
+ "complexType": {
197
+ "original": "ClientListValue[] | null",
198
+ "resolved": "ClientListValue[] | null",
199
+ "references": {
200
+ "ClientListValue": {
201
+ "location": "import",
202
+ "path": "../../../types/client-layout",
203
+ "id": "src/types/client-layout.ts::ClientListValue",
204
+ "referenceLocation": "ClientListValue"
205
+ }
206
+ }
207
+ },
208
+ "required": false,
209
+ "optional": false,
210
+ "docs": {
211
+ "tags": [],
212
+ "text": "listItems of the FIELD bound to the `status` slot, resolved by mrd-layout-section\nfrom its own items array (archetype metadata does not publish listItems), so the\nstatus badge can get a real label/color instead of the raw stored key."
213
+ },
214
+ "getter": false,
215
+ "setter": false,
216
+ "defaultValue": "null"
217
+ }
218
+ };
219
+ }
220
+ static get events() {
221
+ return [{
222
+ "method": "mrdNavigate",
223
+ "name": "mrdNavigate",
224
+ "bubbles": true,
225
+ "cancelable": true,
226
+ "composed": true,
227
+ "docs": {
228
+ "tags": [],
229
+ "text": "Navigate to the customer/supplier/container. Host resolves the href and routes."
230
+ },
231
+ "complexType": {
232
+ "original": "{ href?: string; label: string }",
233
+ "resolved": "{ href?: string | undefined; label: string; }",
234
+ "references": {}
235
+ }
236
+ }, {
237
+ "method": "mrdDownload",
238
+ "name": "mrdDownload",
239
+ "bubbles": true,
240
+ "cancelable": true,
241
+ "composed": true,
242
+ "docs": {
243
+ "tags": [],
244
+ "text": "Download an attachment. Host resolves the href to a signed URL."
245
+ },
246
+ "complexType": {
247
+ "original": "{ href: string; fileName: string }",
248
+ "resolved": "{ href: string; fileName: string; }",
249
+ "references": {}
250
+ }
251
+ }, {
252
+ "method": "mrdAttachments",
253
+ "name": "mrdAttachments",
254
+ "bubbles": true,
255
+ "cancelable": true,
256
+ "composed": true,
257
+ "docs": {
258
+ "tags": [],
259
+ "text": "The file(s) belonging to this invoice, fired once on load and again whenever the\nattachments slot's value changes. The host resolves the href(s) itself."
260
+ },
261
+ "complexType": {
262
+ "original": "{ attachments: DocumentAttachment[] }",
263
+ "resolved": "{ attachments: DocumentAttachment[]; }",
264
+ "references": {
265
+ "DocumentAttachment": {
266
+ "location": "import",
267
+ "path": "../../../types/client-layout",
268
+ "id": "src/types/client-layout.ts::DocumentAttachment",
269
+ "referenceLocation": "DocumentAttachment"
270
+ }
271
+ }
272
+ }
273
+ }];
274
+ }
275
+ static get watchers() {
276
+ return [{
277
+ "propName": "data",
278
+ "methodName": "dataChanged"
279
+ }];
280
+ }
281
+ }
@@ -27,7 +27,7 @@ function renderSingleFieldValue(ctx, item, value) {
27
27
  const v = value;
28
28
  const fileName = (_e = v === null || v === void 0 ? void 0 : v.fileName) !== null && _e !== void 0 ? _e : String(value);
29
29
  const href = (_f = v === null || v === void 0 ? void 0 : v.href) !== null && _f !== void 0 ? _f : '';
30
- return downloadLink(ctx, href, fileName, t('download', ctx.locale));
30
+ return downloadLink(ctx, href, fileName, fileName || t('download', ctx.locale));
31
31
  }
32
32
  case ClientLayoutItemFieldDataType.IMAGE: {
33
33
  const v = value;
@@ -66,6 +66,10 @@ export function renderFieldValue(ctx, item, rawValue) {
66
66
  if (rawValue == null || rawValue === '')
67
67
  return null;
68
68
  if (item.multiple && Array.isArray(rawValue)) {
69
+ const isFileType = item.dataType === ClientLayoutItemFieldDataType.FILE || item.dataType === ClientLayoutItemFieldDataType.IMAGE;
70
+ if (isFileType) {
71
+ return (h("div", { class: "mrd-layout-section__file-list" }, rawValue.map((v, i) => (h("div", { class: "mrd-layout-section__file-list-item", key: String(i) }, renderSingleFieldValue(ctx, item, v))))));
72
+ }
69
73
  const rendered = rawValue.map(v => renderSingleFieldValue(ctx, item, v));
70
74
  if (rendered.every(r => typeof r === 'string' || r == null)) {
71
75
  return rendered.filter(Boolean).join(', ') || null;
@@ -170,6 +170,16 @@
170
170
  width: 1rem;
171
171
  height: 1rem;
172
172
  }
173
+ .mrd-layout-section__file-list {
174
+ display: flex;
175
+ flex-direction: column;
176
+ align-items: flex-start;
177
+ gap: var(--mrd-space-1);
178
+ }
179
+ .mrd-layout-section__file-list-item {
180
+ min-width: 0;
181
+ max-width: 100%;
182
+ }
173
183
  .mrd-layout-section__boolean--true {
174
184
  color: var(--mrd-color-success);
175
185
  font-weight: var(--mrd-font-weight-semibold);
@@ -1,5 +1,5 @@
1
1
  import { Host, h } from "@stencil/core";
2
- import { ClientLayoutItemType, ClientLayoutItemFieldDataType, ClientLayoutNavigateBehaviour, resolveArchetype, ARCHETYPE_DOCUMENT, } from "../../../types/client-layout";
2
+ import { ClientLayoutItemType, ClientLayoutItemFieldDataType, ClientLayoutNavigateBehaviour, resolveArchetype, ARCHETYPE_DOCUMENT, ARCHETYPE_INVOICE, ARCHETYPE_EMAIL, ARCHETYPE_INVOICE_LINE, } from "../../../types/client-layout";
3
3
  import { viewKeyFor, flattenItems, buildContainerHref } from "./layout-helpers";
4
4
  import { renderField } from "./field-value";
5
5
  export class MrdLayoutSection {
@@ -313,23 +313,53 @@ export class MrdLayoutSection {
313
313
  return null;
314
314
  return (h("div", { class: "mrd-layout-section__modal-backdrop", onClick: () => { this.imagePreviewUrl = null; } }, h("div", { class: "mrd-layout-section__modal", onClick: (e) => e.stopPropagation() }, h("button", { class: "mrd-layout-section__modal-close", onClick: () => { this.imagePreviewUrl = null; } }, "\u2715"), h("img", { class: "mrd-layout-section__modal-image", src: this.imagePreviewUrl, alt: "" }))));
315
315
  }
316
+ /** Related VIEW/RELATED_VIEW/SEARCH items to keep rendering below an archetype's
317
+ * single-object view, excluding any item already consumed by that view itself
318
+ * (e.g. invoice's `lines`, see renderInvoiceObject). */
319
+ relatedItems(exclude) {
320
+ return this.items.filter(it => it !== exclude &&
321
+ (it.type === ClientLayoutItemType.VIEW || it.type === ClientLayoutItemType.RELATED_VIEW || it.type === ClientLayoutItemType.SEARCH));
322
+ }
316
323
  /** Single-document view: the record's own type carries commons.document. Renders
317
324
  * a recognisable document instead of generic fields, keeping any related views/
318
325
  * searches below it. */
319
326
  renderDocumentObject(archetype) {
320
- const related = this.items.filter(it => it.type === ClientLayoutItemType.VIEW ||
321
- it.type === ClientLayoutItemType.RELATED_VIEW ||
322
- it.type === ClientLayoutItemType.SEARCH);
323
327
  return [
324
328
  h("mrd-document-view", { archetype: archetype, data: this.data, locale: this.locale, onMrdNavigate: (e) => { e.stopPropagation(); this.mrdNavigate.emit(e.detail); }, onMrdDownload: (e) => { e.stopPropagation(); this.mrdDownload.emit(e.detail); }, onMrdAttachments: (e) => { e.stopPropagation(); this.mrdAttachments.emit(e.detail); } }),
325
- ...related.map(it => this.renderItem(it)),
329
+ ...this.relatedItems().map(it => this.renderItem(it)),
330
+ ];
331
+ }
332
+ /** Single-invoice view: the record's own type carries commons.invoice. The `lines`
333
+ * RELATED_VIEW (matched generically by the commons.invoiceLine archetype on the
334
+ * item, not by name) is rendered *inside* mrd-invoice-view via its `slot="lines"` —
335
+ * reusing the existing renderRelatedView()/renderTable() wiring unchanged, so all
336
+ * page/sort/aggregation state stays owned here, only the DOM placement moves. */
337
+ renderInvoiceObject(archetype) {
338
+ var _a, _b, _c;
339
+ const linesItem = (_a = this.items.find(it => {
340
+ var _a, _b;
341
+ return (it.type === ClientLayoutItemType.VIEW || it.type === ClientLayoutItemType.RELATED_VIEW) &&
342
+ !!resolveArchetype((_a = it.archetypes) !== null && _a !== void 0 ? _a : (_b = it.view) === null || _b === void 0 ? void 0 : _b.archetypes, ARCHETYPE_INVOICE_LINE);
343
+ })) !== null && _a !== void 0 ? _a : null;
344
+ const statusListItems = (_c = (_b = this.items.find(it => { var _a; return it.name === ((_a = archetype.slots) === null || _a === void 0 ? void 0 : _a.status); })) === null || _b === void 0 ? void 0 : _b.listItems) !== null && _c !== void 0 ? _c : null;
345
+ return [
346
+ h("mrd-invoice-view", { archetype: archetype, data: this.data, locale: this.locale, statusListItems: statusListItems, onMrdNavigate: (e) => { e.stopPropagation(); this.mrdNavigate.emit(e.detail); }, onMrdDownload: (e) => { e.stopPropagation(); this.mrdDownload.emit(e.detail); }, onMrdAttachments: (e) => { e.stopPropagation(); this.mrdAttachments.emit(e.detail); } }, linesItem && h("div", { slot: "lines" }, this.renderRelatedView(linesItem))),
347
+ ...this.relatedItems(linesItem).map(it => this.renderItem(it)),
348
+ ];
349
+ }
350
+ /** Single-email view: the record's own type carries commons.email. Fully read-only —
351
+ * emails have no editable child collection, so this mirrors renderDocumentObject. */
352
+ renderEmailObject(archetype) {
353
+ return [
354
+ h("mrd-email-view", { archetype: archetype, data: this.data, locale: this.locale, onMrdNavigate: (e) => { e.stopPropagation(); this.mrdNavigate.emit(e.detail); }, onMrdDownload: (e) => { e.stopPropagation(); this.mrdDownload.emit(e.detail); }, onMrdAttachments: (e) => { e.stopPropagation(); this.mrdAttachments.emit(e.detail); } }),
355
+ ...this.relatedItems().map(it => this.renderItem(it)),
326
356
  ];
327
357
  }
328
358
  render() {
329
359
  const docArchetype = resolveArchetype(this.archetypes, ARCHETYPE_DOCUMENT);
330
- return (h(Host, { key: '8585dae577d38e3c9517039b48aa9e798190937c' }, h("div", { key: '020b942996b1346bdc3bc89938e4b020823cfcea', class: "mrd-layout-section" }, docArchetype
331
- ? this.renderDocumentObject(docArchetype)
332
- : this.items.map(item => this.renderItem(item))), this.renderImageModal()));
360
+ const invoiceArchetype = !docArchetype ? resolveArchetype(this.archetypes, ARCHETYPE_INVOICE) : undefined;
361
+ const emailArchetype = !docArchetype && !invoiceArchetype ? resolveArchetype(this.archetypes, ARCHETYPE_EMAIL) : undefined;
362
+ return (h(Host, { key: '0078764a136e7ca9561632718c1b527ec3bc01c5' }, h("div", { key: '0c1428ce20bd7d2bbce467a36b6c25ffa0e6367e', class: "mrd-layout-section" }, docArchetype && this.renderDocumentObject(docArchetype), invoiceArchetype && this.renderInvoiceObject(invoiceArchetype), emailArchetype && this.renderEmailObject(emailArchetype), !docArchetype && !invoiceArchetype && !emailArchetype && this.items.map(item => this.renderItem(item))), this.renderImageModal()));
333
363
  }
334
364
  static get is() { return "mrd-layout-section"; }
335
365
  static get encapsulation() { return "scoped"; }
@@ -0,0 +1,99 @@
1
+ import { ClientLayoutItemType } from "../../../types/client-layout";
2
+ /**
3
+ * Whether a layout item can appear in the merge diff at all. m-n relations
4
+ * (`multiple: true`) are unioned automatically server-side (DataService.merge)
5
+ * and must never be surfaced as a user choice — only FIELD and single-value
6
+ * (n-1) RELATION items are diffable.
7
+ */
8
+ export function isDiffable(item) {
9
+ if (item.type === ClientLayoutItemType.FIELD)
10
+ return true;
11
+ if (item.type === ClientLayoutItemType.RELATION)
12
+ return !item.multiple;
13
+ return false;
14
+ }
15
+ /**
16
+ * Raw value for a field/relation off a record in the API's row shape (flat
17
+ * fields + `_links`). History-enabled fields come back as `{ current,
18
+ * history: [...] }` — this returns that whole object as-is, since a winning
19
+ * B value must carry its entire history into the patch (FINDING-0114: "the
20
+ * entire history list follows whichever value wins ... never interleaved").
21
+ */
22
+ export function getRawValue(item, record) {
23
+ var _a, _b, _c, _d, _e;
24
+ const name = (_a = item.name) !== null && _a !== void 0 ? _a : '';
25
+ if (item.type === ClientLayoutItemType.RELATION) {
26
+ return (_d = (_c = (_b = record === null || record === void 0 ? void 0 : record._links) === null || _b === void 0 ? void 0 : _b[name]) === null || _c === void 0 ? void 0 : _c.href) !== null && _d !== void 0 ? _d : null;
27
+ }
28
+ return (_e = record === null || record === void 0 ? void 0 : record[name]) !== null && _e !== void 0 ? _e : null;
29
+ }
30
+ /** Unwraps a history-enabled field's `{ current, history }` shape to its current value, for comparison only. */
31
+ function comparableValue(item, value) {
32
+ if (item.historyEnabled && value != null && typeof value === 'object' && !Array.isArray(value) && 'current' in value) {
33
+ return value.current;
34
+ }
35
+ return value;
36
+ }
37
+ function normalize(value) {
38
+ return value === undefined || value === '' ? null : value;
39
+ }
40
+ function valuesEqual(a, b) {
41
+ const na = normalize(a);
42
+ const nb = normalize(b);
43
+ if (na === nb)
44
+ return true;
45
+ if (na === null || nb === null)
46
+ return false;
47
+ return JSON.stringify(na) === JSON.stringify(nb);
48
+ }
49
+ function isEmpty(value) {
50
+ return normalize(value) === null;
51
+ }
52
+ /**
53
+ * Splits diffable fields into `conflicts` (both records have a — different —
54
+ * value, user must pick) and `autoFilled` (A has no value, B does; silently
55
+ * taken from B without a UI choice).
56
+ */
57
+ export function diffFields(fields, recordA, recordB) {
58
+ const conflicts = [];
59
+ const autoFilled = [];
60
+ for (const item of fields.filter(isDiffable)) {
61
+ const aValue = getRawValue(item, recordA);
62
+ const bValue = getRawValue(item, recordB);
63
+ const aComparable = comparableValue(item, aValue);
64
+ const bComparable = comparableValue(item, bValue);
65
+ if (valuesEqual(aComparable, bComparable))
66
+ continue;
67
+ if (isEmpty(aComparable) && !isEmpty(bComparable)) {
68
+ autoFilled.push({ item, aValue, bValue });
69
+ }
70
+ else {
71
+ conflicts.push({ item, aValue, bValue });
72
+ }
73
+ }
74
+ return { conflicts, autoFilled };
75
+ }
76
+ /**
77
+ * Builds the patch payload per the merge endpoint contract: every `autoFilled`
78
+ * entry is always included (A had nothing to lose); a `conflicts` entry is
79
+ * only included when the user explicitly resolved it to B — anything left
80
+ * unresolved keeps A's value by omission.
81
+ */
82
+ export function buildPatch(diff, resolutions) {
83
+ var _a, _b, _c;
84
+ const patch = {};
85
+ for (const { item, bValue } of diff.autoFilled) {
86
+ patch[(_a = item.name) !== null && _a !== void 0 ? _a : ''] = bValue;
87
+ }
88
+ for (const { item, bValue } of diff.conflicts) {
89
+ if (resolutions[(_b = item.name) !== null && _b !== void 0 ? _b : ''] === 'B') {
90
+ patch[(_c = item.name) !== null && _c !== void 0 ? _c : ''] = bValue;
91
+ }
92
+ }
93
+ return patch;
94
+ }
95
+ /** Display label for a candidate record in the search results list. */
96
+ export function candidateLabel(record) {
97
+ var _a, _b, _c, _d, _e;
98
+ return (_e = (_b = (_a = record === null || record === void 0 ? void 0 : record.name) !== null && _a !== void 0 ? _a : record === null || record === void 0 ? void 0 : record.label) !== null && _b !== void 0 ? _b : (_d = (_c = record === null || record === void 0 ? void 0 : record._links) === null || _c === void 0 ? void 0 : _c.self) === null || _d === void 0 ? void 0 : _d.href) !== null && _e !== void 0 ? _e : '';
99
+ }