/* Huy hiệu gamification — bấm xem tiến độ (mobile-friendly, không hover). */ function ProfileBadgesGrid({ allBadges, rankInfo, onCelebrate }) { const [tipId, setTipId] = React.useState(""); const unlocked = allBadges.filter((b) => b.active); const locked = allBadges.filter((b) => !b.active); React.useEffect(() => { if (!rankInfo) return; const activeIds = unlocked.map((b) => b.id); const key = "dcp_badges_win_" + (rankInfo.phone || rankInfo.name || "user"); const celebrated = JSON.parse(localStorage.getItem(key) || "[]"); const newly = activeIds.filter((id) => !celebrated.includes(id)); if (newly.length > 0) { const match = unlocked.find((b) => b.id === newly[0]); if (match) setTimeout(() => onCelebrate(match.cheer, true), 1000); localStorage.setItem(key, JSON.stringify([...celebrated, ...newly])); } }, [rankInfo, unlocked.length]); const tapBadge = (b) => { setTipId(tipId === b.id ? "" : b.id); onCelebrate(b.active ? b.cheer : b.hint, b.active); }; const tipBadge = allBadges.find((b) => b.id === tipId); const renderBadge = (b) => { const pct = b.isRank ? (b.current <= 10 ? 100 : Math.round((10 / b.current) * 100)) : Math.min(100, Math.round((b.current / b.goal) * 100)); return (
tapBadge(b)}>
{b.icon} {!b.active && (
)}
{b.name}
{b.active ? "✓ Đã đạt" : (b.isRank ? (b.current === 99 ? "—" : "#" + b.current) : `${b.current}/${b.goal}`)}
); }; return (

🏆 Huy hiệu đã chinh phục ({unlocked.length})

{unlocked.length > 0 ? unlocked.map(renderBadge) :

Chưa có huy hiệu. Hoàn thành bài đầu tiên nhé! 🚀

}

🎯 Mục tiêu tiếp theo ({locked.length})

{locked.map(renderBadge)}
{tipBadge && (
{tipBadge.icon} {tipBadge.name} — {tipBadge.desc} {tipBadge.active ? "🎉 Đã mở khóa! Nhấn lại để chúc mừng." : tipBadge.hint}
)}
); } Object.assign(window, { ProfileBadgesGrid });