From e05aa510a5f6d0c149adf9a989528decc9042a17 Mon Sep 17 00:00:00 2001 From: lashman Date: Sat, 10 Jan 2026 23:06:56 +0200 Subject: [PATCH] puzzle ratings --- frontend/src/components/PuzzleRating.tsx | 59 ++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 frontend/src/components/PuzzleRating.tsx diff --git a/frontend/src/components/PuzzleRating.tsx b/frontend/src/components/PuzzleRating.tsx new file mode 100644 index 0000000..54ff4be --- /dev/null +++ b/frontend/src/components/PuzzleRating.tsx @@ -0,0 +1,59 @@ +import { useState, useEffect } from 'react' +import { useTranslation } from 'react-i18next' +import { IconStarFilled } from '@tabler/icons-react' +import { motion } from 'motion/react' +import * as api from '../api' +import { useStore } from '../store' + +export default function PuzzleRating() { + const { t } = useTranslation() + const puzzleId = useStore(s => s.puzzleId) + const [avg, setAvg] = useState(0) + const [count, setCount] = useState(0) + const [myRating, setMyRating] = useState(null) + const [hover, setHover] = useState(0) + + useEffect(() => { + if (!puzzleId) return + api.fetchRating(puzzleId).then(r => { + setAvg(r.average) + setCount(r.count) + setMyRating(r.my_rating) + }) + }, [puzzleId]) + + const rate = async (rating: number) => { + if (!puzzleId) return + const r = await api.ratePuzzle(puzzleId, rating) + setAvg(r.average) + setCount(r.count) + setMyRating(r.my_rating) + } + + return ( + + {t('rating.label')} +
setHover(0)}> + {[1, 2, 3, 4, 5].map(n => ( + + ))} +
+ {count > 0 && ( + {t('rating.avg', { avg, count, unit: count === 1 ? t('rating.rating') : t('rating.ratings') })} + )} +
+ ) +}