web: account avatars, member /profile, framed admin panel
- an account can carry a picture: POST /api/auth/avatar (raw bytes, sniffed, replaces and unlinks the old file) and the public GET /api/users/:id/avatar. It rides wherever the account is named — the landing chip, the studio TopBar, the profile form. - new /profile page for members, sharing one Profile form (picture, email, password) with the admin drawer. - /admin is now one bordered frame whose left column is Profile / User account / Pictures / Close. Pictures lists every photo in the system with the slot that shows it; User account lists each account's name, email, picture and contribution count. - account control opens a menu: Admin page + Log out for an admin, Profile + Log out for a member.
This commit is contained in:
@@ -337,9 +337,99 @@ input[type="range"] { width: 100%; accent-color: var(--accent); }
|
||||
.adm-danger { color: var(--danger); border-color: var(--border); }
|
||||
.adm-danger:hover:not(:disabled) { border-color: var(--danger); }
|
||||
|
||||
/* Settings drawer: the tabs that organise the page, plus the three panels. */
|
||||
/* The management frame: a bordered box whose left column is the menu and whose
|
||||
right side is the pane for the chosen section. */
|
||||
.adm-frame {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
background: var(--bg-elev);
|
||||
overflow: hidden;
|
||||
}
|
||||
.adm-side {
|
||||
flex: 0 0 200px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: 12px 10px;
|
||||
border-right: 1px solid var(--border);
|
||||
background: var(--bg-sunken);
|
||||
}
|
||||
.adm-side-title {
|
||||
padding: 0 8px 8px;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
.adm-side-item {
|
||||
display: block;
|
||||
padding: 8px 10px;
|
||||
border: 0;
|
||||
border-radius: var(--radius-sm);
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
font: inherit;
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.adm-side-item:hover { background: var(--bg-elev); }
|
||||
.adm-side-item.on { background: var(--accent); color: var(--accent-fg); font-weight: 600; }
|
||||
.adm-pane { flex: 1 1 auto; min-width: 0; padding: 14px 16px; }
|
||||
.adm-table-wrap { overflow-x: auto; }
|
||||
.adm-face {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
background: var(--bg-sunken);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-dim);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.adm-face img { display: block; width: 100%; height: 100%; object-fit: cover; }
|
||||
.adm-filter { display: flex; align-items: center; gap: 8px; }
|
||||
.adm-form { display: flex; flex-direction: column; gap: 8px; max-width: 360px; }
|
||||
.field-label { font-size: 11.5px; color: var(--text-dim); }
|
||||
|
||||
/* --- profile picture ---------------------------------------------------- */
|
||||
/* One face for every control that names the signed-in user: the workspace
|
||||
TopBar, the landing chip and the profile form. No picture = initial. */
|
||||
.avatar {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 auto;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
background: var(--bg-sunken);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-dim);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
}
|
||||
.avatar img { display: block; width: 100%; height: 100%; object-fit: cover; }
|
||||
.avatar-sm { width: 20px; height: 20px; font-size: 10px; }
|
||||
.avatar-row { display: flex; align-items: center; gap: 10px; }
|
||||
.avatar-row .avatar { width: 46px; height: 46px; font-size: 17px; }
|
||||
.account-btn { display: inline-flex; align-items: center; gap: 7px; }
|
||||
|
||||
/* --- responsive --------------------------------------------------------- */
|
||||
@media (max-width: 860px) {
|
||||
.workspace { flex-direction: column; }
|
||||
/* The frame's left column becomes a wrapping row above the pane. */
|
||||
.adm-frame { flex-direction: column; }
|
||||
.adm-side { flex: 0 0 auto; flex-direction: row; flex-wrap: wrap; border-right: 0; border-bottom: 1px solid var(--border); }
|
||||
/* The header wraps on narrow screens, so a menu-wrap can land at the start of
|
||||
a row — the popover is right-anchored to its button and would hang off the
|
||||
left of the screen (measured: x=-280 of 390px). Anchor it to the full-width
|
||||
|
||||
Reference in New Issue
Block a user