Files
pocketbase/ui/src/collections/indexUpsertModal.js
2026-04-18 16:50:39 +03:00

274 lines
9.3 KiB
JavaScript

window.app = window.app || {};
window.app.modals = window.app.modals || {};
window.app.modals.openIndexUpsert = function(
collection,
index = "",
settings = {
onsave: () => {},
ondelete: () => {},
},
) {
const modal = indexUpsertModal(collection, index, settings);
if (!modal) {
return;
}
document.body.appendChild(modal);
app.modals.open(modal);
};
function indexUpsertModal(collection, index = "", settings = {}) {
if (!collection) {
console.warn("[indexUpsertModal] missing required collection argument");
return;
}
let modal;
const uniqueId = app.utils.randomString();
const data = store({
originalIndex: "",
index: "",
get isNew() {
return data.originalIndex == "";
},
get indexParts() {
return app.utils.parseIndex(data.index);
},
get lowerCasedIndexColumnNames() {
return data.indexParts.columns.map((c) => c.name.toLowerCase());
},
get canSave() {
return data.lowerCasedIndexColumnNames.length > 0;
},
});
const presetColumns = collection?.fields?.filter((f) => !f["@toDelete"] && f.name != "id")?.map((f) => f.name)
|| [];
function loadIndex(index) {
data.originalIndex = index || "";
if (!index) {
const parsed = app.utils.parseIndex("");
parsed.tableName = collection?.name || "";
index = app.utils.buildIndex(parsed);
}
data.index = index;
}
function saveIndex() {
if (!collection || !data.canSave) {
console.warn("[saveIndex] no collection or invalid save state:", collection, data.canSave);
return;
}
collection.indexes = collection.indexes || [];
// search for existing
const pos = collection.indexes.findIndex((index) => index == data.originalIndex);
if (pos >= 0) {
// replace
collection.indexes[pos] = data.index;
app.utils.deleteByPath(app.store.errors, "indexes." + pos);
} else {
// push missing
collection.indexes.push(data.index);
}
if (typeof settings?.onsave == "function") {
settings.onsave({
collection: collection,
index: data.index,
oldIndex: data.originalIndex,
});
}
clearIndexError();
app.modals.close(modal);
}
function deleteIndex() {
if (!collection || !data.originalIndex) {
console.warn("[deleteIndex] no collection or index:", collection, data.originalIndex);
return;
}
const pos = collection.indexes?.findIndex((index) => index == data.originalIndex);
if (pos == -1) {
console.warn("[deleteIndex] missing index:", data.originalIndex);
return;
}
collection.indexes.splice(pos, 1);
app.utils.deleteByPath(app.store.errors, "indexes." + pos);
if (typeof settings?.ondelete == "function") {
settings.ondelete({
collection: collection,
position: pos,
index: data.originalIndex,
});
}
clearIndexError();
app.modals.close(modal);
}
function toggleColumn(column) {
const clone = JSON.parse(JSON.stringify(data.indexParts));
clone.tableName = collection?.name || "";
const colLowerCased = column.toLowerCase();
const i = clone.columns.findIndex((c) => c.name.toLowerCase() == colLowerCased);
if (i >= 0) {
clone.columns.splice(i, 1);
} else {
app.utils.pushUnique(clone.columns, { name: column });
}
data.index = app.utils.buildIndex(clone);
clearIndexError();
}
function clearIndexError() {
if (app.store.errors?.indexes) {
const pos = collection.indexes.findIndex((idx) => idx == data.originalIndex);
app.utils.deleteByPath(app.store.errors, "indexes." + pos);
}
}
modal = t.div(
{
className: "modal popup index-upsert-modal",
onbeforeopen: () => {
loadIndex(index);
},
onafteropen: () => {
// retrigger indexes error (if any)
if (app.store.errors?.indexes) {
app.store.errors.indexes = JSON.parse(JSON.stringify(app.store.errors.indexes));
}
},
onafterclose: (el) => {
el?.remove();
},
},
t.header(
{ className: "modal-header" },
t.h6(
{ className: "modal-title" },
t.span({ className: "txt" }, () => (data.isNew ? "Create index" : "Update index")),
),
),
t.div(
{ className: "modal-content" },
t.form(
{
id: uniqueId + "form",
className: "grid sm index-upsert-form",
onsubmit: (e) => {
e.preventDefault();
saveIndex();
},
},
t.div(
{ className: "col-12" },
t.div(
{ className: "field" },
t.input({
type: "checkbox",
className: "switch",
id: uniqueId + "checkbox_unique",
checked: () => data.indexParts.unique,
onchange: (e) => {
const newIndexParts = JSON.parse(JSON.stringify(data.indexParts));
newIndexParts.unique = e.target.checked;
newIndexParts.tableName = newIndexParts.tableName || collection?.name || "";
data.index = app.utils.buildIndex(newIndexParts);
},
}),
t.label({ htmlFor: uniqueId + "checkbox_unique" }, "Unique"),
),
),
t.div(
{ className: "col-12" },
t.div(
{ className: "field" },
app.components.codeEditor({
required: true,
className: "collection-index-input pre-wrap",
name: () => "indexes." + collection.indexes?.findIndex((idx) => idx == data.originalIndex),
placeholder: () => `e.g. CREATE INDEX idx_test on ${collection?.name || "X"} (created)`,
value: () => data.index,
oninput: (val) => (data.index = val),
}),
),
t.div(
{ hidden: () => !presetColumns.length, className: "field-help m-t-sm" },
t.div(
{ className: "flex flex-wrap gap-5" },
t.span({ className: "txt", textContent: "Presets:" }),
() => {
return presetColumns?.map((col) => {
const isSelected = data.lowerCasedIndexColumnNames.includes(col.toLowerCase());
return t.button({
type: "button",
textContent: col,
className: () => `label handle ${isSelected ? "success" : ""}`,
onclick: () => toggleColumn(col),
});
});
},
),
),
),
),
),
t.footer(
{ className: "modal-footer gap-base" },
t.button(
{
type: "button",
className: "btn transparent m-r-auto",
onclick: () => app.modals.close(modal),
},
t.span({ className: "txt" }, "Close"),
),
t.button(
{
hidden: () => data.isNew,
type: "button",
className: () => "btn sm circle transparent secondary",
ariaDescription: app.attrs.tooltip("Delete index", "left"),
onclick: () => {
app.modals.confirm(
"Do you really want to remove the selected index from the collection?",
deleteIndex,
);
},
},
t.i({ className: "ri-delete-bin-7-line" }),
),
t.button(
{
"type": "submit",
"html-form": uniqueId + "form",
"disabled": () => !data.canSave,
"className": () => "btn expanded",
},
t.span({ className: "txt" }, "Set index"),
),
),
);
return modal;
}