diff --git a/src/quartermaster/routes.py b/src/quartermaster/routes.py index b9283c4..041a03e 100644 --- a/src/quartermaster/routes.py +++ b/src/quartermaster/routes.py @@ -39,11 +39,16 @@ def _section_view(db: Session, section: Section) -> service.SectionView: def _render_section( - request: Request, db: Session, section: Section + request: Request, + db: Session, + section: Section, + editing_id: int | None = None, ) -> HTMLResponse: view = _section_view(db, section) return templates.TemplateResponse( - request, "partials/section.html", {"section": view} + request, + "partials/section.html", + {"section": view, "editing_id": editing_id}, ) diff --git a/src/quartermaster/static/app.css b/src/quartermaster/static/app.css index 328bbfe..a1f2067 100644 --- a/src/quartermaster/static/app.css +++ b/src/quartermaster/static/app.css @@ -361,64 +361,160 @@ details.group[open] > summary .chevron::after { opacity: 0; } .total .applied { color: var(--ink-soft); } .total.empty { color: var(--muted); } -/* =============== ENTRY TABLE =============== */ +/* =============== ENTRY LIST (BUDGET TEMPLATE) =============== */ -table.entries { - width: 100%; - border-collapse: collapse; - font-family: var(--sans); -} -table.entries thead { display: none; } +.budget-entries { display: flex; flex-direction: column; } -table.entries tbody tr.entry { +.entry-row.reading { display: grid; - grid-template-columns: minmax(0, 1fr) 5.5rem 5.5rem 1.2rem; + grid-template-columns: minmax(0, 1fr) 5.5rem auto; gap: 0.6rem; align-items: baseline; padding: 0.26rem 0.25rem 0.28rem; - position: relative; border-bottom: 1px dotted var(--rule); + position: relative; } -table.entries tbody tr.entry:hover { background: var(--paper-stripe); } -table.entries tbody tr.entry td { - padding: 0; - border: none; - vertical-align: baseline; +.entry-row.reading:hover { background: var(--paper-stripe); } + +.entry-row.reading .entry-name { + font-family: var(--sans); + font-weight: 500; + font-size: 1.02rem; + color: var(--ink); + letter-spacing: 0.01em; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.entry-row.reading .note-badge { + font-family: var(--sans); + font-style: italic; + font-size: 0.82rem; + color: var(--muted); + margin-left: 0.5rem; + letter-spacing: 0.02em; + opacity: 0.85; +} +.entry-row.reading .note-badge::before { + content: "· "; + color: var(--rule); + font-style: normal; +} +.entry-row.reading .entry-amount { + font-family: var(--sans); + font-weight: 500; + font-size: 1rem; + color: var(--ink); + font-feature-settings: "lnum" 1, "tnum" 1; + text-align: right; min-width: 0; } -table.entries tbody tr.entry::after { - content: ""; - position: absolute; - left: 0; right: 0; bottom: -1px; - height: 2px; - background: var(--sage-soft); - width: min(100%, calc(var(--ratio, 1) * 100%)); - transition: width 0.25s ease; - opacity: 0.7; +.entry-row.reading .entry-actions { + display: flex; + gap: 0.3rem; + align-items: center; } -table.entries tbody tr.entry.state-edited::after, -table.entries tbody tr.entry[data-deviation="over"]::after { - background: var(--accent); - opacity: 0.85; +.entry-row.reading .entry-actions button { + background: none; + border: none; + cursor: pointer; + padding: 0; + line-height: 1; + color: var(--rule); + opacity: 0; + transition: color 0.12s ease, opacity 0.12s ease; } -table.entries tbody tr.entry.state-new_in_month::after { - background: var(--indigo); - opacity: 0.55; +.entry-row.reading:hover .entry-actions button { opacity: 1; } +.entry-row.reading .entry-actions button.edit { + font-size: 0.95rem; + color: var(--rule); +} +.entry-row.reading .entry-actions button.edit:hover { color: var(--ink); } +.entry-row.reading .entry-actions button.delete { + font-size: 1.2rem; + font-weight: 400; +} +.entry-row.reading .entry-actions button.delete:hover { color: var(--accent); } + +.entry-row.editing { + display: grid; + grid-template-columns: minmax(0, 1fr) 5.5rem minmax(0, 1.4fr) auto; + gap: 0.5rem; + align-items: center; + padding: 0.26rem 0.25rem 0.28rem; + border-bottom: 1px dotted var(--rule); + background: var(--paper-soft); + margin: 0; +} +.entry-row.editing input { + font-family: var(--sans); + font-size: 0.95rem; + padding: 0.2rem 0.4rem; + border: 1px solid var(--rule); + background: var(--paper); + color: var(--ink); + outline: none; + transition: border-color 0.12s; + min-width: 0; +} +.entry-row.editing input:focus { border-color: var(--ink); } +.entry-row.editing input[type="number"] { + text-align: right; + font-variant-numeric: tabular-nums; +} +.entry-row.editing .notes-input { + font-style: italic; + font-size: 0.88rem; + color: var(--muted); +} +.entry-row.editing .entry-actions { + display: flex; + gap: 0.4rem; + align-items: center; +} +.entry-row.editing .save-btn, +.entry-row.editing .cancel-btn { + font-family: var(--sans); + font-weight: 600; + font-size: 0.78rem; + letter-spacing: 0.12em; + text-transform: uppercase; + padding: 0.22rem 0.6rem; + cursor: pointer; + transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease; +} +.entry-row.editing .save-btn { + border: 1px solid var(--ink); + background: var(--paper-soft); + color: var(--ink); +} +.entry-row.editing .save-btn:hover { + background: var(--sage); + color: var(--paper); + border-color: var(--sage); +} +.entry-row.editing .cancel-btn { + border: 1px solid var(--rule); + background: transparent; + color: var(--muted); +} +.entry-row.editing .cancel-btn:hover { + color: var(--accent); + border-color: var(--accent); } -tr.empty td { - padding: 0.4rem 0.5rem !important; +.empty-row { + padding: 0.4rem 0.5rem; color: var(--muted); font-style: italic; font-size: 0.9rem; } -tr.add-row td { - padding: 0.4rem 0.25rem 0.2rem !important; - border-bottom: none !important; +.add-row { + padding: 0.4rem 0.25rem 0.2rem; grid-column: 1 / -1; - display: block; } /* Add-entry disclosure: collapsed trigger, expanded form */ @@ -455,112 +551,18 @@ details.add-entry > .month-add-form { margin-top: 0.45rem; } -.entry-name { - font-family: var(--sans); - font-weight: 500; - font-size: 1.02rem; - color: var(--ink); - letter-spacing: 0.01em; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; +@media (max-width: 520px) { + .entry-row.reading { + grid-template-columns: minmax(0, 1fr) 4.6rem auto; + gap: 0.4rem; + } + .entry-row.editing { + grid-template-columns: minmax(0, 1fr) 4.6rem; + gap: 0.4rem; + } + .entry-row.editing .notes-input, + .entry-row.editing .entry-actions { grid-column: 1 / -1; } } -.entry-name input { - font: inherit; - color: inherit; - background: transparent; - border: none; - border-bottom: 1px solid transparent; - padding: 0; - width: 100%; - outline: none; - transition: border-color 0.12s; -} -.entry-name input:hover { border-bottom-color: var(--rule); } -.entry-name input:focus { border-bottom-color: var(--ink); } - -.entry-amount { - font-family: var(--sans); - font-weight: 500; - font-size: 1rem; - color: var(--ink); - font-feature-settings: "lnum" 1, "tnum" 1; - text-align: right; - min-width: 0; -} -.entry-amount input { - font: inherit; - font-variant-numeric: tabular-nums; - color: inherit; - background: transparent; - border: none; - border-bottom: 1px solid transparent; - padding: 0; - width: 100%; - outline: none; - text-align: right; - transition: border-color 0.12s; -} -.entry-amount input:hover { border-bottom-color: var(--rule); } -.entry-amount input:focus { border-bottom-color: var(--ink); } - -/* On the budget page the planned-amount cell is the only numeric cell; - still render it like a ledger number. */ -tr.entry td.entry-amount:first-of-type { color: var(--ink); } - -.entry-actions button.delete { - font-family: var(--sans); - font-size: 1.2rem; - line-height: 1; - color: var(--rule); - background: none; - border: none; - cursor: pointer; - padding: 0; - opacity: 0; - transition: color 0.12s ease, opacity 0.12s ease; - align-self: center; - font-weight: 400; -} -tr.entry:hover .entry-actions button.delete { opacity: 1; } -.entry-actions button.delete:hover { color: var(--accent); } - -/* Notes row — hidden when empty, shown on hover or when value is set */ -tr.entry-notes-row { - display: block; - grid-column: 1 / -1; - font-family: var(--sans); - font-style: italic; - font-weight: 400; - font-size: 0.85rem; - color: var(--muted); - line-height: 1.3; - padding: 0; - margin-top: -0.25rem; -} -tr.entry-notes-row td { - padding: 0 0.25rem 0.25rem !important; - border-bottom: 1px dotted var(--rule) !important; -} -tr.entry-notes-row input.notes-input { - font: inherit; - font-style: italic; - color: inherit; - background: transparent; - border: none; - padding: 0; - width: 100%; - outline: none; -} -tr.entry-notes-row input.notes-input::placeholder { - color: var(--rule); - font-style: italic; -} -/* Empty notes render subtly (placeholder only) so they stay clickable. */ -tr.entry-notes-row:has(input:placeholder-shown) { opacity: 0.55; } -tr.entry-notes-row:hover, -tr.entry-notes-row:has(input:focus) { opacity: 1; } .tag { font-family: var(--sans); @@ -980,13 +982,6 @@ form.add-posting-form button[type="submit"] { } form.add-posting-form button[type="submit"]:hover { background: var(--ink); color: var(--paper); } -.empty-row { - padding: 0.5rem 0.5rem; - color: var(--muted); - font-style: italic; - font-size: 0.9rem; -} - @media (max-width: 640px) { .entry-block > summary { grid-template-columns: 0.9rem minmax(0, 1fr) 4.2rem 7rem 1rem; @@ -1118,8 +1113,4 @@ button[disabled] { text-align: center; justify-self: center; } - table.entries tbody tr.entry { - grid-template-columns: minmax(0, 1fr) 4.6rem 4.6rem 1rem; - gap: 0.4rem; - } } diff --git a/src/quartermaster/templates/partials/section.html b/src/quartermaster/templates/partials/section.html index 1b45f7d..116db33 100644 --- a/src/quartermaster/templates/partials/section.html +++ b/src/quartermaster/templates/partials/section.html @@ -5,14 +5,67 @@ ${{ '{:,.2f}'.format(section.total) }} -
| {{ entry.name }} | -${{ '{:,.2f}'.format(entry.amount) }} | -- |
+
+ {% for entry in section.entries %}
+ {% if editing_id is not none and entry.id == editing_id %}
+
+ {% else %}
+
+
+ {{ entry.name }}{% if entry.notes %}{{ entry.notes }}{% endif %}
+
+ ${{ '{:,.2f}'.format(entry.amount) }}
+
+
- |
-
| - - | -|||
| No entries yet. | |||
|
-
-
- + add {{ section.label|lower }}- - |
- |||