WhiteNightsAdminPanel/src/shared/modals/PreviewMediaDialog/index.tsx
2025-05-31 21:17:27 +03:00

230 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
articlesStore,
authStore,
Language,
mediaStore,
MEDIA_TYPE_LABELS,
API_URL,
} from "@shared";
import { observer } from "mobx-react-lite";
import { useEffect, useRef, useState, useCallback } from "react";
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
TextField,
Paper,
Box,
Typography,
CircularProgress,
Alert,
Snackbar,
} from "@mui/material";
import { Download, Save } from "lucide-react";
import { ReactMarkdownComponent, MediaViewer } from "@widgets";
import { authInstance } from "@shared";
interface PreviewMediaDialogProps {
open: boolean;
onClose: () => void;
mediaId: string;
}
export const PreviewMediaDialog = observer(
({ open, onClose, mediaId }: PreviewMediaDialogProps) => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState(false);
const media = mediaId
? mediaStore.media.find((m) => m.id === mediaId)
: null;
const [mediaName, setMediaName] = useState(media?.media_name ?? "");
const [mediaFilename, setMediaFilename] = useState(media?.filename ?? "");
// Reset form when media changes
useEffect(() => {
if (media) {
setMediaName(media.media_name);
setMediaFilename(media.filename);
}
}, [media]);
useEffect(() => {
const handleKeyPress = (event: KeyboardEvent) => {
if (event.key.toLowerCase() === "enter" && !event.ctrlKey) {
event.preventDefault();
onClose();
}
};
window.addEventListener("keydown", handleKeyPress);
return () => window.removeEventListener("keydown", handleKeyPress);
}, [onClose]);
const handleSave = async () => {
if (!mediaId) return;
setIsLoading(true);
setError(null);
try {
await authInstance.patch(`/media/${mediaId}`, {
media_name: mediaName,
filename: mediaFilename,
type: media?.media_type,
});
// Update local store
await mediaStore.getMedia();
setSuccess(true);
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to save media");
} finally {
setIsLoading(false);
}
};
const handleClose = () => {
setError(null);
setSuccess(false);
onClose();
};
if (!media) {
return null;
}
return (
<>
<Dialog open={open} onClose={handleClose} maxWidth="md" fullWidth>
<DialogTitle>Просмотр медиа</DialogTitle>
<DialogContent
className="flex gap-4"
dividers
sx={{
height: "600px",
display: "flex",
flexDirection: "column",
gap: 2,
pt: 2,
}}
>
<Box className="flex flex-col gap-4">
<Box className="flex gap-2">
<TextField
fullWidth
value={mediaName}
onChange={(e) => setMediaName(e.target.value)}
label="Название медиа"
disabled={isLoading}
/>
<TextField
fullWidth
value={mediaFilename}
onChange={(e) => setMediaFilename(e.target.value)}
label="Название файла"
disabled={isLoading}
/>
</Box>
<TextField
fullWidth
label="Тип медиа"
value={
MEDIA_TYPE_LABELS[
media.media_type as keyof typeof MEDIA_TYPE_LABELS
]
}
disabled
sx={{ width: "50%" }}
/>
<Box className="flex gap-4 h-full">
<Paper
elevation={2}
sx={{
flex: 1,
p: 2,
display: "flex",
alignItems: "center",
justifyContent: "center",
minHeight: 400,
}}
>
<MediaViewer
media={{
id: mediaId,
media_type: media.media_type,
filename: media.filename,
}}
/>
</Paper>
<Box className="flex flex-col gap-2 self-end">
<Button
variant="contained"
color="primary"
startIcon={<Download size={16} />}
component="a"
href={`${
import.meta.env.VITE_KRBL_MEDIA
}${mediaId}/download?token=${localStorage.getItem(
"token"
)}`}
target="_blank"
disabled={isLoading}
>
Скачать
</Button>
<Button
variant="contained"
color="success"
startIcon={
isLoading ? (
<CircularProgress size={16} />
) : (
<Save size={16} />
)
}
onClick={handleSave}
disabled={isLoading || (!mediaName && !mediaFilename)}
>
Сохранить
</Button>
</Box>
</Box>
</Box>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} disabled={isLoading}>
Отмена
</Button>
</DialogActions>
</Dialog>
<Snackbar
open={!!error}
autoHideDuration={6000}
onClose={() => setError(null)}
>
<Alert severity="error" onClose={() => setError(null)}>
{error}
</Alert>
</Snackbar>
<Snackbar
open={success}
autoHideDuration={3000}
onClose={() => setSuccess(false)}
>
<Alert severity="success" onClose={() => setSuccess(false)}>
Медиа успешно сохранено
</Alert>
</Snackbar>
</>
);
}
);