mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:30:45 +00:00
split account modal into multiple tabs (#3986)
## Description: The account modal was getting too large, too much scrolling. <img width="1091" height="779" alt="Screenshot 2026-05-22 at 2 23 23 PM" src="https://github.com/user-attachments/assets/9553c80d-7d17-4cfd-8992-88bb335a972e" /> <img width="970" height="781" alt="Screenshot 2026-05-22 at 2 23 33 PM" src="https://github.com/user-attachments/assets/847e70eb-57b7-440b-adb6-bb7c18ada43c" /> <img width="1100" height="718" alt="Screenshot 2026-05-22 at 2 23 42 PM" src="https://github.com/user-attachments/assets/5b08d44c-743a-4245-86a3-bf62e01008e9" /> ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan
This commit is contained in:
@@ -380,7 +380,12 @@
|
||||
"clear_session": "Clear Session",
|
||||
"failed_to_send_recovery_email": "Failed to send recovery email",
|
||||
"enter_email_address": "Please enter an email address",
|
||||
"public_player_id": "Public Player ID:"
|
||||
"public_player_id": "Public Player ID:",
|
||||
"tab_account": "Account",
|
||||
"tab_stats": "Stats",
|
||||
"tab_games": "Games",
|
||||
"no_stats": "No stats available yet. Play some games to start tracking.",
|
||||
"no_games": "No games played yet."
|
||||
},
|
||||
"leaderboard_modal": {
|
||||
"title": "Leaderboard",
|
||||
|
||||
+101
-58
@@ -93,81 +93,124 @@ export class AccountModal extends BaseModal {
|
||||
});
|
||||
}
|
||||
|
||||
protected renderBody() {
|
||||
private isLinkedAccount(): boolean {
|
||||
const me = this.userMeResponse?.user;
|
||||
return !!(me?.discord ?? me?.email);
|
||||
}
|
||||
|
||||
protected modalConfig() {
|
||||
if (this.isLoadingUser || !this.isLinkedAccount()) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
tabs: [
|
||||
{ key: "account", label: translateText("account_modal.tab_account") },
|
||||
{ key: "stats", label: translateText("account_modal.tab_stats") },
|
||||
{ key: "games", label: translateText("account_modal.tab_games") },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
protected renderBody(tab: string) {
|
||||
if (this.isLoadingUser) {
|
||||
return this.renderLoadingSpinner(
|
||||
translateText("account_modal.fetching_account"),
|
||||
);
|
||||
}
|
||||
const isLoggedIn = !!this.userMeResponse?.user;
|
||||
if (!this.isLinkedAccount()) {
|
||||
return html`<div class="custom-scrollbar mr-1">
|
||||
${this.renderLoginOptions()}
|
||||
</div>`;
|
||||
}
|
||||
return html`
|
||||
<div class="custom-scrollbar mr-1">
|
||||
${isLoggedIn ? this.renderAccountInfo() : this.renderLoginOptions()}
|
||||
<div class="p-6">${this.renderTab(tab)}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderAccountInfo() {
|
||||
const me = this.userMeResponse?.user;
|
||||
const isLinked = me?.discord ?? me?.email;
|
||||
|
||||
if (!isLinked) {
|
||||
return this.renderLoginOptions();
|
||||
private renderTab(tab: string): TemplateResult {
|
||||
switch (tab) {
|
||||
case "stats":
|
||||
return this.renderStatsTab();
|
||||
case "games":
|
||||
return this.renderGamesTab();
|
||||
default:
|
||||
return this.renderAccountTab();
|
||||
}
|
||||
}
|
||||
|
||||
private renderAccountTab(): TemplateResult {
|
||||
return html`
|
||||
<div class="p-6">
|
||||
<div class="flex flex-col gap-6">
|
||||
<!-- Top Row: Connected As -->
|
||||
<div class="bg-white/5 rounded-xl border border-white/10 p-6">
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<div
|
||||
class="text-xs text-white/40 uppercase tracking-widest font-bold border-b border-white/5 pb-2 px-8"
|
||||
>
|
||||
${translateText("account_modal.connected_as")}
|
||||
</div>
|
||||
<div class="flex items-center gap-8 justify-center flex-wrap">
|
||||
<discord-user-header
|
||||
.data=${this.userMeResponse?.user?.discord ?? null}
|
||||
></discord-user-header>
|
||||
${this.renderLoggedInAs()}
|
||||
</div>
|
||||
<div class="flex flex-col gap-6">
|
||||
<div class="bg-white/5 rounded-xl border border-white/10 p-6">
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<div
|
||||
class="text-xs text-white/40 uppercase tracking-widest font-bold border-b border-white/5 pb-2 px-8"
|
||||
>
|
||||
${translateText("account_modal.connected_as")}
|
||||
</div>
|
||||
<div class="flex items-center gap-8 justify-center flex-wrap">
|
||||
<discord-user-header
|
||||
.data=${this.userMeResponse?.user?.discord ?? null}
|
||||
></discord-user-header>
|
||||
${this.renderLoggedInAs()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${this.renderSubscriptionPanel()}
|
||||
|
||||
<!-- Middle Row: Stats Section -->
|
||||
${this.hasAnyStats()
|
||||
? html`<div
|
||||
class="bg-white/5 rounded-xl border border-white/10 p-6"
|
||||
>
|
||||
<h3
|
||||
class="text-lg font-bold text-white mb-4 flex items-center gap-2"
|
||||
>
|
||||
<span class="text-blue-400">📊</span>
|
||||
${translateText("account_modal.stats_overview")}
|
||||
</h3>
|
||||
<player-stats-tree-view
|
||||
.statsTree=${this.statsTree}
|
||||
></player-stats-tree-view>
|
||||
</div>`
|
||||
: ""}
|
||||
|
||||
<!-- Bottom Row: Recent Games Section -->
|
||||
<div class="bg-white/5 rounded-xl border border-white/10 p-6">
|
||||
<h3
|
||||
class="text-lg font-bold text-white mb-4 flex items-center gap-2"
|
||||
>
|
||||
<span class="text-blue-400">🎮</span>
|
||||
${translateText("game_list.recent_games")}
|
||||
</h3>
|
||||
<game-list
|
||||
.games=${this.recentGames}
|
||||
.onViewGame=${(id: string) => void this.viewGame(id)}
|
||||
></game-list>
|
||||
</div>
|
||||
</div>
|
||||
${this.renderSubscriptionPanel()}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderStatsTab(): TemplateResult {
|
||||
if (!this.hasAnyStats()) {
|
||||
return this.renderEmptyState(
|
||||
"📊",
|
||||
translateText("account_modal.no_stats"),
|
||||
);
|
||||
}
|
||||
return html`
|
||||
<div class="bg-white/5 rounded-xl border border-white/10 p-6">
|
||||
<h3 class="text-lg font-bold text-white mb-4 flex items-center gap-2">
|
||||
<span class="text-blue-400">📊</span>
|
||||
${translateText("account_modal.stats_overview")}
|
||||
</h3>
|
||||
<player-stats-tree-view
|
||||
.statsTree=${this.statsTree}
|
||||
></player-stats-tree-view>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderGamesTab(): TemplateResult {
|
||||
if (this.recentGames.length === 0) {
|
||||
return this.renderEmptyState(
|
||||
"🎮",
|
||||
translateText("account_modal.no_games"),
|
||||
);
|
||||
}
|
||||
return html`
|
||||
<div class="bg-white/5 rounded-xl border border-white/10 p-6">
|
||||
<h3 class="text-lg font-bold text-white mb-4 flex items-center gap-2">
|
||||
<span class="text-blue-400">🎮</span>
|
||||
${translateText("game_list.recent_games")}
|
||||
</h3>
|
||||
<game-list
|
||||
.games=${this.recentGames}
|
||||
.onViewGame=${(id: string) => void this.viewGame(id)}
|
||||
></game-list>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderEmptyState(icon: string, message: string): TemplateResult {
|
||||
return html`
|
||||
<div
|
||||
class="bg-white/5 rounded-xl border border-white/10 p-12 flex flex-col items-center justify-center text-center"
|
||||
>
|
||||
<div class="text-4xl mb-3">${icon}</div>
|
||||
<p class="text-white/60 text-sm">${message}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user