Skip to content

Commit b62e916

Browse files
committed
Enhance RandomMemePage with improved navigation and filtering features
- Integrated `useNavigate` for streamlined navigation to order details and edit pages. - Added `showToast` functionality for user feedback on actions like copying links. - Implemented URL parameter handling for filtering meme orders by type and search term. - Updated `RandomMemeList` to reflect active filters and provide a copy link feature. - Refactored state management for better synchronization with URL parameters, enhancing user experience.
1 parent 0ca5c30 commit b62e916

File tree

7 files changed

+507
-46
lines changed

7 files changed

+507
-46
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { useCallback, useEffect, useMemo, useState } from "react";
2+
import { useParams, useNavigate } from "react-router-dom";
3+
import { Container, Spinner, Alert, Button } from "react-bootstrap";
4+
import { ArrowLeft } from "lucide-react";
5+
6+
import { RandomMeme } from "@/shared/api";
7+
import { MemeOrderDto } from "@/shared/api/http-clients/data-contracts";
8+
import { useToastModal } from "@/shared/Utils/ToastModal";
9+
10+
import { RandomMemeDetails } from "./components";
11+
import styles from "./RandomMemePage.module.scss";
12+
13+
const RandomMemeDetailsPage: React.FC = () => {
14+
const { id } = useParams<{ id: string }>();
15+
const navigate = useNavigate();
16+
const api = useMemo(() => new RandomMeme(), []);
17+
const { showToast } = useToastModal();
18+
19+
const [memeOrder, setMemeOrder] = useState<MemeOrderDto | null>(null);
20+
const [isLoading, setIsLoading] = useState<boolean>(true);
21+
const [error, setError] = useState<string>("");
22+
23+
// Загрузка деталей мема по ID
24+
const loadMemeDetails = useCallback(async () => {
25+
if (!id) {
26+
setError("ID мема не указан");
27+
setIsLoading(false);
28+
return;
29+
}
30+
31+
try {
32+
setIsLoading(true);
33+
setError("");
34+
35+
const response = await api.randomMemeOrdersDetail(id);
36+
setMemeOrder(response.data ?? null);
37+
38+
if (!response.data) {
39+
setError("Мем с указанным ID не найден");
40+
}
41+
} catch (e) {
42+
console.error("Ошибка загрузки деталей мема:", e);
43+
const errorMessage = e instanceof Error ? e.message : "Неизвестная ошибка";
44+
setError(errorMessage);
45+
showToast({
46+
type: "error",
47+
title: "Ошибка загрузки",
48+
message: errorMessage,
49+
});
50+
} finally {
51+
setIsLoading(false);
52+
}
53+
}, [id, api, showToast]);
54+
55+
// Загрузка при монтировании
56+
useEffect(() => {
57+
loadMemeDetails();
58+
}, [loadMemeDetails]);
59+
60+
// Обработчики
61+
const handleBack = useCallback(() => {
62+
navigate("/random-meme");
63+
}, [navigate]);
64+
65+
const handleEdit = useCallback(() => {
66+
if (memeOrder) {
67+
navigate(`/random-meme/edit/${memeOrder.id}`);
68+
}
69+
}, [navigate, memeOrder]);
70+
71+
const handleDelete = useCallback(async () => {
72+
if (!memeOrder) return;
73+
74+
try {
75+
await api.randomMemeOrdersDelete(memeOrder.id);
76+
showToast({
77+
type: "success",
78+
title: "Успешно",
79+
message: "Заказ мема успешно удален",
80+
});
81+
navigate("/random-meme");
82+
} catch (e) {
83+
console.error("Ошибка удаления заказа:", e);
84+
showToast({
85+
type: "error",
86+
title: "Ошибка",
87+
message: "Ошибка удаления заказа мема",
88+
});
89+
}
90+
}, [memeOrder, api, showToast, navigate]);
91+
92+
const handleRefresh = useCallback(() => {
93+
loadMemeDetails();
94+
}, [loadMemeDetails]);
95+
96+
// Отображение загрузки
97+
if (isLoading) {
98+
return (
99+
<Container className={styles.randomMemePage}>
100+
<div className="text-center py-5">
101+
<Spinner animation="border" role="status" className="mb-3">
102+
<span className="visually-hidden">Загрузка...</span>
103+
</Spinner>
104+
<h4>Загрузка деталей мема...</h4>
105+
<p className="text-muted">Получаем информацию о меме</p>
106+
</div>
107+
</Container>
108+
);
109+
}
110+
111+
// Отображение ошибки
112+
if (error || !memeOrder) {
113+
return (
114+
<Container className={styles.randomMemePage}>
115+
<Alert variant="danger">
116+
<Alert.Heading>Ошибка загрузки</Alert.Heading>
117+
<p className="mb-3">{error || "Мем не найден"}</p>
118+
<div className="d-flex gap-2">
119+
<Button variant="outline-primary" onClick={handleBack}>
120+
<ArrowLeft size={16} className="me-2" />
121+
Вернуться к списку
122+
</Button>
123+
<Button variant="outline-secondary" onClick={handleRefresh}>
124+
Попробовать снова
125+
</Button>
126+
</div>
127+
</Alert>
128+
</Container>
129+
);
130+
}
131+
132+
// Отображение деталей мема
133+
return (
134+
<Container className={styles.randomMemePage}>
135+
<RandomMemeDetails
136+
memeOrder={memeOrder}
137+
isLoading={isLoading}
138+
onBack={handleBack}
139+
onEdit={handleEdit}
140+
onDelete={handleDelete}
141+
onRefresh={handleRefresh}
142+
/>
143+
</Container>
144+
);
145+
};
146+
147+
export default RandomMemeDetailsPage;
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { useCallback, useEffect, useMemo, useState } from "react";
2+
import { useParams, useNavigate } from "react-router-dom";
3+
import { Container, Spinner, Alert, Button } from "react-bootstrap";
4+
import { ArrowLeft } from "lucide-react";
5+
6+
import { RandomMeme } from "@/shared/api";
7+
import { MemeOrderDto, MemeTypeDto } from "@/shared/api/http-clients/data-contracts";
8+
import { useToastModal } from "@/shared/Utils/ToastModal";
9+
10+
import { RandomMemeForm } from "./components";
11+
import styles from "./RandomMemePage.module.scss";
12+
13+
const RandomMemeEditPage: React.FC = () => {
14+
const { id } = useParams<{ id: string }>();
15+
const navigate = useNavigate();
16+
const api = useMemo(() => new RandomMeme(), []);
17+
const { showToast } = useToastModal();
18+
19+
const [memeOrder, setMemeOrder] = useState<MemeOrderDto | null>(null);
20+
const [memeTypes, setMemeTypes] = useState<MemeTypeDto[]>([]);
21+
const [isLoading, setIsLoading] = useState<boolean>(true);
22+
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
23+
const [error, setError] = useState<string>("");
24+
25+
// Загрузка данных
26+
const loadData = useCallback(async () => {
27+
if (!id) {
28+
setError("ID мема не указан");
29+
setIsLoading(false);
30+
return;
31+
}
32+
33+
try {
34+
setIsLoading(true);
35+
setError("");
36+
37+
// Загружаем заказ мема и типы параллельно
38+
const [orderResponse, typesResponse] = await Promise.all([
39+
api.randomMemeOrdersDetail(id),
40+
api.randomMemeTypesList()
41+
]);
42+
43+
setMemeOrder(orderResponse.data ?? null);
44+
setMemeTypes(typesResponse.data ?? []);
45+
46+
if (!orderResponse.data) {
47+
setError("Мем с указанным ID не найден");
48+
}
49+
} catch (e) {
50+
console.error("Ошибка загрузки данных:", e);
51+
const errorMessage = e instanceof Error ? e.message : "Неизвестная ошибка";
52+
setError(errorMessage);
53+
showToast({
54+
type: "error",
55+
title: "Ошибка загрузки",
56+
message: errorMessage,
57+
});
58+
} finally {
59+
setIsLoading(false);
60+
}
61+
}, [id, api, showToast]);
62+
63+
// Загрузка при монтировании
64+
useEffect(() => {
65+
loadData();
66+
}, [loadData]);
67+
68+
// Обработчики
69+
const handleBack = useCallback(() => {
70+
if (memeOrder) {
71+
navigate(`/random-meme/${memeOrder.id}`);
72+
} else {
73+
navigate("/random-meme");
74+
}
75+
}, [navigate, memeOrder]);
76+
77+
const handleSubmit = useCallback(async (data: Record<string, unknown>) => {
78+
if (!memeOrder) return;
79+
80+
try {
81+
setIsSubmitting(true);
82+
83+
const orderData = data as unknown as {
84+
filePath: string;
85+
memeTypeId: number;
86+
order: number;
87+
};
88+
89+
await api.randomMemeOrdersUpdate(memeOrder.id, orderData);
90+
91+
showToast({
92+
type: "success",
93+
title: "Успешно",
94+
message: "Заказ мема успешно обновлен",
95+
});
96+
97+
navigate(`/random-meme/${memeOrder.id}`);
98+
} catch (e) {
99+
console.error("Ошибка сохранения:", e);
100+
showToast({
101+
type: "error",
102+
title: "Ошибка",
103+
message: "Ошибка сохранения данных",
104+
});
105+
} finally {
106+
setIsSubmitting(false);
107+
}
108+
}, [memeOrder, api, showToast, navigate]);
109+
110+
// Отображение загрузки
111+
if (isLoading) {
112+
return (
113+
<Container className={styles.randomMemePage}>
114+
<div className="text-center py-5">
115+
<Spinner animation="border" role="status" className="mb-3">
116+
<span className="visually-hidden">Загрузка...</span>
117+
</Spinner>
118+
<h4>Загрузка данных для редактирования...</h4>
119+
<p className="text-muted">Получаем информацию о меме</p>
120+
</div>
121+
</Container>
122+
);
123+
}
124+
125+
// Отображение ошибки
126+
if (error || !memeOrder) {
127+
return (
128+
<Container className={styles.randomMemePage}>
129+
<Alert variant="danger">
130+
<Alert.Heading>Ошибка загрузки</Alert.Heading>
131+
<p className="mb-3">{error || "Мем не найден"}</p>
132+
<div className="d-flex gap-2">
133+
<Button variant="outline-primary" onClick={handleBack}>
134+
<ArrowLeft size={16} className="me-2" />
135+
Вернуться
136+
</Button>
137+
<Button variant="outline-secondary" onClick={loadData}>
138+
Попробовать снова
139+
</Button>
140+
</div>
141+
</Alert>
142+
</Container>
143+
);
144+
}
145+
146+
// Отображение формы редактирования
147+
return (
148+
<Container className={styles.randomMemePage}>
149+
<RandomMemeForm
150+
memeOrder={memeOrder}
151+
memeTypes={memeTypes}
152+
isSubmitting={isSubmitting}
153+
mode="edit"
154+
onSubmit={handleSubmit}
155+
onCancel={handleBack}
156+
/>
157+
</Container>
158+
);
159+
};
160+
161+
export default RandomMemeEditPage;

0 commit comments

Comments
 (0)