import { Card, Rating, Flowbite, Button, CustomFlowbiteTheme, Modal, } from "flowbite-react"; import { numberDeclension } from "#/api/utils"; import { useState } from "react"; import { ENDPOINTS } from "#/api/config"; const RatingTheme: CustomFlowbiteTheme = { ratingAdvanced: { progress: { base: "mx-4 h-5 w-3/4 rounded bg-gray-200 dark:bg-gray-700", }, }, }; export const ReleaseInfoRating = (props: { release_id: number; grade: number; token: string | null; votes: { 1: number; 2: number; 3: number; 4: number; 5: number; total: number; user: number | null; }; }) => { const [isRatingModalOpen, setIsRatingModalOpen] = useState(false); const [vote, setVote] = useState(props.votes.user); return ( <> <Card> <div className="flex flex-col gap-2 sm:items-center sm:flex-row"> <Rating> <Rating.Star /> <p className="ml-2 text-sm font-bold dark:text-white"> {props.grade.toFixed(2)} из 5 </p> </Rating> {props.token && ( <> <span className="mx-1.5 h-1 w-1 rounded-full bg-gray-500 dark:bg-gray-400 hidden lg:block" /> <div className="flex items-center gap-2"> {vote ? ( <> <p className="text-sm font-medium text-gray-500 dark:text-gray-400"> ваша оценка: {vote} </p> <Button size={"xs"} className="text-gray-500 border border-gray-600 rounded-full hover:bg-black hover:text-white hover:border-black dark:text-gray-400 dark:border-gray-500" color="inline" onClick={() => setIsRatingModalOpen(true)} > изменить </Button> </> ) : ( <Button size={"xs"} className="text-gray-500 border border-gray-600 rounded-full hover:bg-black hover:text-white hover:border-black dark:text-gray-400 dark:border-gray-500" color="inline" onClick={() => setIsRatingModalOpen(true)} > оценить </Button> )} </div> </> )} </div> <p className="text-sm font-medium text-gray-500 dark:text-gray-400"> {props.votes.total}{" "} {numberDeclension(props.votes.total, "голос", "голоса", "голосов")} </p> <Flowbite theme={{ theme: RatingTheme }}> <Rating.Advanced percentFilled={Math.floor( (props.votes["5"] / props.votes.total) * 100 )} className="mb-2" > 5 </Rating.Advanced> <Rating.Advanced percentFilled={Math.floor( (props.votes["4"] / props.votes.total) * 100 )} className="mb-2" > 4 </Rating.Advanced> <Rating.Advanced percentFilled={Math.floor( (props.votes["3"] / props.votes.total) * 100 )} className="mb-2" > 3 </Rating.Advanced> <Rating.Advanced percentFilled={Math.floor( (props.votes["2"] / props.votes.total) * 100 )} className="mb-2" > 2 </Rating.Advanced> <Rating.Advanced percentFilled={Math.floor( (props.votes["1"] / props.votes.total) * 100 )} > 1 </Rating.Advanced> </Flowbite> </Card> <ReleaseInfoRatingModal isOpen={isRatingModalOpen} setIsOpen={setIsRatingModalOpen} token={props.token} vote={vote} release_id={props.release_id} setUserVote={setVote} /> </> ); }; const ReleaseInfoRatingModal = (props: { isOpen: boolean; setIsOpen: any; setUserVote: any; token: string | null; vote: number; release_id: number; }) => { const [curElement, setCurElement] = useState(props.vote); const [vote, setVote] = useState(props.vote); const [isSending, setIsSending] = useState(false); async function _sendVote( action: "delete" | "add" = "add", vote: number | null = null ) { let url = `${ENDPOINTS.release.info}/vote/${action}/${props.release_id}`; if (action === "add") { url += `/${vote}`; } url += `?token=${props.token}`; fetch(url); } function _setVote( action: "delete" | "add" = "add", vote: number | null = null ) { if (props.token) { _sendVote(action, vote); setVote(vote); props.setUserVote(vote); props.setIsOpen(false); setIsSending(false); } } return ( <Modal dismissible show={props.isOpen} onClose={() => props.setIsOpen(false)} > <Modal.Header>Оценка</Modal.Header> <Modal.Body> <div> <div className="block sm:hidden"> <Rating size="md" className="justify-center"> {[1, 2, 3, 4, 5].map((element, index) => ( <Button key={`rating-md-${element}`} color={"inline"} onMouseOver={() => setCurElement(element)} onMouseOut={() => setCurElement(0)} onClick={() => setVote(element)} > <Rating.Star filled={index + 1 <= curElement || index + 1 <= vote} /> </Button> ))} </Rating> </div> <div className="hidden sm:block"> <Rating size="lg" className="justify-center"> {[1, 2, 3, 4, 5].map((element, index) => ( <Button key={`rating-lg-${element}`} color={"inline"} onMouseOver={() => setCurElement(element)} onMouseOut={() => setCurElement(0)} onClick={() => setVote(element)} > <Rating.Star filled={index + 1 <= curElement || index + 1 <= vote} /> </Button> ))} </Rating> </div> </div> </Modal.Body> <Modal.Footer> <div className="flex gap-1 ml-auto"> <Button disabled={isSending} color={"red"} onClick={() => { setIsSending(true); _setVote("delete", null); }} > Убрать оценку </Button> <Button disabled={isSending || vote === null} color={"blue"} onClick={() => { setIsSending(true); _sendVote("delete", null); setTimeout(() => _setVote("add", vote), 500); }} > Оценить </Button> </div> </Modal.Footer> </Modal> ); };