Files
pocketbase/ui/src/settings/sync/collectionsDiffTable.js
2026-04-18 16:50:39 +03:00

320 lines
12 KiB
JavaScript

export function collectionsDiffTable(propsArg = {}) {
const props = store({
rid: undefined,
collectionA: null,
collectionB: null,
deleteMissing: false,
className: "",
});
const watchers = app.utils.extendStore(props, propsArg);
const data = store({
hasAnyChange: false,
get isDeleteDiff() {
return !props.collectionB?.id && !props.collectionB?.name;
},
get isCreateDiff() {
return !data.isDeleteDiff && !props.collectionA?.id;
},
get hasAnyChange() {
return app.utils.hasCollectionChanges(props.collectionA, props.collectionB, props.deleteMissing);
},
get fieldsListA() {
return Array.isArray(props.collectionA?.fields) ? props.collectionA?.fields : [];
},
get fieldsListB() {
let fieldsB = Array.isArray(props.collectionB?.fields) ? props.collectionB?.fields : [];
if (!props.deleteMissing) {
fieldsB = fieldsB.concat(
props.collectionA?.fields?.filter((a) => {
return !fieldsB.find((b) => a.id == b.id);
}) || [],
);
}
return fieldsB;
},
get mainModelProps() {
return app.utils
.mergeUnique(Object.keys(props.collectionA || {}), Object.keys(props.collectionB || {}))
.filter((key) => {
return !["fields", "created", "updated"].includes(key);
});
},
get removedFields() {
return data.fieldsListA.filter((a) => {
return !data.fieldsListB.find((b) => a.id == b.id);
});
},
get sharedFields() {
return data.fieldsListB.filter((b) => {
return data.fieldsListA.find((a) => a.id == b.id);
});
},
get addedFields() {
return data.fieldsListB.filter((b) => {
return !data.fieldsListA.find((a) => a.id == b.id);
});
},
});
function stringify(value) {
if (typeof value == "undefined") {
return "";
}
return app.utils.isObject(value) ? JSON.stringify(value, null, 4) : "" + value;
}
function isDifferent(valA, valB) {
if (valA === valB) {
return false; // direct match
}
return JSON.stringify(valA) != JSON.stringify(valB);
}
function getFieldById(fields, id) {
return (fields || []).find((f) => f.id == id);
}
return t.div(
{
rid: props.rid,
pbEvent: "collectionsDiffTableWrapper",
className: () => `collections-diff-table-wrapper ${props.className}`,
onunmount: () => {
watchers.forEach((w) => w?.unwatch());
},
},
t.div(
{ className: "collections-diff-table-title" },
() => {
if (!props.collectionA?.id) {
return [
t.span({
className: "label import-change-label success",
textContent: "Added",
}),
t.strong({ textContent: () => props.collectionB?.name }),
];
}
if (!props.collectionB?.id) {
return [
t.span({
className: "label import-change-label danger",
textContent: "Deleted",
}),
t.strong({ textContent: () => props.collectionA?.name }),
];
}
return [
t.span({
hidden: () => !data.hasAnyChange,
className: "label import-change-label warning",
textContent: "Changed",
}),
t.div(
{ className: "inline-flex gap-5" },
() => {
if (props.collectionA?.name == props.collectionB?.name) {
return;
}
return [
t.strong({
className: "txt-strikethrough txt-hint",
textContent: props.collectionA?.name,
}),
t.i({
className: "ri-arrow-right-line txt-sm",
}),
];
},
t.strong({ textContent: () => props.collectionB?.name }),
),
];
},
),
t.table(
{ className: "collections-diff-table" },
t.thead(
null,
t.tr(
null,
t.th({ className: "min-width" }, "Props"),
t.th({ width: "40%" }, "Old"),
t.th({ width: "40%" }, "New"),
),
),
t.tbody(
null,
() => {
return data.mainModelProps.map((p) => {
const isDiff = isDifferent(props.collectionA?.[p], props.collectionB?.[p]);
return t.tr(
{ className: isDiff ? "txt-primary" : "" },
t.td({ className: "min-width" }, p),
t.td(
{
className: () => {
if (data.isCreateDiff) {
return "changed-non-col";
}
if (isDiff) {
return "changed-old-col";
}
return "";
},
},
t.pre({ className: "txt diff-value" }, stringify(props.collectionA?.[p])),
),
t.td(
{
className: () => {
if (data.isDeleteDiff) {
return "changed-non-col";
}
if (isDiff) {
return "changed-new-col";
}
return "";
},
},
t.pre({ className: "txt diff-value" }, stringify(props.collectionB?.[p])),
),
);
});
},
() => {
if (!props.deleteMissing && !data.isDeleteDiff) {
return;
}
const rows = [];
for (let field of data.removedFields) {
rows.push(
t.tr(
null,
t.th(
{ className: "min-width", colSpan: 3 },
t.span({ className: "txt" }, "field: ", field.name),
t.span(
{ className: "label danger m-l-5" },
"Deleted - ",
t.small(null, `All stored data related to '${field.name}' will be deleted!`),
),
),
),
);
for (let key in field) {
const val = field[key];
rows.push(
t.tr(
null,
t.td({ className: "min-width field-key-col" }, key),
t.td(
{ className: "changed-old-col" },
t.pre({ className: "txt" }, stringify(val)),
),
t.td({ className: "changed-none-col" }),
),
);
}
}
return rows;
},
() => {
const rows = [];
for (let field of data.sharedFields) {
const fieldA = getFieldById(data.fieldsListA, field.id);
const fieldB = getFieldById(data.fieldsListB, field.id);
const hasFieldChanged = isDifferent(fieldA, fieldB);
rows.push(
t.tr(
null,
t.th(
{ className: "min-width", colSpan: 3 },
t.span({ className: "txt" }, "field: ", field.name),
t.span({
className: `label warning m-l-5 ${!hasFieldChanged ? "hidden" : ""}`,
textContent: "Changed",
}),
),
),
);
for (let key in field) {
const newValue = field[key];
const isDiff = isDifferent(fieldA?.[key], newValue);
rows.push(
t.tr(
{ className: isDiff ? "txt-primary" : "" },
t.td({ className: "min-width field-key-col" }, key),
t.td(
{ className: isDiff ? "changed-old-col" : "" },
t.pre({ className: "txt" }, stringify(fieldA?.[key])),
),
t.td(
{ className: isDiff ? "changed-new-col" : "" },
t.pre({ className: "txt" }, stringify(newValue)),
),
),
);
}
}
return rows;
},
() => {
const rows = [];
for (let field of data.addedFields) {
rows.push(
t.tr(
null,
t.th(
{ className: "min-width", colSpan: 3 },
t.span({ className: "txt" }, "field: ", field.name),
t.span({ className: "label success m-l-5" }, "Added"),
),
),
);
for (let key in field) {
const val = field[key];
rows.push(
t.tr(
{ className: "txt-primary" },
t.td({ className: "min-width field-key-col" }, key),
t.td({ className: "changed-none-col" }),
t.td(
{ className: "changed-new-col" },
t.pre({ className: "txt" }, stringify(val)),
),
),
);
}
}
return rows;
},
),
),
);
}