@mmlogic/components 0.3.5 → 0.3.6

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 (169) hide show
  1. package/dist/{collection/utils/cell-renderer.js → mosterdcomponents/cell-renderer-CbRwLOo8.js} +9 -3
  2. package/dist/mosterdcomponents/cell-renderer-CbRwLOo8.js.map +1 -0
  3. package/dist/{esm/index.js → mosterdcomponents/client-layout-D88nn5zf.js} +4 -1
  4. package/dist/mosterdcomponents/client-layout-D88nn5zf.js.map +1 -0
  5. package/dist/{collection/utils/format.js → mosterdcomponents/format-BAfsQfy1.js} +12 -7
  6. package/dist/mosterdcomponents/format-BAfsQfy1.js.map +1 -0
  7. package/dist/{collection/utils/i18n.js → mosterdcomponents/i18n-hoGGKbKU.js} +6 -1
  8. package/dist/mosterdcomponents/i18n-hoGGKbKU.js.map +1 -0
  9. package/dist/mosterdcomponents/index-B_tPFIvS.js +4585 -0
  10. package/dist/mosterdcomponents/index-B_tPFIvS.js.map +1 -0
  11. package/dist/mosterdcomponents/index-I5SuYv7a.js +4 -0
  12. package/dist/mosterdcomponents/index-I5SuYv7a.js.map +1 -0
  13. package/dist/mosterdcomponents/index.esm.js +5 -1
  14. package/dist/mosterdcomponents/index.esm.js.map +1 -0
  15. package/dist/mosterdcomponents/mosterdcomponents.css +180 -1
  16. package/dist/mosterdcomponents/mosterdcomponents.esm.js +50 -1
  17. package/dist/mosterdcomponents/mosterdcomponents.esm.js.map +1 -0
  18. package/dist/mosterdcomponents/mrd-boolean-field.entry.js +37 -0
  19. package/dist/mosterdcomponents/mrd-boolean-field.entry.js.map +1 -0
  20. package/dist/mosterdcomponents/mrd-currency-field.entry.js +67 -0
  21. package/dist/mosterdcomponents/mrd-currency-field.entry.js.map +1 -0
  22. package/dist/mosterdcomponents/mrd-date-field.entry.js +46 -0
  23. package/dist/mosterdcomponents/mrd-date-field.entry.js.map +1 -0
  24. package/dist/mosterdcomponents/mrd-datetime-field.entry.js +78 -0
  25. package/dist/mosterdcomponents/mrd-datetime-field.entry.js.map +1 -0
  26. package/dist/mosterdcomponents/mrd-email-field.entry.js +50 -0
  27. package/dist/mosterdcomponents/mrd-email-field.entry.js.map +1 -0
  28. package/dist/{collection/components/mrd-field/mrd-field.js → mosterdcomponents/mrd-field.entry.js} +28 -179
  29. package/dist/mosterdcomponents/mrd-field.entry.js.map +1 -0
  30. package/dist/mosterdcomponents/mrd-file-field.entry.js +108 -0
  31. package/dist/mosterdcomponents/mrd-file-field.entry.js.map +1 -0
  32. package/dist/{collection/components/mrd-form/mrd-form.js → mosterdcomponents/mrd-form.entry.js} +31 -308
  33. package/dist/mosterdcomponents/mrd-form.entry.js.map +1 -0
  34. package/dist/mosterdcomponents/mrd-hyperlink-field.entry.js +87 -0
  35. package/dist/mosterdcomponents/mrd-hyperlink-field.entry.js.map +1 -0
  36. package/dist/mosterdcomponents/mrd-image-field.entry.js +122 -0
  37. package/dist/mosterdcomponents/mrd-image-field.entry.js.map +1 -0
  38. package/dist/{collection/components/mrd-layout-section/mrd-layout-section.js → mosterdcomponents/mrd-layout-section.entry.js} +31 -418
  39. package/dist/mosterdcomponents/mrd-layout-section.entry.js.map +1 -0
  40. package/dist/mosterdcomponents/mrd-list-field.entry.js +107 -0
  41. package/dist/mosterdcomponents/mrd-list-field.entry.js.map +1 -0
  42. package/dist/mosterdcomponents/mrd-longtext-field.entry.js +47 -0
  43. package/dist/mosterdcomponents/mrd-longtext-field.entry.js.map +1 -0
  44. package/dist/mosterdcomponents/mrd-number-field.entry.js +87 -0
  45. package/dist/mosterdcomponents/mrd-number-field.entry.js.map +1 -0
  46. package/dist/mosterdcomponents/mrd-relation-field.entry.js +267 -0
  47. package/dist/mosterdcomponents/mrd-relation-field.entry.js.map +1 -0
  48. package/dist/mosterdcomponents/mrd-secret-field.entry.js +49 -0
  49. package/dist/mosterdcomponents/mrd-secret-field.entry.js.map +1 -0
  50. package/dist/{collection/components/mrd-table/mrd-table.js → mosterdcomponents/mrd-table.entry.js} +32 -394
  51. package/dist/mosterdcomponents/mrd-table.entry.js.map +1 -0
  52. package/dist/mosterdcomponents/mrd-text-field.entry.js +47 -0
  53. package/dist/mosterdcomponents/mrd-text-field.entry.js.map +1 -0
  54. package/dist/mosterdcomponents/mrd-textarea-field.entry.js +86 -0
  55. package/dist/mosterdcomponents/mrd-textarea-field.entry.js.map +1 -0
  56. package/dist/mosterdcomponents/mrd-time-field.entry.js +46 -0
  57. package/dist/mosterdcomponents/mrd-time-field.entry.js.map +1 -0
  58. package/dist/{esm/quill-CiuCgGz_.js → mosterdcomponents/quill-C9pgw_k-.js} +16282 -1397
  59. package/dist/mosterdcomponents/quill-C9pgw_k-.js.map +1 -0
  60. package/dist/{collection/utils/validation.js → mosterdcomponents/validation-ixb43cqU.js} +12 -5
  61. package/dist/mosterdcomponents/validation-ixb43cqU.js.map +1 -0
  62. package/package.json +1 -1
  63. package/dist/cjs/app-globals-V2Kpy_OQ.js +0 -5
  64. package/dist/cjs/index-BPj2cBXs.js +0 -1570
  65. package/dist/cjs/index.cjs.js +0 -66
  66. package/dist/cjs/loader.cjs.js +0 -13
  67. package/dist/cjs/mosterdcomponents.cjs.js +0 -25
  68. package/dist/cjs/mrd-boolean-field_20.cjs.entry.js +0 -3961
  69. package/dist/cjs/quill-DmFfnC1f.js +0 -16272
  70. package/dist/collection/collection-manifest.json +0 -32
  71. package/dist/collection/components/mrd-boolean-field/mrd-boolean-field.js +0 -199
  72. package/dist/collection/components/mrd-boolean-field/mrd-boolean-field.scss +0 -77
  73. package/dist/collection/components/mrd-currency-field/mrd-currency-field.js +0 -248
  74. package/dist/collection/components/mrd-currency-field/mrd-currency-field.scss +0 -100
  75. package/dist/collection/components/mrd-date-field/mrd-date-field.js +0 -206
  76. package/dist/collection/components/mrd-date-field/mrd-date-field.scss +0 -66
  77. package/dist/collection/components/mrd-datetime-field/mrd-datetime-field.js +0 -240
  78. package/dist/collection/components/mrd-datetime-field/mrd-datetime-field.scss +0 -66
  79. package/dist/collection/components/mrd-email-field/mrd-email-field.js +0 -230
  80. package/dist/collection/components/mrd-email-field/mrd-email-field.scss +0 -69
  81. package/dist/collection/components/mrd-field/mrd-field.scss +0 -118
  82. package/dist/collection/components/mrd-file-field/mrd-file-field.js +0 -341
  83. package/dist/collection/components/mrd-file-field/mrd-file-field.scss +0 -153
  84. package/dist/collection/components/mrd-form/mrd-form.scss +0 -148
  85. package/dist/collection/components/mrd-hyperlink-field/mrd-hyperlink-field.js +0 -291
  86. package/dist/collection/components/mrd-hyperlink-field/mrd-hyperlink-field.scss +0 -91
  87. package/dist/collection/components/mrd-image-field/mrd-image-field.js +0 -356
  88. package/dist/collection/components/mrd-image-field/mrd-image-field.scss +0 -190
  89. package/dist/collection/components/mrd-layout-section/mrd-layout-section.scss +0 -445
  90. package/dist/collection/components/mrd-list-field/mrd-list-field.js +0 -313
  91. package/dist/collection/components/mrd-list-field/mrd-list-field.scss +0 -109
  92. package/dist/collection/components/mrd-longtext-field/mrd-longtext-field.js +0 -227
  93. package/dist/collection/components/mrd-longtext-field/mrd-longtext-field.scss +0 -78
  94. package/dist/collection/components/mrd-number-field/mrd-number-field.js +0 -316
  95. package/dist/collection/components/mrd-number-field/mrd-number-field.scss +0 -77
  96. package/dist/collection/components/mrd-relation-field/mrd-relation-field.js +0 -707
  97. package/dist/collection/components/mrd-relation-field/mrd-relation-field.scss +0 -266
  98. package/dist/collection/components/mrd-secret-field/mrd-secret-field.js +0 -229
  99. package/dist/collection/components/mrd-secret-field/mrd-secret-field.scss +0 -73
  100. package/dist/collection/components/mrd-table/mrd-table.scss +0 -809
  101. package/dist/collection/components/mrd-text-field/mrd-text-field.js +0 -227
  102. package/dist/collection/components/mrd-text-field/mrd-text-field.scss +0 -69
  103. package/dist/collection/components/mrd-textarea-field/mrd-textarea-field.js +0 -267
  104. package/dist/collection/components/mrd-textarea-field/mrd-textarea-field.scss +0 -135
  105. package/dist/collection/components/mrd-time-field/mrd-time-field.js +0 -206
  106. package/dist/collection/components/mrd-time-field/mrd-time-field.scss +0 -66
  107. package/dist/collection/dev/api.js +0 -145
  108. package/dist/collection/dev/app.js +0 -890
  109. package/dist/collection/dev/auth.js +0 -156
  110. package/dist/collection/dev/example-data.js +0 -403
  111. package/dist/collection/dev/sprites.svg +0 -55
  112. package/dist/collection/index.js +0 -1
  113. package/dist/collection/types/client-layout.js +0 -64
  114. package/dist/collection/types/index.js +0 -1
  115. package/dist/components/client-layout.js +0 -1
  116. package/dist/components/format.js +0 -1
  117. package/dist/components/i18n.js +0 -1
  118. package/dist/components/index.js +0 -1
  119. package/dist/components/mrd-boolean-field.js +0 -1
  120. package/dist/components/mrd-boolean-field2.js +0 -1
  121. package/dist/components/mrd-currency-field.js +0 -1
  122. package/dist/components/mrd-currency-field2.js +0 -1
  123. package/dist/components/mrd-date-field.js +0 -1
  124. package/dist/components/mrd-date-field2.js +0 -1
  125. package/dist/components/mrd-datetime-field.js +0 -1
  126. package/dist/components/mrd-datetime-field2.js +0 -1
  127. package/dist/components/mrd-email-field.js +0 -1
  128. package/dist/components/mrd-email-field2.js +0 -1
  129. package/dist/components/mrd-field.js +0 -1
  130. package/dist/components/mrd-field2.js +0 -1
  131. package/dist/components/mrd-file-field.js +0 -1
  132. package/dist/components/mrd-file-field2.js +0 -1
  133. package/dist/components/mrd-form.js +0 -1
  134. package/dist/components/mrd-hyperlink-field.js +0 -1
  135. package/dist/components/mrd-hyperlink-field2.js +0 -1
  136. package/dist/components/mrd-image-field.js +0 -1
  137. package/dist/components/mrd-image-field2.js +0 -1
  138. package/dist/components/mrd-layout-section.js +0 -1
  139. package/dist/components/mrd-list-field.js +0 -1
  140. package/dist/components/mrd-list-field2.js +0 -1
  141. package/dist/components/mrd-longtext-field.js +0 -1
  142. package/dist/components/mrd-longtext-field2.js +0 -1
  143. package/dist/components/mrd-number-field.js +0 -1
  144. package/dist/components/mrd-number-field2.js +0 -1
  145. package/dist/components/mrd-relation-field.js +0 -1
  146. package/dist/components/mrd-relation-field2.js +0 -1
  147. package/dist/components/mrd-secret-field.js +0 -1
  148. package/dist/components/mrd-secret-field2.js +0 -1
  149. package/dist/components/mrd-table.js +0 -1
  150. package/dist/components/mrd-table2.js +0 -1
  151. package/dist/components/mrd-text-field.js +0 -1
  152. package/dist/components/mrd-text-field2.js +0 -1
  153. package/dist/components/mrd-textarea-field.js +0 -1
  154. package/dist/components/mrd-textarea-field2.js +0 -1
  155. package/dist/components/mrd-time-field.js +0 -1
  156. package/dist/components/mrd-time-field2.js +0 -1
  157. package/dist/components/quill.js +0 -1
  158. package/dist/components/validation.js +0 -1
  159. package/dist/esm/app-globals-DQuL1Twl.js +0 -3
  160. package/dist/esm/index-_tsCCkAi.js +0 -1561
  161. package/dist/esm/loader.js +0 -11
  162. package/dist/esm/mosterdcomponents.js +0 -21
  163. package/dist/esm/mrd-boolean-field_20.entry.js +0 -3940
  164. package/dist/index.cjs.js +0 -1
  165. package/dist/index.js +0 -1
  166. package/dist/mosterdcomponents/p-CiuCgGz_.js +0 -1
  167. package/dist/mosterdcomponents/p-DQuL1Twl.js +0 -1
  168. package/dist/mosterdcomponents/p-_tsCCkAi.js +0 -2
  169. package/dist/mosterdcomponents/p-e477187c.entry.js +0 -1
@@ -1,890 +0,0 @@
1
- /* =====================================================================
2
- APP STATE
3
- ===================================================================== */
4
-
5
- let _selectedTenant = null;
6
- let _selectedType = null; // { name, pluralName }
7
- let _relationMeta = {}; // relatedClass / mostSignificantClass → { name, mostSignificantClass }
8
- let _baseHref = null; // tenant base href: /data/{tenant}
9
- let _locale = navigator.language || 'nl-NL';
10
- let _dashboardData = null; // full dashboard response
11
- let _dashboardRecord = null; // data-object voor object/form dashboards
12
- let _activeLayoutIndex = 0;
13
- let _dashboardType = 'class'; // 'class' | 'general' | 'navigation' | 'object' | 'form'
14
- let _sectionGeneration = 0; // incremented on each renderSection(); prevents stale fetches
15
- let _navHistory = []; // stack van { dashboardData, dashboardRecord, activeLayoutIndex }
16
- let _meUser = null; // { id: href, label: name } — resolved once after login via /accounts/me
17
-
18
- /* =====================================================================
19
- UTILITIES
20
- ===================================================================== */
21
-
22
- function escHtml(str) {
23
- if (str == null) return '';
24
- return String(str)
25
- .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
26
- .replace(/"/g, '&quot;').replace(/'/g, '&#39;');
27
- }
28
-
29
- function formatJson(value) {
30
- try {
31
- const parsed = typeof value === 'string' ? JSON.parse(value) : value;
32
- const json = JSON.stringify(parsed, null, 2);
33
- return json.replace(
34
- /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
35
- match => {
36
- let style = 'color:#2aa198';
37
- if (/^"/.test(match)) {
38
- style = /:$/.test(match) ? 'color:#81a1c1' : 'color:#a3be8c';
39
- } else if (/true|false/.test(match)) {
40
- style = 'color:#ebcb8b';
41
- } else if (/null/.test(match)) {
42
- style = 'color:#bf616a';
43
- }
44
- return `<span style="${style}">${match}</span>`;
45
- },
46
- );
47
- } catch {
48
- return escHtml(String(value));
49
- }
50
- }
51
-
52
- function httpStatusText(code) {
53
- const map = {
54
- 200: 'OK', 201: 'Created', 204: 'No Content',
55
- 400: 'Bad Request', 401: 'Unauthorized', 403: 'Forbidden',
56
- 404: 'Not Found', 409: 'Conflict', 422: 'Unprocessable Entity',
57
- 500: 'Internal Server Error',
58
- };
59
- return map[code] || '';
60
- }
61
-
62
- const EVENT_LOG_MAX = 200;
63
-
64
- function logEvent(type, data) {
65
- const log = document.getElementById('event-log');
66
-
67
- const empty = log.querySelector('.event-log-empty');
68
- if (empty) empty.remove();
69
-
70
- // Trim oldest entries when the log grows too large
71
- while (log.children.length >= EVENT_LOG_MAX) log.removeChild(log.firstChild);
72
-
73
- const now = new Date();
74
- const time = now.toTimeString().slice(0, 8) + '.' + String(now.getMilliseconds()).padStart(3, '0');
75
- const json = data !== undefined ? JSON.stringify(data, null, 2) : null;
76
-
77
- const entry = document.createElement('div');
78
- entry.className = 'event-log-entry';
79
- entry.dataset.type = type;
80
- entry.innerHTML = `
81
- <div class="event-log-entry-head">
82
- <span class="event-log-time">${escHtml(time)}</span>
83
- <span class="event-log-type">${escHtml(type)}</span>
84
- ${json ? '<span class="event-log-arrow">▶</span>' : ''}
85
- </div>
86
- ${json ? `<pre class="event-log-detail">${escHtml(json)}</pre>` : ''}`;
87
-
88
- entry.querySelector('.event-log-entry-head')?.addEventListener('click', () => {
89
- if (json) entry.classList.toggle('open');
90
- });
91
-
92
- log.appendChild(entry);
93
- log.scrollTop = log.scrollHeight;
94
- }
95
-
96
- function clearEventLog() {
97
- const log = document.getElementById('event-log');
98
- log.innerHTML = '<span class="event-log-empty">Wacht op events…</span>';
99
- }
100
-
101
- /* =====================================================================
102
- TAB SWITCHING
103
- ===================================================================== */
104
-
105
- function showTab(name) {
106
- const names = ['embedded', 'field-types', 'detail-view', 'live-api'];
107
- document.querySelectorAll('.tab-btn').forEach((btn, i) => {
108
- btn.classList.toggle('active', names[i] === name);
109
- });
110
- names.forEach(n => {
111
- document.getElementById('tab-' + n).classList.toggle('active', n === name);
112
- });
113
- if (name === 'detail-view') initDetailViewTab();
114
- }
115
-
116
- /* =====================================================================
117
- DETAIL VIEW TAB
118
- ===================================================================== */
119
-
120
- let _detailViewInitialized = false;
121
-
122
- function initDetailViewTab() {
123
- if (_detailViewInitialized) return;
124
- _detailViewInitialized = true;
125
-
126
- const table = document.getElementById('companies-table');
127
- const meta = window.EXAMPLE_COMPANY_METADATA;
128
-
129
- table.columns = window.EXAMPLE_COMPANY_COLUMNS;
130
- table.totalElements = window.EXAMPLE_COMPANIES.length;
131
- table.pageSize = 20;
132
- table.locale = 'nl-NL';
133
-
134
- table.addEventListener('mrdLoadPage', async (e) => {
135
- await table.setPage(e.detail.page, window.EXAMPLE_COMPANIES);
136
- });
137
-
138
- table.addEventListener('mrdRowClick', (e) => {
139
- showCompanyDetail(e.detail);
140
- });
141
-
142
- table.init().then(() => table.setPage(0, window.EXAMPLE_COMPANIES));
143
- }
144
-
145
- function showCompanyDetail(row) {
146
- document.getElementById('detail-table-panel').style.display = 'none';
147
- document.getElementById('detail-record-panel').style.display = 'block';
148
-
149
- // Use the static example company (in a real app: fetch /data/:tenant/companies/:id)
150
- const company = window.EXAMPLE_COMPANY;
151
- const meta = window.EXAMPLE_COMPANY_METADATA;
152
- const container = document.getElementById('detail-sections');
153
- container.innerHTML = '';
154
-
155
- meta.layouts.forEach((layout, idx) => {
156
- const wrapper = document.createElement('div');
157
- wrapper.style.marginBottom = '2rem';
158
-
159
- if (layout.label) {
160
- const heading = document.createElement('h3');
161
- heading.style.cssText = 'font-size:1.1rem;font-weight:600;margin:0 0 .75rem;padding-bottom:.5rem;border-bottom:2px solid #e5e7eb';
162
- heading.textContent = layout.label;
163
- wrapper.appendChild(heading);
164
- }
165
-
166
- const section = document.createElement('mrd-layout-section');
167
- section.items = layout.items;
168
- section.data = company;
169
- section.views = meta.views;
170
- section.locale = 'nl-NL';
171
-
172
- section.addEventListener('mrdNavigate', (e) => {
173
- logEvent('mrdNavigate', e.detail);
174
- });
175
-
176
- section.addEventListener('mrdLoadView', async (e) => {
177
- const { name, viewConfig } = e.detail;
178
- const mockRows = name === 'view0' ? window.EXAMPLE_APPOINTMENTS : window.EXAMPLE_SHARE_CLASSES;
179
- await section.setViewPage(name, 0, mockRows, mockRows.length);
180
- });
181
-
182
- section.addEventListener('mrdLoadViewPage', async (e) => {
183
- // No more pages in the mock — just set the same rows again
184
- const { name } = e.detail;
185
- const mockRows = name === 'view0' ? window.EXAMPLE_APPOINTMENTS : window.EXAMPLE_SHARE_CLASSES;
186
- await section.setViewPage(name, e.detail.page, mockRows);
187
- });
188
-
189
- wrapper.appendChild(section);
190
- container.appendChild(wrapper);
191
- });
192
- }
193
-
194
- function showCompanyList() {
195
- document.getElementById('detail-record-panel').style.display = 'none';
196
- document.getElementById('detail-table-panel').style.display = 'block';
197
- }
198
-
199
- /* =====================================================================
200
- TENANT SELECTION
201
- ===================================================================== */
202
-
203
- async function loadTenants() {
204
- document.getElementById('panel-controls').classList.remove('hidden');
205
- const sel = document.getElementById('tenant-select-compact');
206
- sel.innerHTML = '<option value="">⏳ laden...</option>';
207
-
208
- try {
209
- const tenants = await apiFetchTenants(authGetToken());
210
- if (!tenants || tenants.length === 0) {
211
- sel.innerHTML = '<option value="">— geen tenants gevonden —</option>';
212
- return;
213
- }
214
- sel.innerHTML = '<option value="">— selecteer tenant —</option>' +
215
- tenants.map(t => {
216
- const code = t.tenantCode || t.code || t.id;
217
- return `<option value="${escHtml(code)}">${escHtml(t.name || code)}</option>`;
218
- }).join('');
219
-
220
- const saved = localStorage.getItem('last_tenant');
221
- if (saved) {
222
- sel.value = saved;
223
- onCompactTenantChange(saved);
224
- }
225
- } catch (err) {
226
- sel.innerHTML = `<option value="">❌ ${escHtml(err.message)}</option>`;
227
- }
228
- }
229
-
230
- function onCompactTenantChange(code) {
231
- _selectedTenant = code;
232
- _baseHref = `/data/${code}`;
233
- localStorage.setItem('last_tenant', code);
234
- if (_dashboardType === 'class' && code) loadCompactTypes(code);
235
- if (code) apiFetchMe(authGetToken(), code).then(me => { _meUser = me; }).catch(() => {});
236
- }
237
-
238
- async function loadCompactTypes(tenantCode) {
239
- const sel = document.getElementById('type-select-compact');
240
- sel.innerHTML = '<option value="">⏳ laden...</option>';
241
- sel.disabled = true;
242
- try {
243
- const types = await apiFetchTypes(authGetToken(), tenantCode);
244
- if (!types || types.length === 0) {
245
- sel.innerHTML = '<option value="">— geen types —</option>';
246
- return;
247
- }
248
- sel.innerHTML = '<option value="">— selecteer type —</option>' +
249
- types.map(t => {
250
- const plural = t.pluralName || t.name;
251
- return `<option value="${escHtml(plural)}">${escHtml(t.name)}</option>`;
252
- }).join('');
253
- sel.disabled = false;
254
-
255
- const saved = localStorage.getItem('last_type');
256
- if (saved && sel.querySelector(`option[value="${saved}"]`)) {
257
- sel.value = saved;
258
- }
259
- } catch (err) {
260
- sel.innerHTML = `<option value="">❌ ${escHtml(err.message)}</option>`;
261
- sel.disabled = false;
262
- }
263
- }
264
-
265
- function onDashboardTypeChange(type) {
266
- _dashboardType = type;
267
- const needsClass = type === 'class' || type === 'create';
268
- const needsObject = type === 'object' || type === 'form';
269
- document.getElementById('controls-class-group').classList.toggle('hidden', !needsClass);
270
- document.getElementById('controls-object-group').classList.toggle('hidden', !needsObject);
271
- if (needsClass && _selectedTenant) loadCompactTypes(_selectedTenant);
272
- }
273
-
274
- /* =====================================================================
275
- DASHBOARD — laad + render layout sections
276
- ===================================================================== */
277
-
278
- async function loadDashboard() {
279
- const name = document.getElementById('dashboard-name-input').value.trim() || undefined;
280
- const token = authGetToken();
281
-
282
- document.getElementById('sections-container').innerHTML =
283
- '<span class="spinner"></span> Dashboard laden...';
284
- document.getElementById('panel-sections').classList.remove('hidden');
285
- document.getElementById('panel-response').innerHTML = '';
286
-
287
- try {
288
- let dashboard;
289
- let recordData = null;
290
-
291
- if (_dashboardType === 'create') {
292
- const pluralName = document.getElementById('type-select-compact').value;
293
- if (!pluralName) throw new Error('Selecteer een type');
294
- const opt = document.getElementById('type-select-compact').selectedOptions[0];
295
- _selectedType = { name: opt.text, pluralName };
296
- localStorage.setItem('last_type', pluralName);
297
- await openCreateForm(pluralName, false);
298
- return;
299
-
300
- } else if (_dashboardType === 'class') {
301
- const pluralName = document.getElementById('type-select-compact').value;
302
- if (!pluralName) throw new Error('Selecteer een type');
303
- const opt = document.getElementById('type-select-compact').selectedOptions[0];
304
- _selectedType = { name: opt.text, pluralName };
305
- localStorage.setItem('last_type', pluralName);
306
- dashboard = await apiFetchClassDashboard(token, _selectedTenant, pluralName, name);
307
-
308
- } else if (_dashboardType === 'general') {
309
- dashboard = await apiFetchGeneralDashboard(token, _selectedTenant, name);
310
-
311
- } else if (_dashboardType === 'navigation') {
312
- dashboard = await apiFetchNavigationPane(token, _selectedTenant, name);
313
-
314
- } else {
315
- // 'object' of 'form': fetch record → gebruik _links.metadata.href of _links.form.href
316
- const objectHref = document.getElementById('object-href-input').value.trim();
317
- if (!objectHref) throw new Error('Vul een Object URL in');
318
- const recResp = await apiRequest('GET', objectHref, token);
319
- if (!recResp.ok) throw new Error(`${recResp.status}: kon object niet ophalen`);
320
- recordData = recResp.body;
321
-
322
- const linkKey = _dashboardType === 'form' ? 'form' : 'metadata';
323
- const dashHref = recordData._links?.[linkKey]?.href;
324
- if (!dashHref) throw new Error(`Geen _links.${linkKey} gevonden in object`);
325
-
326
- const dashResp = await apiRequest('GET', dashHref, token);
327
- if (!dashResp.ok) throw new Error(`${dashResp.status}: dashboard ophalen mislukt`);
328
- dashboard = dashResp.body;
329
- }
330
-
331
- _dashboardData = dashboard;
332
- _dashboardRecord = recordData;
333
- _activeLayoutIndex = 0;
334
- _navHistory = [];
335
-
336
- renderSectionTabs(dashboard.layouts ?? []);
337
- renderSection(0);
338
- updateBackButton();
339
- } catch (err) {
340
- document.getElementById('sections-container').innerHTML =
341
- `<span style="color:var(--mrd-color-danger)">❌ ${escHtml(err.message)}</span>`;
342
- }
343
- }
344
-
345
- function renderSectionTabs(layouts) {
346
- const bar = document.getElementById('sections-tab-bar');
347
- bar.innerHTML = '';
348
- if (layouts.length <= 1) return;
349
-
350
- layouts.forEach((layout, i) => {
351
- const label = layout.label ?? layout.type ?? `Sectie ${i + 1}`;
352
- const btn = document.createElement('button');
353
- btn.className = 'section-tab-btn' + (i === 0 ? ' active' : '');
354
- btn.textContent = label;
355
- btn.onclick = () => renderSection(i);
356
- bar.appendChild(btn);
357
- });
358
- }
359
-
360
- async function renderSection(index) {
361
- _activeLayoutIndex = index;
362
- const generation = ++_sectionGeneration; // snapshot for stale-fetch detection
363
-
364
- document.querySelectorAll('.section-tab-btn').forEach((btn, i) =>
365
- btn.classList.toggle('active', i === index));
366
-
367
- const layout = _dashboardData.layouts[index];
368
- document.getElementById('json-viewer').innerHTML = formatJson(layout);
369
-
370
- const container = document.getElementById('sections-container');
371
- container.innerHTML = '';
372
-
373
- if (_dashboardType === 'form') {
374
- await renderForm(layout, _dashboardRecord, _dashboardRecord?._links?.self?.href ?? null, container);
375
- return;
376
- }
377
-
378
- const section = document.createElement('mrd-layout-section');
379
- section.items = layout.items;
380
- section.data = _dashboardRecord ?? { _links: _dashboardData._links ?? {} };
381
- section.locale = _locale;
382
-
383
- section.addEventListener('mrdLoadViewPage', async (e) => {
384
- if (generation !== _sectionGeneration) return;
385
- const { name, page, path, qs } = e.detail;
386
- logEvent('mrdLoadViewPage', e.detail);
387
- try {
388
- const url = `${_baseHref}${path}${qs ? '?' + qs : ''}`;
389
- const result = await apiFetchPage(authGetToken(), url, page);
390
- const rows = Object.values(result._embedded ?? {})[0] ?? [];
391
- const total = result.page?.totalElements;
392
- await section.setViewPage(name, page, rows, total);
393
- } catch (err) {
394
- console.error('[mrdLoadViewPage] mislukt', name, err);
395
- }
396
- });
397
-
398
- section.addEventListener('mrdLoadViewAggregations', async (e) => {
399
- if (generation !== _sectionGeneration) return;
400
- const { name, path, aggQs } = e.detail;
401
- logEvent('mrdLoadViewAggregations', e.detail);
402
- try {
403
- const aggUrl = `${_baseHref}${path}/aggregations`;
404
- const result = await apiRequest('GET', aggQs ? `${aggUrl}?${aggQs}` : aggUrl, authGetToken());
405
- if (result.ok) await section.setViewAggregations(name, result.body);
406
- } catch (err) {
407
- console.error('[mrdLoadViewAggregations] mislukt', name, err);
408
- }
409
- });
410
-
411
- section.addEventListener('mrdViewAction', async (e) => {
412
- logEvent('mrdViewAction', e.detail);
413
- const { action, dataClass } = e.detail;
414
- if (action === 'create' && dataClass) {
415
- await openCreateForm(dataClass, true);
416
- }
417
- });
418
-
419
- section.addEventListener('mrdNavigate', async (e) => {
420
- if (generation !== _sectionGeneration) return;
421
- await handleNavigate(e.detail);
422
- });
423
- section.addEventListener('mrdDownload', (e) => logEvent('mrdDownload', e.detail));
424
- section.addEventListener('mrdSearch', (e) => logEvent('mrdSearch', e.detail));
425
-
426
- container.appendChild(section);
427
- }
428
-
429
-
430
- function toggleJsonViewer() {
431
- const viewer = document.getElementById('json-viewer');
432
- const btn = document.getElementById('btn-json-toggle');
433
- const open = !viewer.classList.toggle('hidden');
434
- btn.classList.toggle('active', open);
435
- }
436
-
437
- function updateBackButton() {
438
- const btn = document.getElementById('btn-nav-back');
439
- if (btn) btn.classList.toggle('hidden', _navHistory.length === 0);
440
- }
441
-
442
- function syncDashboardTypeUI(type, objectHref) {
443
- _dashboardType = type;
444
- document.getElementById('dashboard-type-select').value = type;
445
- const needsClass = type === 'class' || type === 'create';
446
- const needsObject = type === 'object' || type === 'form';
447
- document.getElementById('controls-class-group').classList.toggle('hidden', !needsClass);
448
- document.getElementById('controls-object-group').classList.toggle('hidden', !needsObject);
449
- if (needsObject && objectHref) {
450
- document.getElementById('object-href-input').value = objectHref;
451
- }
452
- }
453
-
454
- function navigateBack() {
455
- if (_navHistory.length === 0) return;
456
- const prev = _navHistory.pop();
457
- _dashboardData = prev.dashboardData;
458
- _dashboardRecord = prev.dashboardRecord;
459
- _activeLayoutIndex = prev.activeLayoutIndex;
460
- syncDashboardTypeUI(prev.dashboardType ?? _dashboardType, prev.objectHref ?? '');
461
- renderSectionTabs(_dashboardData.layouts ?? []);
462
- renderSection(_activeLayoutIndex);
463
- updateBackButton();
464
- }
465
-
466
- function pushHistory() {
467
- _navHistory.push({
468
- dashboardData: _dashboardData,
469
- dashboardRecord: _dashboardRecord,
470
- activeLayoutIndex: _activeLayoutIndex,
471
- dashboardType: _dashboardType,
472
- objectHref: document.getElementById('object-href-input').value,
473
- });
474
- }
475
-
476
- async function navigateToObjectDashboard(selfHref) {
477
- if (!selfHref) return;
478
-
479
- document.getElementById('sections-container').innerHTML =
480
- '<span class="spinner"></span> Object laden...';
481
- document.getElementById('panel-sections').classList.remove('hidden');
482
-
483
- try {
484
- const recResp = await apiRequest('GET', selfHref, authGetToken());
485
- if (!recResp.ok) throw new Error(`${recResp.status}: object ophalen mislukt`);
486
- const record = recResp.body;
487
-
488
- const dashHref = record._links?.metadata?.href;
489
- if (!dashHref) throw new Error('Geen _links.metadata gevonden in object');
490
-
491
- const dashResp = await apiRequest('GET', dashHref, authGetToken());
492
- if (!dashResp.ok) throw new Error(`${dashResp.status}: dashboard ophalen mislukt`);
493
- const dashboard = dashResp.body;
494
-
495
- pushHistory();
496
- _dashboardData = dashboard;
497
- _dashboardRecord = record;
498
- _activeLayoutIndex = 0;
499
-
500
- const displayHref = selfHref.startsWith('http') ? selfHref : API_BASE + selfHref;
501
- syncDashboardTypeUI('object', displayHref);
502
- renderSectionTabs(dashboard.layouts ?? []);
503
- renderSection(0);
504
- updateBackButton();
505
- } catch (err) {
506
- document.getElementById('sections-container').innerHTML =
507
- `<span style="color:var(--mrd-color-danger)">❌ ${escHtml(err.message)}</span>`;
508
- }
509
- }
510
-
511
- async function navigateToClassDashboard(pluralName) {
512
- if (!_selectedTenant || !pluralName) return;
513
-
514
- document.getElementById('sections-container').innerHTML =
515
- '<span class="spinner"></span> Dashboard laden...';
516
- document.getElementById('panel-sections').classList.remove('hidden');
517
-
518
- try {
519
- const dashboard = await apiFetchClassDashboard(authGetToken(), _selectedTenant, pluralName);
520
-
521
- pushHistory();
522
- _dashboardData = dashboard;
523
- _dashboardRecord = null;
524
- _activeLayoutIndex = 0;
525
-
526
- syncDashboardTypeUI('class', '');
527
- const sel = document.getElementById('type-select-compact');
528
- if (sel.querySelector(`option[value="${pluralName}"]`)) sel.value = pluralName;
529
-
530
- renderSectionTabs(dashboard.layouts ?? []);
531
- renderSection(0);
532
- updateBackButton();
533
- } catch (err) {
534
- document.getElementById('sections-container').innerHTML =
535
- `<span style="color:var(--mrd-color-danger)">❌ ${escHtml(err.message)}</span>`;
536
- }
537
- }
538
-
539
- async function handleNavigate(detail) {
540
- const { href, label, navigate } = detail;
541
- if (href) {
542
- logEvent('mrdNavigate', { href, label });
543
- await navigateToObjectDashboard(href);
544
- } else if (navigate?.dataClass) {
545
- logEvent('mrdNavigate', { dataClass: navigate.dataClass, navigationType: navigate.navigationType, label });
546
- await navigateToClassDashboard(navigate.dataClass);
547
- } else {
548
- logEvent('mrdNavigate', detail);
549
- }
550
- }
551
-
552
- /* =====================================================================
553
- FORM — LOAD & RENDER
554
- ===================================================================== */
555
-
556
- /**
557
- * Load an existing record by clicking a table row.
558
- * Fetches the record (GET _links.self.href) and the form layout in parallel,
559
- * then opens the form pre-filled and in PATCH mode.
560
- */
561
- async function loadRecord(row) {
562
- const selfHref = row?._links?.self?.href;
563
- if (!selfHref) { console.warn('[loadRecord] geen _links.self.href in rij', row); return; }
564
- if (!_selectedType) return;
565
-
566
- const formPanel = document.getElementById('panel-form');
567
- formPanel.classList.remove('hidden');
568
- formPanel.innerHTML = '<span class="spinner"></span> Record laden...';
569
- document.getElementById('panel-response').innerHTML = '';
570
-
571
- try {
572
- const recordResp = await apiRequest('GET', selfHref, authGetToken());
573
- if (!recordResp.ok) throw new Error(`${recordResp.status}: kon record niet ophalen`);
574
-
575
- const formHref = recordResp.body?._links?.form?.href ?? null;
576
- const layout = await apiFetchForm(authGetToken(), _selectedTenant, _selectedType.pluralName, formHref);
577
- await renderForm(layout, recordResp.body, selfHref);
578
- } catch (err) {
579
- formPanel.innerHTML = `<span style="color:var(--mrd-color-danger)">❌ ${escHtml(err.message)}</span>`;
580
- }
581
- }
582
-
583
- async function loadForm() {
584
- const pluralName = _selectedType?.pluralName
585
- || document.getElementById('type-select-compact').value;
586
- await openCreateForm(pluralName);
587
- }
588
-
589
- async function openCreateForm(pluralName, pushNav = false) {
590
- if (!pluralName || !_selectedTenant) return;
591
-
592
- if (pushNav) pushHistory();
593
-
594
- syncDashboardTypeUI('create', '');
595
- const sel = document.getElementById('type-select-compact');
596
- if (sel.querySelector(`option[value="${pluralName}"]`)) sel.value = pluralName;
597
-
598
- document.getElementById('sections-tab-bar').innerHTML = '';
599
- document.getElementById('panel-form').classList.add('hidden');
600
- document.getElementById('panel-form').innerHTML = '';
601
- document.getElementById('panel-response').innerHTML = '';
602
- document.getElementById('panel-sections').classList.remove('hidden');
603
- updateBackButton();
604
-
605
- const container = document.getElementById('sections-container');
606
- container.innerHTML = '<span class="spinner"></span> Formulier laden...';
607
-
608
- try {
609
- const layout = await apiFetchForm(authGetToken(), _selectedTenant, pluralName);
610
- await renderForm(layout, null, null, container);
611
- } catch (err) {
612
- container.innerHTML = `<span style="color:var(--mrd-color-danger)">❌ ${escHtml(err.message)}</span>`;
613
- }
614
- }
615
-
616
- /**
617
- * Mount mrd-form and wire up all event handlers.
618
- *
619
- * @param layout - mrd-form layout descriptor
620
- * @param record - existing record for edit mode (null = new record)
621
- * @param selfHref - absolute URL for PATCH; null = POST (new record)
622
- */
623
- async function renderForm(layout, record = null, selfHref = null, containerEl = null) {
624
- // Build relation metadata map keyed by both relatedClass and mostSignificantClass
625
- // so lookups work regardless of which value arrives in events (mrdSearch uses mostSignificantClass).
626
- _relationMeta = {};
627
- function collectRelations(items) {
628
- if (!Array.isArray(items)) return;
629
- items.forEach(item => {
630
- if (item.type === 'RELATION' && item.relatedClass) {
631
- const meta = {
632
- name: item.name,
633
- mostSignificantClass: item.mostSignificantClass,
634
- };
635
- _relationMeta[item.relatedClass] = meta;
636
- if (item.mostSignificantClass) {
637
- _relationMeta[item.mostSignificantClass] = meta;
638
- }
639
- }
640
- if (Array.isArray(item.items)) collectRelations(item.items);
641
- });
642
- }
643
- collectRelations(layout.items);
644
-
645
- const relationFieldNames = new Set(Object.values(_relationMeta).map(m => m.name));
646
-
647
- // Build initial form values from the fetched record (edit mode).
648
- // Flat fields and _links are merged: the component normalises raw API link formats.
649
- const { _links, _embedded, ...fields } = record ?? {};
650
- const initialValues = record ? { ...fields, ...(_links ?? {}) } : {};
651
-
652
- const formPanel = containerEl ?? document.getElementById('panel-form');
653
- formPanel.innerHTML = `<mrd-form id="live-form" locale="${escHtml(_locale)}"></mrd-form>`;
654
-
655
- const form = formPanel.querySelector('#live-form') ?? document.getElementById('live-form');
656
- // Wait for Stencil to fully initialize this specific instance before setting props.
657
- // componentOnReady() resolves after componentDidLoad — more reliable than customElements.whenDefined.
658
- if (typeof form.componentOnReady === 'function') {
659
- await form.componentOnReady();
660
- } else {
661
- await customElements.whenDefined('mrd-form');
662
- }
663
-
664
- form.layout = layout;
665
- if (_meUser) form.me = _meUser;
666
- form.values = record ? initialValues : {};
667
-
668
- // Upload files immediately on selection; write binary URI back via setFieldValue
669
- form.addEventListener('mrdChange', async (e) => {
670
- const { name, value } = e.detail;
671
- if (!(value instanceof File)) return;
672
- try {
673
- const uri = await apiUploadFile(authGetToken(), _selectedTenant, value);
674
- await form.setFieldValue(name, uri);
675
- } catch (err) {
676
- console.error(`[upload] mislukt voor veld "${name}":`, err);
677
- }
678
- });
679
-
680
- form.addEventListener('mrdSubmit', async (e) => {
681
- document.getElementById('panel-response').innerHTML =
682
- '<div class="response-card" style="background:var(--mrd-color-neutral-100);border-color:var(--mrd-color-neutral-300);max-width:860px"><span class="spinner"></span> Bezig met indienen...</div>';
683
-
684
- // Transform relation values: { id: href, label } → href string
685
- const transformValues = (source) => {
686
- const out = {};
687
- for (const [key, val] of Object.entries(source)) {
688
- if (relationFieldNames.has(key)) {
689
- if (Array.isArray(val)) {
690
- out[key] = val.map(v => (v && typeof v === 'object' ? v.id : v));
691
- } else if (val && typeof val === 'object' && val.id) {
692
- out[key] = val.id;
693
- } else {
694
- out[key] = val;
695
- }
696
- } else {
697
- out[key] = val;
698
- }
699
- }
700
- return out;
701
- };
702
-
703
- if (selfHref) {
704
- // PATCH mode: send only changed fields
705
- const submitted = transformValues(e.detail);
706
- const original = transformValues(initialValues);
707
- const patch = {};
708
- for (const [key, val] of Object.entries(submitted)) {
709
- if (JSON.stringify(val) !== JSON.stringify(original[key])) {
710
- patch[key] = val;
711
- }
712
- }
713
- if (Object.keys(patch).length === 0) {
714
- renderResponse(200, 'Geen wijzigingen.');
715
- return;
716
- }
717
- const result = await apiRequest('PATCH', selfHref, authGetToken(), patch);
718
- renderResponse(result.status, result.body);
719
- } else {
720
- // POST mode: new record
721
- const values = transformValues(e.detail);
722
- const result = await apiSubmitForm(authGetToken(), _selectedTenant, _selectedType.pluralName, values);
723
- renderResponse(result.status, result.body);
724
- }
725
- });
726
-
727
- form.addEventListener('mrdSearch', async (e) => {
728
- logEvent('mrdSearch (live)', e.detail);
729
- const { name, query, relatedClass } = e.detail;
730
- if (!query || query.length < 2) return;
731
-
732
- // relatedClass here is actually mostSignificantClass (the URL segment).
733
- // Use name to find the exact field, avoiding collisions when multiple
734
- // fields share the same mostSignificantClass (e.g. 'companies').
735
- try {
736
- const results = await apiSearchRelation(authGetToken(), _selectedTenant, relatedClass, query);
737
- const host = Array.from(form.querySelectorAll('mrd-relation-field'))
738
- .find(el => el.name === name);
739
- if (host && typeof host.setSearchResults === 'function') {
740
- host.setSearchResults(results);
741
- }
742
- } catch (err) {
743
- console.error('[mrdSearch] relation search failed:', err);
744
- }
745
- });
746
-
747
- form.addEventListener('mrdFetchAll', async (e) => {
748
- logEvent('mrdFetchAll (live)', e.detail);
749
- const { name, mostSignificantClass, filter, filterValue } = e.detail;
750
- if (!mostSignificantClass) return;
751
-
752
- const host = Array.from(document.querySelectorAll('mrd-relation-field'))
753
- .find(el => el.name === name);
754
- if (!host || typeof host.setAllRecords !== 'function') return;
755
-
756
- // If a filter is required but no value is set yet, clear the dropdown
757
- if (filter && !filterValue) {
758
- host.setAllRecords([]);
759
- return;
760
- }
761
-
762
- try {
763
- // Build URL: /data/{tenant}/{mostSignificantClass}?{filter}={filterValue}&page=0
764
- let baseHref = `/data/${_selectedTenant}/${mostSignificantClass}`;
765
- if (filter && filterValue) baseHref += `?${encodeURIComponent(filter)}=${encodeURIComponent(filterValue)}`;
766
- const result = await apiFetchPage(authGetToken(), baseHref, 0);
767
- const embedded = result._embedded ?? {};
768
- const records = Object.values(embedded)[0] ?? [];
769
- host.setAllRecords(records.map(r => ({ id: r._links?.self?.href ?? r.id, label: r.name })));
770
- } catch (err) {
771
- console.error('[mrdFetchAll] failed:', err);
772
- }
773
- });
774
- }
775
-
776
- /* =====================================================================
777
- RESPONSE RENDERING
778
- ===================================================================== */
779
-
780
- function renderResponse(status, body) {
781
- const isOk = status >= 200 && status < 300;
782
- const cls = isOk ? 'success' : 'error';
783
- const icon = isOk ? '✅' : '❌';
784
- const label = httpStatusText(status);
785
-
786
- let inner = '';
787
-
788
- if (!isOk && body && typeof body === 'object') {
789
- const msg = body.message || body.error || '';
790
- const trace = body.trace || body.stackTrace || '';
791
- inner = `
792
- <h4>${icon} ${status} ${escHtml(label)}</h4>
793
- ${msg ? `<p style="margin:.25rem 0;font-size:.875rem">${escHtml(msg)}</p>` : ''}
794
- ${trace ? `<details><summary>trace</summary><pre>${escHtml(trace)}</pre></details>` : ''}
795
- ${!msg && !trace ? `<pre>${escHtml(JSON.stringify(body, null, 2))}</pre>` : ''}`;
796
- } else {
797
- const text = typeof body === 'string' ? body : JSON.stringify(body, null, 2);
798
- inner = `
799
- <h4>${icon} ${status} ${escHtml(label)}</h4>
800
- ${text ? `<pre>${escHtml(text)}</pre>` : ''}`;
801
- }
802
-
803
- document.getElementById('panel-response').innerHTML =
804
- `<div class="response-card ${cls}">${inner}</div>`;
805
- }
806
-
807
- /* =====================================================================
808
- EMBEDDED FORM (demo tab)
809
- ===================================================================== */
810
-
811
- customElements.whenDefined('mrd-form').then(() => {
812
- const form = document.getElementById('demo-form');
813
- form.layout = window.EXAMPLE_LAYOUT;
814
- form.values = window.EXAMPLE_VALUES;
815
-
816
- form.addEventListener('mrdSubmit', (e) => { console.log('[mrdSubmit]', e.detail); logEvent('mrdSubmit', e.detail); });
817
- form.addEventListener('mrdSearch', (e) => { console.log('[mrdSearch]', e.detail); logEvent('mrdSearch', e.detail); });
818
- form.addEventListener('mrdFetchAll', (e) => { console.log('[mrdFetchAll]', e.detail); logEvent('mrdFetchAll', e.detail); });
819
- });
820
-
821
- document.getElementById('locale-select').addEventListener('change', (e) => {
822
- _locale = e.target.value;
823
- const demoForm = document.getElementById('demo-form');
824
- if (demoForm) demoForm.locale = _locale;
825
- const liveForm = document.getElementById('live-form');
826
- if (liveForm) liveForm.locale = _locale;
827
- // Update actieve section als aanwezig
828
- const section = document.querySelector('#sections-container mrd-layout-section');
829
- if (section) section.locale = _locale;
830
- });
831
-
832
- document.getElementById('btn-inject-results').addEventListener('click', () => {
833
- const relationField = document.querySelector('mrd-relation-field');
834
- if (relationField && typeof relationField.setSearchResults === 'function') {
835
- relationField.setSearchResults([
836
- { id: '1', label: 'Alice Johnson', description: 'Senior Engineer' },
837
- { id: '2', label: 'Bob van der Berg', description: 'Product Manager' },
838
- { id: '3', label: 'Carol Martínez', description: 'UX Designer' },
839
- { id: '4', label: 'David Müller', description: 'DevOps Lead' },
840
- ]);
841
- } else {
842
- logEvent('info', 'Type 2+ chars in the Project Manager field first, then click this');
843
- }
844
- });
845
-
846
- /* =====================================================================
847
- BOOT
848
- ===================================================================== */
849
-
850
- document.addEventListener('DOMContentLoaded', async () => {
851
- // Initialize Field Types tab (static, no auth needed)
852
- const ftSection = document.getElementById('field-types-section');
853
- ftSection.items = window.EXAMPLE_FIELD_TYPES_ITEMS;
854
- ftSection.data = window.EXAMPLE_FIELD_TYPES_DATA;
855
- ftSection.locale = 'nl-NL';
856
-
857
- // Simulate host resolving a signed URL for IMAGE fields (thumbnail on load).
858
- ftSection.addEventListener('mrdLoadImage', (e) => {
859
- const staticUrl = 'https://picsum.photos/seed/mosterd/800/600';
860
- ftSection.setImagePreview(e.detail.fieldName, staticUrl);
861
- });
862
-
863
- ftSection.addEventListener('mrdNavigate', (e) => {
864
- logEvent('mrdNavigate', e.detail);
865
- });
866
-
867
- ftSection.addEventListener('mrdDownload', (e) => {
868
- logEvent('mrdDownload', e.detail);
869
- });
870
-
871
- ftSection.addEventListener('mrdSearch', (e) => {
872
- logEvent('mrdSearch', e.detail);
873
- const { query, dataClass } = e.detail;
874
- const mock = [
875
- { id: '/data/demo/1', label: 'Alice Johnson', description: 'Senior Engineer' },
876
- { id: '/data/demo/2', label: 'Bob van der Berg', description: 'Product Manager' },
877
- { id: '/data/demo/3', label: 'Carol Martínez', description: 'UX Designer' },
878
- ].filter(r => r.label.toLowerCase().includes(query.toLowerCase()));
879
- ftSection.setSearchResults(mock, dataClass);
880
- });
881
-
882
- authRestoreSession();
883
- await authHandleCallback();
884
-
885
- if (authGetToken()) {
886
- showTab('live-api');
887
- showAuthStatus();
888
- loadTenants();
889
- }
890
- });