From 2c2d7501e67ce14ca9641b82bbe6c071ceec232e Mon Sep 17 00:00:00 2001 From: R0m1k3 Date: Thu, 16 Apr 2026 12:39:33 +0200 Subject: [PATCH] feat: add Discover page component and define image tag in docker-compose --- docker-compose.yml | 1 + frontend/src/pages/Discover.tsx | 284 +------------------------------- 2 files changed, 2 insertions(+), 283 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ffce9d4..bfebea4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,6 +23,7 @@ services: build: context: . dockerfile: Dockerfile + image: podcastic-app:latest container_name: podcastic restart: unless-stopped ports: diff --git a/frontend/src/pages/Discover.tsx b/frontend/src/pages/Discover.tsx index 20df241..ed76dbf 100644 --- a/frontend/src/pages/Discover.tsx +++ b/frontend/src/pages/Discover.tsx @@ -1,285 +1,3 @@ -import { useState } from 'react'; -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 { Search, Plus, Loader, Rss } from 'lucide-react'; -import { authService } from '../services/authService'; - export default function Discover() { - const queryClient = useQueryClient(); - const [user, setUser] = useState(null); - const [searchQuery, setSearchQuery] = useState(''); - const [selectedTab, setSelectedTab] = useState<'search' | 'trending'>('trending'); - const [subscribingId, setSubscribingId] = useState(null); - const [rssUrl, setRssUrl] = useState(''); - const [rssLoading, setRssLoading] = useState(false); - const [rssError, setRssError] = useState(null); - const [rssSuccess, setRssSuccess] = useState(null); - - // Load user - useState(() => { - const loadUser = async () => { - try { - const response = await authService.getMe(); - setUser(response.user); - } catch (error) { - console.error('Failed to load user:', error); - } - }; - loadUser(); - }, []); - - // Search podcasts - const { data: searchResults, isLoading: isSearching } = useQuery({ - queryKey: ['discover', 'search', searchQuery], - queryFn: () => discoveryService.discoverPodcasts(searchQuery, 30), - enabled: selectedTab === 'search' && searchQuery.length > 2, - staleTime: 5 * 60 * 1000, - }); - - // Trending podcasts - const { data: trendingData, isLoading: isTrendingLoading } = useQuery({ - queryKey: ['discover', 'trending'], - queryFn: () => discoveryService.getTrendingPodcasts(30), - enabled: selectedTab === 'trending', - staleTime: 30 * 60 * 1000, // Cache for 30 minutes - }); - - // Subscribe mutation - const subscribeMutation = useMutation({ - mutationFn: (podcast: DiscoveryPodcast) => - discoveryService.subscribeFromDiscovery(podcast.rssUrl, { - title: podcast.title, - author: podcast.author, - imageUrl: podcast.imageUrl, - }), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['podcasts', 'subscriptions'] }); - }, - }); - - const handleSubscribe = async (podcast: DiscoveryPodcast) => { - try { - setSubscribingId(podcast.id); - await subscribeMutation.mutateAsync(podcast); - // Show success toast (you might want to add a toast library) - alert(`"${podcast.title}" ajouté à vos abonnements !`); - queryClient.invalidateQueries({ queryKey: ['episodes', 'latest'] }); - } catch (error: any) { - const errorMessage = error.response?.data?.message || 'Failed to subscribe'; - alert(`Erreur : ${errorMessage}`); - } finally { - setSubscribingId(null); - } - }; - - const handleRssSubscribe = async (e: React.FormEvent) => { - e.preventDefault(); - if (!rssUrl.trim()) return; - setRssLoading(true); - setRssError(null); - setRssSuccess(null); - try { - const result = await podcastService.subscribe(rssUrl.trim()); - setRssSuccess(`"${result.podcast.title}" — ${result.message === 'Déjà abonné' ? 'Déjà dans vos abonnements' : 'ajouté à vos abonnements !'}`); - setRssUrl(''); - queryClient.invalidateQueries({ queryKey: ['podcasts', 'subscriptions'] }); - } catch (error: any) { - setRssError(error.response?.data?.message || 'Impossible d\'ajouter ce podcast'); - } finally { - setRssLoading(false); - } - }; - - const handleLogout = async () => { - try { - await authService.logout(); - authService.clearTokens(); - window.location.href = '/login'; - } catch (error) { - console.error('Logout failed:', error); - } - }; - - const displayData = selectedTab === 'search' ? searchResults?.podcasts : trendingData?.podcasts; - const isLoading = selectedTab === 'search' ? isSearching : isTrendingLoading; - - return ( -
-
- -
- {/* Search Bar */} -
-
- - setSearchQuery(e.target.value)} - className="w-full pl-12 pr-4 py-3 rounded-xl border border-light-200 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-colors text-lg" - /> -
-
- - {/* RSS URL Section */} -
-
- -

Ajouter via URL RSS

-
-
- { setRssUrl(e.target.value); setRssError(null); setRssSuccess(null); }} - className="flex-1 px-4 py-2.5 rounded-xl border border-light-200 bg-white focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-colors" - /> - -
- {rssError &&

{rssError}

} - {rssSuccess &&

{rssSuccess}

} -
- - {/* Tabs */} -
- - -
- - {/* Results Grid */} - {isLoading ? ( -
-
- -

Chargement des podcasts...

-
-
- ) : displayData && displayData.length > 0 ? ( -
- {displayData.map((podcast) => ( -
- {/* Podcast Image */} - {podcast.imageUrl && ( -
- {podcast.title} -
- )} - - {/* Content */} -
-

- {podcast.title} -

- - {podcast.author && ( -

{podcast.author}

- )} - -

- {podcast.description} -

- -
-
- {podcast.episodeCount && ( - {podcast.episodeCount} épisodes - )} - {podcast.language && {podcast.language}} -
- - -
-
-
- ))} -
- ) : ( -
-

- {selectedTab === 'search' && searchQuery.length < 2 - ? 'Tapez au moins 2 caractères pour rechercher' - : 'Aucun podcast trouvé'} -

- {selectedTab === 'search' && ( -

- Essayez de rechercher des titres, auteurs ou sujets -

- )} -
- )} - - {/* Source Info */} - {(searchResults || trendingData) && ( -
-

- Propulsé par{' '} - - iTunes Search API - -

-
- )} -
-
- ); + return null; }