diff --git a/src/components/screens/ProfileScreen.tsx b/src/components/screens/ProfileScreen.tsx index 140b1af..dda4ea6 100644 --- a/src/components/screens/ProfileScreen.tsx +++ b/src/components/screens/ProfileScreen.tsx @@ -41,6 +41,7 @@ export function ProfileScreen() { const fileRef = useRef(null); const [editing, setEditing] = useState(false); const [name, setName] = useState(profile?.displayName ?? ""); + const [tab, setTab] = useState<"basic" | "collection" | "settings">("basic"); if (!profile) return null; const canUpload = profile.level >= PHOTO_UPLOAD_MIN_LEVEL; @@ -67,300 +68,322 @@ export function ProfileScreen() { return ( - {/* showXp=false: the identity card below already has a detailed XP bar. */} - {/* identity */} - - {/* soft gold glow behind avatar */} -
- -
- - - - {/* level badge */} - - - {profile.level} - + {/* tabs */} +
+ {( + [ + ["basic", t("profile.tabBasic")], + ["collection", t("profile.tabCollection")], + ["settings", t("profile.tabSettings")], + ] as const + ).map(([key, label]) => ( - -
+ ))} +
- {titleName && ( -
{titleName}
- )} + {/* ===== Basic: player card + stats/achievements ===== */} + {tab === "basic" && ( +
+ {/* player card */} + +
- {editing ? ( -
- setName(e.target.value)} - className="rounded-lg bg-navy-900/70 gold-border px-3 py-1.5 text-center text-cream outline-none focus:ring-2 focus:ring-gold-500/40" - /> - +
+ + + + + + {profile.level} + + + +
+ + {titleName &&
{titleName}
} + + {editing ? ( +
+ setName(e.target.value)} + className="rounded-lg bg-navy-900/70 gold-border px-3 py-1.5 text-center text-cream outline-none focus:ring-2 focus:ring-gold-500/40" + /> + +
+ ) : ( + + )} + +
+ + +
+ +
+ +
+ +
+ {profile.plan === "pro" ? ( + + + {t("plan.active")} + + ) : ( + + )} +
+ + + {/* stats + achievements */} +
+
{t("profile.lifeRibbon")}
+ +
+
+ + + + + + +
+
+ +
+
+

+ {t("profile.achievements")} + + {" "} + ({profile.unlocked.length}/{ACHIEVEMENTS.length}) + +

+ +
+
+ {[...ACHIEVEMENTS] + .sort((a, b) => { + const ua = profile.unlocked.includes(a.id) ? 1 : 0; + const ub = profile.unlocked.includes(b.id) ? 1 : 0; + return ub - ua; + }) + .slice(0, 6) + .map((a, idx) => { + const prog = achievementProgress(a, s, profile.rating, profile.level); + const unlocked = profile.unlocked.includes(a.id) || prog >= a.goal; + const pct = Math.min(100, Math.round((prog / a.goal) * 100)); + return ( + + {a.icon} +
+
+ {locale === "fa" ? a.nameFa : a.nameEn} +
+
+ {locale === "fa" ? a.descFa : a.descEn} +
+ {!unlocked && a.goal > 1 && ( +
+
+
+ )} +
+ {unlocked && } + + ); + })} +
+
- ) : ( - - )} - -
- -
+ )} -
- + {/* ===== Collection: cosmetics pickers ===== */} + {tab === "collection" && ( +
+ {/* avatar picker */} +
+

{t("profile.chooseAvatar")}

+
+ {AVATARS.filter((a) => ownedAvatars.has(a.id)).map((a) => ( + + ))} +
+
+ + {/* title picker */} +
+

{t("profile.titleLabel")}

+
+ {TITLES.filter((tt) => profile.ownedTitles.includes(tt.id)).map((tt) => ( + + ))} +
+
+ + {/* card front picker */} +
+

{t("profile.cardFront")}

+
+ {CARD_FRONTS.filter((c) => ownedFronts.has(c.id)).map((c) => ( + + ))} +
+
+ + {/* card back picker */} +
+

{t("profile.cardBack")}

+
+ {CARD_BACKS.filter((c) => ownedBacks.has(c.id)).map((c) => ( + + ))} +
+
+ )} -
- {profile.plan === "pro" ? ( - - - {t("plan.active")} - - ) : ( + {/* ===== Settings ===== */} + {tab === "settings" && ( +
+ + + {isAuthed && ( )}
- - - {/* avatar picker */} -
-

{t("profile.chooseAvatar")}

-
- {AVATARS.filter((a) => ownedAvatars.has(a.id)).map((a) => ( - - ))} -
-
- - {/* title picker */} -
-

{t("profile.titleLabel")}

-
- {TITLES.filter((tt) => profile.ownedTitles.includes(tt.id)).map((tt) => ( - - ))} -
-
- - {/* card front picker */} -
-

{t("profile.cardFront")}

-
- {CARD_FRONTS.filter((c) => ownedFronts.has(c.id)).map((c) => ( - - ))} -
-
- - {/* card back picker */} -
-

{t("profile.cardBack")}

-
- {CARD_BACKS.filter((c) => ownedBacks.has(c.id)).map((c) => ( - - ))} -
-
- - {/* gender + social links */} - - - {/* audio settings */} - - - {/* stats */} -
-

{t("profile.stats")}

-
- - - - - - -
-
- - {/* achievements */} -
-
-

- {t("profile.achievements")} - - {" "} - ({profile.unlocked.length}/{ACHIEVEMENTS.length}) - -

- -
-
- {[...ACHIEVEMENTS] - .sort((a, b) => { - const ua = profile.unlocked.includes(a.id) ? 1 : 0; - const ub = profile.unlocked.includes(b.id) ? 1 : 0; - return ub - ua; - }) - .slice(0, 6) - .map((a, idx) => { - const prog = achievementProgress(a, s, profile.rating, profile.level); - const unlocked = profile.unlocked.includes(a.id) || prog >= a.goal; - const pct = Math.min(100, Math.round((prog / a.goal) * 100)); - return ( - - - {a.icon} - -
-
- {locale === "fa" ? a.nameFa : a.nameEn} -
-
- {locale === "fa" ? a.descFa : a.descEn} -
- {!unlocked && a.goal > 1 && ( -
-
-
- )} -
- {unlocked && } - - ); - })} -
-
- - {/* Sign out */} - {isAuthed && ( - )} ); @@ -408,7 +431,7 @@ function SocialSettings() { }; return ( -
+

{t("profile.social")}

{/* gender */} @@ -492,7 +515,7 @@ function SoundSettings() { { id: "playful" as const, label: t("settings.trackPlayful") }, ]; return ( -
+

{t("settings.audio")}

} label={t("settings.sound")} on={sfx} onClick={toggleSfx} /> } label={t("settings.music")} on={music} onClick={toggleMusic} /> diff --git a/src/lib/i18n.tsx b/src/lib/i18n.tsx index c088345..b1eb95d 100644 --- a/src/lib/i18n.tsx +++ b/src/lib/i18n.tsx @@ -155,6 +155,10 @@ const fa: Dict = { "friends.message": "پیام", "profile.title": "پروفایل", + "profile.tabBasic": "نمایه", + "profile.tabCollection": "مجموعه", + "profile.tabSettings": "تنظیمات", + "profile.lifeRibbon": "زندگیِ حکم", "profile.stats": "آمار", "profile.games": "بازی‌ها", "profile.wins": "بردها", @@ -500,6 +504,10 @@ const en: Dict = { "friends.message": "Message", "profile.title": "Profile", + "profile.tabBasic": "Profile", + "profile.tabCollection": "Collection", + "profile.tabSettings": "Settings", + "profile.lifeRibbon": "Hokm Life", "profile.stats": "Stats", "profile.games": "Games", "profile.wins": "Wins",