import { Bookmark, Clock3, Eye, MessageCircle, Send, Share2, Star } from "lucide-react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMemo, useState } from "react"; import { Link, useParams } from "react-router-dom"; import { useSession } from "../app/store/session"; import { commentsApi, contentApi } from "../shared/api/endpoints"; import { normalizeContentItem, normalizeContentList, toEntity, toList } from "../shared/api/normalize"; import { queryKeys } from "../shared/api/queryKeys"; import { Badge } from "../shared/ui/Badge"; import { Button } from "../shared/ui/Button"; import { Textarea } from "../shared/ui/Field"; import { MaterialCard } from "../shared/ui/MaterialCard"; import { ResponsiveImage } from "../shared/ui/ResponsiveImage"; import { EmptyState, ErrorState, Skeleton } from "../shared/ui/States"; import { NotFoundPage } from "./NotFoundPage"; function normalizeComment(comment) { return { id: comment.id ?? `${comment.createdAt ?? Date.now()}-${comment.author?.id ?? comment.user?.id ?? "comment"}`, author: comment.author?.name ?? comment.author ?? comment.user?.name ?? "Гость", text: comment.text ?? comment.content ?? comment.body ?? "", createdAt: comment.createdAt ?? comment.date ?? "", }; } function formatDate(value) { if (!value) return ""; const date = new Date(value); if (Number.isNaN(date.getTime())) return value; return date.toLocaleString("ru-RU", { day: "numeric", month: "long", year: "numeric" }); } function RatingBlock({ material, detailKey }) { const queryClient = useQueryClient(); const initialAverage = Number(material.ratingAverage ?? material.rating ?? 0); const initialCount = Number(material.ratingCount ?? material.ratingsCount ?? 0); const currentRating = Number(material.myRating ?? 0); const [selectedRating, setSelectedRating] = useState(currentRating); const rateMutation = useMutation({ mutationFn: (rating) => contentApi.update(material.id, { rating }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.contentDetail(detailKey) }); queryClient.invalidateQueries({ queryKey: queryKeys.contentDetail(material.id) }); }, }); const average = initialAverage.toFixed(1); return (

Оцените материал

Средняя оценка: {average} из 5, оценок: {initialCount}

{average}
{[1, 2, 3, 4, 5].map((value) => ( ))}
{rateMutation.isPending &&

Сохраняем оценку...

} {rateMutation.isSuccess &&

Спасибо, ваша оценка учтена.

} {rateMutation.isError &&

Не удалось сохранить оценку.

}
); } function CommentsBlock({ materialId }) { const user = useSession((state) => state.user); const [commentText, setCommentText] = useState(""); const [notice, setNotice] = useState(""); const queryClient = useQueryClient(); const commentsQuery = useQuery({ queryKey: queryKeys.comments(materialId), queryFn: () => commentsApi.list(materialId), enabled: Boolean(materialId), }); const createComment = useMutation({ mutationFn: (text) => commentsApi.create(materialId, { text }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.comments(materialId) }); setCommentText(""); setNotice("Комментарий добавлен."); }, }); const comments = useMemo(() => toList(commentsQuery.data).map(normalizeComment), [commentsQuery.data]); const submitComment = (event) => { event.preventDefault(); const text = commentText.trim(); if (!text) return; setNotice(""); createComment.mutate(text); }; return (

Комментарии

{comments.length}