diff --git a/.env.local.example b/.env.local.example
deleted file mode 100644
index 88d321e..0000000
--- a/.env.local.example
+++ /dev/null
@@ -1,8 +0,0 @@
-# URL de l'application
-NEXT_PUBLIC_APP_URL=http://localhost:3000
-
-# URL par défaut du serveur Komga (optionnel)
-NEXT_PUBLIC_DEFAULT_KOMGA_URL=http://localhost:8080
-
-# Version de l'application (depuis package.json)
-NEXT_PUBLIC_APP_VERSION=$npm_package_version
\ No newline at end of file
diff --git a/src/app/api/komga/home/route.ts b/src/app/api/komga/home/route.ts
new file mode 100644
index 0000000..338eca2
--- /dev/null
+++ b/src/app/api/komga/home/route.ts
@@ -0,0 +1,122 @@
+import { NextResponse } from "next/server";
+import { cookies } from "next/headers";
+import { redirect } from "next/navigation";
+import { config } from "@/lib/config";
+import { cacheService } from "@/lib/services/cache.service";
+
+export async function GET() {
+ try {
+ // Récupérer les credentials Komga depuis le cookie
+ const cookieStore = cookies();
+ const configCookie = cookieStore.get("komgaCredentials");
+ console.log("API Home - Cookie komgaCredentials:", configCookie?.value);
+
+ if (!configCookie) {
+ console.log("API Home - Cookie komgaCredentials manquant");
+ return NextResponse.json({ error: "Configuration Komga manquante" }, { status: 401 });
+ }
+
+ let komgaConfig;
+ try {
+ komgaConfig = JSON.parse(atob(configCookie.value));
+ console.log("API Home - Config décodée:", {
+ serverUrl: komgaConfig.serverUrl,
+ hasCredentials: !!komgaConfig.credentials,
+ });
+ } catch (error) {
+ console.error("API Home - Erreur de décodage du cookie:", error);
+ return NextResponse.json({ error: "Configuration Komga invalide" }, { status: 401 });
+ }
+
+ if (!komgaConfig.credentials?.username || !komgaConfig.credentials?.password) {
+ console.log("API Home - Credentials manquants dans la config");
+ return NextResponse.json({ error: "Credentials Komga manquants" }, { status: 401 });
+ }
+
+ const auth = Buffer.from(
+ `${komgaConfig.credentials.username}:${komgaConfig.credentials.password}`
+ ).toString("base64");
+
+ console.log("API Home - Début des appels API");
+
+ try {
+ // Appels API parallèles
+ const [ongoingResponse, recentlyReadResponse, popularResponse] = await Promise.all([
+ // Séries en cours
+ fetch(
+ `${komgaConfig.serverUrl}/api/v1/series?read_status=IN_PROGRESS&sort=readDate,desc&page=0&size=20&media_status=READY`,
+ {
+ headers: {
+ Authorization: `Basic ${auth}`,
+ },
+ cache: "no-store", // Désactiver le cache
+ }
+ ).catch((error) => {
+ console.error("API Home - Erreur fetch ongoing:", error);
+ throw error;
+ }),
+ // Derniers livres lus
+ fetch(
+ `${komgaConfig.serverUrl}/api/v1/books?read_status=READ&sort=readDate,desc&page=0&size=20`,
+ {
+ headers: {
+ Authorization: `Basic ${auth}`,
+ },
+ cache: "no-store", // Désactiver le cache
+ }
+ ).catch((error) => {
+ console.error("API Home - Erreur fetch recently read:", error);
+ throw error;
+ }),
+ // Séries populaires
+ fetch(
+ `${komgaConfig.serverUrl}/api/v1/series?page=0&size=20&sort=metadata.titleSort,asc&media_status=READY`,
+ {
+ headers: {
+ Authorization: `Basic ${auth}`,
+ },
+ cache: "no-store", // Désactiver le cache
+ }
+ ).catch((error) => {
+ console.error("API Home - Erreur fetch popular:", error);
+ throw error;
+ }),
+ ]);
+
+ console.log("API Home - Status des réponses:", {
+ ongoing: ongoingResponse.status,
+ recentlyRead: recentlyReadResponse.status,
+ popular: popularResponse.status,
+ });
+
+ // Vérifier les réponses et récupérer les données
+ const [ongoing, recentlyRead, popular] = await Promise.all([
+ ongoingResponse.json(),
+ recentlyReadResponse.json(),
+ popularResponse.json(),
+ ]);
+
+ console.log("API Home - Données récupérées:", {
+ ongoingCount: ongoing.content?.length || 0,
+ recentlyReadCount: recentlyRead.content?.length || 0,
+ popularCount: popular.content?.length || 0,
+ });
+
+ // Retourner les données
+ return NextResponse.json({
+ ongoing: ongoing.content || [],
+ recentlyRead: recentlyRead.content || [],
+ popular: popular.content || [],
+ });
+ } catch (error) {
+ console.error("API Home - Erreur lors de la récupération des données:", error);
+ throw error;
+ }
+ } catch (error) {
+ console.error("API Home - Erreur générale:", error);
+ return NextResponse.json(
+ { error: "Erreur lors de la récupération des données" },
+ { status: 500 }
+ );
+ }
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 063ac6a..7276e10 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,37 +1,80 @@
"use client";
-export default function Home() {
- return (
-
-
-
Bienvenue sur Paniels
-
- Votre lecteur Komga moderne pour lire vos BD, mangas et comics préférés.
-
-
+import { HomeContent } from "@/components/home/HomeContent";
+import { useState, useEffect } from "react";
+import { useRouter } from "next/navigation";
+import { KomgaBook, KomgaSeries } from "@/types/komga";
-
-
-
Bibliothèques
-
- Accédez à vos bibliothèques Komga et parcourez vos collections.
-
-
-
-
-
Collections
-
- Organisez vos lectures en collections thématiques.
-
-
-
-
-
Lecture
-
- Profitez d'une expérience de lecture fluide et confortable.
-
-
-
-
- );
+interface HomeData {
+ onGoingSeries: KomgaSeries[];
+ recentlyRead: KomgaBook[];
+ popularSeries: KomgaSeries[];
+}
+
+export default function HomePage() {
+ const router = useRouter();
+ const [data, setData] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const fetchHomeData = async () => {
+ try {
+ const response = await fetch("/api/komga/home");
+ if (!response.ok) {
+ const errorData = await response.json();
+ throw new Error(errorData.error || `Erreur ${response.status}`);
+ }
+
+ const jsonData = await response.json();
+ // Transformer les données pour correspondre à l'interface HomeData
+ setData({
+ onGoingSeries: jsonData.ongoing || [],
+ recentlyRead: jsonData.recentlyRead || [],
+ popularSeries: jsonData.popular || [],
+ });
+ } catch (error) {
+ console.error("Erreur lors de la récupération des données:", error);
+ setError(error instanceof Error ? error.message : "Une erreur est survenue");
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ fetchHomeData();
+ }, []);
+
+ if (isLoading) {
+ return (
+
+
+
+ {[...Array(3)].map((_, i) => (
+
+
+
+ {[...Array(6)].map((_, j) => (
+
+ ))}
+
+
+ ))}
+
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
+
+ );
+ }
+
+ if (!data) return null;
+
+ return ;
}
diff --git a/src/components/home/HeroSection.tsx b/src/components/home/HeroSection.tsx
new file mode 100644
index 0000000..bc5aaa6
--- /dev/null
+++ b/src/components/home/HeroSection.tsx
@@ -0,0 +1,69 @@
+"use client";
+
+import { KomgaSeries } from "@/types/komga";
+import Image from "next/image";
+import { useState } from "react";
+import { ImageOff } from "lucide-react";
+import { cn } from "@/lib/utils";
+
+interface HeroSectionProps {
+ series: KomgaSeries[];
+}
+
+export function HeroSection({ series }: HeroSectionProps) {
+ console.log("HeroSection - Séries reçues:", {
+ count: series?.length || 0,
+ firstSeries: series?.[0],
+ });
+
+ return (
+
+ {/* Grille de couvertures en arrière-plan */}
+
+ {series?.map((series) => (
+
+ ))}
+
+
+ {/* Overlay gradient */}
+
+
+ {/* Contenu */}
+
+
+ Bienvenue sur Paniels
+
+
+ Votre bibliothèque numérique pour lire vos BD, mangas et comics préférés.
+
+
+
+ );
+}
+
+interface CoverImageProps {
+ series: KomgaSeries;
+}
+
+function CoverImage({ series }: CoverImageProps) {
+ const [imageError, setImageError] = useState(false);
+
+ return (
+
+ {!imageError ? (
+
setImageError(true)}
+ />
+ ) : (
+
+
+
+ )}
+
+ );
+}
diff --git a/src/components/home/HomeContent.tsx b/src/components/home/HomeContent.tsx
new file mode 100644
index 0000000..044b4fd
--- /dev/null
+++ b/src/components/home/HomeContent.tsx
@@ -0,0 +1,71 @@
+"use client";
+
+import { HeroSection } from "./HeroSection";
+import { MediaRow } from "./MediaRow";
+import { KomgaBook, KomgaSeries } from "@/types/komga";
+import { useRouter } from "next/navigation";
+
+interface HomeContentProps {
+ data: {
+ onGoingSeries: KomgaSeries[];
+ recentlyRead: KomgaBook[];
+ popularSeries: KomgaSeries[];
+ };
+}
+
+export function HomeContent({ data }: HomeContentProps) {
+ const router = useRouter();
+
+ const handleItemClick = (item: KomgaSeries | KomgaBook) => {
+ // Si c'est une série (a la propriété booksCount), on va vers la page de la série
+ if ("booksCount" in item) {
+ router.push(`/series/${item.id}`);
+ } else {
+ // Si c'est un livre, on va directement vers la page de lecture
+ router.push(`/books/${item.id}`);
+ }
+ };
+
+ // Vérification des données pour le debug
+ console.log("HomeContent - Données reçues:", {
+ onGoingCount: data.onGoingSeries?.length || 0,
+ recentlyReadCount: data.recentlyRead?.length || 0,
+ popularCount: data.popularSeries?.length || 0,
+ });
+
+ return (
+
+ {/* Hero Section - Afficher uniquement si nous avons des séries populaires */}
+ {data.popularSeries && data.popularSeries.length > 0 && (
+
+ )}
+
+ {/* Sections de contenu */}
+
+ {data.onGoingSeries && data.onGoingSeries.length > 0 && (
+
+ )}
+
+ {data.recentlyRead && data.recentlyRead.length > 0 && (
+
+ )}
+
+ {data.popularSeries && data.popularSeries.length > 0 && (
+
+ )}
+
+
+ );
+}
diff --git a/src/components/home/MediaRow.tsx b/src/components/home/MediaRow.tsx
new file mode 100644
index 0000000..eb89c82
--- /dev/null
+++ b/src/components/home/MediaRow.tsx
@@ -0,0 +1,126 @@
+"use client";
+
+import { KomgaBook, KomgaSeries } from "@/types/komga";
+import { ChevronLeft, ChevronRight, ImageOff } from "lucide-react";
+import Image from "next/image";
+import { useRef, useState } from "react";
+import { cn } from "@/lib/utils";
+
+interface MediaRowProps {
+ title: string;
+ items: (KomgaSeries | KomgaBook)[];
+ onItemClick?: (item: KomgaSeries | KomgaBook) => void;
+}
+
+export function MediaRow({ title, items, onItemClick }: MediaRowProps) {
+ const scrollContainerRef = useRef(null);
+ const [showLeftArrow, setShowLeftArrow] = useState(false);
+ const [showRightArrow, setShowRightArrow] = useState(true);
+
+ const handleScroll = () => {
+ if (!scrollContainerRef.current) return;
+
+ const { scrollLeft, scrollWidth, clientWidth } = scrollContainerRef.current;
+ setShowLeftArrow(scrollLeft > 0);
+ setShowRightArrow(scrollLeft < scrollWidth - clientWidth - 10);
+ };
+
+ const scroll = (direction: "left" | "right") => {
+ if (!scrollContainerRef.current) return;
+
+ const scrollAmount = direction === "left" ? -400 : 400;
+ scrollContainerRef.current.scrollBy({ left: scrollAmount, behavior: "smooth" });
+ };
+
+ if (!items.length) return null;
+
+ return (
+
+
{title}
+
+ {/* Bouton de défilement gauche */}
+ {showLeftArrow && (
+
+ )}
+
+ {/* Conteneur défilant */}
+
+ {items.map((item) => (
+ onItemClick?.(item)} />
+ ))}
+
+
+ {/* Bouton de défilement droit */}
+ {showRightArrow && (
+
+ )}
+
+
+ );
+}
+
+interface MediaCardProps {
+ item: KomgaSeries | KomgaBook;
+ onClick?: () => void;
+}
+
+function MediaCard({ item, onClick }: MediaCardProps) {
+ const [imageError, setImageError] = useState(false);
+
+ // Déterminer si c'est une série ou un livre
+ const isSeries = "booksCount" in item;
+ const title = isSeries
+ ? item.metadata.title
+ : item.metadata.title || `Tome ${item.metadata.number}`;
+
+ return (
+
+ );
+}
diff --git a/src/lib/config.ts b/src/lib/config.ts
index 8357982..c4470d2 100644
--- a/src/lib/config.ts
+++ b/src/lib/config.ts
@@ -1,3 +1 @@
-export const config = {
- serverUrl: process.env.NEXT_PUBLIC_KOMGA_URL || "http://localhost:8080",
-};
+export const config = {};