fix(admin): style the Teams admin screen with the site's own classes
The Teams admin screen was written against four CSS classes that do not exist anywhere in the project — `.table`, `.kv`, `.list` and `.notice` — and against `.btn-ghost` / `.btn` used without the `.btn` box they depend on. The result rendered as unstyled UA tables and bare browser buttons sitting flush against unpadded panels, and looked nothing like the rest of the admin panel. Nothing here changes behaviour, data or routes; it is presentation only. - Tables become `adm-table` / `adm-th` / `adm-td` inside `panel-flat`, the markup the other fourteen admin views use, and scroll rather than clip when a row is wider than the shell (a status badge and the action buttons are both nowrap by design, so a narrow viewport can always overflow one). - Buttons take the full `btn btn-primary btn-sq` / `btn btn-ghost btn-sq` triplet. `.btn` carries the padding, border and radius; the variants carry only colour, so a bare `.btn-ghost` had none of the box and a bare `.btn` fell back to the UA's light button face. - `.panel` supplies no padding, so every panel now sets it explicitly at 22px, as ModulesAdmin and EmptyState already do. - Headings become `h2.display`, and the in-page `<h1>Teams</h1>` goes away in favour of AdminLayout's topbar title — which needed `/admin/teams` adding to TITLES, the reason the bar read "ADMIN". - Status pills use the existing `badge-pub` / `badge-moderator` / `badge-ban` / `badge-draft` modifiers. `.badge` alone declares no border, so the old inline `borderColor` was inert. - The bridge and voice panels drop their private palette (`#e08b77` / `#8fbf7a` / `#e0b877`) for the site's `#d98b84` / `#7fd0a4` / `#e0b070`, and a literal `rgba(255,255,255,.12)` rule and a `borderRadius: 4` for `var(--line-soft)` and `var(--radius-input)`. Walked live against the dev DB as an admin: sync panel, review queue, all Teams, the forum ledger, the bridge draft form and its acknowledgement dialog. Client 288/288, server 1162/1162, client build clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WnDSWzpUjw8t8C2hghysNz
This commit is contained in:
@@ -19,6 +19,9 @@ import {
|
||||
// lacks Manage Roles otherwise has a screen full of controls that cannot work,
|
||||
// and finds out one Team at a time from a column of identical errors.
|
||||
|
||||
const PANEL = { padding: 22, marginBottom: 22, maxWidth: 760 }
|
||||
const HEADING = { margin: '0 0 6px', fontSize: '1.2rem', color: 'var(--head)' }
|
||||
|
||||
export default function TeamVoice() {
|
||||
const [config, setConfig] = useState(null)
|
||||
const [draft, setDraft] = useState(null)
|
||||
@@ -50,9 +53,9 @@ export default function TeamVoice() {
|
||||
|
||||
if (!config || !draft) {
|
||||
return (
|
||||
<section style={{ marginTop: 34, maxWidth: 760 }}>
|
||||
<h2 className="display" style={{ fontSize: '1.05rem', marginBottom: 4 }}>Voice channels</h2>
|
||||
{error && <p className="sans" style={{ color: '#e08b77', fontSize: '0.82rem' }}>{error}</p>}
|
||||
<section className="panel" style={PANEL}>
|
||||
<h2 className="display" style={HEADING}>Voice channels</h2>
|
||||
{error && <p className="sans" style={{ color: '#d98b84', fontSize: '0.82rem' }}>{error}</p>}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -120,8 +123,8 @@ export default function TeamVoice() {
|
||||
}
|
||||
|
||||
return (
|
||||
<section style={{ marginTop: 34, maxWidth: 760 }}>
|
||||
<h2 className="display" style={{ fontSize: '1.05rem', marginBottom: 4 }}>Voice channels</h2>
|
||||
<section className="panel" style={PANEL}>
|
||||
<h2 className="display" style={HEADING}>Voice channels</h2>
|
||||
<p className="sans dim" style={{ fontSize: '0.78rem', margin: '0 0 14px' }}>
|
||||
Give each Team a {config.platform} voice channel of its own. Access is granted with a role per
|
||||
Team, so members of a Team can see and join their channel and nobody else can. Members need a
|
||||
@@ -129,7 +132,7 @@ export default function TeamVoice() {
|
||||
</p>
|
||||
|
||||
{blocked && (
|
||||
<p className="sans" style={{ color: '#e0b877', fontSize: '0.82rem' }}>
|
||||
<p className="sans" style={{ color: '#e0b070', fontSize: '0.82rem' }}>
|
||||
{blocked} Voice channels cannot be switched on until that is fixed.
|
||||
</p>
|
||||
)}
|
||||
@@ -145,12 +148,12 @@ export default function TeamVoice() {
|
||||
</p>
|
||||
)}
|
||||
|
||||
{error && <p className="sans" style={{ color: '#e08b77', fontSize: '0.82rem' }}>{error}</p>}
|
||||
{notice && <p className="sans" style={{ color: '#8fbf7a', fontSize: '0.82rem' }}>{notice}</p>}
|
||||
{error && <p className="sans" style={{ color: '#d98b84', fontSize: '0.82rem' }}>{error}</p>}
|
||||
{notice && <p className="sans" style={{ color: '#7fd0a4', fontSize: '0.82rem' }}>{notice}</p>}
|
||||
|
||||
<p className="sans" style={{ fontSize: '0.8rem' }}>{statusSummary(config.settings, config.rows)}</p>
|
||||
|
||||
<div style={{ marginTop: 14, borderTop: '1px solid rgba(255,255,255,0.12)', paddingTop: 16 }}>
|
||||
<div style={{ marginTop: 14, borderTop: '1px solid var(--line-soft)', paddingTop: 16 }}>
|
||||
<label className="sans" style={{ display: 'block', marginBottom: 12, fontSize: '0.82rem' }}>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -210,39 +213,53 @@ export default function TeamVoice() {
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<button type="button" className="btn" disabled={busy} onClick={save}>Save</button>
|
||||
<button type="button" className="btn-ghost" disabled={busy} onClick={runPass}>Sync now</button>
|
||||
<div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
|
||||
<button type="button" className="btn btn-primary btn-sq" disabled={busy} onClick={save}>Save</button>
|
||||
<button type="button" className="btn btn-ghost btn-sq" disabled={busy} onClick={runPass}>Sync now</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{config.rows.length > 0 && (
|
||||
<table className="table" style={{ marginTop: 18 }}>
|
||||
<thead>
|
||||
<tr><th>Team</th><th>Members</th><th>Channel</th><th>State</th><th /></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{config.rows.map((row) => (
|
||||
<tr key={row.teamId}>
|
||||
<td>{row.teamName}</td>
|
||||
<td className="sans" style={{ fontSize: '0.76rem' }}>{row.memberCount}</td>
|
||||
<td className="sans" style={{ fontSize: '0.76rem' }}>
|
||||
{row.channelRef || <span className="dim">none</span>}
|
||||
</td>
|
||||
<td className="sans" style={{ fontSize: '0.76rem' }}>
|
||||
{stateLabel(row.state)}
|
||||
{removalCountdown(row) && (
|
||||
<span className="dim" style={{ display: 'block' }}>{removalCountdown(row)}</span>
|
||||
)}
|
||||
{row.lastError && (
|
||||
<span style={{ display: 'block', color: '#e08b77' }}>{row.lastError}</span>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" className="btn-ghost" disabled={busy} onClick={() => remove(row)}>Remove</button>
|
||||
</td>
|
||||
<div className="panel-flat" style={{ marginTop: 18, overflowX: 'auto' }}>
|
||||
<table className="adm-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="adm-th">Team</th>
|
||||
<th className="adm-th">Members</th>
|
||||
<th className="adm-th">Channel</th>
|
||||
<th className="adm-th">State</th>
|
||||
<th className="adm-th" />
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
{config.rows.map((row) => (
|
||||
<tr key={row.teamId}>
|
||||
<td className="adm-td" style={{ color: 'var(--head)' }}>{row.teamName}</td>
|
||||
<td className="adm-td">{row.memberCount}</td>
|
||||
<td className="adm-td dim">
|
||||
{row.channelRef || <span className="dim">none</span>}
|
||||
</td>
|
||||
<td className="adm-td">
|
||||
{stateLabel(row.state)}
|
||||
{removalCountdown(row) && (
|
||||
<span className="dim" style={{ display: 'block', fontSize: '0.78rem', marginTop: 3 }}>
|
||||
{removalCountdown(row)}
|
||||
</span>
|
||||
)}
|
||||
{row.lastError && (
|
||||
<span style={{ display: 'block', color: '#d98b84', fontSize: '0.78rem', marginTop: 3 }}>
|
||||
{row.lastError}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="adm-td" style={{ textAlign: 'right' }}>
|
||||
<button type="button" className="btn btn-ghost btn-sq" disabled={busy} onClick={() => remove(row)}>Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{config.lastPass && config.lastPass.at && (
|
||||
|
||||
Reference in New Issue
Block a user