Refine legal profile dropdown UI

This commit is contained in:
Sean McElwain 2026-06-11 08:04:41 -05:00
parent adcba89350
commit deea989f77
3 changed files with 165 additions and 69 deletions

View File

@ -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();

View File

@ -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>

View File

@ -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;
}