Refine legal profile dropdown UI
This commit is contained in:
parent
adcba89350
commit
deea989f77
195
static/app.js
195
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 = `
|
||||
<button class="document-picker-toggle select-picker-toggle" type="button">
|
||||
<span class="select-picker-label">${fallbackLabel}</span>
|
||||
<span class="dropdown-arrow">▼</span>
|
||||
</button>
|
||||
<div class="document-picker-menu select-picker-menu"></div>
|
||||
`;
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Utility App</title>
|
||||
<link rel="stylesheet" href="/static/styles.css?v=legalui3">
|
||||
<link rel="stylesheet" href="/static/styles.css?v=legalui4">
|
||||
</head>
|
||||
<body class="app-loading">
|
||||
<header class="app-header">
|
||||
|
|
@ -152,6 +152,6 @@
|
|||
<div id="status"></div>
|
||||
</main>
|
||||
|
||||
<script src="/static/app.js?v=legalui3"></script>
|
||||
<script src="/static/app.js?v=legalui4"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue