• StatisticsPage component

    Returns Element

    export default function StatisticsPage() {
    const [loading, setLoading] = useState(true);
    const [statistics, setStatistics] = useState<StatisticsData | null>(null);
    const [activeTab, setActiveTab] = useState("overview");
    const [clearDialogOpen, setClearDialogOpen] = useState(false);
    const [clearing, setClearing] = useState(false);

    /**
    * Retrieves comprehensive listening statistics data
    *
    * Fetches the complete statistical dataset from the main process via IPC,
    * including temporal patterns, artist metrics, and session analytics.
    * Manages loading state for UI feedback and implements error handling
    * with appropriate user notifications. This is the primary data source
    * for all visualization components and statistical displays.
    *
    * The retrieved data includes:
    * - Global listening metrics and aggregated statistics
    * - Time-based distribution patterns (hourly, daily)
    * - Artist and track frequency analysis
    * - Detailed session history with metadata
    */
    const fetchStatistics = async () => {
    setLoading(true);
    try {
    const stats = await window.spotify.getStatistics();
    setStatistics(stats);
    } catch (error) {
    console.error("Failed to load statistics:", error);
    toast.error("Failed to load statistics", {
    description: "Could not retrieve listening statistics data.",
    });
    window.spotify.saveLog(`Error loading statistics: ${error}`, "ERROR");
    } finally {
    setLoading(false);
    }
    };

    useEffect(() => {
    fetchStatistics();
    }, []);

    // Calculate derived statistics
    const statsSummary = useMemo(() => {
    if (!statistics) return null;

    // Get the latest daily metrics
    const dates = Object.keys(statistics.dailyMetrics).sort().reverse();
    const recentDayKey = dates[0];
    const recentDay = recentDayKey
    ? statistics.dailyMetrics[recentDayKey]
    : null;

    // Calculate values
    return {
    totalListeningTime: formatTime(statistics.totalListeningTimeMs),
    skipRate: formatPercent(statistics.overallSkipRate),
    skipRateValue: statistics.overallSkipRate,
    discoveryRate: formatPercent(statistics.discoveryRate),
    totalTracks: statistics.totalUniqueTracks,
    totalArtists: statistics.totalUniqueArtists,
    mostActiveDay: getDayName(
    statistics.dailyDistribution.indexOf(
    Math.max(...statistics.dailyDistribution),
    ),
    ),
    peakListeningHour: getHourLabel(
    statistics.hourlyDistribution.indexOf(
    Math.max(...statistics.hourlyDistribution),
    ),
    ),
    recentTracksCount: recentDay?.tracksPlayed || 0,
    recentSkipCount: recentDay?.tracksSkipped || 0,
    recentListeningTime: formatTime(recentDay?.listeningTimeMs || 0),
    };
    }, [statistics]);

    // Get listening session details
    const recentSessions = useMemo(() => {
    if (!statistics?.sessions) return [];

    return statistics.sessions
    .sort(
    (a, b) => new Date(b.endTime).getTime() - new Date(a.endTime).getTime(),
    )
    .slice(0, 5)
    .map((session) => ({
    ...session,
    formattedDate: new Date(session.startTime).toLocaleDateString(),
    formattedTime: new Date(session.startTime).toLocaleTimeString([], {
    hour: "2-digit",
    minute: "2-digit",
    }),
    formattedDuration: formatTime(session.durationMs),
    skipRate:
    session.trackIds.length > 0
    ? session.skippedTracks / session.trackIds.length
    : 0,
    }));
    }, [statistics]);

    /**
    * Permanently erases all collected statistics data
    *
    * Implements a protected operation for removing all statistical data
    * from persistent storage. Includes safety measures like confirmation
    * dialog and loading states to prevent accidental data loss. After
    * successful data removal, refreshes the statistics view to reflect
    * the cleared state.
    *
    * This operation:
    * 1. Shows confirmation UI before proceeding
    * 2. Displays loading state during the operation
    * 3. Communicates with main process to perform actual data removal
    * 4. Provides clear success/failure feedback to the user
    * 5. Refreshes UI state to reflect the data removal
    */
    const handleClearStatistics = async () => {
    try {
    setClearing(true);
    const result = await window.spotify.clearStatistics();
    if (result) {
    toast.success("Statistics cleared successfully");
    fetchStatistics();
    } else {
    toast.error("Failed to clear statistics");
    }
    } catch (error) {
    console.error("Error clearing statistics:", error);
    toast.error("Failed to clear statistics", {
    description: "An error occurred while clearing statistics data.",
    });
    window.spotify.saveLog(`Error clearing statistics: ${error}`, "ERROR");
    } finally {
    setClearing(false);
    setClearDialogOpen(false);
    }
    };

    return (
    <div className="container mx-auto py-6">
    <div className="mb-6 flex flex-col justify-between gap-3 sm:flex-row">
    <div className="flex-1">
    <h1 className="text-2xl font-bold">Listening Statistics</h1>
    <p className="text-muted-foreground text-sm">
    Detailed insights into your Spotify listening habits and
    preferences.
    </p>
    </div>
    <div className="flex shrink-0 items-center gap-2">
    <Button
    onClick={fetchStatistics}
    disabled={loading}
    className="flex items-center gap-1"
    >
    {loading ? (
    "Loading..."
    ) : (
    <>
    <RefreshCw className="h-4 w-4" />
    <span>Refresh</span>
    </>
    )}
    </Button>

    <Button
    onClick={() => setClearDialogOpen(true)}
    disabled={loading || !statistics || clearing}
    variant="outline"
    className="flex items-center gap-1"
    >
    {clearing ? (
    "Clearing..."
    ) : (
    <>
    <Trash2 className="h-4 w-4" />
    <span>Clear</span>
    </>
    )}
    </Button>

    <ClearStatisticsDialog
    open={clearDialogOpen}
    onOpenChange={setClearDialogOpen}
    onClear={handleClearStatistics}
    clearing={clearing}
    />
    </div>
    </div>

    <Tabs
    defaultValue="overview"
    value={activeTab}
    onValueChange={setActiveTab}
    className="space-y-4"
    >
    <div className="bg-card mb-4 h-auto rounded-lg border shadow">
    <TabsList className="no-scrollbar flex min-h-fit w-full flex-wrap justify-start gap-2 p-2">
    <TooltipProvider>
    <Tooltip>
    <TooltipTrigger asChild>
    <TabsTrigger
    value="overview"
    className={`flex items-center gap-1.5 rounded-md border px-3 py-2 shadow-sm transition-all duration-200 ${
    activeTab === "overview"
    ? "bg-primary/10 border-primary/50 text-primary -translate-y-0.5 font-semibold shadow-md"
    : "bg-background/50 hover:bg-background/70 hover:border-border/50 border-transparent hover:-translate-y-0.5 hover:shadow"
    }`}
    >
    <LayoutDashboard
    className={`h-4 w-4 ${activeTab === "overview" ? "text-primary" : ""}`}
    />
    <span className="hidden sm:inline">Overview</span>
    </TabsTrigger>
    </TooltipTrigger>
    <TooltipContent className="sm:hidden">Overview</TooltipContent>
    </Tooltip>
    </TooltipProvider>

    <TooltipProvider>
    <Tooltip>
    <TooltipTrigger asChild>
    <TabsTrigger
    value="listening"
    className={`flex items-center gap-1.5 rounded-md border px-3 py-2 shadow-sm transition-all duration-200 ${
    activeTab === "listening"
    ? "bg-primary/10 border-primary/50 text-primary -translate-y-0.5 font-semibold shadow-md"
    : "bg-background/50 hover:bg-background/70 hover:border-border/50 border-transparent hover:-translate-y-0.5 hover:shadow"
    }`}
    >
    <GitBranch
    className={`h-4 w-4 ${activeTab === "listening" ? "text-primary" : ""}`}
    />
    <span className="hidden sm:inline">Listening Patterns</span>
    </TabsTrigger>
    </TooltipTrigger>
    <TooltipContent className="sm:hidden">
    Listening Patterns
    </TooltipContent>
    </Tooltip>
    </TooltipProvider>

    <TooltipProvider>
    <Tooltip>
    <TooltipTrigger asChild>
    <TabsTrigger
    value="time"
    className={`flex items-center gap-1.5 rounded-md border px-3 py-2 shadow-sm transition-all duration-200 ${
    activeTab === "time"
    ? "bg-primary/10 border-primary/50 text-primary -translate-y-0.5 font-semibold shadow-md"
    : "bg-background/50 hover:bg-background/70 hover:border-border/50 border-transparent hover:-translate-y-0.5 hover:shadow"
    }`}
    >
    <Clock
    className={`h-4 w-4 ${activeTab === "time" ? "text-primary" : ""}`}
    />
    <span className="hidden sm:inline">Time Analytics</span>
    </TabsTrigger>
    </TooltipTrigger>
    <TooltipContent className="sm:hidden">
    Time Analytics
    </TooltipContent>
    </Tooltip>
    </TooltipProvider>

    <TooltipProvider>
    <Tooltip>
    <TooltipTrigger asChild>
    <TabsTrigger
    value="artists"
    className={`flex items-center gap-1.5 rounded-md border px-3 py-2 shadow-sm transition-all duration-200 ${
    activeTab === "artists"
    ? "bg-primary/10 border-primary/50 text-primary -translate-y-0.5 font-semibold shadow-md"
    : "bg-background/50 hover:bg-background/70 hover:border-border/50 border-transparent hover:-translate-y-0.5 hover:shadow"
    }`}
    >
    <Music
    className={`h-4 w-4 ${activeTab === "artists" ? "text-primary" : ""}`}
    />
    <span className="hidden sm:inline">Artists</span>
    </TabsTrigger>
    </TooltipTrigger>
    <TooltipContent className="sm:hidden">Artists</TooltipContent>
    </Tooltip>
    </TooltipProvider>

    <TooltipProvider>
    <Tooltip>
    <TooltipTrigger asChild>
    <TabsTrigger
    value="sessions"
    className={`flex items-center gap-1.5 rounded-md border px-3 py-2 shadow-sm transition-all duration-200 ${
    activeTab === "sessions"
    ? "bg-primary/10 border-primary/50 text-primary -translate-y-0.5 font-semibold shadow-md"
    : "bg-background/50 hover:bg-background/70 hover:border-border/50 border-transparent hover:-translate-y-0.5 hover:shadow"
    }`}
    >
    <ListMusic
    className={`h-4 w-4 ${activeTab === "sessions" ? "text-primary" : ""}`}
    />
    <span className="hidden sm:inline">Sessions</span>
    </TabsTrigger>
    </TooltipTrigger>
    <TooltipContent className="sm:hidden">Sessions</TooltipContent>
    </Tooltip>
    </TooltipProvider>

    <TooltipProvider>
    <Tooltip>
    <TooltipTrigger asChild>
    <TabsTrigger
    value="tracks"
    className={`flex items-center gap-1.5 rounded-md border px-3 py-2 shadow-sm transition-all duration-200 ${
    activeTab === "tracks"
    ? "bg-primary/10 border-primary/50 text-primary -translate-y-0.5 font-semibold shadow-md"
    : "bg-background/50 hover:bg-background/70 hover:border-border/50 border-transparent hover:-translate-y-0.5 hover:shadow"
    }`}
    >
    <Disc
    className={`h-4 w-4 ${activeTab === "tracks" ? "text-primary" : ""}`}
    />
    <span className="hidden sm:inline">Tracks</span>
    </TabsTrigger>
    </TooltipTrigger>
    <TooltipContent className="sm:hidden">Tracks</TooltipContent>
    </Tooltip>
    </TooltipProvider>

    <TooltipProvider>
    <Tooltip>
    <TooltipTrigger asChild>
    <TabsTrigger
    value="devices"
    className={`flex items-center gap-1.5 rounded-md border px-3 py-2 shadow-sm transition-all duration-200 ${
    activeTab === "devices"
    ? "bg-primary/10 border-primary/50 text-primary -translate-y-0.5 font-semibold shadow-md"
    : "bg-background/50 hover:bg-background/70 hover:border-border/50 border-transparent hover:-translate-y-0.5 hover:shadow"
    }`}
    >
    <Smartphone
    className={`h-4 w-4 ${activeTab === "devices" ? "text-primary" : ""}`}
    />
    <span className="hidden sm:inline">Devices</span>
    </TabsTrigger>
    </TooltipTrigger>
    <TooltipContent className="sm:hidden">Devices</TooltipContent>
    </Tooltip>
    </TooltipProvider>

    <TooltipProvider>
    <Tooltip>
    <TooltipTrigger asChild>
    <TabsTrigger
    value="patterns"
    className={`flex items-center gap-1.5 rounded-md border px-3 py-2 shadow-sm transition-all duration-200 ${
    activeTab === "patterns"
    ? "bg-primary/10 border-primary/50 text-primary -translate-y-0.5 font-semibold shadow-md"
    : "bg-background/50 hover:bg-background/70 hover:border-border/50 border-transparent hover:-translate-y-0.5 hover:shadow"
    }`}
    >
    <SkipForward
    className={`h-4 w-4 ${activeTab === "patterns" ? "text-primary" : ""}`}
    />
    <span className="hidden sm:inline">Skip Patterns</span>
    </TabsTrigger>
    </TooltipTrigger>
    <TooltipContent className="sm:hidden">
    Skip Patterns
    </TooltipContent>
    </Tooltip>
    </TooltipProvider>

    <TooltipProvider>
    <Tooltip>
    <TooltipTrigger asChild>
    <TabsTrigger
    value="export"
    className={`flex items-center gap-1.5 rounded-md border px-3 py-2 shadow-sm transition-all duration-200 ${
    activeTab === "export"
    ? "bg-primary/10 border-primary/50 text-primary -translate-y-0.5 font-semibold shadow-md"
    : "bg-background/50 hover:bg-background/70 hover:border-border/50 border-transparent hover:-translate-y-0.5 hover:shadow"
    }`}
    >
    <FileDown
    className={`h-4 w-4 ${activeTab === "export" ? "text-primary" : ""}`}
    />
    <span className="hidden sm:inline">Export Data</span>
    </TabsTrigger>
    </TooltipTrigger>
    <TooltipContent className="sm:hidden">
    Export Data
    </TooltipContent>
    </Tooltip>
    </TooltipProvider>
    </TabsList>
    </div>

    {/* Overview Tab */}
    <TabsContent value="overview">
    <OverviewTab
    loading={loading}
    statistics={statistics}
    statsSummary={statsSummary}
    />
    </TabsContent>

    {/* Listening Patterns Tab */}
    <TabsContent value="listening">
    <ListeningPatternsTab loading={loading} statistics={statistics} />
    </TabsContent>

    {/* Time Analytics Tab */}
    <TabsContent value="time">
    <TimeAnalyticsTab loading={loading} statistics={statistics} />
    </TabsContent>

    {/* Artists Tab */}
    <TabsContent value="artists">
    <ArtistsTab loading={loading} statistics={statistics} />
    </TabsContent>

    {/* Sessions Tab */}
    <TabsContent value="sessions">
    <SessionsTab
    loading={loading}
    statistics={statistics}
    recentSessions={recentSessions}
    />
    </TabsContent>

    {/* Tracks Tab */}
    <TabsContent value="tracks">
    <TracksTab loading={loading} statistics={statistics} />
    </TabsContent>

    {/* Devices Tab */}
    <TabsContent value="devices">
    <DevicesTab loading={loading} statistics={statistics} />
    </TabsContent>

    {/* Skip Patterns Tab */}
    <TabsContent value="patterns">
    <SkipPatternsTab loading={loading} statistics={statistics} />
    </TabsContent>

    {/* Export Data Tab */}
    <TabsContent value="export">
    <ExportDataTab loading={loading} statistics={statistics} />
    </TabsContent>
    </Tabs>

    {/* Add the Toaster component from sonner */}
    <Toaster />
    </div>
    );
    }