simplifications and refactorings
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
||||
# Frontend - Media Library Viewer
|
||||
# Manage Frontend
|
||||
|
||||
React + TypeScript SPA for the Media Library Viewer, consuming the FastAPI backend.
|
||||
React + TypeScript SPA for Manage, consuming the FastAPI backend.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>frontend</title>
|
||||
<title>Manage</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -56,7 +56,7 @@ function Shell({
|
||||
<AppBar position="sticky" color="inherit" elevation={0}>
|
||||
<Toolbar sx={{ display: "flex", gap: 2, minHeight: 68 }}>
|
||||
<Typography variant="h6" sx={{ mr: 2, fontWeight: 700 }}>
|
||||
Media Library Viewer
|
||||
Manage
|
||||
</Typography>
|
||||
<Tabs
|
||||
value={current}
|
||||
@@ -147,7 +147,7 @@ function SignInScreen({ onSignIn }: { onSignIn: () => void }) {
|
||||
<Stack spacing={2} sx={{ alignItems: "center", textAlign: "center" }}>
|
||||
<Typography variant="h5">Sign in required</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Use your Authentik account to access the media library viewer.
|
||||
Use your Authentik account to access Manage.
|
||||
</Typography>
|
||||
<Button variant="contained" onClick={onSignIn}>
|
||||
Sign in with OIDC
|
||||
|
||||
@@ -9,7 +9,6 @@ import type {
|
||||
UserDirectoryResponse,
|
||||
UserMessageResponse,
|
||||
UserMessageQueueStatus,
|
||||
SmtpTestResponse,
|
||||
NowPlayingSession,
|
||||
MonitoringStatus,
|
||||
MonitoringMetrics,
|
||||
@@ -189,8 +188,5 @@ export const runJob = (jobKey: string, path: string) =>
|
||||
export const fetchUserMessageQueueStatus = () =>
|
||||
get<UserMessageQueueStatus>("/api/users/message/status");
|
||||
|
||||
export const testUserSmtpConnection = () =>
|
||||
post<SmtpTestResponse>("/api/users/message/test-smtp");
|
||||
|
||||
export const sendUserMessage = (formData: FormData) =>
|
||||
postForm<UserMessageResponse>("/api/users/message", formData);
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { testUserSmtpConnection } from "../api/client";
|
||||
|
||||
export function useTestUserSmtp() {
|
||||
return useMutation({
|
||||
mutationFn: testUserSmtpConnection,
|
||||
});
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
IconButton,
|
||||
LinearProgress,
|
||||
Paper,
|
||||
CircularProgress,
|
||||
Stack,
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -43,7 +42,6 @@ import { SessionActivityPanel } from "../components/SessionActivityPanel";
|
||||
import { useUsers } from "../hooks/useUsers";
|
||||
import { useActivity } from "../hooks/useDashboard";
|
||||
import { useSendUserMessage } from "../hooks/useSendUserMessage";
|
||||
import { useTestUserSmtp } from "../hooks/useTestUserSmtp";
|
||||
import { useUserMessageQueueStatus } from "../hooks/useUserMessageQueueStatus";
|
||||
import type { UserDirectoryItem } from "../types";
|
||||
import { buildUserDrawerModel } from "../users";
|
||||
@@ -57,15 +55,13 @@ function userLabel(user: UserDirectoryItem) {
|
||||
return user.display_name || user.username || user.jellyfin_id;
|
||||
}
|
||||
|
||||
const DEFAULT_HTML_BODY =
|
||||
"<p>Hello,</p><p> </p><p>Best,<br />Media Library Viewer</p>";
|
||||
const DEFAULT_HTML_BODY = "<p>Hello,</p><p> </p><p>Best,<br />Manage</p>";
|
||||
|
||||
export function UsersPage() {
|
||||
const { data, isError, error } = useUsers();
|
||||
const { data: activity } = useActivity();
|
||||
const queueStatusQuery = useUserMessageQueueStatus();
|
||||
const sendUserMessage = useSendUserMessage();
|
||||
const testUserSmtp = useTestUserSmtp();
|
||||
const [search, setSearch] = useState("");
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [selectedUserIds, setSelectedUserIds] = useState<string[]>([]);
|
||||
@@ -236,7 +232,7 @@ export function UsersPage() {
|
||||
sendUserMessage.reset();
|
||||
if (!subject.trim()) {
|
||||
setSubject(
|
||||
`Media Library Viewer update for ${selectedDeliverableRows.length} user${selectedDeliverableRows.length === 1 ? "" : "s"}`,
|
||||
`Manage update for ${selectedDeliverableRows.length} user${selectedDeliverableRows.length === 1 ? "" : "s"}`,
|
||||
);
|
||||
}
|
||||
if (!htmlBody.trim()) {
|
||||
@@ -389,76 +385,6 @@ export function UsersPage() {
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
{testUserSmtp.isPending ? (
|
||||
<Alert severity="info" variant="outlined">
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
|
||||
<CircularProgress size={16} />
|
||||
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
||||
Testing SMTP connection...
|
||||
</Typography>
|
||||
</Box>
|
||||
</Alert>
|
||||
) : testUserSmtp.isSuccess ? (
|
||||
testUserSmtp.data.status === "ok" ? (
|
||||
<Alert severity="success" variant="outlined">
|
||||
SMTP connection succeeded: {testUserSmtp.data.smtp_host}:
|
||||
{testUserSmtp.data.smtp_port} using{" "}
|
||||
{testUserSmtp.data.use_ssl
|
||||
? "SSL"
|
||||
: testUserSmtp.data.use_tls
|
||||
? "STARTTLS"
|
||||
: "plain SMTP"}
|
||||
.
|
||||
<Typography
|
||||
variant="caption"
|
||||
component="div"
|
||||
color="text.secondary"
|
||||
>
|
||||
{testUserSmtp.data.message}
|
||||
</Typography>
|
||||
{testUserSmtp.data.selected_mode ? (
|
||||
<Typography
|
||||
variant="caption"
|
||||
component="div"
|
||||
color="text.secondary"
|
||||
>
|
||||
Selected mode: {testUserSmtp.data.selected_mode.label}
|
||||
</Typography>
|
||||
) : null}
|
||||
</Alert>
|
||||
) : (
|
||||
<Alert severity="warning" variant="outlined">
|
||||
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
||||
{testUserSmtp.data.message}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="caption"
|
||||
component="div"
|
||||
color="text.secondary"
|
||||
>
|
||||
Tried {testUserSmtp.data.attempts.length} mode
|
||||
{testUserSmtp.data.attempts.length === 1 ? "" : "s"}.
|
||||
</Typography>
|
||||
<Stack spacing={0.5} sx={{ mt: 1 }}>
|
||||
{testUserSmtp.data.attempts.map((attempt) => (
|
||||
<Typography
|
||||
key={`${attempt.label}-${attempt.smtp_port}`}
|
||||
variant="caption"
|
||||
color="text.secondary"
|
||||
>
|
||||
{attempt.label}: {attempt.error || "failed"}
|
||||
</Typography>
|
||||
))}
|
||||
</Stack>
|
||||
</Alert>
|
||||
)
|
||||
) : testUserSmtp.isError ? (
|
||||
<Alert severity="error" variant="outlined">
|
||||
SMTP test failed:{" "}
|
||||
{(testUserSmtp.error as Error)?.message || "Unknown error"}
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
@@ -520,20 +446,6 @@ export function UsersPage() {
|
||||
>
|
||||
Message selected
|
||||
</Button>
|
||||
<Button
|
||||
startIcon={
|
||||
testUserSmtp.isPending ? (
|
||||
<CircularProgress size={16} color="inherit" />
|
||||
) : (
|
||||
<SendIcon />
|
||||
)
|
||||
}
|
||||
variant="outlined"
|
||||
disabled={testUserSmtp.isPending}
|
||||
onClick={() => testUserSmtp.mutate()}
|
||||
>
|
||||
{testUserSmtp.isPending ? "Testing SMTP..." : "Test SMTP"}
|
||||
</Button>
|
||||
<Button
|
||||
variant="text"
|
||||
disabled={!selectedRows.length}
|
||||
|
||||
@@ -78,38 +78,6 @@ export interface UserMessageQueueStatus {
|
||||
failed_count: number;
|
||||
}
|
||||
|
||||
export interface SmtpTestAttempt {
|
||||
label: string;
|
||||
smtp_host: string;
|
||||
smtp_port: number;
|
||||
use_tls: boolean;
|
||||
use_ssl: boolean;
|
||||
status: "ok" | "failed";
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface SmtpTestSelectedMode {
|
||||
label: string;
|
||||
smtp_host: string;
|
||||
smtp_port: number;
|
||||
use_tls: boolean;
|
||||
use_ssl: boolean;
|
||||
}
|
||||
|
||||
export interface SmtpTestResponse {
|
||||
status: "ok" | "error";
|
||||
message: string;
|
||||
from_address: string;
|
||||
from_name: string;
|
||||
smtp_host: string;
|
||||
smtp_port: number;
|
||||
use_tls: boolean;
|
||||
use_ssl: boolean;
|
||||
authenticated: boolean;
|
||||
selected_mode: SmtpTestSelectedMode | null;
|
||||
attempts: SmtpTestAttempt[];
|
||||
}
|
||||
|
||||
export interface NowPlayingSession {
|
||||
user: string;
|
||||
title: string;
|
||||
|
||||
Reference in New Issue
Block a user