feat(web): redesign Discord settings as dedicated /discord route#115
Conversation
Replace the four-tab DiscordPanel crammed into the dashboard with a proper /discord route built on the shadcn/ui foundation. Adds first-run setup wizard, snowflake validation on every ID input, show/hide eye toggles for the bot token + public key (with a green check when already set), chip-based bulk-paste editors for admin user/role IDs, and an inline-editable per-game permissions table. Closes #62 https://claude.ai/code/session_01AiQPoJT9p8MS5odgMc8frh
…er-deploy into claude/issue-62-ePUwx
Extends the e2e harness with Discord fixtures (FIRST_RUN_DISCORD_CONFIG,
CONFIGURED_DISCORD_CONFIG, snowflake samples) and stub routes for
/api/discord/{config,guilds,admins,permissions,...} so non-Discord specs
hitting /discord (e.g. sidebar nav) no longer trip the catch-all 404.
New discord.spec.ts covers the AC from #62: setup wizard visibility on
first run, the bot-token "set" indicator + show/hide eye toggle,
verification that the API never echoes the secrets back, snowflake
validation on guild add, the Guilds table render, the per-game
permissions table, and the not-deployed empty state.
https://claude.ai/code/session_01AiQPoJT9p8MS5odgMc8frh
…er-deploy into claude/issue-62-ePUwx # Conflicts: # app/packages/web/e2e/fixtures/game-data.ts # app/packages/web/e2e/fixtures/index.ts # app/packages/web/src/pages/DashboardPage.tsx
There was a problem hiding this comment.
Pull request overview
This PR replaces the old dashboard-embedded Discord configuration panel with a dedicated /discord page in the web app, moving the existing Discord management flows onto the newer shadcn/ui-based route structure.
Changes:
- Adds a full
DiscordPagewith tabs for credentials, guilds, admins, and per-game permissions, plus a first-run setup wizard. - Removes the legacy
DiscordPanelfrom the dashboard and leaves the dashboard focused on game/log views. - Adds Playwright fixtures and E2E coverage for the new Discord route and its basic states.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
app/packages/web/src/pages/DiscordPage.tsx |
New dedicated Discord settings page and all section components. |
app/packages/web/src/pages/DashboardPage.tsx |
Removes the embedded Discord panel from the dashboard. |
app/packages/web/src/components/DiscordPanel.tsx |
Deletes the legacy dashboard Discord panel implementation. |
app/packages/web/e2e/specs/discord.spec.ts |
Adds E2E tests for the new Discord route. |
app/packages/web/e2e/fixtures/index.ts |
Extends shared Playwright stubs with Discord endpoints and /api/games. |
app/packages/web/e2e/fixtures/game-data.ts |
Adds Discord-specific fixture data and valid snowflake constants. |
| <p className="text-xs text-[var(--color-muted-foreground)]"> | ||
| Full walkthrough:{' '} | ||
| <a | ||
| href="/docs/setup" |
There was a problem hiding this comment.
Fixed in d238092. Switched to the published Docusaurus URL https://codercoco.github.io/game-server-deploy/setup and added target="_blank" since the docs site is a separate origin from the management app.
Generated by Claude Code
| function handleRegister(guildId: string) { | ||
| setRegistered((prev) => new Set(prev).add(guildId)); | ||
| onRegister(guildId); |
There was a problem hiding this comment.
Fixed in d238092. handleRegister is now async, awaits onRegister, and only sets the registered flag inside a try block — failures leave the badge in the not-registered state. The Register all action serialises so partial failures stay visible per-guild.
Generated by Claude Code
| <Input | ||
| id="client-id" | ||
| value={clientId} | ||
| onChange={(e) => setClientId(e.target.value)} | ||
| placeholder="000000000000000000" | ||
| /> |
There was a problem hiding this comment.
Fixed in d238092. Save now blocks with an inline error + aria-invalid when Client ID is non-empty and not a snowflake. Empty is still allowed so token-only updates don't require re-typing the ID.
Generated by Claude Code
- Setup-wizard docs link now points to the published Docusaurus URL (https://codercoco.github.io/game-server-deploy/setup) instead of the in-app /docs/setup path that 404s. - "registered" badge in the Guilds table now flips only after api.discordRegisterCommands() resolves successfully; failures leave the badge in the not-registered state. The bulk action serialises so partial failures stay visible. - Credentials form validates the Application (Client) ID as a Discord snowflake before submit, with an inline error and aria-invalid on the input. Empty is still allowed so operators can save token-only updates without retyping the ID. https://claude.ai/code/session_01AiQPoJT9p8MS5odgMc8frh
Pins the fix from d238092 — the "registered" badge must only flip after the API call resolves successfully. Two new specs: - success path: clicking Register on a guild row flips the badge from "not registered" to "registered". - failure path: when the register-commands endpoint returns 500, the badge stays at "not registered" so operators see the actual outcome instead of an optimistic success flash. https://claude.ai/code/session_01AiQPoJT9p8MS5odgMc8frh
| {games.map((game) => ( | ||
| <PermissionRow | ||
| key={game} | ||
| game={game} | ||
| initial={ | ||
| cfg.gamePermissions[game] ?? { userIds: [], roleIds: [], actions: [] } | ||
| } | ||
| busy={busy} | ||
| onSave={(perm) => onSave(game, perm)} | ||
| onDelete={() => onDelete(game)} | ||
| /> | ||
| ))} |
There was a problem hiding this comment.
Fixed in d4d1b77. The row is now keyed by `${game}:${JSON.stringify(initial)}` so it remounts whenever the server-side entry changes — Clear (and Save) reinitialise the chips and checkboxes from the freshly-fetched cfg. Pinned by the new e2e spec should reset chips on a permission row after Clear.
Generated by Claude Code
| const allGuilds = useMemo( | ||
| () => [ | ||
| ...cfg.baseAllowedGuilds.map((g) => ({ id: g, locked: true })), | ||
| ...cfg.allowedGuilds.map((g) => ({ id: g, locked: false })), | ||
| ], | ||
| [cfg.allowedGuilds, cfg.baseAllowedGuilds], | ||
| ); | ||
|
|
There was a problem hiding this comment.
Fixed in d4d1b77. The merge now dedupes by guild ID (terraform entry wins, so the row is shown as locked), and handleAdd rejects a guild that's already in either list with That guild is already allowlisted. instead of POSTing a duplicate. Pinned by the new e2e spec should reject adding a guild that is already allowlisted.
Generated by Claude Code
…n Clear Two more Copilot review comments on PR #115: - The Guilds table merged baseAllowedGuilds + allowedGuilds without deduping, so a guild that appeared in both rendered twice with a colliding React key. handleAdd also let an operator add a guild that was already in either list. The merge now dedupes (terraform entry wins) and Add rejects an ID that is already allowlisted with an inline error. - PermissionRow's local userIds/roleIds/actions state was keyed only by game name, so clicking Clear (or Save) refreshed cfg without resetting the row — the cleared chips and checkboxes lingered until reload. The row is now keyed by `${game}:${JSON.stringify(initial)}` so it remounts whenever the server-side entry changes. Also adds two e2e specs to pin the fixes: - discord settings › should reject adding a guild that is already allowlisted - discord settings › should reset chips on a permission row after Clear https://claude.ai/code/session_01AiQPoJT9p8MS5odgMc8frh
|
@copilot please review |
All five issues from both review rounds are addressed and the branch is in good shape. Lint is clean and all 258 unit tests pass. Round 1 fixes (d238092):
Round 2 fixes (d4d1b77):
Both re-key and duplicate-guild fixes are pinned by dedicated E2E specs ( |
Resolves App.tsx/DashboardPage/CostsPage/DiscordPage/LogsPage conflicts between the polling indicator work (#65) and the page-redesign series that landed on main (#77, #79, #115, #116, #117). - App.tsx now wraps the redesigned ApiTokenModal (rendered inline as a Dialog overlay rather than swapping the tree) inside PollingProvider + GameStatusProvider so polling persists across the auth-token flow. - Each redesigned page (Dashboard, Costs, Discord, Logs) now hosts a PollingIndicator next to its existing header chrome (search input, range selector, ServerlessBadge, LIVE badge). - LogsPage.test.tsx mounts the page through a PollingProvider wrapper so the new indicator can read the registry under jsdom. - polling.spec.ts updated to use main's page-object fixtures (DashboardPage / AppLayout) and the redesigned STOPPED status label.
Replace the four-tab DiscordPanel crammed into the dashboard with a
proper /discord route built on the shadcn/ui foundation. Adds first-run
setup wizard, snowflake validation on every ID input, show/hide eye
toggles for the bot token + public key (with a green check when already
set), chip-based bulk-paste editors for admin user/role IDs, and an
inline-editable per-game permissions table.
Closes #62
https://claude.ai/code/session_01AiQPoJT9p8MS5odgMc8frh