feat: polish mobile detail tabs and persist active tab
This commit is contained in:
parent
9387e4166e
commit
0ef1392b19
|
|
@ -5277,3 +5277,93 @@ table {
|
|||
}
|
||||
}
|
||||
/* ===== end extra json matches OCR editor with line numbers ===== */
|
||||
|
||||
|
||||
/* ===== line items mobile final sizing ===== */
|
||||
@media (max-width: 900px) {
|
||||
.tab-panel[data-panel="line-items"] table {
|
||||
min-width: 68rem !important;
|
||||
}
|
||||
|
||||
/* # */
|
||||
.tab-panel[data-panel="line-items"] table th:nth-child(1),
|
||||
.tab-panel[data-panel="line-items"] table td:nth-child(1) {
|
||||
min-width: 2.4rem !important;
|
||||
width: 2.4rem !important;
|
||||
}
|
||||
|
||||
/* Date */
|
||||
.tab-panel[data-panel="line-items"] table th:nth-child(2),
|
||||
.tab-panel[data-panel="line-items"] table td:nth-child(2) {
|
||||
min-width: 6.2rem !important;
|
||||
width: 6.2rem !important;
|
||||
}
|
||||
|
||||
/* Description - larger */
|
||||
.tab-panel[data-panel="line-items"] table th:nth-child(3),
|
||||
.tab-panel[data-panel="line-items"] table td:nth-child(3) {
|
||||
min-width: 13rem !important;
|
||||
width: 13rem !important;
|
||||
}
|
||||
|
||||
/* Qty */
|
||||
.tab-panel[data-panel="line-items"] table th:nth-child(4),
|
||||
.tab-panel[data-panel="line-items"] table td:nth-child(4) {
|
||||
min-width: 3rem !important;
|
||||
width: 3rem !important;
|
||||
}
|
||||
|
||||
/* Unit */
|
||||
.tab-panel[data-panel="line-items"] table th:nth-child(5),
|
||||
.tab-panel[data-panel="line-items"] table td:nth-child(5) {
|
||||
min-width: 4rem !important;
|
||||
width: 4rem !important;
|
||||
}
|
||||
|
||||
/* Total - larger */
|
||||
.tab-panel[data-panel="line-items"] table th:nth-child(6),
|
||||
.tab-panel[data-panel="line-items"] table td:nth-child(6) {
|
||||
min-width: 6rem !important;
|
||||
width: 6rem !important;
|
||||
}
|
||||
|
||||
/* Tax - smaller */
|
||||
.tab-panel[data-panel="line-items"] table th:nth-child(7),
|
||||
.tab-panel[data-panel="line-items"] table td:nth-child(7) {
|
||||
min-width: 2.8rem !important;
|
||||
width: 2.8rem !important;
|
||||
}
|
||||
|
||||
/* Category */
|
||||
.tab-panel[data-panel="line-items"] table th:nth-child(8),
|
||||
.tab-panel[data-panel="line-items"] table td:nth-child(8) {
|
||||
min-width: 7rem !important;
|
||||
width: 7rem !important;
|
||||
}
|
||||
|
||||
/* Notes */
|
||||
.tab-panel[data-panel="line-items"] table th:nth-child(9),
|
||||
.tab-panel[data-panel="line-items"] table td:nth-child(9) {
|
||||
min-width: 7rem !important;
|
||||
width: 7rem !important;
|
||||
}
|
||||
|
||||
.tab-panel[data-panel="line-items"] input {
|
||||
min-width: 100% !important;
|
||||
width: 100% !important;
|
||||
font-size: 0.82rem !important;
|
||||
padding-left: 0.45rem !important;
|
||||
padding-right: 0.45rem !important;
|
||||
}
|
||||
|
||||
/* numeric columns align right */
|
||||
.tab-panel[data-panel="line-items"] table td:nth-child(4) input,
|
||||
.tab-panel[data-panel="line-items"] table td:nth-child(5) input,
|
||||
.tab-panel[data-panel="line-items"] table td:nth-child(6) input,
|
||||
.tab-panel[data-panel="line-items"] table td:nth-child(7) input {
|
||||
text-align: right !important;
|
||||
font-variant-numeric: tabular-nums !important;
|
||||
}
|
||||
}
|
||||
/* ===== end line items mobile final sizing ===== */
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}Document Processor{% endblock %}</title>
|
||||
<link rel="stylesheet" href="/static/app.css?v=146">
|
||||
<link rel="stylesheet" href="/static/app.css?v=153">
|
||||
<link rel="stylesheet" href="/static/app-shell.css?v=66">
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -97,5 +97,50 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const tabButtons = document.querySelectorAll(".right-pane-tabs [data-tab]");
|
||||
const tabPanels = document.querySelectorAll(".tab-panel[data-panel]");
|
||||
if (!(tabButtons.length && tabPanels.length)) return;
|
||||
|
||||
const tabKey = "documentDetailActiveTab";
|
||||
|
||||
const applyTab = (tabName) => {
|
||||
tabButtons.forEach((btn) => {
|
||||
btn.classList.toggle("active", btn.dataset.tab === tabName);
|
||||
});
|
||||
tabPanels.forEach((panel) => {
|
||||
panel.classList.toggle("active", panel.dataset.panel === tabName);
|
||||
});
|
||||
localStorage.setItem(tabKey, tabName);
|
||||
};
|
||||
|
||||
const url = new URL(window.location.href);
|
||||
const urlTab = url.searchParams.get("tab");
|
||||
const savedTab = localStorage.getItem(tabKey);
|
||||
const defaultTab =
|
||||
urlTab ||
|
||||
savedTab ||
|
||||
document.querySelector(".right-pane-tabs .tab-button.active")?.dataset.tab ||
|
||||
tabButtons[0]?.dataset.tab;
|
||||
|
||||
if (defaultTab) {
|
||||
applyTab(defaultTab);
|
||||
}
|
||||
|
||||
tabButtons.forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const nextTab = btn.dataset.tab;
|
||||
applyTab(nextTab);
|
||||
const nextUrl = new URL(window.location.href);
|
||||
nextUrl.searchParams.set("tab", nextTab);
|
||||
window.history.replaceState({}, "", nextUrl.toString());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,43 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
});
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const panel = document.querySelector('.tab-panel[data-panel="line-items"]');
|
||||
if (!panel) return;
|
||||
|
||||
const moneyCols = [6, 7]; // total, tax
|
||||
const qtyCols = [4, 5]; // qty, unit
|
||||
|
||||
const rows = panel.querySelectorAll("tbody tr");
|
||||
rows.forEach((row) => {
|
||||
const cells = row.querySelectorAll("td");
|
||||
[...moneyCols, ...qtyCols].forEach((col) => {
|
||||
const cell = cells[col - 1];
|
||||
if (!cell) return;
|
||||
const input = cell.querySelector("input");
|
||||
if (!input) return;
|
||||
|
||||
const trimDecimals = () => {
|
||||
const v = input.value.trim();
|
||||
if (!v) return;
|
||||
const n = Number(v.replace(/,/g, ""));
|
||||
if (!Number.isFinite(n)) return;
|
||||
|
||||
if (moneyCols.includes(col)) {
|
||||
input.value = n.toFixed(2);
|
||||
} else {
|
||||
input.value = Number.isInteger(n) ? String(n) : String(n);
|
||||
}
|
||||
};
|
||||
|
||||
input.addEventListener("blur", trimDecimals);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% if error == "line_count_mismatch" %}
|
||||
|
|
@ -451,10 +488,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
<td style="padding:0.35rem;">{{ i + 1 }}</td>
|
||||
<td style="padding:0.35rem;"><input type="date" name="entry_date_{{ i }}" value="{{ item.entry_date.isoformat() if item and item.entry_date else '' }}" style="width:100%;"></td>
|
||||
<td style="padding:0.35rem;"><input type="text" name="description_{{ i }}" value="{{ item.description if item else '' }}" style="width:100%;"></td>
|
||||
<td style="padding:0.35rem;"><input type="text" name="quantity_{{ i }}" value="{{ item.quantity if item and item.quantity is not none else '' }}" style="width:100%;"></td>
|
||||
<td style="padding:0.35rem;"><input type="text" name="unit_price_{{ i }}" value="{{ item.unit_price if item and item.unit_price is not none else '' }}" style="width:100%;"></td>
|
||||
<td style="padding:0.35rem;"><input type="text" name="line_total_{{ i }}" value="{{ item.line_total if item and item.line_total is not none else '' }}" style="width:100%;"></td>
|
||||
<td style="padding:0.35rem;"><input type="text" name="tax_amount_{{ i }}" value="{{ item.tax_amount if item and item.tax_amount is not none else '' }}" style="width:100%;"></td>
|
||||
<td style="padding:0.35rem;"><input type="text" name="quantity_{{ i }}" value="{{ (item.quantity | string | replace('.0000', '') | replace('.00', '')) if item and item.quantity is not none else '' }}" style="width:100%;"></td>
|
||||
<td style="padding:0.35rem;"><input type="text" name="unit_price_{{ i }}" value="{{ (item.unit_price | string | replace('.0000', '') | replace('.00', '')) if item and item.unit_price is not none else '' }}" style="width:100%;"></td>
|
||||
<td style="padding:0.35rem;"><input type="text" name="line_total_{{ i }}" value="{{ '%.2f'|format(item.line_total|float) if item and item.line_total is not none else '' }}" style="width:100%;"></td>
|
||||
<td style="padding:0.35rem;"><input type="text" name="tax_amount_{{ i }}" value="{{ '%.2f'|format(item.tax_amount|float) if item and item.tax_amount is not none else '' }}" style="width:100%;"></td>
|
||||
<td style="padding:0.35rem;"><input type="text" name="category_{{ i }}" value="{{ item.category if item else '' }}" style="width:100%;"></td>
|
||||
<td style="padding:0.35rem;"><input type="text" name="notes_{{ i }}" value="{{ item.notes if item else '' }}" style="width:100%;"></td>
|
||||
</tr>
|
||||
|
|
@ -652,4 +689,41 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
});
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const panel = document.querySelector('.tab-panel[data-panel="line-items"]');
|
||||
if (!panel) return;
|
||||
|
||||
const moneyCols = [6, 7]; // total, tax
|
||||
const qtyCols = [4, 5]; // qty, unit
|
||||
|
||||
const rows = panel.querySelectorAll("tbody tr");
|
||||
rows.forEach((row) => {
|
||||
const cells = row.querySelectorAll("td");
|
||||
[...moneyCols, ...qtyCols].forEach((col) => {
|
||||
const cell = cells[col - 1];
|
||||
if (!cell) return;
|
||||
const input = cell.querySelector("input");
|
||||
if (!input) return;
|
||||
|
||||
const trimDecimals = () => {
|
||||
const v = input.value.trim();
|
||||
if (!v) return;
|
||||
const n = Number(v.replace(/,/g, ""));
|
||||
if (!Number.isFinite(n)) return;
|
||||
|
||||
if (moneyCols.includes(col)) {
|
||||
input.value = n.toFixed(2);
|
||||
} else {
|
||||
input.value = Number.isInteger(n) ? String(n) : String(n);
|
||||
}
|
||||
};
|
||||
|
||||
input.addEventListener("blur", trimDecimals);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Reference in New Issue