import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import {
Edit, BookOpen, Award, Trophy, Shield, Star, Zap, Target, BadgeCheck, Medal, Flame, LockIcon, Camera, ChevronRight, Download, RefreshCcw
} from "lucide-react";
import AvatarUploadDialog from "@/components/generic/AvatarUploadDialog";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
import { Separator } from "@/components/ui/separator";
import { Skeleton } from "@/components/ui/skeleton";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import ResponsiveModal from "@/components/generic/ResponsiveModal";
import { useProfile } from "@/contexts/ProfileProvider";
import { useAuth } from "@/contexts/AuthContext";
import { useClientTiers } from "@/contexts/ClientTiersProvider";
import api from "@/utils/api.util";
import { toast } from "sonner";
// ─── Tier badge config ────────────────────────────────────────────────────────
const TIER_BADGES = {
free: {
src: "/badges/free-access-badge-leaf-flaticon.svg",
label: "Free",
description: "Active member using the Free Plan.",
information: "Free users have access to the platform's core features without a subscription.",
},
premium: {
src: "/badges/premium-badge-sheriff-flaticon.svg",
label: "Premium",
description: "Subscriber with access to premium learning content.",
information: "Premium members can access all premium courses available on the platform.",
},
exclusive: {
src: "/badges/exclusive-award-flaticon.svg",
label: "Exclusive",
description: "Premium member with access to exclusive course content.",
information: "This badge represents a member with access to exclusive courses.",
},
};
const EARLY_ACCESS_BADGE = {
src: "/badges/early-access-badge-percent-flaticon.svg",
label: "Early Access",
description: "Registered during the Philproperties beta period.",
information: "Exclusive to members who registered before Dec 31, 2026.",
};
// ─── Achievement icon map (by key) ───────────────────────────────────────────
const ACHIEVEMENT_ICONS = {
early_access: Star,
premium_first_time: BadgeCheck,
exclusive_first_time: Medal,
first_course_completed: BookOpen,
courses_completed_5: Flame,
courses_completed_10: Zap,
perfect_quiz_score: Target,
profile_completed: Shield,
first_referral: Award,
};
const getFallbackIcon = (type) => type === "badge" ? BadgeCheck : Trophy;
// ─── Certificate landscape card (profile preview) ────────────────────────────
const CertBadgeIcon = ({ className }) => (
);
const LandscapeCertCard = ({ courseTitle, issuedAt, courseUuid }) => {
const [downloading, setDownloading] = useState(false);
const issuedLabel = issuedAt
? new Date(issuedAt).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })
: "—";
const handleDownload = async () => {
setDownloading(true);
try {
const res = await api.get(`/client/certificates/${courseUuid}`, { responseType: "blob" });
const disposition = res.headers["content-disposition"] ?? "";
const match = disposition.match(/filename="([^"]+)"/);
const filename = match ? match[1] : `certificate-${courseUuid}.pdf`;
const url = URL.createObjectURL(new Blob([res.data], { type: "application/pdf" }));
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
} catch {
toast.error("Could not download certificate.");
} finally {
setDownloading(false);
}
};
return (
Certificate of Completion
{courseTitle}
Issued {issuedLabel}
Verified
);
};
// ─── Component ───────────────────────────────────────────────────────────────
const ProfilePage = () => {
const navigate = useNavigate();
const { user } = useAuth();
const {
profile, profileLoading, getProfile,
achievements, achievementsLoading, getAchievements,
fullName, avatarUrl, occupation,
uploadAvatar, deleteAvatar, avatarLoading,
} = useProfile();
const [avatarDialogOpen, setAvatarDialogOpen] = useState(false);
const { myTier, tierLoading, getMyTier } = useClientTiers();
const [badgeOpen, setBadgeOpen] = useState(false);
const [selectedBadge, setSelectedBadge] = useState(null);
const [inProgressCourses, setInProgressCourses] = useState([]);
const [inProgressCoursesLoading, setInProgressCoursesLoading] = useState(false);
const [pendingModalOpen, setPendingModalOpen] = useState(false);
const [pendingModalCourse, setPendingModalCourse] = useState(null);
useEffect(() => {
getProfile();
getAchievements();
getMyTier();
(async () => {
setInProgressCoursesLoading(true);
try {
const { data } = await api.get("/client/courses/in-progress");
setInProgressCourses(data.data ?? []);
} catch {
// silent — empty state handles it
} finally {
setInProgressCoursesLoading(false);
}
})();
}, []);
// ── Derived ────────────────────────────────────────────────────────────────
const tier = myTier?.tier ?? user?.tier ?? "free";
const tierBadge = TIER_BADGES[tier] ?? TIER_BADGES.free;
const displayName = fullName || user?.personal_info?.name?.full_name || user?.email?.split("@")[0] || "—";
const initials = displayName.split(" ").map((w) => w[0]).join("").slice(0, 2).toUpperCase();
const tierRank = { free: 0, premium: 1, exclusive: 2 };
const userRank = tierRank[tier] ?? 0;
const certAchievements = achievements.filter((a) => a.key.startsWith("course_completed_"));
const hasEarlyAccess = achievements.some((a) => a.key === "early_access");
// Premium badge only shows when currently exclusive (went through premium to get here)
const hasPremiumBadge = achievements.some((a) => a.key === "premium_first_time") && userRank >= 2;
// Exclusive badge only shows when currently exclusive
const hasExclusiveBadge = achievements.some((a) => a.key === "exclusive_first_time") && userRank >= 2;
const badgesReady = !tierLoading && !achievementsLoading;
// Build badge object with earnedAt from achievements for modal
const getBadgeWithDate = (achievementKey, tierKey) => {
const achievement = achievements.find((a) => a.key === achievementKey);
const earnedAt = achievement?.granted_at
? new Date(achievement.granted_at).toLocaleDateString("en-US", {
month: "long", day: "numeric", year: "numeric",
})
: null;
return { ...TIER_BADGES[tierKey], earnedAt };
};
const getEarlyAccessBadge = () => {
const achievement = achievements.find((a) => a.key === "early_access");
const earnedAt = achievement?.granted_at
? new Date(achievement.granted_at).toLocaleDateString("en-US", {
month: "long", day: "numeric", year: "numeric",
})
: null;
return { ...EARLY_ACCESS_BADGE, earnedAt };
};
const getActiveTierBadgeWithDate = () => {
const achievementKey = tier === "premium"
? "premium_first_time"
: tier === "exclusive"
? "exclusive_first_time"
: null;
return achievementKey ? getBadgeWithDate(achievementKey, tier) : tierBadge;
};
return (
{/* ── Header card ──────────────────────────────────────────────────── */}
{initials}
{profileLoading ? (
) : (
{displayName}
)}
{/* ── Badges ─────────────────────────────────────────────── */}
{badgesReady ? (
{/* Early access — always show if earned */}
{hasEarlyAccess && (
{ setSelectedBadge(getEarlyAccessBadge()); setBadgeOpen(true); }}
/>
{EARLY_ACCESS_BADGE.label}
)}
{/* Premium badge — only when currently exclusive */}
{hasPremiumBadge && (
{ setSelectedBadge(getBadgeWithDate("premium_first_time", "premium")); setBadgeOpen(true); }}
/>
Premium
)}
{/* Exclusive badge — only when currently exclusive */}
{hasExclusiveBadge && (
{ setSelectedBadge(getBadgeWithDate("exclusive_first_time", "exclusive")); setBadgeOpen(true); }}
/>
Exclusive
)}
{/* Current active tier badge — always shown */}
{/* For free tier, show free badge. For premium/exclusive already shown above via hasPremiumBadge/hasExclusiveBadge */}
{userRank === 0 && (
{ setSelectedBadge(tierBadge); setBadgeOpen(true); }}
/>
{tierBadge.label}
)}
{/* For premium tier without achievement yet (edge case) */}
{userRank === 1 && !hasPremiumBadge && (
{ setSelectedBadge(getActiveTierBadgeWithDate()); setBadgeOpen(true); }}
/>
{tierBadge.label}
)}
) : (
)}
{user?.email ?? "—"}
{occupation && {occupation}}
{/* ── Badge modal ───────────────────────────────────────────────────── */}
setBadgeOpen(false)}>Close
}
>
{selectedBadge?.src && (

)}
{selectedBadge?.label}
{selectedBadge?.earnedAt && (
{selectedBadge.earnedAt}
)}
{selectedBadge?.information}
{/* ── Two columns ──────────────────────────────────────────────────── */}
{/* Left column */}
{/* Learning progress */}
Learning progress
Only you
{inProgressCoursesLoading ? (
{[...Array(2)].map((_, i) => (
))}
) : inProgressCourses.length === 0 ? (
No courses in progress.
) : (
inProgressCourses.slice(0, 2).map((course, i) => {
const pct = course.lessons_total > 0
? Math.round((course.lessons_completed / course.lessons_total) * 100)
: 0;
const quizPending = course.reading_status === 'completed';
return (
{quizPending ? (
{ setPendingModalCourse(course); setPendingModalOpen(true); }}
>
Quiz / Assessment Pending
) : (
{course.lessons_completed} / {course.lessons_total} lessons
)}
{i < Math.min(inProgressCourses.length, 2) - 1 &&
}
);
})
)}
{/* Quiz / Assessment Pending detail modal */}
{ setPendingModalOpen(v); if (!v) setPendingModalCourse(null); }}
title={pendingModalCourse?.title ?? 'Pending Items'}
description="You've finished reading all lessons. Complete the items below to earn your certificate."
hideDrawerClose={false}
footer={
}
>
{pendingModalCourse && (() => {
const { pending_quizzes = [], pending_assessment } = pendingModalCourse;
const hasQuizzes = pending_quizzes.length > 0;
const hasAssessment = !!pending_assessment;
if (!hasQuizzes && !hasAssessment) {
return (
No pending items found.
);
}
return (
{hasQuizzes && (
Unit Quizzes
{pending_quizzes.map((q) => (
-
{q.title || 'Unit Quiz'}
{q.unit_title}
Pass: {q.passing_score}%
{q.attempt_count === 0 ? 'Not attempted' : `${q.attempt_count} attempt${q.attempt_count === 1 ? '' : 's'}`}
{q.is_required && (
Required
)}
))}
)}
{hasAssessment && (
Final Assessment
{pending_assessment.title || 'Course Assessment'}
Pass: {pending_assessment.passing_score}%
{pending_assessment.attempt_count === 0 ? 'Not attempted' : `${pending_assessment.attempt_count} attempt${pending_assessment.attempt_count === 1 ? '' : 's'}`}
{pending_assessment.is_required && (
Required
)}
)}
);
})()}
{/* Account info */}
Account info
Member since
{profile?.createdAt
? new Date(profile.createdAt).toLocaleDateString("en-US", { month: "long", year: "numeric" })
: "—"}
Plan
{tierLoading ? (
) : (
{tier}{myTier?.expires_at && ` · until ${new Date(myTier.expires_at).toLocaleDateString("en-US", {
month: "short", day: "numeric", year: "numeric",
})}`}
)}
{/* Right column */}
{/* Certificates — preview + full list at /certificates */}
navigate("/certificates")}
>
{achievementsLoading ? (
) : certAchievements.length > 0 ? (
{certAchievements.length}
) : null}
{!achievementsLoading && certAchievements.length > 0 && (
)}
{/* Achievements — latest 2, full list at /achievements */}
navigate("/achievements")}
>
Achievements
{achievementsLoading ? (
) : achievements.length > 0 ? (
{achievements.length}
) : null}
{achievementsLoading ? (
{[...Array(2)].map((_, i) => (
))}
) : achievements.length > 0 ? (() => {
const latest = [...achievements]
.sort((a, b) => new Date(b.granted_at) - new Date(a.granted_at))
.slice(0, 2);
return (
{latest.map((item, i) => {
const Icon = ACHIEVEMENT_ICONS[item.key] ?? getFallbackIcon(item.type);
return (
{item.label}
{item.description}
{item.granted_at && (
{new Date(item.granted_at).toLocaleDateString("en-US", {
month: "long", day: "numeric", year: "numeric",
})}
)}
{i < latest.length - 1 &&
}
);
})}
);
})() : (
Nothing to show yet
Complete more courses to unlock achievements.
)}
setAvatarDialogOpen(false)}
currentAvatarUrl={avatarUrl ?? ''}
initials={initials}
onUpload={uploadAvatar}
onDelete={deleteAvatar}
loading={avatarLoading}
/>
);
};
export default ProfilePage;