Skip to content

split account modal into multiple tabs#3986

Merged
evanpelle merged 1 commit into
mainfrom
account-refactor
May 22, 2026
Merged

split account modal into multiple tabs#3986
evanpelle merged 1 commit into
mainfrom
account-refactor

Conversation

@evanpelle
Copy link
Copy Markdown
Collaborator

@evanpelle evanpelle commented May 22, 2026

Description:

The account modal was getting too large, too much scrolling.

Screenshot 2026-05-22 at 2 23 23 PM Screenshot 2026-05-22 at 2 23 33 PM Screenshot 2026-05-22 at 2 23 42 PM

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • 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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

Review Change Stack

Walkthrough

AccountModal is refactored from a monolithic single-renderer into a tab-driven modal that dynamically shows account, stats, and games tabs only when the user is linked. The modal detects linked status, defines tab configuration, routes content through a tab renderer, and displays dedicated panels with empty-state fallbacks. Five new localization keys support the tab labels and empty messages.

Changes

Tab-Driven Refactor

Layer / File(s) Summary
Linked Account Detection & Tab Configuration
src/client/AccountModal.ts
isLinkedAccount() checks for linked user fields to determine if tabs should be shown; modalConfig() conditionally defines account, stats, and games tabs when not loading and user is linked.
Body Renderer Refactoring & Tab Routing
src/client/AccountModal.ts
renderBody(tab: string) signature updated to accept a tab parameter and route to loading spinner, unlinked-login options, or tab content wrapped in the scroll/padding container based on state.
Tab Content Rendering
src/client/AccountModal.ts
renderTab() routes to tab-specific content; renderAccountTab() shows connected-as header and subscription panel; renderStatsTab() and renderGamesTab() display data or empty state via shared renderEmptyState() helper.
Localization Keys for Tabs & Empty States
resources/lang/en.json
New English strings added: tab_account, tab_stats, tab_games for tab navigation labels, and no_stats, no_games for empty-state fallback messages.

Possibly Related PRs

  • openfrontio/OpenFrontIO#3923: Introduces the BaseModal modal-shell refactor and hook-based rendering that enables this tab-driven AccountModal pattern.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

📑 From one big view to tabs so neat,
Account and stats now organize the sheet,
Linked users unlock the tabbed display,
With empty states to guide the way,
Clean component logic saves the day! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title 'split account modal into multiple tabs' directly and clearly describes the main change: refactoring AccountModal from a single layout into a tab-driven interface with account, stats, and games tabs.
Description check ✅ Passed The pull request description clearly explains the motivation (account modal was too large with too much scrolling) and includes supporting screenshots of the UI changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@evanpelle evanpelle changed the title account split account modal into multiple tabs May 22, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/client/AccountModal.ts (1)

101-110: ⚡ Quick win

Use a typed union for AccountModal tab keys (narrow before routing).

AccountModal.ts currently routes on plain string literals ("account" | "stats" | "games") in modalConfig(), renderBody(tab: string), and renderTab(tab: string). Keep renderBody(tab: string) as-is to match BaseModal’s (tab: string) signature, but narrow to a AccountModalTab union before the switch (and type-check the tabs array with satisfies to avoid unsafe casts).

Proposed shape
type AccountModalTab = "account" | "stats" | "games";

function isAccountModalTab(tab: string): tab is AccountModalTab {
  return tab === "account" || tab === "stats" || tab === "games";
}

protected modalConfig() {
  if (this.isLoadingUser || !this.isLinkedAccount()) return {};

  const 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") },
  ] satisfies Array<{ key: AccountModalTab; label: string }>;

  return { tabs };
}

protected renderBody(tab: string) {
  const safeTab = isAccountModalTab(tab) ? tab : "account";
  // ...
  return this.renderTab(safeTab);
}

private renderTab(tab: AccountModalTab): TemplateResult {
  switch (tab) {
    case "stats":
      return this.renderStatsTab();
    case "games":
      return this.renderGamesTab();
    default:
      return this.renderAccountTab();
  }
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/AccountModal.ts` around lines 101 - 110, Create a narrow union
type AccountModalTab ("account" | "stats" | "games") and a type guard
isAccountModalTab(tab: string): tab is AccountModalTab; in modalConfig() replace
the untyped tabs array with a const tabs = [...] that is type-checked using
satisfies Array<{ key: AccountModalTab; label: string }>; in renderBody(tab:
string) validate the incoming tab with isAccountModalTab and fall back to
"account" (e.g. const safeTab = isAccountModalTab(tab) ? tab : "account") before
calling renderTab; change renderTab signature to renderTab(tab: AccountModalTab)
and switch on the narrowed AccountModalTab to call renderAccountTab,
renderStatsTab, or renderGamesTab accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/client/AccountModal.ts`:
- Around line 101-110: Create a narrow union type AccountModalTab ("account" |
"stats" | "games") and a type guard isAccountModalTab(tab: string): tab is
AccountModalTab; in modalConfig() replace the untyped tabs array with a const
tabs = [...] that is type-checked using satisfies Array<{ key: AccountModalTab;
label: string }>; in renderBody(tab: string) validate the incoming tab with
isAccountModalTab and fall back to "account" (e.g. const safeTab =
isAccountModalTab(tab) ? tab : "account") before calling renderTab; change
renderTab signature to renderTab(tab: AccountModalTab) and switch on the
narrowed AccountModalTab to call renderAccountTab, renderStatsTab, or
renderGamesTab accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9c8b6ad5-0113-4f0f-9f2c-126845b00082

📥 Commits

Reviewing files that changed from the base of the PR and between 42feb36 and 7e4e370.

📒 Files selected for processing (2)
  • resources/lang/en.json
  • src/client/AccountModal.ts

@evanpelle evanpelle marked this pull request as ready for review May 22, 2026 14:06
@evanpelle evanpelle requested a review from a team as a code owner May 22, 2026 14:06
@evanpelle evanpelle added this to the v32 milestone May 22, 2026
@evanpelle evanpelle merged commit 458d04e into main May 22, 2026
17 of 31 checks passed
@github-project-automation github-project-automation Bot moved this from Triage to Complete in OpenFront Release Management May 22, 2026
@evanpelle evanpelle deleted the account-refactor branch May 22, 2026 14:12
@coderabbitai coderabbitai Bot mentioned this pull request May 22, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Complete

Development

Successfully merging this pull request may close these issues.

1 participant