From 1089420030e7110e7865f3422ad0e880ba9d6fa4 Mon Sep 17 00:00:00 2001 From: Amirhossein Khalili Date: Fri, 2 Oct 2026 12:20:02 +0330 Subject: [PATCH] feat: show badge paths on public profiles --- src/components/profile/ActivityBadgePaths.tsx | 119 ++++++++++++++++++ .../profile/PublicProfileBadges.tsx | 16 +-- 2 files changed, 122 insertions(+), 13 deletions(-) create mode 100644 src/components/profile/ActivityBadgePaths.tsx diff --git a/src/components/profile/ActivityBadgePaths.tsx b/src/components/profile/ActivityBadgePaths.tsx new file mode 100644 index 0000000..1ebd11f --- /dev/null +++ b/src/components/profile/ActivityBadgePaths.tsx @@ -0,0 +1,119 @@ +import { useState } from "react"; +import { Check, ChevronLeft, ChevronRight } from "lucide-react"; +import Image from "next/image"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { + activityLadders, + getGamificationBadgeImagePath, +} from "@/lib/gamification-badges"; +import type { PublicGamificationBadgeSchema } from "@/lib/types"; + +const badgeProgressionRows = activityLadders.map((ladder) => ({ + id: ladder.category, + label: ladder.category === "likes" ? "لایک‌های انجمن" : ladder.title, + badges: ladder.badges.map((badge) => ({ + id: badge.id, + label: badge.label, + description: ladder.category === "likes" ? badge.description.replace(/پسند/g, "لایک") : badge.description, + })), +})); + +export function ActivityBadgePaths({ + badges, + title = "نشان‌های هر مسیر", + gridClassName = "grid gap-3 sm:grid-cols-2", +}: { + badges: PublicGamificationBadgeSchema[]; + title?: string | null; + gridClassName?: string; +}) { + const [selectedBadgeIndices, setSelectedBadgeIndices] = useState>({}); + const earnedBadgeIds = new Set(badges.map((badge) => badge.id)); + + return ( +
+ {title ?

{title}

: null} +
+ {badgeProgressionRows.map((row) => { + const latestEarnedIndex = row.badges.reduce( + (latest, badge, index) => earnedBadgeIds.has(badge.id) ? index : latest, + -1, + ); + const selectedIndex = Math.min( + selectedBadgeIndices[row.id] ?? Math.max(latestEarnedIndex, 0), + row.badges.length - 1, + ); + const selectedBadge = row.badges[selectedIndex]; + const selectedEarned = earnedBadgeIds.has(selectedBadge.id); + const selectBadge = (index: number) => { + setSelectedBadgeIndices((current) => ({ ...current, [row.id]: index })); + }; + + return ( + + + {getGamificationBadgeImagePath(selectedBadge.id) ? ( + + ) : null} +

+ {selectedBadge.label} +

+

{selectedBadge.description}

+
+ +
+ {row.badges.map((badge, index) => { + const earned = earnedBadgeIds.has(badge.id); + const isSelected = index === selectedIndex; + return ( + + ); + })} +
+ +
+
+
+ ); + })} +
+
+ ); +} diff --git a/src/components/profile/PublicProfileBadges.tsx b/src/components/profile/PublicProfileBadges.tsx index 99a0b71..ebe0445 100644 --- a/src/components/profile/PublicProfileBadges.tsx +++ b/src/components/profile/PublicProfileBadges.tsx @@ -1,21 +1,15 @@ import Image from "next/image"; import { Trophy } from "lucide-react"; -import { GamificationBadges } from "@/components/profile/GamificationBadges"; +import { ActivityBadgePaths } from "@/components/profile/ActivityBadgePaths"; import { getGamificationBadgeImagePath, getLatestBadgePerCategory, } from "@/lib/gamification-badges"; import type { PublicGamificationBadgeSchema } from "@/lib/types"; -const activityCategories = ["attendance", "posts", "comments", "likes"] as const; - export function PublicProfileBadges({ badges }: { badges: PublicGamificationBadgeSchema[] }) { const latestBadges = getLatestBadgePerCategory(badges); const overallRank = latestBadges.level; - const activityBadges = activityCategories.flatMap((category) => { - const badge = latestBadges[category]; - return badge ? [badge] : []; - }); const rankImage = overallRank ? getGamificationBadgeImagePath(overallRank.id) : undefined; return ( @@ -33,19 +27,15 @@ export function PublicProfileBadges({ badges }: { badges: PublicGamificationBadg

{overallRank.label}

-

بالاترین سطح کلی کسب‌شده

) : null} -
-

نشان‌ها

- -
+ ); }