diff --git a/static/app.js b/static/app.js index ceca8cc..a860a8c 100644 --- a/static/app.js +++ b/static/app.js @@ -1165,36 +1165,116 @@ if (downloadExcelTemplateButton) { /* final legal profile UI normalization */ +function legalProfileDisplayName(label) { + const value = String(label || "").trim(); + if (value === "Legal Profile" || value === "legal_profile") return "Legal"; + return value; +} function normalizeLegalUi() { - const profileSelect = document.getElementById("documentTypeSelect"); - const excelSelect = document.getElementById("excelMapSelect"); - const templateSelect = document.getElementById("legacyTemplateSelect"); - - for (const select of [profileSelect, excelSelect, templateSelect]) { - if (select) { - select.classList.add("same-dropdown-style"); - } + const selectedLabel = document.getElementById("selectedDocumentLabel"); + if (selectedLabel) { + selectedLabel.textContent = legalProfileDisplayName(selectedLabel.textContent); } - if (profileSelect) { - for (const option of [...profileSelect.options]) { - if (option.value === "legal_profile" || option.textContent.trim() === "Legal Profile") { - option.textContent = "Legal"; - } - } + const description = document.getElementById("documentDescription"); + if (description && currentDocumentType?.id === "legal_profile") { + description.textContent = "Consumer Debt Defense Legal Profile"; + description.classList.add("legal-profile-caption"); } - const longText = "Consumer debt defense legal profile based on the legacy app form fields. Additional template fields are calculated at generation time."; - for (const el of [...document.querySelectorAll("p, div, span")]) { - const value = el.textContent.trim(); - if (value === longText || value === "Consumer Debt Defense Legal Profile") { - el.textContent = "Consumer Debt Defense Legal Profile"; - el.classList.add("legal-profile-caption"); - } + setupSelectPicker("excelMapSelect", "Excel Template / Map"); + setupSelectPicker("legacyTemplateSelect", "Document Template"); + + renderUploadedTemplateDeleteUi().catch(() => {}); +} + +function setupSelectPicker(selectId, fallbackLabel) { + const select = document.getElementById(selectId); + if (!select) return; + + select.classList.add("native-select-hidden"); + + let picker = document.querySelector(`[data-select-picker-for="${selectId}"]`); + if (!picker) { + picker = document.createElement("div"); + picker.className = "document-picker dropdown-picker select-picker"; + picker.dataset.selectPickerFor = selectId; + picker.innerHTML = ` + +
+ `; + select.insertAdjacentElement("afterend", picker); + + const toggle = picker.querySelector(".select-picker-toggle"); + toggle.addEventListener("click", event => { + event.preventDefault(); + event.stopPropagation(); + document.querySelectorAll(".select-picker.open").forEach(openPicker => { + if (openPicker !== picker) openPicker.classList.remove("open"); + }); + picker.classList.toggle("open"); + }); + } + + const menu = picker.querySelector(".select-picker-menu"); + const label = picker.querySelector(".select-picker-label"); + + menu.innerHTML = ""; + + const options = [...select.options].filter(option => option.value !== ""); + if (!options.length) { + label.textContent = fallbackLabel; + return; + } + + const selectedOption = select.selectedOptions?.[0] || options[0]; + label.textContent = selectedOption?.textContent?.trim() || fallbackLabel; + + for (const option of options) { + const button = document.createElement("button"); + button.type = "button"; + button.className = "picker-item"; + if (option.value === select.value) button.classList.add("active"); + button.textContent = option.textContent.trim(); + button.addEventListener("click", event => { + event.preventDefault(); + select.value = option.value; + select.dispatchEvent(new Event("change", { bubbles: true })); + picker.classList.remove("open"); + setupSelectPicker(selectId, fallbackLabel); + }); + menu.appendChild(button); } } +document.addEventListener("click", () => { + document.querySelectorAll(".select-picker.open").forEach(picker => picker.classList.remove("open")); +}); + +const originalSetActivePickerForLegalUi = typeof setActivePicker === "function" ? setActivePicker : null; +if (originalSetActivePickerForLegalUi) { + setActivePicker = function(kind, id, label) { + originalSetActivePickerForLegalUi(kind, id, legalProfileDisplayName(label)); + normalizeLegalUi(); + }; +} + +const originalRenderDocumentTypeForLegalUi = renderDocumentType; +renderDocumentType = function(documentType) { + originalRenderDocumentTypeForLegalUi(documentType); + normalizeLegalUi(); +}; + +const originalRenderTemplateSelectorForLegalUi = renderTemplateSelector; +renderTemplateSelector = function(documentType) { + originalRenderTemplateSelectorForLegalUi(documentType); + normalizeLegalUi(); +}; + async function fetchUploadedTemplatesForDeleteUi() { if (!currentDocumentType?.id) return []; const response = await fetch(`/api/doc-generator/uploaded-templates/${encodeURIComponent(currentDocumentType.id)}`); @@ -1206,10 +1286,8 @@ async function fetchUploadedTemplatesForDeleteUi() { function ensureUploadedTemplateManager() { const row = document.getElementById("legacyTemplateRow"); if (!row) return null; - let manager = document.getElementById("uploadedTemplatesManager"); if (manager) return manager; - manager = document.createElement("div"); manager.id = "uploadedTemplatesManager"; manager.className = "uploaded-template-manager"; @@ -1243,68 +1321,51 @@ async function renderUploadedTemplateDeleteUi() { manager.style.display = ""; list.innerHTML = ""; - for (const item of uploaded) { - const rawId = item.id || item.filename || ""; - const filename = rawId.replace(/^uploaded:/, ""); + for (const template of uploaded) { + const id = template.id || template.filename; + const name = template.label || template.filename || id; const row = document.createElement("div"); row.className = "uploaded-template-row"; - const name = document.createElement("span"); - name.className = "uploaded-template-name"; - name.textContent = item.label || filename; + const nameSpan = document.createElement("span"); + nameSpan.textContent = name; - const button = document.createElement("button"); - button.type = "button"; - button.className = "small-delete-button"; - button.textContent = "Delete"; + const deleteButton = document.createElement("button"); + deleteButton.type = "button"; + deleteButton.className = "delete-json-button"; + deleteButton.textContent = "x"; + deleteButton.title = `Delete ${name}`; - button.addEventListener("click", async () => { - if (!confirm(`Delete uploaded template "${filename}"?`)) return; + deleteButton.addEventListener("click", async event => { + event.preventDefault(); + event.stopPropagation(); - const response = await fetch( - `/api/doc-generator/uploaded-template/${encodeURIComponent(currentDocumentType.id)}/${encodeURIComponent(filename)}`, - {method: "DELETE"} - ); + if (!confirm(`Delete uploaded template?\n\n${name}`)) return; + + const deleteId = String(id).replace(/^uploaded:/, ""); + const response = await fetch(`/api/doc-generator/uploaded-template/${encodeURIComponent(currentDocumentType.id)}/${encodeURIComponent(deleteId)}`, { + method: "DELETE" + }); if (!response.ok) { - const json = await response.json().catch(() => ({})); - setStatus(`Could not delete template: ${json.detail || response.statusText}`); + setStatus("Unable to delete uploaded template."); return; } setStatus("Uploaded template deleted."); - - if (typeof renderTemplateSelector === "function") { - await renderTemplateSelector(currentDocumentType); - } - - await renderUploadedTemplateDeleteUi(); + await refreshUploadedTemplatesForCurrentProfile(); + normalizeLegalUi(); }); - row.appendChild(name); - row.appendChild(button); + row.appendChild(nameSpan); + row.appendChild(deleteButton); list.appendChild(row); } } -function runLegalUiCleanup() { +setInterval(() => { normalizeLegalUi(); - renderUploadedTemplateDeleteUi(); -} +}, 1000); -document.addEventListener("DOMContentLoaded", () => { - runLegalUiCleanup(); - setTimeout(runLegalUiCleanup, 300); - setTimeout(runLegalUiCleanup, 1000); -}); - -const finalLegalUiObserver = new MutationObserver(() => { - clearTimeout(window.__legalUiTimer); - window.__legalUiTimer = setTimeout(runLegalUiCleanup, 100); -}); - -finalLegalUiObserver.observe(document.body, { - childList: true, - subtree: true -}); +normalizeLegalUi(); diff --git a/static/index.html b/static/index.html index 603141b..6122388 100644 --- a/static/index.html +++ b/static/index.html @@ -2,7 +2,7 @@ Utility App - +
@@ -152,6 +152,6 @@
- + diff --git a/static/styles.css b/static/styles.css index 8e0ba14..0bb3ad4 100644 --- a/static/styles.css +++ b/static/styles.css @@ -907,3 +907,38 @@ label.tool-action-button { grid-template-columns: 1fr !important; } } + +/* Legal profile dropdown normalization */ +#documentDescription.legal-profile-caption, +.legal-profile-caption { + margin-top: 0.35rem; + color: #8a8f98; + font-size: 0.88rem; + font-weight: 400; +} + +.native-select-hidden { + display: none !important; +} + +.select-picker { + width: 100%; + margin-top: 0.35rem; +} + +.select-picker .document-picker-toggle { + width: 100%; +} + +.select-picker-menu { + max-height: 18rem; + overflow-y: auto; +} + +.uploaded-template-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + margin-top: 0.4rem; +}