feat: sidebar nav (default), group member management, per-customer ticketing identity mapping

- Navigation: new per-user setting (default "side") renders the page links as a
  left sidebar while keeping theme/bell/avatar/logout in the top-right; falls
  back to a top bar under 760px or when set to "top". Profile selector added.
- Group conversations: record a creatorId; the creator gets a "Manage members"
  button to add/remove participants (new GET/POST/DELETE members endpoints,
  creator-only). Existing groups backfilled.
- Customer ticketing: admin can map each assigned consultant to their username
  in that customer's ticketing system. Per-customer mapping takes precedence
  over the consultant's global ticketingIdentities during sync.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
etalon
2026-06-13 11:05:33 +02:00
parent 0d28f69320
commit e8beb7d4f3
16 changed files with 367 additions and 19 deletions
+46
View File
@@ -303,6 +303,52 @@ table, .badge, input[type=number] { font-variant-numeric: tabular-nums; }
}
.topnav .right { display: flex; align-items: center; gap: 8px; }
/* ---- sidebar navigation (default; user can switch to top bar) ---- */
body[data-nav=side] .topnav {
justify-content: flex-end; /* only the right controls remain in the flow */
padding-left: 232px;
}
body[data-nav=side] .topnav .brand {
position: fixed; top: 14px; left: 18px; z-index: 41;
font-size: 1.05rem;
}
body[data-nav=side] .topnav .links {
position: fixed; top: 0; left: 0; bottom: 0;
width: 214px;
flex: none; flex-direction: column; flex-wrap: nowrap; align-items: stretch;
gap: 4px;
background-color: var(--surface2);
background-image: var(--dither);
border-right: 2px solid var(--border);
padding: 58px 12px 18px;
overflow-y: auto;
z-index: 40;
}
:root[data-theme=light] body[data-nav=side] .topnav .links { background-image: none; }
body[data-nav=side] .topnav .links a {
border-bottom: none;
border-left: 3px solid transparent;
border-radius: 0;
padding: 9px 12px;
}
body[data-nav=side] .topnav .links a[aria-current=page] {
border-left-color: var(--accent);
background: var(--surface);
}
body[data-nav=side] main.container { margin-left: 214px; }
@media (max-width: 760px) {
/* collapse the sidebar back into a normal top bar on small screens */
body[data-nav=side] .topnav { padding-left: 20px; justify-content: flex-start; flex-wrap: wrap; }
body[data-nav=side] .topnav .brand { position: static; }
body[data-nav=side] .topnav .links {
position: static; width: auto; flex: 1; flex-direction: row; flex-wrap: wrap;
background: none; border-right: none; padding: 0; overflow: visible;
}
body[data-nav=side] .topnav .links a { border-left: none; border-bottom: 2px solid transparent; }
body[data-nav=side] .topnav .links a[aria-current=page] { border-bottom-color: var(--accent); background: none; }
body[data-nav=side] main.container { margin-left: auto; }
}
/* ---- cards: printed stock with hard Y2K shadows ---- */
.card {
background: var(--surface);