Skip to content

feat(admin): add observability dashboard - #91

Merged
DIvanCode merged 3 commits into
masterfrom
codex/issue-89-admin-page
Aug 9, 2026
Merged

feat(admin): add observability dashboard#91
DIvanCode merged 3 commits into
masterfrom
codex/issue-89-admin-page

Conversation

@DIvanCode

@DIvanCode DIvanCode commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • add the protected /admin dashboard with five collapsible sections for users, duels, submissions, groups, and tournaments
  • consume all eleven admin list endpoints from Backend PR feat(duely): add admin list endpoints CoDuels-Backend#337 through the shared RTK Query slice
  • separate active users and completed submissions on the client, show live ranked-search wait time, and link users, groups, tournaments, and duels to their existing pages
  • render testing submissions in yellow, Accepted in green, and every other terminal verdict in red
  • link every admin submission row directly to its detail inside the owning duel and preserve the task selection
  • let administrators open submission details while keeping the duel/code UI read-only for non-participants
  • keep authenticated non-admins on /admin with a dedicated access-denied screen without starting admin requests or clearing their session
  • add loading, empty, partial-failure, and 403 states plus responsive table styling
  • document the dashboard flow and backend contracts

Backend dependency and contract gaps

This PR depends on DIvanCode/CoDuels-Backend#337. Its admin submission-list DTO provides duel_id and task_key, and its regular duel/submission read endpoints allow administrator access.

The current backend contract does not expose the Friendly/Group/Tournament origin or group/tournament references on active and finished DuelDto records. Those lists therefore show rated versus unrated mode but cannot yet be grouped by origin. TournamentDto also exposes created_at, not a tournament start time, so the dashboard labels that column as creation time.

Verification

  • pnpm test — 26 files, 82 tests passed
  • pnpm lint — passed; 4 pre-existing repository warnings remain
  • pnpm fsd:lint — existing baseline remains at 12 errors and 11 warnings; the admin change adds no new finding
  • VITE_BASE_URL=/api pnpm build — passed
  • the final production dist was served with Vite preview and smoke-tested in headless Chrome:
    • non-admin stayed at /admin, remained authenticated, saw “Нет доступа”, and made 0 admin requests
    • admin stayed at /admin, made all 11 admin requests, and saw all 5 dashboard sections
    • admin rows rendered Accepted in green, Wrong answer in red, and Running as yellow “Проверяется”
    • clicking submission #101 opened /duel/77/submissions/101?task=B and rendered its Accepted detail
    • all scenarios mounted a non-empty #root without uncaught exceptions or an application error-boundary

What to test manually

Prerequisites: an administrator account, a non-admin account, Backend PR 337 available in the test environment, and representative active/inactive users, pending/in-progress/finished duels, submissions, groups, and tournaments.

  1. Sign in as an administrator and open /admin.
  2. Collapse and reopen every main section; each should keep its tables and counts.
  3. Confirm active users are listed first and are absent from the remaining-users table.
  4. Confirm ranked searchers, all three pending-duel types, active duels, and finished duels appear in the documented order; open user/group/tournament/duel links.
  5. In “Посылки”, confirm queued/running rows are yellow, Accepted rows are green, and every other completed verdict is red.
  6. Click a submission number; expect /duel/{duelId}/submissions/{submissionId}?task={taskKey}, the correct task, and the selected submission detail without a redirect.
  7. Click “Все решения”; as an administrator, expect both participants' submissions to remain visible and openable while editing/submitting stays unavailable.
  8. Sign in as a non-admin and open /admin; expect “Нет доступа”, no redirect to /auth, and a preserved authenticated session.
  9. Follow “Вернуться на главную” and confirm the non-admin can continue using the application without signing in again.
  10. Recheck the dashboard in light/dark themes and a narrow viewport; wide tables should scroll without breaking the page.

Closes #89

@DIvanCode
DIvanCode marked this pull request as ready for review August 9, 2026 15:27

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 776299800f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +455 to +457
isLoading={usersQuery.isLoading || activeUsersQuery.isLoading}
isError={usersQuery.isError || activeUsersQuery.isError}
hasData={inactiveUsers.length > 0}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop labeling users offline when the active-user query fails

When /users/admin/all succeeds but /users/admin/active fails, activeUsersQuery.data ?? [] makes inactiveUsers contain every user, and the non-empty hasData value causes QueryContent to ignore isError. The dashboard consequently labels all users “Не в сети,” including users whose status is unknown; this block should render an error unless both source queries have usable data.

AGENTS.md reference: AGENTS.md:L34-L35

Useful? React with 👍 / 👎.

Comment on lines +357 to +359
useEffect(() => {
const timer = window.setInterval(() => setNow(Date.now()), 1000);
return () => window.clearInterval(timer);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep the live clock out of the whole dashboard

While the page is open, this one-second state update re-renders the entire AdminPage, rebuilding every user, duel, submission, group, and tournament row merely to update ranked-search wait times. Because the admin endpoints include complete historical lists, accounts with sizable histories will pay this reconciliation cost every second; isolate the clock in the ranked-search table or update only those cells.

Useful? React with 👍 / 👎.

@DIvanCode
DIvanCode marked this pull request as draft August 9, 2026 15:48
@DIvanCode
DIvanCode marked this pull request as ready for review August 9, 2026 15:55

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9556b54fb0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


const skipAdminQueries = { skip: !isAdmin };
const usersQuery = useGetAdminUsersQuery(undefined, skipAdminQueries);
const activeUsersQuery = useGetAdminActiveUsersQuery(undefined, skipAdminQueries);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Refresh the volatile admin lists

When an administrator keeps /admin open while backend state changes, these RTK Query subscriptions use the default one-time fetch with no polling, focus/reconnect refetch, cache invalidation tags, or realtime handler. Consequently active users, ranked searchers, pending/active duels, testing submissions, and tournaments remain frozen at their initial snapshots indefinitely—even as the displayed search wait time continues advancing—so add polling, realtime invalidation, or an explicit refresh mechanism for these volatile lists.

AGENTS.md reference: AGENTS.md:L37-L37

Useful? React with 👍 / 👎.

@DIvanCode
DIvanCode marked this pull request as draft August 9, 2026 16:36
@DIvanCode
DIvanCode marked this pull request as ready for review August 9, 2026 16:39
@DIvanCode
DIvanCode merged commit ddc408e into master Aug 9, 2026
7 checks passed
@DIvanCode
DIvanCode deleted the codex/issue-89-admin-page branch August 9, 2026 16:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[observe][Frontend] Реализовать страницу админки и отображение всех списков

1 participant