"use client"; import { Music2, Link2, Unlink, Loader2, AlertTriangle, Eye, EyeOff } from "lucide-react"; import { useEffect, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import StatCard from "@/components/StatCard"; import StatsChart from "@/components/StatsCharts"; interface TikTokStats { followers: number; likes: number; videoCount: number; views: number; displayName: string; avatarUrl: string; plan: "free" | "pro" | "elite" | "team"; } type CardId = "followers" | "likes" | "videos" | "views" | "ratio"; const defaultVisibleCards: Record = { followers: true, likes: true, videos: true, views: true, ratio: true, }; const visibleCardsStorageKey = "tiktok.visibleCards"; function parseVisibleCards(value: string | null): Record { if (!value) return defaultVisibleCards; try { const parsed = JSON.parse(value) as Partial>; return { followers: typeof parsed.followers === "boolean" ? parsed.followers : defaultVisibleCards.followers, likes: typeof parsed.likes === "boolean" ? parsed.likes : defaultVisibleCards.likes, videos: typeof parsed.videos === "boolean" ? parsed.videos : defaultVisibleCards.videos, views: typeof parsed.views === "boolean" ? parsed.views : defaultVisibleCards.views, ratio: typeof parsed.ratio === "boolean" ? parsed.ratio : defaultVisibleCards.ratio, }; } catch { return defaultVisibleCards; } } export default function TikTokPage() { const [stats, setStats] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [disconnecting, setDisconnecting] = useState(false); const [showDisconnectConfirm, setShowDisconnectConfirm] = useState(false); const [visibleCards, setVisibleCards] = useState>(defaultVisibleCards); const [visibleCardsHydrated, setVisibleCardsHydrated] = useState(false); const searchParams = useSearchParams(); const router = useRouter(); async function loadStats() { setLoading(true); setError(null); try { const res = await fetch("/api/tiktok/stats"); if (res.status === 404) { setStats(null); } else if (!res.ok) { const data = await res.json(); setError(data.error ?? "Erreur inconnue"); } else { const data = await res.json(); setStats(data); } } catch { setError("Erreur réseau"); } finally { setLoading(false); } } useEffect(() => { const persisted = parseVisibleCards(window.localStorage.getItem(visibleCardsStorageKey)); setVisibleCards(persisted); setVisibleCardsHydrated(true); loadStats(); }, []); useEffect(() => { if (!visibleCardsHydrated) return; window.localStorage.setItem(visibleCardsStorageKey, JSON.stringify(visibleCards)); }, [visibleCards, visibleCardsHydrated]); async function handleDisconnect() { if (disconnecting) return; setDisconnecting(true); try { await fetch("/api/tiktok/disconnect", { method: "POST" }); setStats(null); setShowDisconnectConfirm(false); router.replace("/tiktok"); } finally { setDisconnecting(false); } } const urlError = searchParams.get("error"); const cards = stats ? [ { id: "followers" as const, label: "Followers", value: stats.followers.toLocaleString("fr-FR"), sub: "Abonnés totaux", accent: "purple" as const, }, { id: "likes" as const, label: "Likes totaux", value: stats.likes.toLocaleString("fr-FR"), sub: "Cumul likes", accent: "red" as const, }, { id: "videos" as const, label: "Vidéos publiées", value: stats.videoCount.toLocaleString("fr-FR"), sub: "Vidéos au total", accent: "blue" as const, }, { id: "views" as const, label: "Vues vidéos", value: stats.views.toLocaleString("fr-FR"), sub: "Vues totales des vidéos", accent: "green" as const, }, { id: "ratio" as const, label: "Ratio likes/vidéo", value: stats.videoCount > 0 ? Math.round(stats.likes / stats.videoCount).toLocaleString("fr-FR") : "—", sub: "Moy. likes par vidéo", accent: "gold" as const, }, ] : []; const shownCards = cards.filter((card) => visibleCards[card.id]); const hiddenCards = cards.filter((card) => !visibleCards[card.id]); return (
{/* Header */}
Plateforme

TikTok

{stats && !loading && ( )}
{/* Erreur URL */} {urlError && (
{urlError === "access_denied" && "Accès refusé par TikTok."} {urlError === "token_exchange" && "Erreur lors de l'échange du token."} {urlError === "session_error" && "Erreur de session."} {!["access_denied", "token_exchange", "session_error"].includes(urlError) && `Erreur : ${urlError}`}
)} {/* Chargement */} {loading && (
Chargement...
)} {/* Connecté : stats */} {!loading && stats && ( <> {stats.displayName && (
{stats.avatarUrl && ( avatar )} {stats.displayName} Connecté
)} {hiddenCards.length > 0 && (
{hiddenCards.map((card) => ( ))}
)} {shownCards.length > 0 ? (
{shownCards.map((card) => (
))}
) : (

Toutes les cartes sont masquées

)} {/* Graph */} )} {!loading && !stats && !error && (

Connectez votre compte TikTok
pour afficher vos statistiques

Connecter TikTok
)} {!loading && error && (

{error}

)} {showDisconnectConfirm && (

Confirmer la déconnexion

Êtes-vous sûr de vouloir déconnecter votre compte TikTok ?

)}
); }