inital working version

This commit is contained in:
Stefan Hardegger
2025-07-22 21:49:40 +02:00
parent bebb799784
commit 59d29dceaf
98 changed files with 8027 additions and 856 deletions

View File

@@ -0,0 +1,79 @@
'use client';
import { useState } from 'react';
interface StoryRatingProps {
rating: number;
onRatingChange: (rating: number) => void;
readonly?: boolean;
size?: 'sm' | 'md' | 'lg';
}
export default function StoryRating({
rating,
onRatingChange,
readonly = false,
size = 'md'
}: StoryRatingProps) {
const [hoveredRating, setHoveredRating] = useState(0);
const [updating, setUpdating] = useState(false);
const sizeClasses = {
sm: 'text-sm',
md: 'text-lg',
lg: 'text-2xl',
};
const handleRatingClick = async (newRating: number) => {
if (readonly || updating) return;
try {
setUpdating(true);
await onRatingChange(newRating);
} catch (error) {
console.error('Failed to update rating:', error);
} finally {
setUpdating(false);
}
};
const displayRating = hoveredRating || rating;
return (
<div className="flex items-center gap-1">
{[1, 2, 3, 4, 5].map((star) => (
<button
key={star}
onClick={() => handleRatingClick(star)}
onMouseEnter={() => !readonly && setHoveredRating(star)}
onMouseLeave={() => !readonly && setHoveredRating(0)}
disabled={readonly || updating}
className={`${sizeClasses[size]} ${
star <= displayRating
? 'text-yellow-400'
: 'text-gray-300 dark:text-gray-600'
} ${
readonly
? 'cursor-default'
: updating
? 'cursor-not-allowed'
: 'cursor-pointer hover:text-yellow-400'
} transition-colors`}
aria-label={`Rate ${star} star${star !== 1 ? 's' : ''}`}
>
</button>
))}
{!readonly && (
<span className="ml-2 text-sm theme-text">
{rating > 0 ? `(${rating}/5)` : 'Rate this story'}
</span>
)}
{updating && (
<span className="ml-2 text-sm theme-text">Saving...</span>
)}
</div>
);
}