fix: Language cache sight
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { articlesStore } from "@shared";
|
||||
import { articlesStore, authStore, Language } from "@shared";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
@@ -31,38 +31,56 @@ export const SelectArticleModal = observer(
|
||||
open,
|
||||
onClose,
|
||||
onSelectArticle,
|
||||
|
||||
linkedArticleIds = [], // Default to empty array if not provided
|
||||
linkedArticleIds = [],
|
||||
}: SelectArticleModalProps) => {
|
||||
const { articles, getArticle } = articlesStore; // articles here refers to fetched articles from store
|
||||
const { articles, getArticle, getArticleMedia } = articlesStore;
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [hoveredArticleId, setHoveredArticleId] = useState<string | null>(
|
||||
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
|
||||
null
|
||||
);
|
||||
const hoverTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
// Reset selection when modal opens/closes
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setSelectedArticleId(null);
|
||||
articlesStore.articleData = null;
|
||||
articlesStore.articleMedia = null;
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hoveredArticleId) {
|
||||
hoverTimerRef.current = setTimeout(() => {
|
||||
getArticle(hoveredArticleId);
|
||||
}, 200);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (hoverTimerRef.current) {
|
||||
clearTimeout(hoverTimerRef.current);
|
||||
const handleKeyPress = (event: KeyboardEvent) => {
|
||||
if (event.key.toLowerCase() === "enter") {
|
||||
event.preventDefault();
|
||||
if (selectedArticleId) {
|
||||
onSelectArticle(selectedArticleId);
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [hoveredArticleId, getArticle]);
|
||||
|
||||
const handleArticleHover = (articleId: string) => {
|
||||
setHoveredArticleId(articleId);
|
||||
};
|
||||
window.addEventListener("keydown", handleKeyPress);
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyPress);
|
||||
};
|
||||
}, [selectedArticleId, onSelectArticle, onClose]);
|
||||
|
||||
const handleArticleLeave = () => {
|
||||
setHoveredArticleId(null);
|
||||
if (hoverTimerRef.current) {
|
||||
clearTimeout(hoverTimerRef.current);
|
||||
const handleArticleClick = async (articleId: string) => {
|
||||
if (selectedArticleId === articleId) return;
|
||||
|
||||
setSelectedArticleId(articleId);
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
await Promise.all([getArticle(articleId), getArticleMedia(articleId)]);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch article data:", error);
|
||||
// Reset article data on error
|
||||
articlesStore.articleData = null;
|
||||
articlesStore.articleMedia = null;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -72,21 +90,38 @@ export const SelectArticleModal = observer(
|
||||
article.service_name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
|
||||
const token = localStorage.getItem("token");
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose} maxWidth="lg" fullWidth>
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
maxWidth="lg"
|
||||
fullWidth
|
||||
PaperProps={{
|
||||
sx: {
|
||||
minHeight: "80vh",
|
||||
maxHeight: "90vh",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle>Выберите существующую статью</DialogTitle>
|
||||
<DialogContent
|
||||
className="flex gap-4"
|
||||
dividers // Adds a divider below the title and above the actions
|
||||
sx={{ height: "600px", display: "flex", flexDirection: "row" }} // Fixed height for DialogContent
|
||||
dividers
|
||||
sx={{
|
||||
height: "600px",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
p: 2,
|
||||
}}
|
||||
>
|
||||
<Paper className="w-[66%] flex flex-col">
|
||||
<Paper className="w-[66%] flex flex-col" elevation={2}>
|
||||
<TextField
|
||||
fullWidth
|
||||
placeholder="Поиск статей..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
sx={{ mb: 2, mt: 1 }}
|
||||
sx={{ mb: 2, mt: 1, px: 2 }}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
@@ -95,27 +130,51 @@ export const SelectArticleModal = observer(
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<List sx={{ flexGrow: 1, overflowY: "auto" }}>
|
||||
{filteredArticles.map((article) => (
|
||||
<ListItemButton
|
||||
key={article.id}
|
||||
onClick={() => onSelectArticle(article.id)}
|
||||
onMouseEnter={() => handleArticleHover(article.id)}
|
||||
onMouseLeave={handleArticleLeave}
|
||||
sx={{
|
||||
borderRadius: 1,
|
||||
mb: 0.5,
|
||||
"&:hover": {
|
||||
backgroundColor: "action.hover",
|
||||
},
|
||||
}}
|
||||
<List sx={{ flexGrow: 1, overflowY: "auto", px: 2 }}>
|
||||
{filteredArticles.length === 0 ? (
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="text.secondary"
|
||||
sx={{ p: 2, textAlign: "center" }}
|
||||
>
|
||||
<ListItemText primary={article.service_name} />
|
||||
</ListItemButton>
|
||||
))}
|
||||
{searchQuery ? "Статьи не найдены" : "Нет доступных статей"}
|
||||
</Typography>
|
||||
) : (
|
||||
filteredArticles.map((article) => (
|
||||
<ListItemButton
|
||||
key={article.id}
|
||||
onClick={() => handleArticleClick(article.id)}
|
||||
onDoubleClick={() => onSelectArticle(article.id)}
|
||||
selected={selectedArticleId === article.id}
|
||||
disabled={isLoading}
|
||||
sx={{
|
||||
borderRadius: 1,
|
||||
mb: 0.5,
|
||||
"&:hover": {
|
||||
backgroundColor: "action.hover",
|
||||
},
|
||||
"&.Mui-selected": {
|
||||
backgroundColor: "primary.main",
|
||||
color: "primary.contrastText",
|
||||
"&:hover": {
|
||||
backgroundColor: "primary.dark",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ListItemText
|
||||
primary={article.service_name}
|
||||
primaryTypographyProps={{
|
||||
fontWeight:
|
||||
selectedArticleId === article.id ? "bold" : "normal",
|
||||
}}
|
||||
/>
|
||||
</ListItemButton>
|
||||
))
|
||||
)}
|
||||
</List>
|
||||
</Paper>
|
||||
<Paper className="flex-1 flex flex-col">
|
||||
<Paper className="flex-1 flex flex-col" elevation={2}>
|
||||
<Box
|
||||
className="rounded-2xl overflow-hidden"
|
||||
sx={{
|
||||
@@ -127,60 +186,109 @@ export const SelectArticleModal = observer(
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
{/* Media Preview Area */}
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: 200,
|
||||
flexShrink: 0,
|
||||
backgroundColor: "grey.300",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<ImagePlus size={48} color="grey" />
|
||||
</Box>
|
||||
{isLoading ? (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Typography color="white">Загрузка...</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
<>
|
||||
{articlesStore.articleMedia && (
|
||||
<Box sx={{ p: 2, backgroundColor: "rgba(0,0,0,0.1)" }}>
|
||||
<img
|
||||
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
||||
articlesStore.articleMedia.id
|
||||
}/download?token=${token}`}
|
||||
alt={articlesStore.articleMedia.filename}
|
||||
style={{
|
||||
maxWidth: "100%",
|
||||
height: "auto",
|
||||
maxHeight: "300px",
|
||||
objectFit: "contain",
|
||||
borderRadius: 8,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{!articlesStore.articleMedia && (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: 200,
|
||||
flexShrink: 0,
|
||||
backgroundColor: "rgba(0,0,0,0.1)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<ImagePlus size={48} color="white" />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Title Area */}
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "70px",
|
||||
background: "#877361",
|
||||
display: "flex",
|
||||
flexShrink: 0,
|
||||
alignItems: "center",
|
||||
borderBottom: "1px solid",
|
||||
px: 2,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" color="white">
|
||||
{articlesStore.articleData?.heading ||
|
||||
"Нет данных для предпросмотра"}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
minHeight: "70px",
|
||||
background: "#877361",
|
||||
display: "flex",
|
||||
flexShrink: 0,
|
||||
alignItems: "center",
|
||||
borderBottom: "1px solid rgba(255,255,255,0.1)",
|
||||
px: 2,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" color="white">
|
||||
{articlesStore.articleData?.heading || "Выберите статью"}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Body Preview Area */}
|
||||
<Box
|
||||
sx={{
|
||||
px: 2,
|
||||
flexGrow: 1,
|
||||
overflowY: "auto",
|
||||
backgroundColor: "#877361", // To make markdown readable
|
||||
color: "white",
|
||||
py: 1,
|
||||
}}
|
||||
>
|
||||
<ReactMarkdownComponent
|
||||
value={articlesStore.articleData?.body || ""}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
px: 2,
|
||||
flexGrow: 1,
|
||||
overflowY: "auto",
|
||||
backgroundColor: "#877361",
|
||||
color: "white",
|
||||
py: 1,
|
||||
}}
|
||||
>
|
||||
{articlesStore.articleData?.body ? (
|
||||
<ReactMarkdownComponent
|
||||
value={articlesStore.articleData.body}
|
||||
/>
|
||||
) : (
|
||||
<Typography
|
||||
color="rgba(255,255,255,0.7)"
|
||||
sx={{ textAlign: "center", mt: 4 }}
|
||||
>
|
||||
Предпросмотр статьи появится здесь
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Paper>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<DialogActions sx={{ p: 2 }}>
|
||||
<Button onClick={onClose}>Отмена</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() =>
|
||||
selectedArticleId && onSelectArticle(selectedArticleId)
|
||||
}
|
||||
disabled={!selectedArticleId || isLoading}
|
||||
>
|
||||
Выбрать
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user