Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions src/app/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
'use client';

import React, { useEffect } from 'react';
import AnimatedBackground from '@/components/ui/AnimatedBackground';
import { FiRefreshCcw, FiHome, FiAlertTriangle } from 'react-icons/fi';
import Link from 'next/link';

export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Log the error to an error reporting service if needed
console.error('Application Error:', error);
}, [error]);

return (
<div className="relative min-h-screen w-full flex flex-col items-center justify-center bg-gray-900 overflow-hidden py-20 px-4 text-center">
{/* Background */}
<div className="absolute inset-0 z-0">
<AnimatedBackground />
</div>

{/* Decorative gradient overlay - Red tinted for error */}
<div className="absolute inset-0 bg-gradient-to-b from-red-600/5 via-transparent to-gray-900/40 z-1" />

<div className="relative z-10 max-w-2xl w-full">
{/* Error Icon / Illustration */}
<div className="mb-8 inline-flex items-center justify-center p-6 bg-red-500/10 border border-red-500/20 rounded-3xl shadow-[0_0_50px_rgba(239,68,68,0.1)]">
<FiAlertTriangle className="text-red-500 text-6xl md:text-7xl animate-pulse" />
</div>

{/* Text Content */}
<h1 className="text-4xl md:text-6xl font-extrabold text-white mb-6 tracking-tight">
Oops! Something <span className="text-transparent bg-clip-text bg-gradient-to-r from-red-400 to-orange-500">tripped up</span>.
</h1>

<p className="text-xl text-gray-400 mb-10 leading-relaxed">
The BrowsePing engine hit an unexpected bump. Don&apos;t worry, your data is safe. We can try to reload the current view or head back to safety.
</p>

{/* Technical Hint (Subtle) */}
{process.env.NODE_ENV === 'development' && (
<div className="bg-black/40 backdrop-blur-md border border-white/10 rounded-xl p-6 mb-12 text-left font-mono text-sm overflow-hidden overflow-x-auto text-red-300">
<p className="font-bold mb-2">Error Log:</p>
<p className="whitespace-pre-wrap">{error.message || 'No description available.'}</p>
{error.digest && <p className="mt-2 text-gray-500">Digest: {error.digest}</p>}
</div>
)}

{/* Action Buttons */}
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
<button
onClick={reset}
className="group relative flex items-center space-x-3 px-8 py-4 bg-white text-gray-900 font-bold rounded-2xl hover:scale-105 transition-all duration-300 shadow-xl"
>
<FiRefreshCcw className="group-hover:rotate-180 transition-transform duration-500" />
<span>Try to Reset</span>
</button>

<Link
href="/"
className="group flex items-center space-x-3 px-8 py-4 bg-gray-800 text-white font-bold rounded-2xl border border-white/10 hover:bg-gray-700 hover:scale-105 transition-all duration-300"
>
<FiHome className="text-gray-400" />
<span>Back to Homepage</span>
</Link>
</div>

{/* Footer Link */}
<div className="mt-16 text-gray-500 text-sm">
Persistent problem? <Link href="/contact" className="text-blue-400 hover:text-blue-300 underline underline-offset-4">Report it to us</Link>
</div>
</div>

{/* Bottom accent line */}
<div className="absolute bottom-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-red-500/20 to-transparent" />
</div>
);
}
23 changes: 23 additions & 0 deletions src/app/leaderboard/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import LeaderboardSkeleton from '@/components/leaderboard/LeaderboardSkeleton';

export default function LeaderboardLoading() {
return (
<div className="min-h-screen bg-gray-900 pt-20">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Skeleton Header */}
<div className="text-center mb-16 animate-pulse">
<div className="h-12 w-64 bg-gray-800 rounded-xl mx-auto mb-4" />
<div className="h-6 w-96 bg-gray-800/50 rounded-lg mx-auto" />
</div>

{/* Skeleton Toolbar */}
<div className="flex justify-end mb-8 animate-pulse">
<div className="h-10 w-28 bg-gray-800 rounded-lg" />
</div>

<LeaderboardSkeleton />
</div>
</div>
);
}
47 changes: 47 additions & 0 deletions src/app/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import AnimatedBackground from '@/components/ui/AnimatedBackground';

export default function Loading() {
return (
<div className="relative min-h-screen w-full flex flex-col items-center justify-center bg-gray-900 overflow-hidden">
{/* Background with higher opacity for the loading screen */}
<div className="absolute inset-0 z-0">
<AnimatedBackground />
</div>

{/* Decorative gradient overlay */}
<div className="absolute inset-0 bg-gradient-to-b from-blue-600/10 via-transparent to-purple-600/10 z-10" />

{/* Main Content */}
<div className="relative z-20 flex flex-col items-center px-4 text-center">
{/* Logo/Brand element */}
<div className="relative mb-12">
<div className="w-24 h-24 rounded-full bg-gradient-to-tr from-blue-500 to-purple-600 flex items-center justify-center shadow-[0_0_50px_rgba(59,130,246,0.5)] animate-pulse">
<div className="w-16 h-16 rounded-full border-4 border-white/20 border-t-white animate-spin" />
</div>

{/* Subtle glow effect */}
<div className="absolute -inset-4 bg-blue-500/20 blur-2xl rounded-full -z-10 animate-pulse" />
</div>

{/* Text */}
<h2 className="text-3xl md:text-4xl font-bold text-white mb-4 tracking-tight drop-shadow-lg">
BrowsePing
</h2>
<div className="flex items-center space-x-2">
<div className="w-2 h-2 rounded-full bg-blue-400 animate-bounce [animation-delay:-0.3s]" />
<div className="w-2 h-2 rounded-full bg-blue-500 animate-bounce [animation-delay:-0.15s]" />
<div className="w-2 h-2 rounded-full bg-blue-600 animate-bounce" />
<span className="text-gray-400 font-medium ml-2">Launching Social Experience...</span>
</div>
</div>

{/* Modern bottom indicator (Subtle) */}
<div className="absolute bottom-12 left-1/2 -translate-x-1/2 flex items-center space-x-3 text-gray-500 text-sm font-medium tracking-widest uppercase">
<div className="w-8 h-[1px] bg-gray-800" />
<span>Syncing Data</span>
<div className="w-8 h-[1px] bg-gray-800" />
</div>
</div>
);
}
63 changes: 63 additions & 0 deletions src/components/leaderboard/LeaderboardSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import { FiRefreshCw, FiClock } from 'react-icons/fi';

const LeaderboardSkeleton = () => {
return (
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Skeleton Table */}
<div className="bg-gray-800/30 backdrop-blur-sm border border-gray-700 rounded-2xl overflow-hidden shadow-2xl">
{/* Header Row Skeleton */}
<div className="bg-gray-800/50 px-6 py-4 border-b border-gray-700">
<div className="grid grid-cols-10 gap-4">
<div className="h-4 bg-gray-700 rounded w-12" />
<div className="col-span-5 h-4 bg-gray-700 rounded w-20" />
<div className="col-span-2 h-4 bg-gray-700 rounded w-24" />
<div className="col-span-2 h-4 bg-gray-700 rounded w-24" />
</div>
</div>

{/* Body Rows Skeleton */}
<div className="divide-y divide-gray-700">
{[...Array(5)].map((_, i) => (
<div key={i} className="px-6 py-4 animate-pulse">
<div className="grid grid-cols-10 gap-4 items-center">
{/* Rank Skeleton */}
<div className="col-span-1">
<div className="h-8 w-8 bg-gray-700/50 rounded-full" />
</div>

{/* User Info Skeleton */}
<div className="col-span-5 flex items-center space-x-3">
<div className="w-10 h-10 bg-gray-700/50 rounded-full" />
<div className="space-y-2 flex-1">
<div className="h-4 bg-gray-700/50 rounded w-32" />
<div className="h-3 bg-gray-800 rounded w-20" />
</div>
</div>

{/* Stats Skeletons */}
<div className="col-span-2 flex items-center space-x-2">
<FiClock className="text-gray-700" size={16} />
<div className="h-4 bg-gray-700/50 rounded w-12" />
</div>
<div className="col-span-2">
<div className="h-4 bg-gray-700/30 rounded w-16" />
</div>
</div>
</div>
))}
</div>

{/* Footer Syncing State */}
<div className="p-8 text-center bg-gray-800/20">
<div className="flex items-center justify-center space-x-3 text-gray-400">
<FiRefreshCw className="animate-spin text-blue-500" size={18} />
<span className="font-medium tracking-wide">Syncing browser data...</span>
</div>
</div>
</div>
</div>
);
};

export default LeaderboardSkeleton;
11 changes: 4 additions & 7 deletions src/components/leaderboard/PublicLeaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import React, { useState, useEffect } from 'react';
import { FiClock, FiUser, FiRefreshCw, FiAward } from 'react-icons/fi';
import LeaderboardSkeleton from './LeaderboardSkeleton';

interface LeaderboardUser {
rank: number;
Expand All @@ -28,6 +29,8 @@ const PublicLeaderboard = () => {
try {
setLoading(true);
setError(null);
// Artificial delay for visual verification of the skeleton
await new Promise(resolve => setTimeout(resolve, 1500));
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
const url = `${apiUrl}/api/leaderboard/public-top`;
const response = await fetch(url, {
Expand Down Expand Up @@ -103,13 +106,7 @@ const PublicLeaderboard = () => {
Community Leaderboard
</h2>
</div>

<div className="bg-gray-800/50 backdrop-blur-sm border border-gray-700 rounded-2xl p-8">
<div className="flex items-center justify-center space-x-3">
<FiRefreshCw className="animate-spin text-blue-400" size={24} aria-hidden="true" />
<span className="text-white text-lg">Loading leaderboard...</span>
</div>
</div>
<LeaderboardSkeleton />
</div>
</section>
);
Expand Down