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
Binary file added public/reader-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/reader-profile-dummy-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
151 changes: 151 additions & 0 deletions src/app/dashboard/stats-and-achievements/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
"use client";

import { Header } from "@/components/dashboard/header";
import { TimeFilter } from "@/components/stats-achievements/time-filter";
import { StatsGrid } from "@/components/stats-achievements/stats-grid";
import { BadgeSection } from "@/components/stats-achievements/badge-section";
import { GoalsSection } from "@/components/stats-achievements/goals-section";
import { AchievementsSection } from "@/components/stats-achievements/achievements-section";
import { useState } from "react";

// Mock data for different time periods
const timeData = {
"This Week": {
hours: 16,
minutes: 37,
stats: [
{ label: "Books Completed", value: "2", color: "#096CFF" },
{
label: "Average Session",
value: "45",
unit: "Minutes",
color: "#096CFF",
},
{ label: "Most Read Genre", value: "Fiction", color: "#096CFF" },
{ label: "Most Read Author", value: "J.K. Rowling", color: "#096CFF" },
{ label: "Longest Read", value: "4", unit: "Hours", color: "#096CFF" },
{ label: "Sessions", value: "12", color: "#096CFF" },
{ label: "Abandoned", value: "1", color: "#096CFF" },
],
},
"This Month": {
hours: 68,
minutes: 24,
stats: [
{ label: "Books Completed", value: "8", color: "#096CFF" },
{
label: "Average Session",
value: "52",
unit: "Minutes",
color: "#096CFF",
},
{ label: "Most Read Genre", value: "Mystery", color: "#096CFF" },
{ label: "Most Read Author", value: "Agatha Christie", color: "#096CFF" },
{ label: "Longest Read", value: "6", unit: "Hours", color: "#096CFF" },
{ label: "Sessions", value: "45", color: "#096CFF" },
{ label: "Abandoned", value: "3", color: "#096CFF" },
],
},
"This Year": {
hours: 324,
minutes: 18,
stats: [
{ label: "Books Completed", value: "42", color: "#096CFF" },
{
label: "Average Session",
value: "48",
unit: "Minutes",
color: "#096CFF",
},
{ label: "Most Read Genre", value: "Sci-Fi", color: "#096CFF" },
{ label: "Most Read Author", value: "Isaac Asimov", color: "#096CFF" },
{ label: "Longest Read", value: "8", unit: "Hours", color: "#096CFF" },
{ label: "Sessions", value: "186", color: "#096CFF" },
{ label: "Abandoned", value: "12", color: "#096CFF" },
],
},
"All Time": {
hours: 1247,
minutes: 52,
stats: [
{ label: "Books Completed", value: "156", color: "#096CFF" },
{
label: "Average Session",
value: "51",
unit: "Minutes",
color: "#096CFF",
},
{ label: "Most Read Genre", value: "Fantasy", color: "#096CFF" },
{
label: "Most Read Author",
value: "Brandon Sanderson",
color: "#096CFF",
},
{ label: "Longest Read", value: "12", unit: "Hours", color: "#096CFF" },
{ label: "Sessions", value: "743", color: "#096CFF" },
{ label: "Abandoned", value: "28", color: "#096CFF" },
],
},
};

export default function StatsAndAchievementsPage() {
const [selectedPeriod, setSelectedPeriod] = useState("This Week");
const currentData = timeData[selectedPeriod as keyof typeof timeData];

return (
<div className="min-h-screen bg-[#f8f9fa]">
<Header title="Stats & Achievements" />

<div className="p-4 lg:p-6 space-y-6">
{/* Total Hours Read Section */}
<div className="bg-white rounded-lg border border-[#e7e7e7] px-2 py-4 lg:p-6">
<div className="mb-4">
<h2 className="text-lg font-bold text-gray-400 mb-4">
Total Hours Read
</h2>
<TimeFilter
selectedPeriod={selectedPeriod}
onPeriodChange={setSelectedPeriod}
/>
</div>

{/* Large Time Display */}
<div className="bg-[#096CFF0A] rounded-lg p-6 mb-6 border-[0.5px] border-[#4BB1FF]">
<div className="flex items-center justify-center gap-4">
<div className="flex text-center items-center gap-2">
<div className="text-2xl lg:text-4xl font-bold text-blue-600 transition-all duration-300">
{currentData.hours}
</div>
<div className="text-sm text-blue-500 font-light mt-1 bg-[#096CFF1A] px-3 py-1 rounded-3xl">
Hours
</div>
</div>
<span className="text-[#D6ECFF] text-3xl">:</span>
<div className="text-center flex items-center gap-2">
<div className="text-2xl lg:text-4xl font-bold text-blue-600 transition-all duration-300">
{currentData.minutes}
</div>
<div className="text-sm font-light text-blue-500 mt-1 bg-[#096CFF1A] px-3 py-1 rounded-3xl">
Minutes
</div>
</div>
</div>
</div>

{/* Stats Grid */}
<StatsGrid stats={currentData.stats} />
</div>
<div className="rounded-xl border border-[#e7e7e7] bg-white space-y-4">
{/* Badge Section */}
<BadgeSection />

{/* Goals Section */}
<GoalsSection />

{/* Achievements Section */}
<AchievementsSection />
</div>
</div>
</div>
);
}
138 changes: 138 additions & 0 deletions src/app/reader-profile/following/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
"use client";

import { useState } from "react";
import { ArrowLeft, BadgeCheck, CheckCircle } from "lucide-react";
import { useRouter } from "next/navigation";
import { useMobileMenu } from "@/hooks/useMobileMenu";
import Image from "next/image";

interface FollowingUser {
id: string;
name: string;
username: string;
isVerified: boolean;
avatar?: string;
}

export default function FollowingPage() {
const router = useRouter();
const { openMobileMenu } = useMobileMenu();
const [activeTab, setActiveTab] = useState<
"total" | "verified" | "unverified"
>("total");

const followingUsers: FollowingUser[] = Array.from({ length: 9 }, (_, i) => ({
id: `user-${i + 1}`,
name: "Darrin Collins",
username: "@darrin_collins",
isVerified: i < 7, // First 7 are verified
avatar: "/reader-image.png?height=40&width=40",
}));

const totalCount = followingUsers.length;
const verifiedCount = followingUsers.filter((user) => user.isVerified).length;
const unverifiedCount = totalCount - verifiedCount;

const filteredUsers = followingUsers.filter((user) => {
if (activeTab === "verified") return user.isVerified;
if (activeTab === "unverified") return !user.isVerified;
return true;
});

const handleUnfollow = (userId: string) => {
console.log("Unfollow user:", userId);
};

return (
<div className="bg-white min-h-screen m-4 md:m-6">
{/* Mobile Header */}
<div className="lg:hidden bg-gray-50 p-4 flex items-center gap-3">
<button onClick={openMobileMenu} className="p-1">
<ArrowLeft size={20} className="text-gray-600" />
</button>
<h1 className="text-lg font-semibold text-gray-800">Following</h1>
</div>

<div className="p-6 max-w-4xl mx-auto">
{/* Desktop Header */}
<div className="hidden lg:flex items-start gap-3 mb-10">
<h1 className="text-xl font-semibold text-gray-800">Following</h1>
</div>

{/* Stats Tabs */}
<div className="flex gap-6 mb-6 ">
<button
onClick={() => setActiveTab("total")}
className={`px-3 py-1 text-sm font-medium border-[0.5px] rounded-2xl transition-colors ${
activeTab === "total"
? "text-gray-700"
: "border-transparent text-gray-500 hover:text-gray-700"
}`}
>
Total <span className="ml-1 font-bold">{totalCount}</span>
</button>
<button
onClick={() => setActiveTab("verified")}
className={`px-3 py-1 text-sm font-medium border-[0.5px] rounded-2xl transition-colors ${
activeTab === "verified"
? "text-gray-700"
: "border-transparent text-gray-500 hover:text-gray-700"
}`}
>
Verified <span className="ml-1 font-bold">{verifiedCount}</span>
</button>
<button
onClick={() => setActiveTab("unverified")}
className={`px-3 py-1 text-sm font-medium border-[0.5px] rounded-2xl transition-colors ${
activeTab === "unverified"
? "text-gray-700"
: "border-transparent text-gray-500 hover:text-gray-700"
}`}
>
Unverified <span className="ml-1 font-bold">{unverifiedCount}</span>
</button>
</div>

{/* Following List */}
<div className="space-y-4 ">
{filteredUsers.map((user) => (
<div
key={user.id}
className="flex items-center justify-between p-4 hover:bg-gray-50 transition-colors border-b-[0.5px] border-gray-100"
>
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-full bg-gray-200 overflow-hidden">
<Image
src={user.avatar || "/reader-image.png"}
alt={user.name}
width={40}
height={40}
className="object-cover w-full h-full"
/>
</div>
<div className="flex items-center gap-2">
<span className="font-medium text-gray-900">{user.name}</span>
{user.isVerified && (
<BadgeCheck size={16} className="text-blue-500" />
)}
</div>
</div>
<button
onClick={() => handleUnfollow(user.id)}
className="px-4 py-2 text-sm text-gray-600 hover:text-gray-800 bg-gray-50 hover:bg-gray-100 rounded-2xl transition-colors"
>
Unfollow
</button>
</div>
))}
</div>

{filteredUsers.length === 0 && (
<div className="text-center py-12">
<p className="text-gray-500">No users found in this category.</p>
</div>
)}
</div>
</div>
);
}
Loading
Loading