diff --git a/README.md b/README.md index b34ddaa..d34c250 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ Per-controller actions in the modal: | Control | Description | |---|---| -| **Date picker** | Choose which day to view | +| **Date picker** | Choose which day to view (defaults to the browser's local date) | | **Badged in by** | Set your on-time cutoff (HH:MM) | | **Controller** | Filter the table to one controller, or show All | | **Show filtered** | Include filtered tenants in the table (dimmed and tagged) | @@ -209,6 +209,17 @@ Per-controller actions in the modal: > controller the person badged into that day, plus a "MERGED" pill so it's > clear the row represents N UniFi UUIDs. +### Interface notes + +- On narrow screens, the attendance table automatically changes into stacked + row cards with labels for each field, so all columns remain visible without + sideways scrolling. +- Destructive or structural actions such as removing a controller, dissolving a + merged person, splitting an identity, and resetting a day use in-app + confirmation dialogs instead of browser popups. +- Toast notifications announce success/failure states; keyboard users can close + open dialogs with `Esc`. + --- ## Merging identities across controllers @@ -225,9 +236,10 @@ fixes this: with matching full names across different controllers. One click confirms each suggestion (no auto-apply; you're always in the loop). - The same modal lists every merged person below the suggestions, with - per-person actions: **Rename**, **Split off** (remove one identity from the - group), **Dissolve** (break the whole group up — past badge events are - preserved, the identities just become standalone rows again). + per-person actions: **Rename** (opens an in-app text dialog), **Split off** + (remove one identity from the group), **Dissolve** (break the whole group up — + past badge events are preserved, the identities just become standalone rows + again). Once merged, the attendance table: diff --git a/static/index.html b/static/index.html index 0daeccf..ef3e744 100644 --- a/static/index.html +++ b/static/index.html @@ -242,16 +242,78 @@ .toast.show { opacity: 1; transform: translateY(0); } .toast.error { border-color: rgba(255,100,100,0.6); color: #ffd6d7; } - @media (max-width: 800px) { - header { flex-direction: column; align-items: flex-start; } - .controls { flex-direction: column; align-items: stretch; } - input, select, button { width: 100%; } - th:nth-child(5), td:nth-child(5), - th:nth-child(6), td:nth-child(6) { display: none; } - .form-grid { grid-template-columns: 1fr; } - .ctrl-row { grid-template-columns: 1fr; } - .ctrl-actions { justify-content: flex-start; } - } + @media (max-width: 800px) { + body { padding: 12px; align-items: flex-start; } + .app-shell { padding: 18px 14px 22px; border-radius: 14px; } + header { flex-direction: column; align-items: flex-start; } + h1 { font-size: 1.25rem; letter-spacing: 0.04em; } + .subtitle { font-size: 0.82rem; } + .badge { letter-spacing: 0.08em; } + .controls { flex-direction: column; align-items: stretch; } + .control-group { flex-direction: column; align-items: stretch; gap: 6px; } + input, select, button { width: 100%; } + .show-filtered-toggle { flex-direction: row; align-items: center; width: 100%; } + .show-filtered-toggle input, + .row-action-btn, + .ctrl-actions button, + .picker-row button, + .subject-row input { width: auto; } + .summary-row { display: grid; grid-template-columns: 1fr; } + .table-card { background: transparent; border: 0; overflow: visible; } + .table-card table, + .table-card thead, + .table-card tbody, + .table-card tr, + .table-card td { display: block; width: 100%; } + .table-card thead { display: none; } + .table-card tbody { display: flex; flex-direction: column; gap: 12px; } + .table-card tbody tr { + background: linear-gradient(135deg, rgba(17,17,19,0.98), rgba(8,8,10,0.98)); + border: 1px solid rgba(255,255,255,0.07); + border-radius: 12px; + padding: 10px 12px; + } + .table-card tbody tr:hover { background: linear-gradient(135deg, rgba(24,24,27,0.98), rgba(10,10,12,0.98)); } + .table-card td { + display: grid; + grid-template-columns: minmax(96px, 35%) 1fr; + gap: 12px; + align-items: start; + padding: 9px 0; + border-bottom: 1px solid rgba(255,255,255,0.06); + white-space: normal; + text-align: right; + } + .table-card td:last-child { border-bottom: 0; } + .table-card td::before { + content: attr(data-label); + color: var(--muted); + font-size: 0.68rem; + letter-spacing: 0.1em; + text-transform: uppercase; + text-align: left; + } + .table-card .name-cell { font-size: 1rem; } + .table-card .align-center { text-align: right; } + .table-card .align-center .status-chip, + .table-card .align-center .row-action-group { justify-content: flex-end; margin-left: auto; } + .source-list { justify-content: flex-end; } + .source-chip, .merged-pill { max-width: 100%; overflow-wrap: anywhere; } + .row-action-group { flex-wrap: wrap; justify-content: flex-end; } + .table-card tbody tr.empty-table-row { padding: 0; } + .table-card tbody tr.empty-table-row td { + display: block; + text-align: center; + padding: 24px 14px; + border-bottom: 0; + } + .table-card tbody tr.empty-table-row td::before { content: none; } + .form-grid { grid-template-columns: 1fr; } + .ctrl-row { grid-template-columns: 1fr; } + .ctrl-actions { justify-content: flex-start; } + .modal { padding: 18px; width: 94%; border-radius: 12px; } + .modal-actions { flex-direction: column-reverse; } + } @@ -314,31 +376,31 @@ Actions - - No data yet. Badge into a door and press Refresh. - - - + + No data yet. Badge into a door and press Refresh. + + + - -