feat: implement EpisodeCard component and integrate it into the dashboard view

This commit is contained in:
Michael committed 2026-04-20 10:47:26 +02:00
1 parent 01c179c68b
commit 05eaa96e75
3 files changed
+64 -6

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"Bash(rtk tsc *)"
]
}
}
+36 -6
View File
@@ -1,16 +1,17 @@
import React from 'react';
import { Episode } from '../services/episodeService';
import { Play, Calendar, Activity, Check, Sparkles } from 'lucide-react';
import { Play, Pause, Calendar, Activity, Check, CheckCircle2, Circle } from 'lucide-react';
import { useAudio } from '../context/AudioContext';
interface EpisodeCardProps {
episode: Episode;
onPlay: (episode: Episode) => void;
onDetails?: (episode: Episode) => void;
onToggleRead?: (episode: Episode, completed: boolean) => void;
}
export default function EpisodeCard({ episode, onPlay, onDetails }: EpisodeCardProps) {
const { currentEpisode, isPlaying } = useAudio();
export default function EpisodeCard({ episode, onPlay, onDetails, onToggleRead }: EpisodeCardProps) {
const { currentEpisode, isPlaying, togglePlay } = useAudio();
const isCurrent = currentEpisode?._id === episode._id;
const podcast = typeof episode.podcastId === 'object' ? episode.podcastId : null;
const pubDate = new Date(episode.pubDate).toLocaleDateString('fr-FR', {
@@ -41,7 +42,16 @@ export default function EpisodeCard({ episode, onPlay, onDetails }: EpisodeCardP
const handlePlayClick = (e: React.MouseEvent) => {
e.stopPropagation();
onPlay(episode);
if (isCurrent) {
togglePlay();
} else {
onPlay(episode);
}
};
const handleToggleRead = (e: React.MouseEvent) => {
e.stopPropagation();
if (onToggleRead) onToggleRead(episode, !isCompleted);
};
return (
@@ -72,12 +82,15 @@ export default function EpisodeCard({ episode, onPlay, onDetails }: EpisodeCardP
</div>
{/* Hover Play Button Overlay */}
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center backdrop-blur-[2px]">
<div className={`absolute inset-0 bg-black/40 transition-opacity duration-300 flex items-center justify-center backdrop-blur-[2px] ${isCurrent ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'}`}>
<div
onClick={handlePlayClick}
className="w-16 h-16 rounded-full bg-white text-obsidian flex items-center justify-center shadow-glow-indigo transform scale-90 group-hover:scale-100 transition-transform duration-500 active:scale-95"
>
<Play className="w-6 h-6 fill-current ml-1" />
{isCurrent && isPlaying
? <Pause className="w-6 h-6 fill-current" />
: <Play className="w-6 h-6 fill-current ml-1" />
}
</div>
</div>
@@ -113,6 +126,23 @@ export default function EpisodeCard({ episode, onPlay, onDetails }: EpisodeCardP
<Calendar className="w-3.5 h-3.5" />
<span>{pubDate}</span>
</div>
{onToggleRead && (
<button
onClick={handleToggleRead}
title={isCompleted ? 'Marquer comme non lu' : 'Marquer comme lu'}
className={`flex items-center gap-1 px-2 py-1 rounded-lg transition-all duration-200 border ${
isCompleted
? 'text-emerald-400 border-emerald-500/30 hover:bg-emerald-500/10'
: 'text-[var(--text-secondary)] border-transparent hover:text-emerald-400 hover:border-emerald-500/30 hover:bg-emerald-500/10'
}`}
>
{isCompleted
? <CheckCircle2 className="w-3.5 h-3.5" />
: <Circle className="w-3.5 h-3.5" />
}
<span>{isCompleted ? 'Lu' : 'Lu ?'}</span>
</button>
)}
</div>
</div>
+21
View File
@@ -40,6 +40,25 @@ export default function Dashboard() {
setIsDetailsOpen(true);
};
const handleToggleRead = async (episode: Episode, completed: boolean) => {
try {
await episodeService.saveProgress(
episode._id,
completed ? episode.duration : 0,
completed
);
setEpisodes(prev =>
prev.map(ep =>
ep._id === episode._id
? { ...ep, progress: { position: completed ? ep.duration : 0, isCompleted: completed } }
: ep
)
);
} catch (error) {
console.error('Failed to toggle read state:', error);
}
};
// Load user
useEffect(() => {
const loadUser = async () => {
@@ -191,6 +210,7 @@ export default function Dashboard() {
episode={episode}
onPlay={playEpisode}
onDetails={handleOpenDetails}
onToggleRead={handleToggleRead}
/>
))}
</div>
@@ -223,6 +243,7 @@ export default function Dashboard() {
episode={episode}
onPlay={playEpisode}
onDetails={handleOpenDetails}
onToggleRead={handleToggleRead}
/>
))}
</div>