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
123 changes: 17 additions & 106 deletions frontend/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,30 +1,18 @@
@import url("https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap");

/* Font utility classes */
.font-nunito {
font-family: "Nunito", sans-serif;
}



.font-poppins {
font-family: "Poppins", sans-serif;
}

.font-roboto {
font-family: "Roboto", sans-serif;
}
@import "tailwindcss";

/* Theme variables */
:root {
--background: #ffffff;
--foreground: #171717;
}

/* Tailwind theme mapping (keep if you need it) */
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-sans: "Poppins", sans-serif;
--font-mono: var(--font-geist-mono);
}

Expand All @@ -35,112 +23,35 @@
}
}

/* ✅ Global styles (ONE body rule) */
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}

/* Toast animations */
@keyframes slide-in-from-right {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}

@keyframes slide-out-to-right {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}

.animate-in {
animation: slide-in-from-right 0.3s ease-out;
}

.slide-in-from-right-full {
animation: slide-in-from-right 0.3s ease-out;
}

/* Level Complete Animations */
@keyframes bounce-slow {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
font-family: "Poppins", sans-serif;
}

@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
/* Optional helper classes (if you still want them) */
.font-nunito {
font-family: "Nunito", sans-serif;
}

@keyframes slide-in-from-top {
from {
transform: translateY(-20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
.font-poppins {
font-family: "Poppins", sans-serif;
}

@keyframes slide-in-from-bottom {
from {
transform: translateY(20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
.font-roboto {
font-family: "Roboto", sans-serif;
}

@keyframes zoom-in {
/* Toast animations… (leave your keyframes below here) */
@keyframes slide-in-from-right {
from {
transform: scale(0.95);
transform: translateX(100%);
opacity: 0;
}
to {
transform: scale(1);
transform: translateX(0);
opacity: 1;
}
}

.animate-bounce-slow {
animation: bounce-slow 2s ease-in-out infinite;
}

.animate-fade-in {
animation: fade-in 0.5s ease-out forwards;
}

.animate-slide-in-from-top {
animation: slide-in-from-top 0.7s ease-out forwards;
}

.animate-slide-in-from-bottom {
animation: slide-in-from-bottom 0.5s ease-out forwards;
}

.animate-zoom-in {
animation: zoom-in 0.5s ease-out forwards;
}

/* ... rest of animations unchanged ... */
12 changes: 8 additions & 4 deletions frontend/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { Geist, Geist_Mono } from "next/font/google";
import { Poppins, Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { ToastProvider } from "@/components/ui/ToastProvider";
import StoreProvider from "@/providers/storeProvider";
import ClientLayout from "@/components/ClientLayout";

const poppins = Poppins({
subsets: ["latin"],
weight: ["300", "400", "500", "600", "700"],
variable: "--font-poppins",
});

const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
Expand All @@ -26,9 +32,7 @@ export default function RootLayout({
>
<StoreProvider>
<ToastProvider>

<ClientLayout>
{children}</ClientLayout>
<ClientLayout>{children}</ClientLayout>
</ToastProvider>
</StoreProvider>
</body>
Expand Down
123 changes: 109 additions & 14 deletions frontend/app/profile/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,114 @@
import React from "react";
"use client";

import { LogOut } from "lucide-react";
import Button from "@/components/Button";
import { ProfileHeader } from "@/components/profile/ProfileHeader";
import { ProfileOverview } from "@/components/profile/ProfileOverview";
import { AchievementsSection } from "@/components/profile/AchievementSection";

// Mock data - in a real app, this would come from props or API
const mockUserData = {
avatarUrl: "",
name: "Adeyemi Shola",
handle: "Shola",
joinDate: "Joined August 2025",
following: 5,
followers: 5,
walletId: "Fcwy...Ol5K",
};

const mockOverviewData = {
dayStreak: 3,
totalPoints: 830,
rank: 8,
challengeLevel: "Advanced",
};

const mockAchievements = [
{
id: "1",
icon: "brain" as const,
title: "Perfect Lessons",
date: "Aug 23, 2025",
},
{
id: "2",
icon: "droplet" as const,
title: "Longest Streak",
date: "Aug 23, 2025",
badge: "#3",
},
{
id: "3",
icon: "star" as const,
title: "Perfect Week",
date: "Aug 23, 2025",
badge: 1,
},
];

export default function ProfilePage() {
const handleLogout = () => {
// UI only - no actual logout logic
console.log("Logout clicked");
};

const handleViewAllAchievements = () => {
// UI only - no actual navigation
console.log("View all achievements clicked");
};

const ProfilePage = () => {
return (
<div className="min-h-screen w-full bg-slate-950 text-slate-100 px-6 py-10">
<div className="mx-auto w-full max-w-3xl rounded-3xl border border-slate-800 bg-slate-900/60 p-8">
<p className="text-xs font-semibold uppercase tracking-[0.25em] text-slate-400">
Placeholder Route
</p>
<h1 className="mt-3 text-3xl font-semibold text-white">Profile</h1>
<p className="mt-3 text-sm text-slate-400">
This page will contain your achievements, stats, and account settings.
</p>
<div className="min-h-screen grid bg-[#0A0F1A] ">
<div className="p-8 max-w-6xl ">
{/* Page Header */}

{/* Main Content */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Left Column - Profile Info & Overview */}
<div className="flex flex-col gap-8">
{/* Profile Header Card */}
<div className="rounded-2xl bg-card">
<ProfileHeader
avatarUrl={mockUserData.avatarUrl}
name={mockUserData.name}
handle={mockUserData.handle}
joinDate={mockUserData.joinDate}
following={mockUserData.following}
followers={mockUserData.followers}
walletId={mockUserData.walletId}
/>
</div>

{/* Overview Section */}
<ProfileOverview
dayStreak={mockOverviewData.dayStreak}
totalPoints={mockOverviewData.totalPoints}
rank={mockOverviewData.rank}
challengeLevel={mockOverviewData.challengeLevel}
/>
</div>

{/* Right Column - Achievements & Logout */}
<div className="flex flex-col gap-6">
{/* Achievements Section */}
<AchievementsSection
achievements={mockAchievements}
onViewAll={handleViewAllAchievements}
/>

{/* Logout Button */}
<Button
variant="logOut"
className="w-full border-primary text-primary hover:bg-primary/10 gap-2 "
onClick={handleLogout}
>
<div className="h-4 w-4" />
Log out
</Button>
</div>
</div>
</div>
</div>
);
};

export default ProfilePage;
}
16 changes: 13 additions & 3 deletions frontend/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react';

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
variant?: 'primary' | 'secondary';
variant?: 'primary' | 'secondary'| 'tertiary'| 'logOut';
}

const Button: React.FC<ButtonProps> = ({
Expand All @@ -16,8 +16,8 @@ const Button: React.FC<ButtonProps> = ({
}) => {
const containerBaseClasses = 'rounded-2xl';
const wrapperBaseClasses =
'flex flex-row items-center justify-center py-3 px-6 rounded-2xl -translate-y-1 transition-transform';
const textBaseClasses = 'font-bold text-lg text-center';
'flex flex-row items-center justify-center cursor-pointer py-3 px-6 rounded-md -translate-y-1 transition-transform';
const textBaseClasses = ' text-center';

const variantStyles = {
primary: {
Expand All @@ -30,6 +30,16 @@ const Button: React.FC<ButtonProps> = ({
wrapper: 'bg-transparent border-2 border-blue-600',
text: 'text-[#3B82F6]',
},
tertiary: {
container: 'bg-none w-10 h-5',
wrapper: 'border-2 border-[#E6E6E64D] sm:py-2 sm:px-10',
text: 'text-[#3B82F6]',
},
logOut: {
container: 'bg-none w-10 h-5',
wrapper: 'border-2 border-[#F43F5E4D] sm:py-2 sm:px-10',
text: 'text-[#F43F5E] text-xs',
},
};

const pressEffect = !disabled ? 'active:translate-y-0' : '';
Expand Down
36 changes: 36 additions & 0 deletions frontend/components/profile/AchievementCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { cn } from "@/lib/utils";
import type { ReactNode } from "react";

interface AchievementCardProps {
icon: ReactNode;
title: string;
date: string;
badge?: string | number;
className?: string;
}

export function AchievementCard({
icon,
title,
date,
badge,
className,
}: AchievementCardProps) {
return (
<div
className={cn(
"flex flex-col items-center justify-center gap-1 rounded-xl bg-card border border-[#E6E6E64D] py-5 px-2 min-w-[100px] cursor-pointer transition-transform duration-200 hover:scale-105",
className,
)}
>
<div className="flex items-center justify-center">{icon}</div>
{badge && (
<span className="text-xs flex items-center justify-center">
{badge}
</span>
)}
<span className="text-xs text-center">{title}</span>
<span className="text-[10px] text-muted-foreground">{date}</span>
</div>
);
}
Loading