@mmlogic/components 0.5.1 → 0.5.2
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.
- package/dist/cjs/cell-renderer-CmaO0CZT.js +862 -0
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/mosterdcomponents.cjs.js +1 -1
- package/dist/cjs/mrd-boolean-field_23.cjs.entry.js +2490 -817
- package/dist/cjs/mrd-merge-view.cjs.entry.js +273 -0
- package/dist/collection/collection-manifest.json +1 -0
- package/dist/collection/components/fields/mrd-textarea-field/mrd-textarea-field.js +9 -5
- package/dist/collection/components/merge/mrd-merge-view/merge-diff.js +99 -0
- package/dist/collection/components/merge/mrd-merge-view/merge-parts.js +44 -0
- package/dist/collection/components/merge/mrd-merge-view/mrd-merge-view.css +244 -0
- package/dist/collection/components/merge/mrd-merge-view/mrd-merge-view.js +397 -0
- package/dist/collection/dev/api.js +24 -0
- package/dist/collection/dev/app.js +84 -4
- package/dist/collection/utils/cell-renderer.js +6 -1
- package/dist/collection/utils/i18n.js +104 -0
- package/dist/components/cell-renderer.js +1 -0
- package/dist/components/format.js +1 -1
- package/dist/components/i18n.js +1 -1
- package/dist/components/mrd-layout-section.js +1 -1
- package/dist/components/mrd-merge-view.d.ts +11 -0
- package/dist/components/mrd-merge-view.js +1 -0
- package/dist/components/mrd-table2.js +1 -1
- package/dist/components/mrd-textarea-field2.js +2 -1
- package/dist/esm/cell-renderer-D63AUZxa.js +854 -0
- package/dist/esm/loader.js +1 -1
- package/dist/esm/mosterdcomponents.js +1 -1
- package/dist/esm/mrd-boolean-field_23.entry.js +2420 -747
- package/dist/esm/mrd-merge-view.entry.js +271 -0
- package/dist/mosterdcomponents/mosterdcomponents.esm.js +1 -1
- package/dist/mosterdcomponents/p-2e16a7d0.entry.js +1 -0
- package/dist/mosterdcomponents/p-CnqDM0Gb.js +1 -0
- package/dist/mosterdcomponents/p-b5f38707.entry.js +3 -0
- package/dist/types/components/fields/mrd-textarea-field/mrd-textarea-field.d.ts +1 -0
- package/dist/types/components/merge/mrd-merge-view/merge-diff.d.ts +43 -0
- package/dist/types/components/merge/mrd-merge-view/merge-parts.d.ts +37 -0
- package/dist/types/components/merge/mrd-merge-view/mrd-merge-view.d.ts +68 -0
- package/dist/types/components.d.ts +151 -0
- package/package.json +2 -1
- package/dist/mosterdcomponents/p-cdecc698.entry.js +0 -1
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var index = require('./index-DzRfoAPz.js');
|
|
4
|
+
var clientLayout = require('./client-layout-RZ3Ix-4a.js');
|
|
5
|
+
var cellRenderer = require('./cell-renderer-CmaO0CZT.js');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Whether a layout item can appear in the merge diff at all. m-n relations
|
|
9
|
+
* (`multiple: true`) are unioned automatically server-side (DataService.merge)
|
|
10
|
+
* and must never be surfaced as a user choice — only FIELD and single-value
|
|
11
|
+
* (n-1) RELATION items are diffable.
|
|
12
|
+
*/
|
|
13
|
+
function isDiffable(item) {
|
|
14
|
+
if (item.type === clientLayout.ClientLayoutItemType.FIELD)
|
|
15
|
+
return true;
|
|
16
|
+
if (item.type === clientLayout.ClientLayoutItemType.RELATION)
|
|
17
|
+
return !item.multiple;
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Raw value for a field/relation off a record in the API's row shape (flat
|
|
22
|
+
* fields + `_links`). History-enabled fields come back as `{ current,
|
|
23
|
+
* history: [...] }` — this returns that whole object as-is, since a winning
|
|
24
|
+
* B value must carry its entire history into the patch (FINDING-0114: "the
|
|
25
|
+
* entire history list follows whichever value wins ... never interleaved").
|
|
26
|
+
*/
|
|
27
|
+
function getRawValue(item, record) {
|
|
28
|
+
var _a, _b, _c, _d, _e;
|
|
29
|
+
const name = (_a = item.name) !== null && _a !== void 0 ? _a : '';
|
|
30
|
+
if (item.type === clientLayout.ClientLayoutItemType.RELATION) {
|
|
31
|
+
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;
|
|
32
|
+
}
|
|
33
|
+
return (_e = record === null || record === void 0 ? void 0 : record[name]) !== null && _e !== void 0 ? _e : null;
|
|
34
|
+
}
|
|
35
|
+
/** Unwraps a history-enabled field's `{ current, history }` shape to its current value, for comparison only. */
|
|
36
|
+
function comparableValue(item, value) {
|
|
37
|
+
if (item.historyEnabled && value != null && typeof value === 'object' && !Array.isArray(value) && 'current' in value) {
|
|
38
|
+
return value.current;
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
function normalize(value) {
|
|
43
|
+
return value === undefined || value === '' ? null : value;
|
|
44
|
+
}
|
|
45
|
+
function valuesEqual(a, b) {
|
|
46
|
+
const na = normalize(a);
|
|
47
|
+
const nb = normalize(b);
|
|
48
|
+
if (na === nb)
|
|
49
|
+
return true;
|
|
50
|
+
if (na === null || nb === null)
|
|
51
|
+
return false;
|
|
52
|
+
return JSON.stringify(na) === JSON.stringify(nb);
|
|
53
|
+
}
|
|
54
|
+
function isEmpty(value) {
|
|
55
|
+
return normalize(value) === null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Splits diffable fields into `conflicts` (both records have a — different —
|
|
59
|
+
* value, user must pick) and `autoFilled` (A has no value, B does; silently
|
|
60
|
+
* taken from B without a UI choice).
|
|
61
|
+
*/
|
|
62
|
+
function diffFields(fields, recordA, recordB) {
|
|
63
|
+
const conflicts = [];
|
|
64
|
+
const autoFilled = [];
|
|
65
|
+
for (const item of fields.filter(isDiffable)) {
|
|
66
|
+
const aValue = getRawValue(item, recordA);
|
|
67
|
+
const bValue = getRawValue(item, recordB);
|
|
68
|
+
const aComparable = comparableValue(item, aValue);
|
|
69
|
+
const bComparable = comparableValue(item, bValue);
|
|
70
|
+
if (valuesEqual(aComparable, bComparable))
|
|
71
|
+
continue;
|
|
72
|
+
if (isEmpty(aComparable) && !isEmpty(bComparable)) {
|
|
73
|
+
autoFilled.push({ item, aValue, bValue });
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
conflicts.push({ item, aValue, bValue });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { conflicts, autoFilled };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Builds the patch payload per the merge endpoint contract: every `autoFilled`
|
|
83
|
+
* entry is always included (A had nothing to lose); a `conflicts` entry is
|
|
84
|
+
* only included when the user explicitly resolved it to B — anything left
|
|
85
|
+
* unresolved keeps A's value by omission.
|
|
86
|
+
*/
|
|
87
|
+
function buildPatch(diff, resolutions) {
|
|
88
|
+
var _a, _b, _c;
|
|
89
|
+
const patch = {};
|
|
90
|
+
for (const { item, bValue } of diff.autoFilled) {
|
|
91
|
+
patch[(_a = item.name) !== null && _a !== void 0 ? _a : ''] = bValue;
|
|
92
|
+
}
|
|
93
|
+
for (const { item, bValue } of diff.conflicts) {
|
|
94
|
+
if (resolutions[(_b = item.name) !== null && _b !== void 0 ? _b : ''] === 'B') {
|
|
95
|
+
patch[(_c = item.name) !== null && _c !== void 0 ? _c : ''] = bValue;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return patch;
|
|
99
|
+
}
|
|
100
|
+
/** Display label for a candidate record in the search results list. */
|
|
101
|
+
function candidateLabel(record) {
|
|
102
|
+
var _a, _b, _c, _d, _e;
|
|
103
|
+
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 : '';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** History-enabled fields carry `{ current, history: [...] }` — this is the legal record itself (e.g. past company names), not an audit log. */
|
|
107
|
+
function getHistory(item, record) {
|
|
108
|
+
var _a, _b;
|
|
109
|
+
if (!item.historyEnabled || !item.name)
|
|
110
|
+
return [];
|
|
111
|
+
return (_b = (_a = record === null || record === void 0 ? void 0 : record[item.name]) === null || _a === void 0 ? void 0 : _a.history) !== null && _b !== void 0 ? _b : [];
|
|
112
|
+
}
|
|
113
|
+
/** Small clock badge + popover listing past values for a history-enabled field — same UX as mrd-layout-section's field view. */
|
|
114
|
+
const HistoryBadge = ({ locale, history, isOpen, onToggle }) => {
|
|
115
|
+
if (history.length === 0)
|
|
116
|
+
return null;
|
|
117
|
+
const sorted = [...history].sort((a, b) => b.until.localeCompare(a.until));
|
|
118
|
+
return (index.h("span", { class: "mrd-merge-view__history-wrap" }, index.h("button", { type: "button", class: "mrd-merge-view__history-btn", title: cellRenderer.t('history_badge_tooltip', locale), "aria-label": cellRenderer.t('history_badge_tooltip', locale), onClick: (e) => { e.stopPropagation(); onToggle(); } }, index.h("svg", { class: "mrd-merge-view__history-icon", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true" }, index.h("path", { "fill-rule": "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm.75-13a.75.75 0 00-1.5 0v5c0 .207.085.394.22.53l2.5 2.5a.75.75 0 101.06-1.06L10.75 9.69V5z", "clip-rule": "evenodd" }))), isOpen && (index.h("div", { class: "mrd-merge-view__history-popover", role: "listbox" }, sorted.map((entry, i) => (index.h("div", { key: String(i), class: "mrd-merge-view__history-entry" }, entry.value, " (", cellRenderer.t('history_until', locale), " ", cellRenderer.formatDate(entry.until, locale), ")")))))));
|
|
119
|
+
};
|
|
120
|
+
const MergeSearchStep = ({ locale, searchQuery, searchResults, isLoading, onSearchInput, onSelect }) => (index.h("div", { class: "mrd-merge-view__search-step" }, index.h("p", { class: "mrd-merge-view__hint" }, cellRenderer.t('merge_search_hint', locale)), index.h("div", { class: "mrd-merge-view__search-wrapper" }, index.h("input", { class: "mrd-merge-view__search", type: "text", value: searchQuery, placeholder: cellRenderer.t('search_placeholder', locale), onInput: (e) => onSearchInput(e.target.value) }), isLoading && index.h("span", { class: "mrd-merge-view__spinner", "aria-label": cellRenderer.t('loading', locale) })), searchQuery.trim().length >= 2 && !isLoading && (index.h("div", { class: "mrd-merge-view__results" }, searchResults.length === 0 ? (index.h("div", { class: "mrd-merge-view__no-results" }, cellRenderer.t('no_results', locale))) : (searchResults.map(record => {
|
|
121
|
+
var _a, _b, _c;
|
|
122
|
+
return (index.h("div", { key: (_c = (_b = (_a = record === null || record === void 0 ? void 0 : record._links) === null || _a === void 0 ? void 0 : _a.self) === null || _b === void 0 ? void 0 : _b.href) !== null && _c !== void 0 ? _c : candidateLabel(record), class: "mrd-merge-view__result-item" }, index.h("span", { class: "mrd-merge-view__result-label" }, candidateLabel(record)), index.h("button", { type: "button", class: "mrd-merge-view__btn mrd-merge-view__btn--secondary", onClick: () => onSelect(record) }, cellRenderer.t('merge_select', locale))));
|
|
123
|
+
}))))));
|
|
124
|
+
const MergeCompareStep = ({ locale, currentLabel, candidateLabel: bLabel, conflicts, autoFilled, recordA, recordB, resolutions, openHistoryKey, onResolve, onToggleHistory, onChangeCandidate, onContinue }) => (index.h("div", { class: "mrd-merge-view__compare-step" }, index.h("h3", { class: "mrd-merge-view__heading" }, cellRenderer.t('merge_compare_heading', locale)), conflicts.length === 0 ? (index.h("p", { class: "mrd-merge-view__no-conflicts" }, cellRenderer.t('merge_no_conflicts', locale))) : (index.h("table", { class: "mrd-merge-view__table" }, index.h("thead", null, index.h("tr", null, index.h("th", { class: "mrd-merge-view__header" }), index.h("th", { class: "mrd-merge-view__header" }, cellRenderer.t('merge_column_a', locale), index.h("span", { class: "mrd-merge-view__header-name" }, currentLabel)), index.h("th", { class: "mrd-merge-view__header" }, cellRenderer.t('merge_column_b', locale), index.h("span", { class: "mrd-merge-view__header-name" }, bLabel)))), index.h("tbody", null, conflicts.map(({ item }) => {
|
|
125
|
+
var _a, _b, _c;
|
|
126
|
+
const name = (_a = item.name) !== null && _a !== void 0 ? _a : '';
|
|
127
|
+
const resolution = (_b = resolutions[name]) !== null && _b !== void 0 ? _b : 'A';
|
|
128
|
+
const historyA = getHistory(item, recordA);
|
|
129
|
+
const historyB = getHistory(item, recordB);
|
|
130
|
+
const keyA = `${name}:A`;
|
|
131
|
+
const keyB = `${name}:B`;
|
|
132
|
+
return (index.h("tr", { key: name, class: "mrd-merge-view__row" }, index.h("td", { class: "mrd-merge-view__cell mrd-merge-view__cell--label" }, (_c = item.label) !== null && _c !== void 0 ? _c : name), index.h("td", { class: "mrd-merge-view__cell" }, index.h("label", { class: "mrd-merge-view__option" }, index.h("input", { type: "radio", name: `merge-${name}`, checked: resolution === 'A', onChange: () => onResolve(name, 'A') }), index.h("span", null, cellRenderer.CellRenderer.render(item, recordA, locale) || '—')), index.h(HistoryBadge, { locale: locale, history: historyA, isOpen: openHistoryKey === keyA, onToggle: () => onToggleHistory(openHistoryKey === keyA ? null : keyA) })), index.h("td", { class: "mrd-merge-view__cell" }, index.h("label", { class: "mrd-merge-view__option" }, index.h("input", { type: "radio", name: `merge-${name}`, checked: resolution === 'B', onChange: () => onResolve(name, 'B') }), index.h("span", null, cellRenderer.CellRenderer.render(item, recordB, locale) || '—')), index.h(HistoryBadge, { locale: locale, history: historyB, isOpen: openHistoryKey === keyB, onToggle: () => onToggleHistory(openHistoryKey === keyB ? null : keyB) }))));
|
|
133
|
+
})))), autoFilled.length > 0 && (index.h("div", { class: "mrd-merge-view__auto-note" }, index.h("p", null, cellRenderer.t('merge_auto_heading', locale)), index.h("ul", null, autoFilled.map(({ item }) => { var _a; return index.h("li", { key: item.name }, (_a = item.label) !== null && _a !== void 0 ? _a : item.name); })))), index.h("div", { class: "mrd-merge-view__actions" }, index.h("button", { type: "button", class: "mrd-merge-view__btn mrd-merge-view__btn--secondary", onClick: onChangeCandidate }, cellRenderer.t('merge_back', locale)), index.h("button", { type: "button", class: "mrd-merge-view__btn mrd-merge-view__btn--primary", onClick: onContinue }, cellRenderer.t('merge_continue', locale)))));
|
|
134
|
+
const MergeConfirmStep = ({ locale, candidateLabel: bLabel, conflicts, autoFilled, resolutions, recordB, openHistoryKey, onToggleHistory, onBack, onConfirm }) => {
|
|
135
|
+
const changed = [
|
|
136
|
+
...autoFilled,
|
|
137
|
+
...conflicts.filter(({ item }) => { var _a; return resolutions[(_a = item.name) !== null && _a !== void 0 ? _a : ''] === 'B'; }),
|
|
138
|
+
];
|
|
139
|
+
return (index.h("div", { class: "mrd-merge-view__confirm-step" }, index.h("h3", { class: "mrd-merge-view__heading" }, cellRenderer.t('merge_confirm_heading', locale)), changed.length === 0 ? (index.h("p", { class: "mrd-merge-view__no-conflicts" }, cellRenderer.t('merge_no_conflicts', locale))) : (index.h("ul", { class: "mrd-merge-view__summary-list" }, changed.map(({ item }) => {
|
|
140
|
+
var _a;
|
|
141
|
+
const key = `${item.name}:confirm`;
|
|
142
|
+
return (index.h("li", { key: item.name }, (_a = item.label) !== null && _a !== void 0 ? _a : item.name, ": ", cellRenderer.CellRenderer.render(item, recordB, locale) || '—', index.h(HistoryBadge, { locale: locale, history: getHistory(item, recordB), isOpen: openHistoryKey === key, onToggle: () => onToggleHistory(openHistoryKey === key ? null : key) })));
|
|
143
|
+
}))), index.h("p", { class: "mrd-merge-view__warning" }, cellRenderer.t('merge_confirm_warning', locale), " (", bLabel, ")"), index.h("div", { class: "mrd-merge-view__actions" }, index.h("button", { type: "button", class: "mrd-merge-view__btn mrd-merge-view__btn--secondary", onClick: onBack }, cellRenderer.t('merge_back', locale)), index.h("button", { type: "button", class: "mrd-merge-view__btn mrd-merge-view__btn--danger", onClick: onConfirm }, cellRenderer.t('merge_confirm_button', locale)))));
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const mrdMergeViewCss = () => `.sc-mrd-merge-view-h{display:block;width:100%}.mrd-merge-view.sc-mrd-merge-view{font-family:var(--mrd-font-family);font-size:var(--mrd-font-size-sm);color:var(--mrd-color-neutral-900)}.mrd-merge-view__hint.sc-mrd-merge-view{margin:0 0 var(--mrd-space-4);color:var(--mrd-color-neutral-600)}.mrd-merge-view__heading.sc-mrd-merge-view{margin:0 0 var(--mrd-space-4);font-size:var(--mrd-font-size-lg);font-weight:var(--mrd-font-weight-semibold)}.mrd-merge-view__search-wrapper.sc-mrd-merge-view{position:relative;display:flex;align-items:center}.mrd-merge-view__search.sc-mrd-merge-view{width:100%;height:var(--mrd-input-height);padding:var(--mrd-input-padding-y) var(--mrd-input-padding-x);border:var(--mrd-border-width) solid var(--mrd-border-color);border-radius:var(--mrd-border-radius);background:var(--mrd-input-bg);color:var(--mrd-input-color);font-size:var(--mrd-font-size-sm)}.mrd-merge-view__search.sc-mrd-merge-view:focus{outline:none;border-color:var(--mrd-border-color-focus);box-shadow:var(--mrd-shadow-focus)}.mrd-merge-view__spinner.sc-mrd-merge-view{position:absolute;right:var(--mrd-space-3);width:1rem;height:1rem;border:2px solid var(--mrd-color-neutral-300);border-top-color:var(--mrd-color-primary);border-radius:var(--mrd-border-radius-full);animation:mrd-merge-view-spin var(--mrd-transition-slow) linear infinite}.mrd-merge-view__results.sc-mrd-merge-view{margin-top:var(--mrd-space-2);border:var(--mrd-border-width) solid var(--mrd-border-color);border-radius:var(--mrd-border-radius);overflow:hidden}.mrd-merge-view__no-results.sc-mrd-merge-view{padding:var(--mrd-space-3) var(--mrd-space-4);color:var(--mrd-color-neutral-500)}.mrd-merge-view__result-item.sc-mrd-merge-view{display:flex;align-items:center;justify-content:space-between;gap:var(--mrd-space-3);padding:var(--mrd-space-2) var(--mrd-space-4)}.mrd-merge-view__result-item.sc-mrd-merge-view:not(:last-child){border-bottom:var(--mrd-border-width) solid var(--mrd-border-color)}.mrd-merge-view__result-label.sc-mrd-merge-view{font-weight:var(--mrd-font-weight-medium)}.mrd-merge-view__no-conflicts.sc-mrd-merge-view{color:var(--mrd-color-neutral-600)}.mrd-merge-view__table.sc-mrd-merge-view{width:100%;border-collapse:collapse;margin-bottom:var(--mrd-space-4)}.mrd-merge-view__header.sc-mrd-merge-view{text-align:left;padding:var(--mrd-space-2) var(--mrd-space-3);border-bottom:2px solid var(--mrd-border-color);font-size:var(--mrd-font-size-xs);text-transform:uppercase;letter-spacing:0.04em;color:var(--mrd-color-neutral-600)}.mrd-merge-view__header-name.sc-mrd-merge-view{display:block;text-transform:none;letter-spacing:normal;font-size:var(--mrd-font-size-sm);font-weight:var(--mrd-font-weight-medium);color:var(--mrd-color-neutral-900);margin-top:var(--mrd-space-1)}.mrd-merge-view__row.sc-mrd-merge-view:nth-child(even){background:var(--mrd-color-neutral-50)}.mrd-merge-view__cell.sc-mrd-merge-view{padding:var(--mrd-space-2) var(--mrd-space-3);border-bottom:var(--mrd-border-width) solid var(--mrd-border-color);vertical-align:top}.mrd-merge-view__cell--label.sc-mrd-merge-view{font-weight:var(--mrd-font-weight-medium);white-space:nowrap}.mrd-merge-view__option.sc-mrd-merge-view{display:flex;align-items:flex-start;gap:var(--mrd-space-2);cursor:pointer}.mrd-merge-view__option.sc-mrd-merge-view input[type=radio].sc-mrd-merge-view{margin-top:0.2em}.mrd-merge-view__history-wrap.sc-mrd-merge-view{position:relative;display:inline-flex;align-items:center;margin-left:var(--mrd-space-1)}.mrd-merge-view__history-btn.sc-mrd-merge-view{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;padding:0;background:transparent;border:none;border-radius:var(--mrd-border-radius-sm);color:var(--mrd-color-neutral-400);cursor:pointer;flex-shrink:0;transition:color var(--mrd-transition-fast), background-color var(--mrd-transition-fast)}.mrd-merge-view__history-btn.sc-mrd-merge-view:hover{color:var(--mrd-color-neutral-700);background-color:var(--mrd-color-neutral-100)}.mrd-merge-view__history-btn.sc-mrd-merge-view:focus{outline:none;box-shadow:var(--mrd-shadow-focus)}.mrd-merge-view__history-icon.sc-mrd-merge-view{width:0.875rem;height:0.875rem}.mrd-merge-view__history-popover.sc-mrd-merge-view{position:absolute;top:calc(100% + 0.25rem);left:0;min-width:18rem;max-width:18rem;background-color:var(--mrd-color-white);border:var(--mrd-border-width) solid var(--mrd-border-color);border-radius:var(--mrd-border-radius-md);box-shadow:var(--mrd-shadow-md);padding:var(--mrd-space-1) 0;z-index:var(--mrd-z-dropdown)}.mrd-merge-view__history-entry.sc-mrd-merge-view{font-size:var(--mrd-font-size-sm);color:var(--mrd-color-neutral-700);padding:var(--mrd-space-2) var(--mrd-space-3);line-height:var(--mrd-line-height-normal)}.mrd-merge-view__history-entry.sc-mrd-merge-view:not(:last-child){border-bottom:var(--mrd-border-width) solid var(--mrd-color-neutral-100)}.mrd-merge-view__auto-note.sc-mrd-merge-view{margin-bottom:var(--mrd-space-4);padding:var(--mrd-space-3) var(--mrd-space-4);background:var(--mrd-color-neutral-50);border:var(--mrd-border-width) solid var(--mrd-border-color);border-radius:var(--mrd-border-radius);color:var(--mrd-color-neutral-700)}.mrd-merge-view__auto-note.sc-mrd-merge-view p.sc-mrd-merge-view{margin:0 0 var(--mrd-space-1);font-weight:var(--mrd-font-weight-medium)}.mrd-merge-view__auto-note.sc-mrd-merge-view ul.sc-mrd-merge-view{margin:0;padding-left:var(--mrd-space-5)}.mrd-merge-view__summary-list.sc-mrd-merge-view{margin:0 0 var(--mrd-space-4);padding-left:var(--mrd-space-5)}.mrd-merge-view__warning.sc-mrd-merge-view{padding:var(--mrd-space-3) var(--mrd-space-4);background:var(--mrd-color-danger-light);border:var(--mrd-border-width) solid var(--mrd-color-danger);border-radius:var(--mrd-border-radius);color:var(--mrd-color-danger-dark);margin-bottom:var(--mrd-space-4)}.mrd-merge-view__actions.sc-mrd-merge-view{display:flex;gap:var(--mrd-space-3)}.mrd-merge-view__btn.sc-mrd-merge-view{height:var(--mrd-input-height-sm);padding:0 var(--mrd-space-4);border-radius:var(--mrd-border-radius);border:var(--mrd-border-width) solid transparent;font-size:var(--mrd-font-size-sm);font-weight:var(--mrd-font-weight-medium);cursor:pointer;transition:background-color var(--mrd-transition-fast), border-color var(--mrd-transition-fast)}.mrd-merge-view__btn--primary.sc-mrd-merge-view{background:var(--mrd-color-primary);color:var(--mrd-color-white)}.mrd-merge-view__btn--primary.sc-mrd-merge-view:hover{background:var(--mrd-color-primary-hover)}.mrd-merge-view__btn--secondary.sc-mrd-merge-view{background:var(--mrd-color-white);border-color:var(--mrd-border-color);color:var(--mrd-color-neutral-700)}.mrd-merge-view__btn--secondary.sc-mrd-merge-view:hover{background:var(--mrd-color-neutral-50)}.mrd-merge-view__btn--danger.sc-mrd-merge-view{background:var(--mrd-color-danger);color:var(--mrd-color-white)}.mrd-merge-view__btn--danger.sc-mrd-merge-view:hover{background:var(--mrd-color-danger-dark)}@keyframes mrd-merge-view-spin{to{transform:rotate(360deg)}}`;
|
|
147
|
+
|
|
148
|
+
const MrdMergeView = class {
|
|
149
|
+
constructor(hostRef) {
|
|
150
|
+
index.registerInstance(this, hostRef);
|
|
151
|
+
this.mrdSearch = index.createEvent(this, "mrdSearch");
|
|
152
|
+
this.mrdMerge = index.createEvent(this, "mrdMerge");
|
|
153
|
+
this.locale = navigator.language;
|
|
154
|
+
/** Dot-notation class, passed through on mrdSearch (host may use it to pick the right search endpoint). */
|
|
155
|
+
this.basicType = '';
|
|
156
|
+
/** URL segment for the search endpoint; falls back to basicType when absent. */
|
|
157
|
+
this.mostSignificantClass = '';
|
|
158
|
+
/** Flat FIELD/RELATION items describing the basic type's schema (host flattens SECTION/GROUP, same as mrd-table's `columns`). */
|
|
159
|
+
this.fields = [];
|
|
160
|
+
/** Href of A (winner) — the object the merge screen was opened from. */
|
|
161
|
+
this.currentHref = '';
|
|
162
|
+
/** A's full raw record: flat fields + `_links`, the same row shape CellRenderer.render expects. */
|
|
163
|
+
this.currentRecord = {};
|
|
164
|
+
/** Display name for A, shown in the compare step's column header. */
|
|
165
|
+
this.currentLabel = '';
|
|
166
|
+
this.step = 'search';
|
|
167
|
+
this.searchQuery = '';
|
|
168
|
+
this.searchResults = [];
|
|
169
|
+
this.isLoading = false;
|
|
170
|
+
this.candidate = null;
|
|
171
|
+
this.resolutions = {};
|
|
172
|
+
/** Which history badge popover is open, keyed e.g. `legalName:A` / `legalName:confirm`. */
|
|
173
|
+
this.openHistoryKey = null;
|
|
174
|
+
this.historyClickOutside = null;
|
|
175
|
+
this.searchDebounce = null;
|
|
176
|
+
this.handleSearchInput = (query) => {
|
|
177
|
+
this.searchQuery = query;
|
|
178
|
+
if (this.searchDebounce)
|
|
179
|
+
clearTimeout(this.searchDebounce);
|
|
180
|
+
if (query.trim().length >= 2) {
|
|
181
|
+
this.isLoading = true;
|
|
182
|
+
this.searchDebounce = setTimeout(() => {
|
|
183
|
+
this.mrdSearch.emit({
|
|
184
|
+
query,
|
|
185
|
+
basicType: this.basicType,
|
|
186
|
+
mostSignificantClass: this.mostSignificantClass || undefined,
|
|
187
|
+
});
|
|
188
|
+
}, 300);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
this.searchResults = [];
|
|
192
|
+
this.isLoading = false;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
this.handleSelect = (record) => {
|
|
196
|
+
this.candidate = record;
|
|
197
|
+
const { conflicts } = diffFields(this.fields, this.currentRecord, record);
|
|
198
|
+
const resolutions = {};
|
|
199
|
+
conflicts.forEach(({ item }) => { var _a; resolutions[(_a = item.name) !== null && _a !== void 0 ? _a : ''] = 'A'; });
|
|
200
|
+
this.resolutions = resolutions;
|
|
201
|
+
this.openHistoryKey = null;
|
|
202
|
+
this.step = 'compare';
|
|
203
|
+
};
|
|
204
|
+
this.handleChangeCandidate = () => {
|
|
205
|
+
this.candidate = null;
|
|
206
|
+
this.resolutions = {};
|
|
207
|
+
this.openHistoryKey = null;
|
|
208
|
+
this.step = 'search';
|
|
209
|
+
};
|
|
210
|
+
this.handleResolve = (name, side) => {
|
|
211
|
+
this.resolutions = Object.assign(Object.assign({}, this.resolutions), { [name]: side });
|
|
212
|
+
};
|
|
213
|
+
this.handleToggleHistory = (key) => {
|
|
214
|
+
this.openHistoryKey = key;
|
|
215
|
+
};
|
|
216
|
+
this.handleContinue = () => {
|
|
217
|
+
this.step = 'confirm';
|
|
218
|
+
};
|
|
219
|
+
this.handleBack = () => {
|
|
220
|
+
this.step = 'compare';
|
|
221
|
+
};
|
|
222
|
+
this.handleConfirm = () => {
|
|
223
|
+
var _a, _b, _c, _d;
|
|
224
|
+
if (!this.candidate)
|
|
225
|
+
return;
|
|
226
|
+
const diff = diffFields(this.fields, this.currentRecord, this.candidate);
|
|
227
|
+
const patch = buildPatch(diff, this.resolutions);
|
|
228
|
+
const loserHref = (_d = (_c = (_b = (_a = this.candidate) === 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 : '';
|
|
229
|
+
this.mrdMerge.emit({ winnerHref: this.currentHref, loserHref, patch });
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
connectedCallback() {
|
|
233
|
+
this.historyClickOutside = (e) => {
|
|
234
|
+
if (!this.el.contains(e.target))
|
|
235
|
+
this.openHistoryKey = null;
|
|
236
|
+
};
|
|
237
|
+
document.addEventListener('mousedown', this.historyClickOutside);
|
|
238
|
+
}
|
|
239
|
+
disconnectedCallback() {
|
|
240
|
+
if (this.historyClickOutside) {
|
|
241
|
+
document.removeEventListener('mousedown', this.historyClickOutside);
|
|
242
|
+
this.historyClickOutside = null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Stores search results, filtering out A itself regardless of what the
|
|
247
|
+
* host's search returns — A must never appear as a mergeable candidate.
|
|
248
|
+
*/
|
|
249
|
+
async setSearchResults(records) {
|
|
250
|
+
this.searchResults = (records !== null && records !== void 0 ? records : []).filter(r => { var _a, _b, _c; return ((_c = (_b = (_a = r === null || r === void 0 ? void 0 : r._links) === null || _a === void 0 ? void 0 : _a.self) === null || _b === void 0 ? void 0 : _b.href) !== null && _c !== void 0 ? _c : null) !== this.currentHref; });
|
|
251
|
+
this.isLoading = false;
|
|
252
|
+
}
|
|
253
|
+
async setLoading(loading) {
|
|
254
|
+
this.isLoading = loading;
|
|
255
|
+
}
|
|
256
|
+
render() {
|
|
257
|
+
var _a, _b;
|
|
258
|
+
let stepContent;
|
|
259
|
+
if (this.step === 'search') {
|
|
260
|
+
stepContent = (index.h(MergeSearchStep, { key: '4b79504f62f85274c9cd9681b6819c2318f4246a', locale: this.locale, searchQuery: this.searchQuery, searchResults: this.searchResults, isLoading: this.isLoading, onSearchInput: this.handleSearchInput, onSelect: this.handleSelect }));
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
const diff = diffFields(this.fields, this.currentRecord, this.candidate);
|
|
264
|
+
const bLabel = candidateLabel(this.candidate);
|
|
265
|
+
stepContent = this.step === 'compare' ? (index.h(MergeCompareStep, { locale: this.locale, currentLabel: this.currentLabel, candidateLabel: bLabel, conflicts: diff.conflicts, autoFilled: diff.autoFilled, recordA: this.currentRecord, recordB: (_a = this.candidate) !== null && _a !== void 0 ? _a : {}, resolutions: this.resolutions, openHistoryKey: this.openHistoryKey, onResolve: this.handleResolve, onToggleHistory: this.handleToggleHistory, onChangeCandidate: this.handleChangeCandidate, onContinue: this.handleContinue })) : (index.h(MergeConfirmStep, { locale: this.locale, candidateLabel: bLabel, conflicts: diff.conflicts, autoFilled: diff.autoFilled, resolutions: this.resolutions, recordB: (_b = this.candidate) !== null && _b !== void 0 ? _b : {}, openHistoryKey: this.openHistoryKey, onToggleHistory: this.handleToggleHistory, onBack: this.handleBack, onConfirm: this.handleConfirm }));
|
|
266
|
+
}
|
|
267
|
+
return (index.h(index.Host, { key: 'f3fc046fa728646e2ff686d2eae30024da3121e1' }, index.h("div", { key: '55bef786973d22dce49be021cfb78588b119139c', class: "mrd-merge-view" }, stepContent)));
|
|
268
|
+
}
|
|
269
|
+
get el() { return index.getElement(this); }
|
|
270
|
+
};
|
|
271
|
+
MrdMergeView.style = mrdMergeViewCss();
|
|
272
|
+
|
|
273
|
+
exports.mrd_merge_view = MrdMergeView;
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"components/form/mrd-field/mrd-field.js",
|
|
23
23
|
"components/form/mrd-form/mrd-form.js",
|
|
24
24
|
"components/layout/mrd-layout-section/mrd-layout-section.js",
|
|
25
|
+
"components/merge/mrd-merge-view/mrd-merge-view.js",
|
|
25
26
|
"components/table/mrd-table/mrd-table.js"
|
|
26
27
|
],
|
|
27
28
|
"mixins": [],
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Host, h } from "@stencil/core";
|
|
2
|
+
import DOMPurify from "dompurify";
|
|
2
3
|
import { FieldLabel, FieldError, controlClass, requiredError } from "../field-helpers";
|
|
3
4
|
export class MrdTextareaField {
|
|
4
5
|
constructor() {
|
|
@@ -40,14 +41,12 @@ export class MrdTextareaField {
|
|
|
40
41
|
quill.clipboard.dangerouslyPasteHTML(this.value);
|
|
41
42
|
}
|
|
42
43
|
quill.on('text-change', () => {
|
|
43
|
-
|
|
44
|
-
const html = (_b = (_a = this.editorEl.querySelector('.ql-editor')) === null || _a === void 0 ? void 0 : _a.innerHTML) !== null && _b !== void 0 ? _b : '';
|
|
44
|
+
const html = this.getSanitizedHtml();
|
|
45
45
|
this.mrdChange.emit({ name: this.name, value: html });
|
|
46
46
|
});
|
|
47
47
|
quill.on('selection-change', (range) => {
|
|
48
|
-
var _a, _b;
|
|
49
48
|
if (!range) {
|
|
50
|
-
const html =
|
|
49
|
+
const html = this.getSanitizedHtml();
|
|
51
50
|
this.error = requiredError(html, this.required, this.locale);
|
|
52
51
|
this.mrdBlur.emit({ name: this.name, value: html });
|
|
53
52
|
}
|
|
@@ -59,8 +58,13 @@ export class MrdTextareaField {
|
|
|
59
58
|
console.error('Failed to load Quill editor:', err);
|
|
60
59
|
}
|
|
61
60
|
}
|
|
61
|
+
getSanitizedHtml() {
|
|
62
|
+
var _a, _b;
|
|
63
|
+
const html = (_b = (_a = this.editorEl.querySelector('.ql-editor')) === null || _a === void 0 ? void 0 : _a.innerHTML) !== null && _b !== void 0 ? _b : '';
|
|
64
|
+
return DOMPurify.sanitize(html);
|
|
65
|
+
}
|
|
62
66
|
render() {
|
|
63
|
-
return (h(Host, { key: '
|
|
67
|
+
return (h(Host, { key: '708c1f8f14d2de4b5a274155d190fc19a304d7f0' }, h("div", { key: 'cf5c7a571e74cad08e0e320e91db4e22a3b2e080', class: "mrd-textarea-field" }, h(FieldLabel, { key: '4ced05f3914440f89dc5605901fc311e8ad54360', base: "mrd-textarea-field", label: this.label, required: this.required }), h("div", { key: '81147829bc575d05f3521bf3704edfdfe0d9b2cf', class: controlClass('mrd-textarea-field', !!this.error, 'container') }, h("div", { key: 'a9f42d86cab460d5e0cf6de87e0c0a0a1ca7d77a', class: "mrd-textarea-field__editor" })), h(FieldError, { key: '9f4b5271d0901e474ef6c0f1170b3ef718a844a9', base: "mrd-textarea-field", error: this.error }))));
|
|
64
68
|
}
|
|
65
69
|
static get is() { return "mrd-textarea-field"; }
|
|
66
70
|
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
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { h } from "@stencil/core";
|
|
2
|
+
import { CellRenderer } from "../../../utils/cell-renderer";
|
|
3
|
+
import { t } from "../../../utils/i18n";
|
|
4
|
+
import { formatDate } from "../../../utils/format";
|
|
5
|
+
import { candidateLabel } from "./merge-diff";
|
|
6
|
+
/** History-enabled fields carry `{ current, history: [...] }` — this is the legal record itself (e.g. past company names), not an audit log. */
|
|
7
|
+
function getHistory(item, record) {
|
|
8
|
+
var _a, _b;
|
|
9
|
+
if (!item.historyEnabled || !item.name)
|
|
10
|
+
return [];
|
|
11
|
+
return (_b = (_a = record === null || record === void 0 ? void 0 : record[item.name]) === null || _a === void 0 ? void 0 : _a.history) !== null && _b !== void 0 ? _b : [];
|
|
12
|
+
}
|
|
13
|
+
/** Small clock badge + popover listing past values for a history-enabled field — same UX as mrd-layout-section's field view. */
|
|
14
|
+
const HistoryBadge = ({ locale, history, isOpen, onToggle }) => {
|
|
15
|
+
if (history.length === 0)
|
|
16
|
+
return null;
|
|
17
|
+
const sorted = [...history].sort((a, b) => b.until.localeCompare(a.until));
|
|
18
|
+
return (h("span", { class: "mrd-merge-view__history-wrap" }, h("button", { type: "button", class: "mrd-merge-view__history-btn", title: t('history_badge_tooltip', locale), "aria-label": t('history_badge_tooltip', locale), onClick: (e) => { e.stopPropagation(); onToggle(); } }, h("svg", { class: "mrd-merge-view__history-icon", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true" }, h("path", { "fill-rule": "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm.75-13a.75.75 0 00-1.5 0v5c0 .207.085.394.22.53l2.5 2.5a.75.75 0 101.06-1.06L10.75 9.69V5z", "clip-rule": "evenodd" }))), isOpen && (h("div", { class: "mrd-merge-view__history-popover", role: "listbox" }, sorted.map((entry, i) => (h("div", { key: String(i), class: "mrd-merge-view__history-entry" }, entry.value, " (", t('history_until', locale), " ", formatDate(entry.until, locale), ")")))))));
|
|
19
|
+
};
|
|
20
|
+
export const MergeSearchStep = ({ locale, searchQuery, searchResults, isLoading, onSearchInput, onSelect }) => (h("div", { class: "mrd-merge-view__search-step" }, h("p", { class: "mrd-merge-view__hint" }, t('merge_search_hint', locale)), h("div", { class: "mrd-merge-view__search-wrapper" }, h("input", { class: "mrd-merge-view__search", type: "text", value: searchQuery, placeholder: t('search_placeholder', locale), onInput: (e) => onSearchInput(e.target.value) }), isLoading && h("span", { class: "mrd-merge-view__spinner", "aria-label": t('loading', locale) })), searchQuery.trim().length >= 2 && !isLoading && (h("div", { class: "mrd-merge-view__results" }, searchResults.length === 0 ? (h("div", { class: "mrd-merge-view__no-results" }, t('no_results', locale))) : (searchResults.map(record => {
|
|
21
|
+
var _a, _b, _c;
|
|
22
|
+
return (h("div", { key: (_c = (_b = (_a = record === null || record === void 0 ? void 0 : record._links) === null || _a === void 0 ? void 0 : _a.self) === null || _b === void 0 ? void 0 : _b.href) !== null && _c !== void 0 ? _c : candidateLabel(record), class: "mrd-merge-view__result-item" }, h("span", { class: "mrd-merge-view__result-label" }, candidateLabel(record)), h("button", { type: "button", class: "mrd-merge-view__btn mrd-merge-view__btn--secondary", onClick: () => onSelect(record) }, t('merge_select', locale))));
|
|
23
|
+
}))))));
|
|
24
|
+
export const MergeCompareStep = ({ locale, currentLabel, candidateLabel: bLabel, conflicts, autoFilled, recordA, recordB, resolutions, openHistoryKey, onResolve, onToggleHistory, onChangeCandidate, onContinue }) => (h("div", { class: "mrd-merge-view__compare-step" }, h("h3", { class: "mrd-merge-view__heading" }, t('merge_compare_heading', locale)), conflicts.length === 0 ? (h("p", { class: "mrd-merge-view__no-conflicts" }, t('merge_no_conflicts', locale))) : (h("table", { class: "mrd-merge-view__table" }, h("thead", null, h("tr", null, h("th", { class: "mrd-merge-view__header" }), h("th", { class: "mrd-merge-view__header" }, t('merge_column_a', locale), h("span", { class: "mrd-merge-view__header-name" }, currentLabel)), h("th", { class: "mrd-merge-view__header" }, t('merge_column_b', locale), h("span", { class: "mrd-merge-view__header-name" }, bLabel)))), h("tbody", null, conflicts.map(({ item }) => {
|
|
25
|
+
var _a, _b, _c;
|
|
26
|
+
const name = (_a = item.name) !== null && _a !== void 0 ? _a : '';
|
|
27
|
+
const resolution = (_b = resolutions[name]) !== null && _b !== void 0 ? _b : 'A';
|
|
28
|
+
const historyA = getHistory(item, recordA);
|
|
29
|
+
const historyB = getHistory(item, recordB);
|
|
30
|
+
const keyA = `${name}:A`;
|
|
31
|
+
const keyB = `${name}:B`;
|
|
32
|
+
return (h("tr", { key: name, class: "mrd-merge-view__row" }, h("td", { class: "mrd-merge-view__cell mrd-merge-view__cell--label" }, (_c = item.label) !== null && _c !== void 0 ? _c : name), h("td", { class: "mrd-merge-view__cell" }, h("label", { class: "mrd-merge-view__option" }, h("input", { type: "radio", name: `merge-${name}`, checked: resolution === 'A', onChange: () => onResolve(name, 'A') }), h("span", null, CellRenderer.render(item, recordA, locale) || '—')), h(HistoryBadge, { locale: locale, history: historyA, isOpen: openHistoryKey === keyA, onToggle: () => onToggleHistory(openHistoryKey === keyA ? null : keyA) })), h("td", { class: "mrd-merge-view__cell" }, h("label", { class: "mrd-merge-view__option" }, h("input", { type: "radio", name: `merge-${name}`, checked: resolution === 'B', onChange: () => onResolve(name, 'B') }), h("span", null, CellRenderer.render(item, recordB, locale) || '—')), h(HistoryBadge, { locale: locale, history: historyB, isOpen: openHistoryKey === keyB, onToggle: () => onToggleHistory(openHistoryKey === keyB ? null : keyB) }))));
|
|
33
|
+
})))), autoFilled.length > 0 && (h("div", { class: "mrd-merge-view__auto-note" }, h("p", null, t('merge_auto_heading', locale)), h("ul", null, autoFilled.map(({ item }) => { var _a; return h("li", { key: item.name }, (_a = item.label) !== null && _a !== void 0 ? _a : item.name); })))), h("div", { class: "mrd-merge-view__actions" }, h("button", { type: "button", class: "mrd-merge-view__btn mrd-merge-view__btn--secondary", onClick: onChangeCandidate }, t('merge_back', locale)), h("button", { type: "button", class: "mrd-merge-view__btn mrd-merge-view__btn--primary", onClick: onContinue }, t('merge_continue', locale)))));
|
|
34
|
+
export const MergeConfirmStep = ({ locale, candidateLabel: bLabel, conflicts, autoFilled, resolutions, recordB, openHistoryKey, onToggleHistory, onBack, onConfirm }) => {
|
|
35
|
+
const changed = [
|
|
36
|
+
...autoFilled,
|
|
37
|
+
...conflicts.filter(({ item }) => { var _a; return resolutions[(_a = item.name) !== null && _a !== void 0 ? _a : ''] === 'B'; }),
|
|
38
|
+
];
|
|
39
|
+
return (h("div", { class: "mrd-merge-view__confirm-step" }, h("h3", { class: "mrd-merge-view__heading" }, t('merge_confirm_heading', locale)), changed.length === 0 ? (h("p", { class: "mrd-merge-view__no-conflicts" }, t('merge_no_conflicts', locale))) : (h("ul", { class: "mrd-merge-view__summary-list" }, changed.map(({ item }) => {
|
|
40
|
+
var _a;
|
|
41
|
+
const key = `${item.name}:confirm`;
|
|
42
|
+
return (h("li", { key: item.name }, (_a = item.label) !== null && _a !== void 0 ? _a : item.name, ": ", CellRenderer.render(item, recordB, locale) || '—', h(HistoryBadge, { locale: locale, history: getHistory(item, recordB), isOpen: openHistoryKey === key, onToggle: () => onToggleHistory(openHistoryKey === key ? null : key) })));
|
|
43
|
+
}))), h("p", { class: "mrd-merge-view__warning" }, t('merge_confirm_warning', locale), " (", bLabel, ")"), h("div", { class: "mrd-merge-view__actions" }, h("button", { type: "button", class: "mrd-merge-view__btn mrd-merge-view__btn--secondary", onClick: onBack }, t('merge_back', locale)), h("button", { type: "button", class: "mrd-merge-view__btn mrd-merge-view__btn--danger", onClick: onConfirm }, t('merge_confirm_button', locale)))));
|
|
44
|
+
};
|