Skip to content

Release v1.6.0#105

Merged
ianpaschal merged 22 commits intomasterfrom
develop
Jul 1, 2025
Merged

Release v1.6.0#105
ianpaschal merged 22 commits intomasterfrom
develop

Conversation

@ianpaschal
Copy link
Owner

@ianpaschal ianpaschal commented Jun 30, 2025

Summary by CodeRabbit

  • New Features

    • Added dynamic player slot management in tournament competitor forms, allowing users to add multiple player slots for teams.
    • Introduced a PlayerCount component to display active players per team in tournament rosters.
    • Tournament pairings grid now supports internal state management, reset, and "dirty" tracking for manual pairing adjustments.
    • Added a new check to reveal player names when required by tournament rules.
  • Enhancements

    • Tournament competitor and pairing lists are now consistently sorted by team or player name for improved readability.
    • Tournament info and roster now display active player counts instead of total registered players.
    • Improved filtering in match result forms to exclude fully submitted pairings.
    • Enhanced sign-in form email validation and improved error messaging for authentication failures.
  • Bug Fixes

    • Prevented submission of empty player slots in forms.
    • Filtered out players and competitors with zero games played from aggregated tournament data.
  • Style

    • Unified and elevated card styles across multiple components for a more consistent look.
    • Improved layout and styling for tournament rosters, popovers, menus, and accordion items.
  • Refactor

    • Streamlined logic for pairing management and player count validation.
    • Simplified and consolidated SCSS mixins for card and elevation styles.
    • Updated import paths and modularized type exports for pairing grid components.
  • Documentation

    • Added and updated type exports for pairing grid and player count components.

@vercel
Copy link

vercel bot commented Jun 30, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
combat-command ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 1, 2025 5:33am

@coderabbitai
Copy link

coderabbitai bot commented Jun 30, 2025

Walkthrough

This update introduces dynamic player slot management and player count display for tournament competitors, enhances sorting and filtering logic for competitors and pairings, and refactors several UI components and styles. It also adds new helpers for user name visibility in tournaments, consolidates card styling, and improves internal state handling for tournament pairing grids.

Changes

Files/Groups Change Summary
convex/_model/tournamentCompetitors/queries/getTournamentCompetitors.ts
convex/_model/tournamentCompetitors/queries/getTournamentCompetitorsByTournament.ts
Added deterministic sorting of deepened tournament competitors by team or player name.
convex/_model/tournaments/_helpers/deepenTournament.ts Added activePlayerCount property to deepened tournament object.
convex/_model/users/_helpers/checkUserTournamentForcedName.ts Introduced function to check if two users are in a real-name-required tournament.
convex/_model/users/_helpers/redactUser.ts Extended redaction logic to reveal names if required by tournament.
src/components/AccountMenu/AccountMenu.module.scss
src/components/AvatarEditable/AvatarEditable.module.scss
src/components/MatchResultPlayers/MatchResultPlayers.module.scss
src/components/generic/DataTable/DataTable.module.scss
src/components/generic/InputDateTime/InputDateTime.module.scss
src/components/generic/InputLocation/InputLocation.module.scss
src/components/generic/PopoverMenu/PopoverMenu.module.scss
src/pages/TournamentsPage/TournamentsPage.module.scss
Consolidated card, shadow, and corner mixins into variants.card($elevated: true); removed explicit background/border-radius.
src/components/MatchResultCard/MatchResultCard.module.scss
src/components/TournamentCard/TournamentCard.module.scss
src/components/generic/Card/Card.module.scss
Removed shadow and corner mixins from card components.
src/components/FowV4MatchResultForm/FowV4MatchResultForm.tsx Filtered out fully submitted pairings from select options; auto-set player fields when "single" is selected.
src/components/TournamentCompetitorEditDialog/TournamentCompetitorEditDialog.tsx Filtered out players without userId before submitting; wrapped form in a scroll area.
src/components/TournamentCompetitorForm/TournamentCompetitorForm.tsx
src/components/TournamentCompetitorForm/TournamentCompetitorForm.schema.ts
src/components/TournamentCompetitorForm/TournamentCompetitorForm.module.scss
Enabled dynamic player slot addition; filtered empty players on submit; updated validation and default value logic; added grid styling for add button.
src/components/TournamentInfoBlock/TournamentInfoBlock.tsx Changed displayed player count to show only active players.
src/components/TournamentPairingsGrid/TournamentPairingsGrid.tsx
src/components/TournamentPairingsGrid/TournamentPairingsGrid.types.ts
src/components/TournamentPairingsGrid/TournamentPairingsGrid.utils.ts
src/components/TournamentPairingsGrid/PairingsGridRow/PairingsGridRow.tsx
src/components/TournamentPairingsGrid/PairingsGridRow/PairingsGridRow.utils.ts
src/components/TournamentPairingsGrid/index.ts
Refactored pairing grid to use internal state and imperative handle; updated types and utility logic for pairing state and conversions.
src/pages/TournamentAdvanceRoundPage/components/ConfirmPairingsDialog/ConfirmPairingsDialog.tsx
src/pages/TournamentAdvanceRoundPage/components/PairingsStep/PairingsStep.tsx
src/pages/TournamentAdvanceRoundPage/components/PairingsStep/PairingsStep.utils.ts
Updated pairing step to use new grid handle and types; improved conversion logic for unassigned pairings.
src/components/TournamentRoster/TournamentRoster.tsx
src/components/TournamentRoster/TournamentRoster.module.scss
src/components/TournamentRoster/components/CompetitorActions/CompetitorActions.tsx
src/components/TournamentRoster/components/CompetitorActions/CompetitorActions.module.scss
Added player count display to roster; updated layout to CSS grid; improved action menu class handling and visibility logic.
src/components/TournamentRoster/components/PlayerCount/PlayerCount.tsx
src/components/TournamentRoster/components/PlayerCount/PlayerCount.module.scss
src/components/TournamentRoster/components/PlayerCount/index.ts
Introduced new PlayerCount component and styles for displaying competitor player counts.
src/components/generic/Accordion/AccordionItem.tsx
src/components/generic/Accordion/AccordionItem.module.scss
Chevron icon now conditionally rendered; header styling conditional on enabled state.
src/style/_variants.scss Updated card mixin to accept $elevated parameter and consolidate corners/shadows.
convex/_model/fowV4/aggregateFowV4TournamentData.ts
convex/_model/fowV4/flattenFowV4StatMap.ts
Filtered out players and competitors with zero games played; added gamesPlayed property in flattened stats.
src/pages/AuthPage/components/SignInForm/SignInForm.schema.ts
src/services/auth/useSignIn.ts
Improved email validation to require proper format; added error mapping for sign-in failures.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant TournamentCompetitorForm
  participant Server

  User->>TournamentCompetitorForm: Click "Add Player Slot"
  TournamentCompetitorForm->>TournamentCompetitorForm: Append empty player slot if allowed
  User->>TournamentCompetitorForm: Fill player info, submit form
  TournamentCompetitorForm->>TournamentCompetitorForm: Filter out players without userId
  TournamentCompetitorForm->>Server: Submit filtered players
  Server-->>TournamentCompetitorForm: Respond with update result
Loading
sequenceDiagram
  participant TournamentRoster
  participant PlayerCount

  TournamentRoster->>PlayerCount: Render with competitor and competitorSize
  PlayerCount->>PlayerCount: Count active players
  PlayerCount-->>TournamentRoster: Display "active/total" player count
Loading

Possibly related PRs

  • ianpaschal/combat-command#103: Adds and displays player count in TournamentRoster, directly related to new PlayerCount component and styling.
  • ianpaschal/combat-command#104: Updates TournamentCompetitorEditDialog and TournamentCompetitorForm to filter out players without userId and improve dynamic player slot management.
  • ianpaschal/combat-command#107: Improves sign-in form validation and error message mapping, closely related to the changes in sign-in schema and auth hook.

Poem

In the garden where tournaments grow,
A bunny hops, counting players in a row.
With slots that appear at a gentle click,
And cards now styled both bold and slick.
The roster gleams, the pairings flow—
A hop, a skip, the code’s aglow!
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-07-01T06_18_49_681Z-debug-0.log


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec0e41f and 85245b1.

⛔ Files ignored due to path filters (1)
  • convex/_generated/api.d.ts is excluded by !**/_generated/**
📒 Files selected for processing (46)
  • convex/_model/fowV4/aggregateFowV4TournamentData.ts (1 hunks)
  • convex/_model/fowV4/flattenFowV4StatMap.ts (2 hunks)
  • convex/_model/tournamentCompetitors/queries/getTournamentCompetitors.ts (1 hunks)
  • convex/_model/tournamentCompetitors/queries/getTournamentCompetitorsByTournament.ts (1 hunks)
  • convex/_model/tournaments/_helpers/deepenTournament.ts (2 hunks)
  • convex/_model/users/_helpers/checkUserTournamentForcedName.ts (1 hunks)
  • convex/_model/users/_helpers/redactUser.ts (2 hunks)
  • src/components/AccountMenu/AccountMenu.module.scss (1 hunks)
  • src/components/AvatarEditable/AvatarEditable.module.scss (1 hunks)
  • src/components/FowV4MatchResultForm/FowV4MatchResultForm.tsx (3 hunks)
  • src/components/MatchResultCard/MatchResultCard.module.scss (0 hunks)
  • src/components/MatchResultPlayers/MatchResultPlayers.module.scss (1 hunks)
  • src/components/ToastProvider/ToastProvider.module.scss (1 hunks)
  • src/components/TournamentCard/TournamentCard.module.scss (0 hunks)
  • src/components/TournamentCompetitorEditDialog/TournamentCompetitorEditDialog.tsx (2 hunks)
  • src/components/TournamentCompetitorForm/TournamentCompetitorForm.module.scss (1 hunks)
  • src/components/TournamentCompetitorForm/TournamentCompetitorForm.schema.ts (2 hunks)
  • src/components/TournamentCompetitorForm/TournamentCompetitorForm.tsx (6 hunks)
  • src/components/TournamentInfoBlock/TournamentInfoBlock.tsx (1 hunks)
  • src/components/TournamentPairingsGrid/PairingsGridRow/PairingsGridRow.tsx (1 hunks)
  • src/components/TournamentPairingsGrid/PairingsGridRow/PairingsGridRow.utils.ts (1 hunks)
  • src/components/TournamentPairingsGrid/TournamentPairingsGrid.tsx (6 hunks)
  • src/components/TournamentPairingsGrid/TournamentPairingsGrid.types.ts (1 hunks)
  • src/components/TournamentPairingsGrid/TournamentPairingsGrid.utils.ts (2 hunks)
  • src/components/TournamentPairingsGrid/index.ts (1 hunks)
  • src/components/TournamentRoster/TournamentRoster.module.scss (1 hunks)
  • src/components/TournamentRoster/TournamentRoster.tsx (2 hunks)
  • src/components/TournamentRoster/components/CompetitorActions/CompetitorActions.module.scss (1 hunks)
  • src/components/TournamentRoster/components/CompetitorActions/CompetitorActions.tsx (3 hunks)
  • src/components/TournamentRoster/components/PlayerCount/PlayerCount.module.scss (1 hunks)
  • src/components/TournamentRoster/components/PlayerCount/PlayerCount.tsx (1 hunks)
  • src/components/TournamentRoster/components/PlayerCount/index.ts (1 hunks)
  • src/components/generic/Accordion/AccordionItem.module.scss (1 hunks)
  • src/components/generic/Accordion/AccordionItem.tsx (1 hunks)
  • src/components/generic/Card/Card.module.scss (0 hunks)
  • src/components/generic/DataTable/DataTable.module.scss (1 hunks)
  • src/components/generic/InputDateTime/InputDateTime.module.scss (1 hunks)
  • src/components/generic/InputLocation/InputLocation.module.scss (1 hunks)
  • src/components/generic/PopoverMenu/PopoverMenu.module.scss (1 hunks)
  • src/pages/AuthPage/components/SignInForm/SignInForm.schema.ts (1 hunks)
  • src/pages/TournamentAdvanceRoundPage/components/ConfirmPairingsDialog/ConfirmPairingsDialog.tsx (1 hunks)
  • src/pages/TournamentAdvanceRoundPage/components/PairingsStep/PairingsStep.tsx (6 hunks)
  • src/pages/TournamentAdvanceRoundPage/components/PairingsStep/PairingsStep.utils.ts (1 hunks)
  • src/pages/TournamentsPage/TournamentsPage.module.scss (1 hunks)
  • src/services/auth/useSignIn.ts (2 hunks)
  • src/style/_variants.scss (2 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ianpaschal
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jun 30, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@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.

Actionable comments posted: 3

🔭 Outside diff range comments (2)
src/components/ToastProvider/ToastProvider.module.scss (1)

29-40: Shadow now defined twice

variants.card($elevated: true) supplies its own shadow.
Lines 38-40 set an explicit box-shadow, which will override (or duplicate) the mixin’s value. This can lead to inconsistent depth compared to other components that rely solely on the mixin.

-  background-color: var(--card-bg);
-  box-shadow:
-    hsl(206deg 22% 7% / 35%) 0 10px 38px -10px,
-    hsl(206deg 22% 7% / 20%) 0 10px 20px -15px;
+  /* variants.card already handles bg + shadow */

Drop the manual styles unless there’s a specific visual requirement.

convex/_model/tournaments/_helpers/deepenTournament.ts (1)

33-41: activePlayerCount may double-count duplicates – normalise with a Set

If a user appears in multiple competitors (edge-case but possible with data issues), the current reduce will count them multiple times.
De-duplicate first so activePlayerCount truly reflects unique active players.

-const activePlayerUserIds = tournamentCompetitors.reduce((acc, c) => [
-  ...acc,
-  ...c.players.filter((p) => p.active).map((p) => p.userId),
-], [] as Id<'users'>[]);
+const activePlayerUserIds = [
+  ...new Set(
+    tournamentCompetitors.flatMap((c) =>
+      c.players.filter((p) => p.active).map((p) => p.userId),
+    ),
+  ),
+] as Id<'users'>[];

This change keeps activePlayerCount correct without other modifications.

🧹 Nitpick comments (15)
src/components/TournamentPairingsGrid/PairingsGridRow/PairingsGridRow.tsx (3)

4-4: Consider colocating all pairing-related types

TournamentCompetitorRanked is still imported from ~/api while DraftTournamentPairing was moved to a local types file. Keeping related domain types in one place (e.g. the new TournamentPairingsGrid.types.ts) reduces coupling to the API layer and makes refactors cheaper.


8-9: Nit: use barrel export to avoid deep relative paths

'../TournamentPairingsGrid.types' reaches outside the sub-folder. A barrel file at TournamentPairingsGrid/index.ts (already present for the component) could re-export the type:

-import { DraftTournamentPairing } from '../TournamentPairingsGrid.types';
+import type { DraftTournamentPairing } from '..';

This keeps import paths stable if the file hierarchy changes again.


30-31: Guard expression could be clearer & properly typed

The tuple access on a Partial<DraftTournamentPairing> loses type safety. A small rewrite avoids falsey checks and narrows the type:

-const isValid = pairing && pairing[0] && pairing[1] ? checkDraftPairingIsValid(pairing) : undefined;
+const isComplete = pairing?.[0] && pairing?.[1];
+const isValid = isComplete ? checkDraftPairingIsValid(pairing as DraftTournamentPairing) : undefined;

Makes intent explicit and prevents accidental truthy coercion on 0 / empty strings.

src/pages/TournamentAdvanceRoundPage/components/ConfirmPairingsDialog/ConfirmPairingsDialog.tsx (2)

1-5: Minor: keep import grouping consistent

Now that both UnassignedTournamentPairing and DraftTournamentPairing come from separate layers (~/api vs component), consider grouping external/domain imports first and local component imports after for readability.


24-29: unassignedPairings is recomputed on every render

If manualPairings is large, this conversion could be memoised:

-const unassignedPairings = convertDraftPairingsToUnassignedPairings(manualPairings);
+const unassignedPairings = useMemo(
+  () => convertDraftPairingsToUnassignedPairings(manualPairings),
+  [manualPairings],
+);

Not critical but avoids needless work during typing-heavy pairing steps.

src/pages/TournamentsPage/TournamentsPage.module.scss (1)

14-14: Great consolidation, but drop the now-unused shadows import

variants.card($elevated: true) bundles the elevation styles, so the direct @use "/src/style/shadows"; at the top of the file is no longer referenced.
Trimming unused imports keeps the build faster and the intent clear.

-@use "/src/style/shadows";
src/components/generic/InputDateTime/InputDateTime.module.scss (1)

22-27: Unused shadows import & redundant overrides

  1. variants.card($elevated: true) already adds the elevation; remove the @use "/src/style/shadows"; import (line 4) for cleanliness.
  2. The explicit background-color & border-radius block duplicates what the mixin supplies—unless you’re intentionally overriding values, it’s safe to drop.
-@use "/src/style/shadows";
...
-  & {
-    margin: 0.25rem 0;
-    background-color: var(--card-bg);
-    border-radius: variables.$corner-radius;
-  }
+  margin: 0.25rem 0;
src/components/generic/InputLocation/InputLocation.module.scss (1)

8-12: Same clean-up opportunity as other card popovers

variants.card($elevated: true) supersedes the explicit elevation and makes the shadows import (line 4) obsolete.
Consider dropping the unused import and redundant background-color / border-radius declarations for parity with the rest of the refactor.

src/components/TournamentCompetitorEditDialog/TournamentCompetitorEditDialog.tsx (1)

30-35: Remove duplicate player filtering logic.

The TournamentCompetitorForm component already filters out players without userId in its own handleSubmit method (as shown in the relevant code snippet). This additional filtering in the dialog creates redundant logic.

Consider removing the duplicate filtering here since the form component already handles it:

-  const { players, ...restData } = formData;
-  updateTournamentCompetitor({
-    id: data?.tournamentCompetitor._id,
-    ...restData,
-    players: players.filter((player) => player.userId),
-  });
+  updateTournamentCompetitor({
+    id: data?.tournamentCompetitor._id,
+    ...formData,
+  });
convex/_model/users/_helpers/checkUserTournamentForcedName.ts (1)

14-14: Consider performance optimization.

Querying all tournaments could be inefficient for large datasets. Consider filtering tournaments that contain either user ID before checking relationships.

src/components/TournamentCompetitorForm/TournamentCompetitorForm.tsx (1)

102-108: Consider preventing default only for button elements.

The handleAddPlayer function prevents default on all mouse events. Since this handler is attached to a button, you might want to be more specific about when to prevent default behavior.

-const handleAddPlayer = (e: MouseEvent): void => {
-  e.preventDefault();
+const handleAddPlayer = (e: MouseEvent<HTMLButtonElement>): void => {
+  e.preventDefault();
src/components/TournamentPairingsGrid/TournamentPairingsGrid.utils.ts (1)

24-45: Good explicit handling of all pairing scenarios, but consider performance optimization.

The expanded logic correctly handles all four possible pairing states. However, the spread syntax in the reducer creates O(n²) time complexity.

Consider this more performant approach:

 return draftPairings.reduce((acc, pairing, i) => {
   if (pairing[0] && !pairing[1]) {
-    return {
-      ...acc,
-      [pairing[0].id]: 'unpaired',
-    };
+    acc[pairing[0].id] = 'unpaired';
+    return acc;
   }
   if (!pairing[0] && pairing[1]) {
-    return {
-      ...acc,
-      [pairing[1].id]: 'unpaired',
-    };
+    acc[pairing[1].id] = 'unpaired';
+    return acc;
   }
   if (pairing[0] && pairing[1]) {
-    return {
-      ...acc,
-      [pairing[0].id]: `${i}_0`,
-      [pairing[1].id]: `${i}_1`,
-    };
+    acc[pairing[0].id] = `${i}_0`;
+    acc[pairing[1].id] = `${i}_1`;
+    return acc;
   }
   return acc;
 }, {} as Record<TournamentCompetitorId, string>);
src/components/TournamentPairingsGrid/TournamentPairingsGrid.tsx (3)

115-132: Consider optimizing the state update logic for better performance.

The current implementation has O(n²) time complexity due to spread syntax in the reducer. While tournament sizes are typically small, this can be optimized.

-setGridState(Object.entries(gridState).map(([competitorId, slotId]) => {
-  // If this ID is the active one, we're dragging it. Set it's slotID to 'over':
-  if (competitorId === active.id) {
-    return [competitorId, over.id];
-  }
-  // If this slot is the target, move its competitor to 'unpaired':
-  if (slotId === over.id) {
-    return [competitorId, 'unpaired'];
-  }
-  // Otherwise do nothing:
-  return [competitorId, slotId];
-}).reduce((acc, [pairingCompetitorId, slotId]) => ({
-  ...acc,
-  [pairingCompetitorId as TournamentCompetitorId]: slotId,
-}), {}));
+const newState: PairingsGridState = {};
+Object.entries(gridState).forEach(([competitorId, slotId]) => {
+  if (competitorId === active.id) {
+    newState[competitorId as TournamentCompetitorId] = over.id as string;
+  } else if (slotId === over.id) {
+    newState[competitorId as TournamentCompetitorId] = 'unpaired';
+  } else {
+    newState[competitorId as TournamentCompetitorId] = slotId;
+  }
+});
+setGridState(newState);

137-140: Optimize the pivot operation for better performance.

The pivot operation also suffers from O(n²) complexity due to spread syntax.

-const gridStatePivoted = Object.entries(gridState ?? {}).reduce((acc, [competitorId, slotId]) => ({
-  ...acc,
-  [slotId]: competitors.find((c) => c.id === competitorId),
-}), {} as Record<string, TournamentCompetitorRanked | undefined>);
+const gridStatePivoted: Record<string, TournamentCompetitorRanked | undefined> = {};
+Object.entries(gridState ?? {}).forEach(([competitorId, slotId]) => {
+  gridStatePivoted[slotId] = competitors.find((c) => c.id === competitorId);
+});

51-197: Excellent architectural refactor for state encapsulation.

The refactor from a controlled to an uncontrolled component with imperative methods is well-executed. This pattern appropriately encapsulates the complex drag-and-drop state management within the grid component while maintaining clean communication with the parent through the onChange callback and exposed handle methods.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec0e41f and 7839d89.

⛔ Files ignored due to path filters (1)
  • convex/_generated/api.d.ts is excluded by !**/_generated/**
📒 Files selected for processing (42)
  • convex/_model/tournamentCompetitors/queries/getTournamentCompetitors.ts (1 hunks)
  • convex/_model/tournamentCompetitors/queries/getTournamentCompetitorsByTournament.ts (1 hunks)
  • convex/_model/tournaments/_helpers/deepenTournament.ts (2 hunks)
  • convex/_model/users/_helpers/checkUserTournamentForcedName.ts (1 hunks)
  • convex/_model/users/_helpers/redactUser.ts (2 hunks)
  • src/components/AccountMenu/AccountMenu.module.scss (1 hunks)
  • src/components/AvatarEditable/AvatarEditable.module.scss (1 hunks)
  • src/components/FowV4MatchResultForm/FowV4MatchResultForm.tsx (3 hunks)
  • src/components/MatchResultCard/MatchResultCard.module.scss (0 hunks)
  • src/components/MatchResultPlayers/MatchResultPlayers.module.scss (1 hunks)
  • src/components/ToastProvider/ToastProvider.module.scss (1 hunks)
  • src/components/TournamentCard/TournamentCard.module.scss (0 hunks)
  • src/components/TournamentCompetitorEditDialog/TournamentCompetitorEditDialog.tsx (2 hunks)
  • src/components/TournamentCompetitorForm/TournamentCompetitorForm.module.scss (1 hunks)
  • src/components/TournamentCompetitorForm/TournamentCompetitorForm.schema.ts (2 hunks)
  • src/components/TournamentCompetitorForm/TournamentCompetitorForm.tsx (6 hunks)
  • src/components/TournamentInfoBlock/TournamentInfoBlock.tsx (1 hunks)
  • src/components/TournamentPairingsGrid/PairingsGridRow/PairingsGridRow.tsx (1 hunks)
  • src/components/TournamentPairingsGrid/PairingsGridRow/PairingsGridRow.utils.ts (1 hunks)
  • src/components/TournamentPairingsGrid/TournamentPairingsGrid.tsx (6 hunks)
  • src/components/TournamentPairingsGrid/TournamentPairingsGrid.types.ts (1 hunks)
  • src/components/TournamentPairingsGrid/TournamentPairingsGrid.utils.ts (2 hunks)
  • src/components/TournamentPairingsGrid/index.ts (1 hunks)
  • src/components/TournamentRoster/TournamentRoster.module.scss (1 hunks)
  • src/components/TournamentRoster/TournamentRoster.tsx (2 hunks)
  • src/components/TournamentRoster/components/CompetitorActions/CompetitorActions.module.scss (1 hunks)
  • src/components/TournamentRoster/components/CompetitorActions/CompetitorActions.tsx (3 hunks)
  • src/components/TournamentRoster/components/PlayerCount/PlayerCount.module.scss (1 hunks)
  • src/components/TournamentRoster/components/PlayerCount/PlayerCount.tsx (1 hunks)
  • src/components/TournamentRoster/components/PlayerCount/index.ts (1 hunks)
  • src/components/generic/Accordion/AccordionItem.module.scss (1 hunks)
  • src/components/generic/Accordion/AccordionItem.tsx (1 hunks)
  • src/components/generic/Card/Card.module.scss (0 hunks)
  • src/components/generic/DataTable/DataTable.module.scss (1 hunks)
  • src/components/generic/InputDateTime/InputDateTime.module.scss (1 hunks)
  • src/components/generic/InputLocation/InputLocation.module.scss (1 hunks)
  • src/components/generic/PopoverMenu/PopoverMenu.module.scss (1 hunks)
  • src/pages/TournamentAdvanceRoundPage/components/ConfirmPairingsDialog/ConfirmPairingsDialog.tsx (1 hunks)
  • src/pages/TournamentAdvanceRoundPage/components/PairingsStep/PairingsStep.tsx (6 hunks)
  • src/pages/TournamentAdvanceRoundPage/components/PairingsStep/PairingsStep.utils.ts (1 hunks)
  • src/pages/TournamentsPage/TournamentsPage.module.scss (1 hunks)
  • src/style/_variants.scss (2 hunks)
💤 Files with no reviewable changes (3)
  • src/components/TournamentCard/TournamentCard.module.scss
  • src/components/MatchResultCard/MatchResultCard.module.scss
  • src/components/generic/Card/Card.module.scss
🧰 Additional context used
🧬 Code Graph Analysis (7)
src/components/TournamentCompetitorEditDialog/TournamentCompetitorEditDialog.tsx (1)
src/components/TournamentCompetitorForm/TournamentCompetitorForm.tsx (1)
  • TournamentCompetitorForm (49-192)
convex/_model/users/_helpers/redactUser.ts (1)
convex/_model/users/_helpers/checkUserTournamentForcedName.ts (1)
  • checkUserTournamentForcedName (5-30)
src/components/TournamentPairingsGrid/TournamentPairingsGrid.types.ts (1)
src/components/TournamentPairingsGrid/index.ts (1)
  • DraftTournamentPairing (6-6)
src/components/TournamentRoster/TournamentRoster.tsx (1)
src/components/TournamentRoster/components/PlayerCount/PlayerCount.tsx (1)
  • PlayerCount (14-26)
convex/_model/users/_helpers/checkUserTournamentForcedName.ts (2)
convex/_generated/server.d.ts (1)
  • QueryCtx (106-106)
convex/_generated/dataModel.d.ts (1)
  • Id (48-49)
src/pages/TournamentAdvanceRoundPage/components/PairingsStep/PairingsStep.utils.ts (1)
src/components/TournamentPairingsGrid/TournamentPairingsGrid.types.ts (1)
  • DraftTournamentPairing (5-5)
src/components/TournamentPairingsGrid/TournamentPairingsGrid.utils.ts (2)
src/components/TournamentPairingsGrid/TournamentPairingsGrid.types.ts (2)
  • PairingsGridState (3-3)
  • DraftTournamentPairing (5-5)
src/components/TournamentPairingsGrid/index.ts (1)
  • DraftTournamentPairing (6-6)
🪛 Biome (1.9.4)
src/components/TournamentPairingsGrid/TournamentPairingsGrid.utils.ts

[error] 27-27: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 33-33: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 39-39: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

src/components/TournamentPairingsGrid/TournamentPairingsGrid.tsx

[error] 130-130: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 138-138: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🔇 Additional comments (44)
src/components/TournamentCompetitorForm/TournamentCompetitorForm.module.scss (1)

15-17: LGTM! Clean grid styling implementation.

The grid-column: 1/-1; property correctly spans the button across all columns in the grid layout, which is appropriate for an "Add Player Slot" button that should take full width.

src/components/generic/Accordion/AccordionItem.module.scss (2)

18-23: Excellent conditional styling implementation.

Using the data-enabled="true" attribute selector to conditionally apply interactive styles (variants.ghost and cursor: pointer) is a clean approach that improves accessibility and user experience by providing clear visual feedback only when the accordion item is interactive.


30-30: Good separation of layout and interactive styles.

Moving the flex row layout to always apply regardless of enabled state is the right choice, as layout should remain consistent while only interactive behaviors change based on the disabled state.

src/components/generic/Accordion/AccordionItem.tsx (2)

44-44: Good coordination with conditional styling.

The data-enabled={!disabled} attribute properly coordinates with the SCSS conditional styling, enabling clean separation between interactive and non-interactive states.


47-55: Logical conditional rendering of interactive elements.

Conditionally rendering the chevron icon only when the accordion item is not disabled makes perfect sense from a UX perspective - non-interactive items shouldn't display interactive visual cues.

src/components/TournamentRoster/components/PlayerCount/PlayerCount.module.scss (1)

1-12: Clean and consistent styling implementation.

The PlayerCount styling follows established project patterns with appropriate use of flex layout, muted UI text for supplementary information, and consistent SVG sizing. The implementation is clean and maintainable.

src/components/TournamentPairingsGrid/PairingsGridRow/PairingsGridRow.utils.ts (1)

1-1: Good type organization refactoring.

Moving the DraftTournamentPairing type import from global API types to a local component-specific types file improves encapsulation and type organization. Using relative imports for local types is the appropriate approach.

src/components/MatchResultPlayers/MatchResultPlayers.module.scss (1)

111-118: Possible duplicate shadow / elevation styles

variants.card($elevated: true) already applies an elevated shadow in the new mixin implementation; verify the parent containers don’t also add additional shadows, otherwise cards may look doubly-elevated compared to neighbouring components.

src/components/TournamentRoster/components/CompetitorActions/CompetitorActions.module.scss (1)

12-13: Flex item may still grow

You replaced margin-left: auto with flex-shrink: 0.
If the intent was “stick to the right but never shrink”, also ensure flex-grow stays 0 (default) and parent layout still pushes the element rightward now that automatic margin is gone, especially after moving the header to CSS-grid.

src/components/TournamentRoster/components/PlayerCount/index.ts (1)

1-2: Barrel export LGTM

Simple re-export is clear and matches the surrounding pattern.

src/components/TournamentInfoBlock/TournamentInfoBlock.tsx (1)

56-56: Approve use of activePlayerCount in TournamentInfoBlock

Verified that activePlayerCount is populated (in convex/_model/tournaments/_helpers/deepenTournament.ts) and that there are no remaining playerCount references in tournament-display contexts. This change is safe and accurately reflects live participation.

• Location: src/components/TournamentInfoBlock/TournamentInfoBlock.tsx:56
• No other components break or need updates—other “PlayerCount” uses are scoped to roster items and derive their counts locally.

src/components/generic/PopoverMenu/PopoverMenu.module.scss (1)

13-13: variants.card mixin supports $elevated parameter – Approved!

The @mixin card($elevated: false) definition in src/style/_variants.scss correctly includes shadows.elevated when $elevated: true, so the refactoring in src/components/generic/PopoverMenu/PopoverMenu.module.scss is valid and maintains the intended elevated appearance.

• src/style/_variants.scss:

@mixin card($elevated: false) {
  @include borders.normal;
  @include corners.normal;
  @include shadows.surface;
  background-color: var(--card-bg);

  @if $elevated {
    @include shadows.elevated;
  }
}

• src/components/generic/PopoverMenu/PopoverMenu.module.scss, line 13:

@include variants.card($elevated: true);
src/components/AccountMenu/AccountMenu.module.scss (1)

19-19: LGTM! Consistent with styling pattern consolidation.

This change follows the same beneficial pattern seen across other components, consolidating multiple mixin calls into a single variants.card($elevated: true) call. This maintains consistency with the broader styling refactoring effort.

src/components/generic/DataTable/DataTable.module.scss (1)

31-31: LGTM! Maintains consistency with styling refactoring pattern.

The consolidation to variants.card($elevated: true) is consistent with the systematic styling improvements across the codebase, enhancing maintainability while preserving visual appearance.

src/components/AvatarEditable/AvatarEditable.module.scss (1)

28-28: LGTM! Completes the systematic styling consolidation.

This final change completes the consistent application of the variants.card($elevated: true) pattern across all reviewed components. The systematic approach to this styling refactoring demonstrates good architectural discipline and will improve long-term maintainability.

src/components/TournamentPairingsGrid/TournamentPairingsGrid.types.ts (1)

1-5: LGTM! Well-defined type for draft tournament pairings.

The DraftTournamentPairing type appropriately models incomplete pairings with nullable competitors, which is essential for draft tournament management functionality.

convex/_model/users/_helpers/redactUser.ts (2)

6-6: Good addition of tournament-specific name visibility logic.

The import of the helper function enables checking for tournament-specific real name requirements.


55-65: LGTM! Proper implementation of tournament-forced name visibility.

The logic correctly:

  • Awaits the async tournament name requirement check
  • Extends the existing name visibility conditions without breaking them
  • Respects tournament-specific rules for showing real names
src/style/_variants.scss (2)

3-4: Good import additions for styling consolidation.

Adding corners and shadows modules enables the enhanced card mixin functionality.


195-205: Excellent consolidation of card styling logic.

The enhanced card mixin:

  • Maintains backward compatibility with default $elevated: false
  • Consolidates borders, corners, shadows, and background styling
  • Properly applies conditional elevated shadows
  • Simplifies usage across components
src/components/TournamentRoster/TournamentRoster.tsx (2)

7-7: Good addition of PlayerCount component for team tournaments.

The import enables displaying active player counts in team-based tournaments.


18-31: Excellent UI enhancements for tournament roster.

The changes properly:

  • Extract competitorSize from the tournament context
  • Conditionally render PlayerCount only for team tournaments
  • Apply appropriate CSS classes for grid layout styling
  • Maintain existing behavior for non-team tournaments

The conditional rendering and prop passing align well with the PlayerCount component interface.

src/components/TournamentCompetitorEditDialog/TournamentCompetitorEditDialog.tsx (1)

41-49: Good addition of ScrollArea for enhanced UX.

Wrapping the form in a ScrollArea is a good improvement that will handle potentially longer forms due to dynamic player slot additions, ensuring the dialog remains usable with larger team sizes.

src/components/TournamentRoster/TournamentRoster.module.scss (1)

10-30: Excellent migration to CSS Grid layout.

The transition from flexbox to CSS Grid with named areas provides better layout control and maintainability. The semantic naming ("Identity", "PlayerCount", "Actions") makes the layout structure clear and self-documenting.

src/components/FowV4MatchResultForm/FowV4MatchResultForm.tsx (3)

8-8: Good addition of UserId import.

The UserId type import is appropriately used for type casting in the form value setting logic.


126-126: Excellent filtering logic for completed pairings.

Filtering out pairings that have already reached their required number of match results prevents users from submitting duplicate results and improves the UX by showing only relevant options.


135-138: Smart auto-population of user data.

The logic to automatically set the current user as player0UserId and reset player1UserId when selecting "single" match provides a good user experience by reducing manual input.

src/components/TournamentPairingsGrid/index.ts (1)

1-6: Excellent expansion of the module's public API.

Exporting the component types (TournamentPairingsGridHandle, TournamentPairingsGridProps) and the DraftTournamentPairing type improves TypeScript integration for consumers of this module and follows best practices for library design.

src/components/TournamentCompetitorForm/TournamentCompetitorForm.schema.ts (2)

20-33: Clean validation logic simplification.

The refactored validation logic maintains the same functionality while being more readable and concise. The merged conditions for active player count validation are well-structured.


50-56: Good refactoring of getDefaultValues function.

The simplified implementation is more concise and removes unnecessary complexity. The removal of the default array fallback aligns with the dynamic player slot management approach mentioned in the summary.

src/components/TournamentRoster/components/PlayerCount/PlayerCount.tsx (1)

1-26: Well-implemented PlayerCount component.

This is a clean, focused component with clear responsibilities:

  • Properly filters active players for accurate count display
  • Uses semantic data attributes for styling hooks
  • Well-typed with clear props interface
  • Follows React best practices

The implementation correctly handles the core functionality of displaying active player counts relative to competitor size.

src/components/TournamentRoster/components/CompetitorActions/CompetitorActions.tsx (2)

24-24: Good addition of className prop for styling flexibility.

The optional className prop enhances component reusability by allowing external styling control. The implementation using clsx is appropriate.

Also applies to: 29-29, 100-100


82-82: Clean simplification of visibility logic.

The refactored conditions are more concise and maintain the same functionality:

  • The Leave condition is more explicit about excluded statuses
  • Removed redundant user checks since user existence is already validated in variable definitions

Also applies to: 87-87, 92-92

src/pages/TournamentAdvanceRoundPage/components/PairingsStep/PairingsStep.utils.ts (2)

2-2: Import source change aligns with type reorganization.

The import change from ~/api to ~/components/TournamentPairingsGrid aligns with the type definition being moved to the grid component as mentioned in the summary.


6-33: Excellent refactoring with robust edge case handling.

The rewritten function is much more explicit and handles edge cases effectively:

  • Correctly filters out pairings with no competitors
  • Explicitly handles partial pairings (one competitor null)
  • Uses Set to merge playedTables arrays, avoiding duplicates
  • Safe use of non-null assertion after proper null checks

The logic is clear and the implementation is robust.

src/components/TournamentCompetitorForm/TournamentCompetitorForm.tsx (3)

113-113: Good practice filtering out empty player slots.

The logic correctly filters out players without user IDs before submission, preventing empty slots from being sent to the backend.


146-152: Excellent defensive programming with null-safe access.

The null-safe access patterns for players[i]?.active and players[i]?.userId prevent potential runtime errors when dealing with dynamic player arrays.


160-168: Well-implemented dynamic player slot management.

The "Add Player Slot" button implementation includes appropriate disabled states and clear visual indicators. The logic to disable the button when empty slots exist prevents user confusion.

src/pages/TournamentAdvanceRoundPage/components/PairingsStep/PairingsStep.tsx (3)

64-65: Good use of imperative handle pattern for state delegation.

The ref-based approach cleanly delegates state management to the child component. The null-safe access on isDirty appropriately handles the case where the ref might not be initialized yet.


85-85: Clean delegation of reset logic to the grid component.

The reset handler correctly delegates to the grid's imperative reset method, maintaining proper separation of concerns.


114-114: Appropriate use of uncontrolled component pattern.

Using defaultValue instead of a controlled value prop is the right choice here, as it allows the grid to manage its own complex internal state while still notifying the parent of changes.

src/components/TournamentPairingsGrid/TournamentPairingsGrid.utils.ts (1)

48-51: Improved type safety and defensive null checks.

The updated function signature with PairingsGridState | null provides better type safety, and the comprehensive null/empty checks prevent potential runtime errors.

src/components/TournamentPairingsGrid/TournamentPairingsGrid.tsx (2)

91-95: Well-designed imperative API exposure.

The useImperativeHandle implementation provides a clean, minimal interface for parent components to interact with the grid's internal state. The exposed reset method and isDirty flag are exactly what's needed for the use case.


76-80: Correct uncontrolled component state initialization.

The state initialization logic properly implements the uncontrolled component pattern by only setting the internal state once from defaultValue.

…titorsByTournament.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@ianpaschal
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jul 1, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/pages/AuthPage/components/SignInForm/SignInForm.schema.ts (1)

4-4: Improve error message specificity for email validation.

The change from min(1) to email() is excellent as it provides proper email format validation. However, consider updating the error message to be more specific about email format requirements.

-  email: z.string().email('Please enter your email.').transform((val) => val.trim().toLowerCase()),
+  email: z.string().email('Please enter a valid email address.').transform((val) => val.trim().toLowerCase()),
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7839d89 and 85245b1.

📒 Files selected for processing (7)
  • convex/_model/fowV4/aggregateFowV4TournamentData.ts (1 hunks)
  • convex/_model/fowV4/flattenFowV4StatMap.ts (2 hunks)
  • convex/_model/tournamentCompetitors/queries/getTournamentCompetitorsByTournament.ts (1 hunks)
  • convex/_model/users/_helpers/checkUserTournamentForcedName.ts (1 hunks)
  • convex/_model/users/_helpers/redactUser.ts (2 hunks)
  • src/pages/AuthPage/components/SignInForm/SignInForm.schema.ts (1 hunks)
  • src/services/auth/useSignIn.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • convex/_model/users/_helpers/checkUserTournamentForcedName.ts
  • convex/_model/users/_helpers/redactUser.ts
  • convex/_model/tournamentCompetitors/queries/getTournamentCompetitorsByTournament.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
convex/_model/fowV4/aggregateFowV4TournamentData.ts (1)
convex/_model/fowV4/flattenFowV4StatMap.ts (1)
  • flattenFowV4StatMap (14-33)
src/services/auth/useSignIn.ts (1)
src/components/ToastProvider/ToastProvider.store.ts (1)
  • toast (29-50)
🔇 Additional comments (5)
convex/_model/fowV4/flattenFowV4StatMap.ts (2)

16-16: Function signature change is a breaking change but looks correct.

The addition of gamesPlayed: number to the return type is a breaking change for any consumers of this function, but it's necessary for the filtering functionality implemented in the aggregation function.


30-30: Good defensive programming with nullish coalescing.

The use of stats.gamesPlayed ?? 0 properly handles cases where the gamesPlayed property might be undefined, defaulting to a sensible value of 0.

convex/_model/fowV4/aggregateFowV4TournamentData.ts (2)

134-137: Filtering logic for players is well-implemented.

The filter correctly excludes players with zero games played using the gamesPlayed property that was added to the flattenFowV4StatMap output. The boolean coercion approach is appropriate since 0 is falsy.


138-142: Consistent filtering logic applied to competitors.

The same filtering approach is consistently applied to competitors, which maintains logical consistency in the data aggregation. This ensures that both players and competitors are filtered using the same criteria.

src/services/auth/useSignIn.ts (1)

63-65: LGTM! Clean error handling implementation.

The usage of the error mapping helper is implemented correctly, providing a good user experience with clear error messages.

Comment on lines +17 to +28
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function mapConvexAuthError(error: any): string {
if (!error?.message) {
return 'An unexpected error occurred. Please try again.';
}

if (error.message.includes('InvalidAccountId')) {
return 'Your email or password is incorrect.';
}

return 'Sign-in failed. Please check your details and try again.';
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve type safety and error checking robustness.

The error mapping approach is good for providing user-friendly messages, but consider these improvements:

  1. Type Safety: Replace any with a more specific type
  2. Robust Error Checking: Use more reliable error identification methods
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-function mapConvexAuthError(error: any): string {
-  if (!error?.message) {
-    return 'An unexpected error occurred. Please try again.';
-  }
-
-  if (error.message.includes('InvalidAccountId')) {
-    return 'Your email or password is incorrect.';
-  }
-
-  return 'Sign-in failed. Please check your details and try again.';
-}
+interface ConvexAuthError {
+  message?: string;
+  code?: string;
+}
+
+function mapConvexAuthError(error: unknown): string {
+  const authError = error as ConvexAuthError;
+  
+  if (!authError?.message) {
+    return 'An unexpected error occurred. Please try again.';
+  }
+
+  // Use more specific error code checking if available
+  if (authError.code === 'InvalidAccountId' || authError.message.includes('InvalidAccountId')) {
+    return 'Your email or password is incorrect.';
+  }
+
+  return 'Sign-in failed. Please check your details and try again.';
+}
🤖 Prompt for AI Agents
In src/services/auth/useSignIn.ts between lines 17 and 28, replace the use of
'any' for the error parameter with a more specific type such as 'unknown' or a
custom error interface to improve type safety. Then, enhance error checking by
verifying the error object structure more robustly, for example by using type
guards or checking if the error is an instance of Error before accessing the
message property. This will prevent runtime errors and make the error handling
more reliable.

@ianpaschal
Copy link
Owner Author

@coderabbitai summary

@coderabbitai
Copy link

coderabbitai bot commented Jul 1, 2025

✅ Actions performed

Summary regeneration triggered.

@ianpaschal ianpaschal merged commit 7411a16 into master Jul 1, 2025
2 checks passed
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.

1 participant