Phase 2: Docker and OIDC auth
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
import {
|
||||
Button,
|
||||
Chip,
|
||||
Paper,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import type { NowPlayingSession } from "../types";
|
||||
|
||||
interface Props {
|
||||
sessions: NowPlayingSession[];
|
||||
emptyMessage?: string;
|
||||
selectedUserLabel?: string;
|
||||
onSelectSession?: (session: NowPlayingSession) => void;
|
||||
}
|
||||
|
||||
function formatStateLabel(state: string): string {
|
||||
const normalized = String(state || "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
if (normalized === "playing") {
|
||||
return "Playing";
|
||||
}
|
||||
if (normalized === "paused") {
|
||||
return "Paused";
|
||||
}
|
||||
if (normalized === "idle") {
|
||||
return "Idle";
|
||||
}
|
||||
return normalized
|
||||
? normalized.charAt(0).toUpperCase() + normalized.slice(1)
|
||||
: "Unknown";
|
||||
}
|
||||
|
||||
function buildStatusSummary(sessions: NowPlayingSession[]) {
|
||||
const playing = sessions.filter(
|
||||
(session) =>
|
||||
String(session.state || "")
|
||||
.trim()
|
||||
.toLowerCase() === "playing",
|
||||
).length;
|
||||
const paused = sessions.filter(
|
||||
(session) =>
|
||||
String(session.state || "")
|
||||
.trim()
|
||||
.toLowerCase() === "paused",
|
||||
).length;
|
||||
const idle = sessions.filter(
|
||||
(session) =>
|
||||
String(session.state || "")
|
||||
.trim()
|
||||
.toLowerCase() === "idle",
|
||||
).length;
|
||||
return `${sessions.length} session${sessions.length === 1 ? "" : "s"} · ${playing} playing · ${paused} paused · ${idle} idle`;
|
||||
}
|
||||
|
||||
export function SessionActivityPanel({
|
||||
sessions,
|
||||
emptyMessage = "No live sessions matched to this user.",
|
||||
selectedUserLabel,
|
||||
onSelectSession,
|
||||
}: Props) {
|
||||
const userFallback = selectedUserLabel || "Unknown user";
|
||||
|
||||
if (!sessions.length) {
|
||||
return (
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{emptyMessage}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TableContainer
|
||||
component={Paper}
|
||||
variant="outlined"
|
||||
sx={{ maxHeight: 280, borderColor: "divider", borderRadius: 1 }}
|
||||
>
|
||||
<Table size="small" stickyHeader aria-label="Session activity details">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell
|
||||
sx={{
|
||||
fontWeight: 700,
|
||||
bgcolor: "background.default",
|
||||
minWidth: 160,
|
||||
}}
|
||||
>
|
||||
User
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{ fontWeight: 700, bgcolor: "background.default", width: 82 }}
|
||||
>
|
||||
State
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{
|
||||
fontWeight: 700,
|
||||
bgcolor: "background.default",
|
||||
minWidth: 140,
|
||||
}}
|
||||
>
|
||||
Title / Type
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{
|
||||
fontWeight: 700,
|
||||
bgcolor: "background.default",
|
||||
minWidth: 140,
|
||||
}}
|
||||
>
|
||||
Device
|
||||
</TableCell>
|
||||
<TableCell
|
||||
sx={{
|
||||
fontWeight: 700,
|
||||
bgcolor: "background.default",
|
||||
width: 118,
|
||||
}}
|
||||
>
|
||||
Transcoding
|
||||
</TableCell>
|
||||
{onSelectSession ? (
|
||||
<TableCell
|
||||
sx={{
|
||||
fontWeight: 700,
|
||||
bgcolor: "background.default",
|
||||
width: 150,
|
||||
}}
|
||||
>
|
||||
Action
|
||||
</TableCell>
|
||||
) : null}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={onSelectSession ? 6 : 5}
|
||||
sx={{ py: 0.75, bgcolor: "background.paper" }}
|
||||
>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{buildStatusSummary(sessions)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{sessions.map((session) => {
|
||||
const state = String(session.state || "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
const sessionLabel = formatStateLabel(session.state);
|
||||
return (
|
||||
<TableRow
|
||||
key={session.session_id}
|
||||
hover
|
||||
sx={{ cursor: onSelectSession ? "pointer" : "default" }}
|
||||
onClick={
|
||||
onSelectSession ? () => onSelectSession(session) : undefined
|
||||
}
|
||||
>
|
||||
<TableCell sx={{ py: 0.75, minWidth: 160 }}>
|
||||
<Typography
|
||||
variant="body2"
|
||||
noWrap
|
||||
title={session.user || userFallback}
|
||||
>
|
||||
{session.user || userFallback}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="text.secondary"
|
||||
noWrap
|
||||
title={session.session_id}
|
||||
>
|
||||
{session.session_id}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell sx={{ py: 0.75, whiteSpace: "nowrap" }}>
|
||||
<Chip
|
||||
size="small"
|
||||
label={sessionLabel}
|
||||
color={
|
||||
state === "playing"
|
||||
? "primary"
|
||||
: state === "paused"
|
||||
? "warning"
|
||||
: "default"
|
||||
}
|
||||
variant={
|
||||
state === "playing" || state === "paused"
|
||||
? "filled"
|
||||
: "outlined"
|
||||
}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell sx={{ py: 0.75, minWidth: 140 }}>
|
||||
<Typography
|
||||
variant="body2"
|
||||
noWrap
|
||||
title={session.title || ""}
|
||||
>
|
||||
{session.title || "(idle)"}
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary" noWrap>
|
||||
{session.type || "—"}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell sx={{ py: 0.75, minWidth: 140 }}>
|
||||
<Typography variant="body2" noWrap>
|
||||
{session.device || "Unknown device"}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell sx={{ py: 0.75, whiteSpace: "nowrap" }}>
|
||||
<Typography variant="body2" noWrap>
|
||||
{session.transcoding === "yes"
|
||||
? session.transcoding_type
|
||||
? `yes (${session.transcoding_type})`
|
||||
: "yes"
|
||||
: "no"}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
{onSelectSession ? (
|
||||
<TableCell sx={{ py: 0.75, whiteSpace: "nowrap" }}>
|
||||
<Button
|
||||
size="small"
|
||||
variant="outlined"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onSelectSession(session);
|
||||
}}
|
||||
>
|
||||
Open in Users
|
||||
</Button>
|
||||
</TableCell>
|
||||
) : null}
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user