diff --git a/frontend/src/components/PodcastCard.tsx b/frontend/src/components/PodcastCard.tsx
new file mode 100644
index 0000000..7226720
--- /dev/null
+++ b/frontend/src/components/PodcastCard.tsx
@@ -0,0 +1,169 @@
+import React from 'react';
+import { Play, Plus, Check, Loader, Trash2, RefreshCw } from 'lucide-react';
+
+interface PodcastCardProps {
+ title: string;
+ author?: string;
+ imageUrl?: string;
+ rank?: number;
+ episodeCount?: number;
+ language?: string;
+ lastEpisodeTitle?: string;
+ lastEpisodeDate?: string;
+ // Subscribe action
+ onSubscribe?: () => void;
+ isSubscribed?: boolean;
+ isSubscribing?: boolean;
+ // Library actions
+ onSync?: () => void;
+ isSyncing?: boolean;
+ onUnsubscribe?: () => void;
+ confirmDelete?: boolean;
+ onConfirmDelete?: () => void;
+ onCancelDelete?: () => void;
+}
+
+export default function PodcastCard({
+ title,
+ author,
+ imageUrl,
+ rank,
+ episodeCount,
+ language,
+ lastEpisodeTitle,
+ lastEpisodeDate,
+ onSubscribe,
+ isSubscribed,
+ isSubscribing,
+ onSync,
+ isSyncing,
+ onUnsubscribe,
+ confirmDelete,
+ onConfirmDelete,
+ onCancelDelete,
+}: PodcastCardProps) {
+ return (
+
+ {/* Image */}
+
+ {imageUrl ? (
+

+ ) : (
+
🎙️
+ )}
+
+ {rank && (
+
+ #{rank}
+
+ )}
+
+ {isSubscribed && (
+
+
+ Abonné
+
+ )}
+
+ {/* Hover play overlay */}
+
+
+ {(episodeCount !== undefined || language) && (
+
+ {episodeCount !== undefined && (
+
+ {episodeCount} ép.
+
+ )}
+ {language && (
+
+ {language}
+
+ )}
+
+ )}
+
+
+ {/* Content */}
+
+ {author && (
+
+ {author}
+
+ )}
+
+
+ {title}
+
+
+ {lastEpisodeTitle && (
+
+
Dernier épisode
+
{lastEpisodeTitle}
+ {lastEpisodeDate && (
+
+ {new Date(lastEpisodeDate).toLocaleDateString('fr-FR', { day: 'numeric', month: 'long', year: 'numeric' })}
+
+ )}
+
+ )}
+
+ {/* Footer */}
+
+ {confirmDelete ? (
+
+
+
+
+ ) : onUnsubscribe ? (
+
+ {onSync && (
+
+ )}
+
+
+ ) : onSubscribe ? (
+
+ ) : null}
+
+
+
+ );
+}
diff --git a/frontend/src/pages/AddPodcast.tsx b/frontend/src/pages/AddPodcast.tsx
index 087fbb4..15e6349 100644
--- a/frontend/src/pages/AddPodcast.tsx
+++ b/frontend/src/pages/AddPodcast.tsx
@@ -8,7 +8,8 @@ import EpisodeCard from '../components/EpisodeCard';
import EpisodeDetails from '../components/EpisodeDetails';
import { useAudio } from '../context/AudioContext';
import AlertModal, { AlertType } from '../components/AlertModal';
-import { Search, Plus, Loader, Rss, Check, Globe, Sparkles, PlusCircle, LayoutGrid, ListMusic } from 'lucide-react';
+import { Search, Loader, Rss, Globe, Sparkles, PlusCircle, LayoutGrid, ListMusic } from 'lucide-react';
+import PodcastCard from '../components/PodcastCard';
import { authService } from '../services/authService';
import SuccessModal from '../components/SuccessModal';
@@ -237,33 +238,15 @@ export default function AddPodcast() {
) : searchType === 'podcasts' && (searchQuery.length > 0 ? searchResults?.podcasts : trendingResults?.podcasts) ? (
{(searchQuery.length > 0 ? searchResults?.podcasts : trendingResults?.podcasts).map((podcast: any) => (
-
-
- {podcast.imageUrl ? (
-

- ) : (
-
🎙️
- )}
-
-
-
-
-
{podcast.title}
-
{podcast.author || 'Inconnu'}
-
-
-
-
+
handleSubscribe(podcast)}
+ isSubscribed={isSubscribed(podcast.rssUrl)}
+ isSubscribing={subscribingId === podcast.id}
+ />
))}
) : searchType === 'episodes' && episodeResults?.episodes ? (
diff --git a/frontend/src/pages/Library.tsx b/frontend/src/pages/Library.tsx
index c974919..34ac14f 100644
--- a/frontend/src/pages/Library.tsx
+++ b/frontend/src/pages/Library.tsx
@@ -3,7 +3,8 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { podcastService, Podcast } from '../services/podcastService';
import { authService } from '../services/authService';
import Header from '../components/Header';
-import { BookOpen, Trash2, Loader, Rss, Play, RefreshCw, AlertCircle } from 'lucide-react';
+import { BookOpen, Rss } from 'lucide-react';
+import PodcastCard from '../components/PodcastCard';
import { Link } from 'react-router-dom';
import AlertModal, { AlertType } from '../components/AlertModal';
@@ -149,107 +150,22 @@ export default function Library() {
) : (
{podcasts.map((podcast) => (
-
-
-
-
- {podcast.imageUrl ? (
-

- ) : (
-
🎙️
- )}
-
-
-
-
-
-
-
- {podcast.title}
-
-
-
{podcast.author || 'Artiste Inconnu'}
-
-
-
- {podcast.episodeCount} EPISODES
-
- {podcast.language && (
-
- {podcast.language}
-
- )}
-
-
-
-
- {/* Status Section */}
-
- {podcast.episodeCount === 0 ? (
-
-
-
-
Besoin de synchronisation
-
-
-
- ) : podcast.lastEpisodeTitle && (
-
-
Dernier épisode
-
{podcast.lastEpisodeTitle}
-
- {new Date(podcast.lastEpisodeDate!).toLocaleDateString('fr-FR', { day: 'numeric', month: 'long', year: 'numeric' })}
-
-
- )}
-
-
- {/* Actions Footer */}
-
- {confirmDeleteId === (podcast.subscriptionId || podcast._id) ? (
-
-
-
-
- ) : (
-
-
-
-
- )}
-
-
+
syncMutation.mutate(podcast._id)}
+ isSyncing={syncMutation.isPending && syncMutation.variables === podcast._id}
+ onUnsubscribe={() => setConfirmDeleteId(podcast.subscriptionId || podcast._id)}
+ confirmDelete={confirmDeleteId === (podcast.subscriptionId || podcast._id)}
+ onConfirmDelete={handleConfirmUnsubscribe}
+ onCancelDelete={() => setConfirmDeleteId(null)}
+ />
))}
)}
diff --git a/frontend/src/pages/Trending.tsx b/frontend/src/pages/Trending.tsx
index 34cca85..2c89dd3 100644
--- a/frontend/src/pages/Trending.tsx
+++ b/frontend/src/pages/Trending.tsx
@@ -3,7 +3,8 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { discoveryService, DiscoveryPodcast } from '../services/discoveryService';
import { podcastService } from '../services/podcastService';
import Header from '../components/Header';
-import { Loader, Plus, Flame, Check, Sparkles, TrendingUp } from 'lucide-react';
+import { Loader, Flame, Sparkles, TrendingUp } from 'lucide-react';
+import PodcastCard from '../components/PodcastCard';
import { authService } from '../services/authService';
import SuccessModal from '../components/SuccessModal';
import AlertModal, { AlertType } from '../components/AlertModal';
@@ -201,56 +202,16 @@ export default function Trending() {
<>
{podcasts.map((podcast, index) => (
-
-
- {podcast.imageUrl ? (
-

- ) : (
-
🎙️
- )}
-
- Rank #{index + 1}
-
-
-
-
-
-
- {podcast.title}
-
- {podcast.author && (
-
{podcast.author}
- )}
-
-
-
-
+
handleSubscribe(podcast)}
+ isSubscribed={isSubscribed(podcast.rssUrl)}
+ isSubscribing={subscribingId === podcast.id}
+ />
))}