mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2026-02-04 02:25:30 +00:00
+FEATURE: double entry info
This commit is contained in:
96
views/transform/duplicate-warning.js
Normal file
96
views/transform/duplicate-warning.js
Normal file
@@ -0,0 +1,96 @@
|
||||
const DEBOUNCE_DELAY_MS = 100;
|
||||
|
||||
export class DuplicateWarningChecker extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this._fields = null;
|
||||
this._boundHandlers = new Map();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
// Find all fields marked for duplicate checking
|
||||
this._fields = document.querySelectorAll("[data-duplicate-check]");
|
||||
|
||||
this._fields.forEach((field) => {
|
||||
const handler = this._createHandler(field);
|
||||
this._boundHandlers.set(field, handler);
|
||||
field.addEventListener("input", handler);
|
||||
});
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this._boundHandlers.forEach((handler, field) => {
|
||||
field.removeEventListener("input", handler);
|
||||
});
|
||||
this._boundHandlers.clear();
|
||||
}
|
||||
|
||||
_createHandler(field) {
|
||||
let timeout = null;
|
||||
return (event) => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
timeout = setTimeout(() => {
|
||||
this._checkDuplicates(field);
|
||||
}, DEBOUNCE_DELAY_MS);
|
||||
};
|
||||
}
|
||||
|
||||
async _checkDuplicates(field) {
|
||||
const value = field.value.trim();
|
||||
const endpoint = field.getAttribute("data-duplicate-endpoint");
|
||||
const resultKey = field.getAttribute("data-duplicate-result-key");
|
||||
const currentId = field.getAttribute("data-duplicate-current-id") || "";
|
||||
const warningEl = document.querySelector(`[data-duplicate-warning-for="${field.id}"]`);
|
||||
|
||||
if (!warningEl || !endpoint || !resultKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide warning if field is empty
|
||||
if (value === "") {
|
||||
warningEl.classList.add("hidden");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch duplicates
|
||||
try {
|
||||
const url = new URL(endpoint, window.location.origin);
|
||||
url.searchParams.set("q", value);
|
||||
url.searchParams.set("limit", "100"); // Get all to filter
|
||||
|
||||
const response = await fetch(url.toString());
|
||||
if (!response.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const results = data[resultKey] || [];
|
||||
|
||||
// Filter out current item if editing
|
||||
let filtered = results;
|
||||
if (currentId) {
|
||||
filtered = results.filter((item) => item.id !== currentId);
|
||||
}
|
||||
|
||||
// Filter for exact matches only (case-insensitive)
|
||||
const exactMatches = filtered.filter((item) => item.name && item.name.toLowerCase() === value.toLowerCase());
|
||||
|
||||
// Show or hide warning
|
||||
if (exactMatches.length > 0) {
|
||||
const countEl = warningEl.querySelector("[data-duplicate-count]");
|
||||
if (countEl) {
|
||||
const plural = exactMatches.length === 1 ? "" : "e";
|
||||
countEl.textContent = `Der Name ist bereits vorhanden (${exactMatches.length} Treffer${plural})`;
|
||||
}
|
||||
warningEl.classList.remove("hidden");
|
||||
} else {
|
||||
warningEl.classList.add("hidden");
|
||||
}
|
||||
} catch (err) {
|
||||
// Silently fail - don't show errors to user
|
||||
console.error("Duplicate check failed:", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,10 @@
|
||||
disabled:opacity-50 py-1 px-3;
|
||||
}
|
||||
|
||||
.dbform .duplicate-warning {
|
||||
@apply text-sm text-sky-600 mt-0.5 pl-2.5 pb-1.5 flex items-center gap-1;
|
||||
}
|
||||
|
||||
/* Status select color coding */
|
||||
.status-select[data-status="Edited"] {
|
||||
@apply bg-green-100 text-green-900;
|
||||
|
||||
@@ -27,6 +27,7 @@ import { AlmanachEditPage } from "./almanach-edit.js";
|
||||
import { RelationsEditor } from "./relations-editor.js";
|
||||
import { EditPage } from "./edit-page.js";
|
||||
import { FabMenu } from "./fab-menu.js";
|
||||
import { DuplicateWarningChecker } from "./duplicate-warning.js";
|
||||
|
||||
const FILTER_LIST_ELEMENT = "filter-list";
|
||||
const FAB_MENU_ELEMENT = "fab-menu";
|
||||
@@ -47,6 +48,7 @@ const ITEMS_EDITOR_ELEMENT = "items-editor";
|
||||
const ALMANACH_EDIT_PAGE_ELEMENT = "almanach-edit-page";
|
||||
const RELATIONS_EDITOR_ELEMENT = "relations-editor";
|
||||
const EDIT_PAGE_ELEMENT = "edit-page";
|
||||
const DUPLICATE_WARNING_ELEMENT = "duplicate-warning-checker";
|
||||
|
||||
customElements.define(INT_LINK_ELEMENT, IntLink);
|
||||
customElements.define(ABBREV_TOOLTIPS_ELEMENT, AbbreviationTooltips);
|
||||
@@ -67,6 +69,7 @@ customElements.define(ALMANACH_EDIT_PAGE_ELEMENT, AlmanachEditPage);
|
||||
customElements.define(RELATIONS_EDITOR_ELEMENT, RelationsEditor);
|
||||
customElements.define(EDIT_PAGE_ELEMENT, EditPage);
|
||||
customElements.define(FAB_MENU_ELEMENT, FabMenu);
|
||||
customElements.define(DUPLICATE_WARNING_ELEMENT, DuplicateWarningChecker);
|
||||
|
||||
function PathPlusQuery() {
|
||||
const path = window.location.pathname;
|
||||
|
||||
Reference in New Issue
Block a user