Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Create game based validation for Admin form #50

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

tydolla00
Copy link
Owner

@tydolla00 tydolla00 commented Mar 8, 2025

This branch is designed to allow the form to drive the validation and ui for our submission process

Summary by CodeRabbit

  • New Features

    • Enhanced validations for administrative forms to promote consistent and accurate data entry, ensuring inputs adhere to stringent formatting and content requirements.
  • Improvements

    • Improved type safety in match and set management components, ensuring data structures conform to defined types for better reliability.
    • Simplified avatar rendering logic with improved image loading handling.
  • Bug Fixes

    • Addressed discrepancies in player session name assignments within the PlayerSessionManager component.

@tydolla00 tydolla00 linked an issue Mar 8, 2025 that may be closed by this pull request
Copy link

vercel bot commented Mar 8, 2025

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

Name Status Preview Comments Updated (UTC)
project-rdc ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 8, 2025 5:25pm

Copy link

coderabbitai bot commented Mar 8, 2025

Walkthrough

This update modifies the form schema validation in an administrative helper module. The changes enforce stricter formatting rules across various fields, requiring trimmed, non-empty strings with specific length constraints. A URL validation is added for the session URL field, and nested validations ensure that arrays (such as players, sets, and matches) contain properly structured objects with required properties. Additionally, new type exports enhance type safety. The EntryCreatorForm component's validation logic now includes debugging output, while the MatchManager and SetManager components enforce stricter type handling.

Changes

File Change Summary
src/app/.../_utils/form-helpers.ts Enhanced validations for fields: "game", "sessionName", "sessionUrl", "videoId", "date", and "thumbnail" now require trimmed, non-empty strings with length limits. The "players", "sets", and "matches" arrays are updated to enforce non-empty object structures with required fields and validations. New types Matches, MatchWinners, PlayerSessions, and SetWinners are introduced.
src/app/.../form/EntryCreatorForm.tsx Modified form validation logic to include an asynchronous function that logs form data and validation results. Added logging in the validation function for debugging purposes.
src/app/.../form/MatchManager.tsx Updated type handling for matchWinners and playerSessions by enforcing type casting to MatchWinners and PlayerSessions. Removed unused useMemo import.
src/app/.../form/SetManager.tsx Streamlined imports and modified handleAddSet to append objects with type assertions for matches and setWinners, enhancing type safety.
src/app/.../form/PlayerAvatar.tsx Simplified the AvatarFallback component by removing the delayMs prop and added an onLoadingStatusChange prop to AvatarImage for handling image loading status.
src/app/.../form/PlayerSessionManager.tsx Updated the getPlayerNameFromField function to include a comment regarding the assignment of playerSessionName, with no change to functional logic.
src/app/.../_utils/types.ts Introduced a new type alias NonEmptyArray<T> representing a non-empty array of type T.

Poem

I’m a hopping rabbit in the code garden bright,
Trimming strings and rules with all my might.
Fields stand neat, arrays aligned in a row,
Each format validated, letting precision show.
Leaping through logic, I sing with code delight! 🐰✨

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.

src/app/(routes)/admin/_utils/form-helpers.ts

Oops! Something went wrong! :(

ESLint: 9.17.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

✨ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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

@tydolla00 tydolla00 changed the title enforce valid values on schema [feat] Create game based validation for Admin form Mar 8, 2025
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: 0

🧹 Nitpick comments (1)
src/app/(routes)/admin/_utils/form-helpers.ts (1)

138-148: Verify player lookup function with new validation.

The findPlayerByGamerTag function returns a partial Player object that may have undefined values when no player is found. Consider updating this function to either throw a PlayerNotFoundError (already defined) or return null to better integrate with your enhanced validation.

export const findPlayerByGamerTag = (gamerTag: string) => {
  const player = Object.values(PLAYER_MAPPINGS).find((player) =>
    player.gamerTags.some(
      (tag) => tag.toLowerCase() === gamerTag.toLowerCase(),
    ),
  );
-  return {
-    playerId: player?.playerId,
-    playerName: player?.playerName,
-  } as Player;
+  if (!player) {
+    throw new PlayerNotFoundError(gamerTag);
+  }
+  
+  return {
+    playerId: player.playerId,
+    playerName: player.playerName,
+  } as Player;
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cbdd468 and a96a00b.

📒 Files selected for processing (1)
  • src/app/(routes)/admin/_utils/form-helpers.ts (2 hunks)
🔇 Additional comments (6)
src/app/(routes)/admin/_utils/form-helpers.ts (6)

5-6: Good input validation improvements.

Adding .trim() and .min(1) ensures that fields cannot contain only whitespace, improving data quality by requiring meaningful content.


9-12: Appropriate URL validation enhancement.

Adding .url() validation before checking for YouTube-specific requirements is a good practice. This ensures the string is a valid URL format before applying more specific checks.


18-20: Consistent validation approach for readonly fields.

Good job applying consistent validation with .trim().min(1) to readonly fields as well. This ensures backend validation remains robust even though these fields cannot be directly modified by users in the UI.


21-28: Well-structured array validation.

The .nonempty() validation with a clear error message is excellent. This prevents form submission with empty arrays and provides a user-friendly error message explaining what's required.


37-71: Comprehensive nested validation structure.

The validation structure for nested objects (setWinners, matchWinners, playerSessions, playerStats) follows a consistent pattern, ensuring that all string fields are trimmed and have minimum length requirements. The error messages for non-empty arrays are clear and descriptive.


77-77: Consider addressing the TODO comment.

There's a TODO comment about conditionally applying input validations based on stat name. Since you're enhancing form validation, this might be a good opportunity to implement this feature.

Would you like me to suggest an approach for implementing conditional validation based on stat names?

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 (3)
src/app/(routes)/admin/_components/form/SetManager.tsx (1)

44-48: Enhanced type safety for set data.

Casting arrays to their specific types ensures proper type checking throughout the application.

However, the use of unknown as Type casting pattern is generally discouraged. Consider refactoring to initialize with proper types:

-      matches: [] as unknown as Matches,
-      setWinners: [] as unknown as SetWinners,
+      matches: [] as Matches,
+      setWinners: [] as SetWinners,
src/app/(routes)/admin/_components/form/MatchManager.tsx (2)

48-50: Enhanced type safety for match data.

Casting arrays to their specific types ensures proper type checking throughout the application.

However, the use of unknown as Type casting pattern is generally discouraged. Consider refactoring to initialize with proper types:

-      matchWinners: [] as unknown as MatchWinners,
-      playerSessions: playerSessions as unknown as PlayerSessions,
+      matchWinners: [] as MatchWinners,
+      playerSessions: playerSessions as PlayerSessions,

104-106: Enhanced type safety for fallback match data.

Consistent type casting when no winners are found ensures proper type handling throughout the application.

Similar to above, consider simplifying the type casting:

-        matchWinners: [] as unknown as MatchWinners, // Enforce non empty array,
+        matchWinners: [] as MatchWinners, // Enforce non empty array,
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a96a00b and 7412e33.

📒 Files selected for processing (4)
  • src/app/(routes)/admin/_components/form/EntryCreatorForm.tsx (2 hunks)
  • src/app/(routes)/admin/_components/form/MatchManager.tsx (4 hunks)
  • src/app/(routes)/admin/_components/form/SetManager.tsx (2 hunks)
  • src/app/(routes)/admin/_utils/form-helpers.ts (2 hunks)
🔇 Additional comments (9)
src/app/(routes)/admin/_components/form/EntryCreatorForm.tsx (1)

30-38: Good addition of validation debugging.

Adding validation debugging output helps with troubleshooting form issues during development. This ensures that validation errors can be more easily identified and addressed.

src/app/(routes)/admin/_components/form/SetManager.tsx (1)

20-20: Improved type imports for enhanced type safety.

Adding specific type imports from form-helpers improves type checking and code clarity.

src/app/(routes)/admin/_components/form/MatchManager.tsx (2)

13-16: Improved type imports for enhanced type safety.

Adding specific type imports from form-helpers improves type checking and code clarity.


99-101: Improved type handling for vision-generated match data.

Proper type casting for data from external sources enhances type safety and ensures consistency with the rest of the application.

src/app/(routes)/admin/_utils/form-helpers.ts (5)

5-7: Improved validation for basic form fields.

Adding string trimming and minimum length requirements ensures that empty or whitespace-only inputs are properly rejected, enhancing data quality.


8-16: Enhanced URL validation for session links.

Adding URL validation and trimming ensures that only properly formatted YouTube URLs are accepted.


18-28: Strengthened validation for core form fields.

Enforcing trimmed strings with minimum length requirements for required fields and ensuring the players array is non-empty improves data integrity.


33-72: Comprehensive validation for nested arrays and objects.

The enhanced validation for setWinners, matches, and playerSessions ensures that all required nested data is properly structured and validated. This prevents incomplete or malformed data from being submitted.

This validation approach aligns perfectly with the PR objective of allowing the form to drive validation and UI.


80-85: Valuable type exports for cross-component consistency.

Adding explicit type exports for nested data structures improves code reusability and ensures consistent typing across components.

These types help enforce the validation rules defined in the schema and make it easier to maintain type consistency when working with complex nested data.

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 (2)
src/app/(routes)/admin/_components/form/PlayerAvatar.tsx (1)

58-66: Consider implementing the image loading callback functionality.

You've added an onLoadingStatusChange handler with a placeholder comment, but there's no actual implementation. Consider either implementing the functionality or removing the comment to avoid confusion.

  <AvatarImage
    src={avatarSrc}
    alt={player.playerName}
    onLoadingStatusChange={(status) => {
      if (status === "loaded") {
-       // Image is ready
+       // Implement loading completion logic here if needed
      }
    }}
  />
src/app/(routes)/admin/_utils/form-helpers.ts (1)

29-104: Comprehensive validation for nested form structures.

The validation schema now properly enforces data integrity for complex nested structures like sets, matches, and player sessions. The refine method ensures business logic validation for match scoring.

Consider extracting the complex match validation logic into a separate function for better readability and maintainability.

+ // Validate that winning team has more goals than losing team
+ const validateMatchScores = (matches: any[]) => {
+   return matches.every((match, i) => {
+     let winningTeam = 0;
+     let losingTeam = 0;
+     
+     match.playerSessions.forEach((ps: any) => {
+       const onWinningTeam = match.matchWinners.some(
+         (mw: any) => mw.playerId === ps.playerId,
+       );
+       ps.playerStats.forEach((stat: any) => {
+         if (stat.stat === $Enums.StatName.RL_GOALS) {
+           if (onWinningTeam)
+             winningTeam += Number(stat.statValue) || 0;
+           else losingTeam += Number(stat.statValue) || 0;
+         }
+       });
+     });
+     
+     if (winningTeam <= losingTeam)
+       console.warn(
+         `Error in match ${i + 1}. Winning team should have more goals. Winners: ${winningTeam} Losers: ${losingTeam}`,
+       );
+     return winningTeam > losingTeam;
+   });
+ };

  matches: z
    .array(
      z.object({
        // ... existing validation
      }),
    )
    .nonempty("At least one match is required")
    .refine(
-     (data) => {
-       // TODO figure out how to pass this as a param. Maybe explicitly define type.
-       return data.every((match, i) => {
-         let winningTeam = 0;
-         let losingTeam = 0;
-         
-         match.playerSessions.forEach((ps) => {
-           const onWinningTeam = match.matchWinners.some(
-             (mw) => mw.playerId === ps.playerId,
-           );
-           ps.playerStats.forEach((stat) => {
-             if (stat.stat === $Enums.StatName.RL_GOALS) {
-               if (onWinningTeam)
-                 winningTeam += Number(stat.statValue) || 0;
-               else losingTeam += Number(stat.statValue) || 0;
-             }
-           });
-         });
-         if (winningTeam <= losingTeam)
-           console.warn(
-             `Error in match ${i + 1}. Winning team should have more goals. Winners: ${winningTeam} Losers: ${winningTeam}`,
-           );
-         return winningTeam > losingTeam;
-       });
-     },
+     validateMatchScores,
      {
        message: "Winning team should have more goals in each match",
      },
    ),
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7412e33 and 57ee52b.

📒 Files selected for processing (6)
  • src/app/(routes)/admin/_components/form/EntryCreatorForm.tsx (1 hunks)
  • src/app/(routes)/admin/_components/form/MatchManager.tsx (4 hunks)
  • src/app/(routes)/admin/_components/form/PlayerAvatar.tsx (1 hunks)
  • src/app/(routes)/admin/_components/form/PlayerSessionManager.tsx (1 hunks)
  • src/app/(routes)/admin/_utils/form-helpers.ts (2 hunks)
  • src/app/(routes)/admin/_utils/types.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/app/(routes)/admin/_components/form/PlayerSessionManager.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/app/(routes)/admin/_components/form/EntryCreatorForm.tsx
  • src/app/(routes)/admin/_components/form/MatchManager.tsx
🔇 Additional comments (4)
src/app/(routes)/admin/_utils/types.ts (1)

1-1: Good addition of a strongly-typed non-empty array type.

This utility type effectively enforces that arrays must contain at least one element, which is useful for form validation and type safety across the application.

src/app/(routes)/admin/_components/form/PlayerAvatar.tsx (1)

55-57: LGTM on the AvatarFallback simplification.

Removing the delayMs prop simplifies the component and follows the principle of keeping components focused on their core responsibilities.

src/app/(routes)/admin/_utils/form-helpers.ts (2)

5-28: Great improvement on form field validations.

Adding trimming and minimum length requirements ensures data quality and prevents empty submissions. The .nonempty() validation with a custom error message provides clear feedback to users.


110-115: Good addition of type exports for better type safety.

These type exports improve type safety and make it easier to work with complex nested form data structures across components.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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: 0

🧹 Nitpick comments (1)
src/app/(routes)/admin/_utils/form-helpers.ts (1)

72-101: Consider extracting the goal validation logic into a separate function.

The custom refine validation is complex and could be more maintainable if extracted to a named function.

export const formSchema = z.object({
  // ... other fields
  sets: z
    .array(
      z.object({
        // ... other fields
        matches: z
          .array(
            // ... match object definition
          )
          .nonempty("At least one match is required")
-         .refine(
-           (data) => {
-             // TODO figure out how to pass this as a param. Maybe explicitly define type.
-             return data.every((match, i) => {
-               let winningTeam = 0;
-               let losingTeam = 0;
-
-               match.playerSessions.forEach((ps) => {
-                 const onWinningTeam = match.matchWinners.some(
-                   (mw) => mw.playerId === ps.playerId,
-                 );
-                 ps.playerStats.forEach((stat) => {
-                   if (stat.stat === $Enums.StatName.RL_GOALS) {
-                     if (onWinningTeam)
-                       winningTeam += Number(stat.statValue) || 0;
-                     else losingTeam += Number(stat.statValue) || 0;
-                   }
-                 });
-               });
-               if (winningTeam <= losingTeam)
-                 console.warn(
-                   `Error in match ${i + 1}. Winning team should have more goals. Winners: ${winningTeam} Losers: ${losingTeam}`,
-                 );
-               return winningTeam > losingTeam;
-             });
-           },
-           {
-             message: "Winning team should have more goals in each match",
-           },
-         ),
+         .refine(validateWinningTeamGoals, {
+           message: "Winning team should have more goals in each match",
+         }),
      }),
    )
    .nonempty("At least one set is required"),
});

+// Helper function to validate that winning team has more goals
+const validateWinningTeamGoals = (matches: z.infer<typeof matchesSchema>) => {
+  return matches.every((match, i) => {
+    let winningTeam = 0;
+    let losingTeam = 0;
+
+    match.playerSessions.forEach((ps) => {
+      const onWinningTeam = match.matchWinners.some(
+        (mw) => mw.playerId === ps.playerId,
+      );
+      ps.playerStats.forEach((stat) => {
+        if (stat.stat === $Enums.StatName.RL_GOALS) {
+          if (onWinningTeam)
+            winningTeam += Number(stat.statValue) || 0;
+          else losingTeam += Number(stat.statValue) || 0;
+        }
+      });
+    });
+    if (winningTeam <= losingTeam)
+      console.warn(
+        `Error in match ${i + 1}. Winning team should have more goals. Winners: ${winningTeam} Losers: ${losingTeam}`,
+      );
+    return winningTeam > losingTeam;
+  });
+};
+
+// Define match schema separately to use in the validation function
+const matchesSchema = z.array(
+  z.object({
+    matchWinners: z.array(z.object({ playerId: z.number(), playerName: z.string() })),
+    playerSessions: z.array(
+      z.object({
+        playerId: z.number(),
+        playerSessionName: z.string(),
+        playerStats: z.array(
+          z.object({
+            statId: z.string(),
+            stat: z.string(),
+            statValue: z.string()
+          })
+        )
+      })
+    )
+  })
+);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 57ee52b and 14b25a8.

📒 Files selected for processing (1)
  • src/app/(routes)/admin/_utils/form-helpers.ts (2 hunks)
🔇 Additional comments (7)
src/app/(routes)/admin/_utils/form-helpers.ts (7)

5-20: Improved field validations enhance data integrity.

The additions of .trim() and length constraints across all form fields provide consistent validation and prevent empty or whitespace-only values, which is a good improvement for data quality.


21-28: Well-structured player validation ensures required data.

The player array validation with .nonempty() and the object schema ensures all players have both an ID and a properly formatted name.


37-40: Enhanced set winner validation.

Adding trim and min length validation for player names in set winners ensures consistency with the rest of the form.


41-71: Well-structured nested validations for matches.

The comprehensive validation structure for matches, match winners, player sessions, and player stats ensures data completeness throughout the form hierarchy.


91-94: Fixed console warning message.

The console warning now correctly compares the winning team's score with the losing team's score.


110-115: Excellent type exports improve type safety.

These exported types derived from the form schema will enhance type safety when these structures are used in other components.


74-74:

❓ Verification inconclusive

Address TODO by introducing typed parameters.

The TODO comment indicates a need to pass parameters more explicitly. Consider implementing this to improve code maintainability.


🏁 Script executed:

#!/bin/bash
# Search for pattern to see how similar validations are handled elsewhere in the codebase
rg "refine.*\(.*param" --type ts

Length of output: 34


Address the TODO by explicitly typing the parameter and ensure consistency with existing patterns.

The search for similar patterns using the rg "refine.*\(.*param" command returned no output, so we couldn’t automatically infer a standard implementation from the codebase. Please verify manually if there’s a preferred approach elsewhere for handling typed parameters, or update this instance in line 74 of src/app/(routes)/admin/_utils/form-helpers.ts to use explicit type definitions. This change should improve maintainability without deviating from the overall coding practices.

@tydolla00 tydolla00 self-assigned this Mar 8, 2025
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.

Admin Form Schema Improvements
2 participants