import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import React from "react"; import { useToken } from ".."; import { getReviewSubmission, postReviewEvaluation } from "@/shared/api/review"; import { Loading } from "@/components/ui/loading"; import { Review, ReviewCriteria, ReviewEvaluation, } from "@/shared/types/review"; import dayjs from "dayjs"; import { Button } from "@/components/ui/button"; import { cn } from "@/shared/lib/utils"; import { ofetch } from "ofetch"; import { File } from "lucide-react"; interface ReviewDialogProps { reviewId: string; children: React.ReactNode; } export const ReviewDialog = ({ reviewId, children }: ReviewDialogProps) => { return ( {children} ); }; const ReviewScreen = ({ reviewId }: { reviewId: string }) => { const queryClient = useQueryClient(); const token = useToken(); const { data: review, isLoading } = useQuery({ queryKey: ["review", reviewId], queryFn: async () => getReviewSubmission(token, reviewId), }); const [evaluation, setEvaluation] = React.useState<{ [key: string]: ReviewEvaluation; }>({}); React.useEffect(() => { if (review?.evaluation) { setEvaluation( review.evaluation.reduce( (acc, e) => { acc[e.slug] = e; return acc; }, {} as { [key: string]: ReviewEvaluation }, ), ); } }, [review?.evaluation]); const onSubmit = React.useCallback(async () => { const e: ReviewEvaluation[] | undefined = review?.criteries?.map((c) => { return ( evaluation[c.slug] ?? { slug: c.slug, mark: 0, } ); }); if (!e) { return; } await postReviewEvaluation(token, reviewId, e); queryClient.invalidateQueries({ queryKey: ["submissions", token], }); }, [review?.criteries, evaluation, token, queryClient]); if (isLoading) { return ; } if (!review) { queryClient.invalidateQueries({ queryKey: ["submissions", token], }); return; } return (
); }; const ReviewHeader = ({ review }: { review: Review }) => { const id = review.id.split("-").at(-1)?.slice(0, 6); return (

{review.competition_name}

{review.task_title}

{id} {dayjs(review.submitted_at).format("D MMMM, HH:mm")}
); }; const ReviewDescription = ({ review }: { review: Review }) => { if (!review.description) { return; } return (

Условие

{review.description}
); }; const ReviewContent = ({ review }: { review: Review }) => { const extension = review.content.split(".").at(-1); const filename = review.content.split("/").at(-1); const { data: content, isLoading } = useQuery({ queryKey: ["review-file", review.id], queryFn: async () => await ofetch(review.content), }); if (isLoading) { return null; } return (

Ответ

{extension === "txt" ? ( content ) : ( {filename} )}
); }; const ReviewCriteriesList = ({ review, evaluation, setEvaluation, }: { review: Review; evaluation: { [key: string]: ReviewEvaluation }; setEvaluation: React.Dispatch< React.SetStateAction<{ [key: string]: ReviewEvaluation; }> >; }) => { const onChange = React.useCallback( (slug: string, value?: number) => { if (!value || isNaN(value)) { setEvaluation((prev) => ({ ...prev, [slug]: { slug, mark: 0 } })); return; } if ( value < 0 || value > (review.criteries?.filter((c) => c.slug === slug).at(0)?.max_value ?? 0) ) { return setEvaluation((prev) => ({ ...prev, [slug]: { slug, mark: 0 }, })); } setEvaluation((prev) => ({ ...prev, [slug]: { slug, mark: value } })); }, [evaluation], ); return (

Критерии

{review.criteries?.map((c) => { const value = evaluation[c.slug]?.mark; return ( ); })}
); }; const Criteria = ({ criteria, value, onChange, }: { criteria: ReviewCriteria; value?: number; onChange?: (slug: string, value: number) => void; }) => { return (

{criteria.name}

Максимальное значение — {criteria.max_value}

onChange?.(criteria.slug, Number(e.target.value))} />
); }; const ReviewFooter = ({ evaluation, criteries, onSubmit, }: { evaluation: { [key: string]: ReviewEvaluation }; criteries?: ReviewCriteria[]; onSubmit: () => Promise; }) => { const score = Object.values(evaluation).reduce((acc, e) => acc + e.mark, 0); const maxScore = criteries?.reduce((acc, c) => acc + c.max_value, 0); return (
button]:bg-correct-foreground [&>button]:hover:bg-correct-foreground/80 [&>button]:text-correct": score === maxScore, "bg-partial *:text-partial-foreground [&>button]:bg-partial-foreground [&>button]:hover:bg-partial-foreground/80 [&>button]:text-partial": score > 0 && score < (maxScore ?? 0), "bg-wrong *:text-wrong-foreground [&>button]:bg-wrong-foreground [&>button]:hover:bg-wrong-foreground/80 [&>button]:text-wrong": score === 0, })} >

Итого

{score <= 0 ? "Неверный ответ" : `Зачтено ${score}/${maxScore}`}

); };