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: signing volume #1358

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

feat: signing volume #1358

wants to merge 10 commits into from

Conversation

ephraimduncan
Copy link
Contributor

@ephraimduncan ephraimduncan commented Sep 19, 2024

CleanShot 2024-09-19 at 14 28 49@2x

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a LeaderboardTable component for displaying and managing signing volumes with search, sorting, and pagination capabilities.
    • Added a new admin interface for the leaderboard, ensuring only authorized users can access it.
    • Enhanced navigation with a new "Leaderboard" button in the admin panel.
  • Translations

    • Comprehensive updates to German and English translations for various user actions and notifications, including new entries for features like document management and team settings.
    • Improved clarity and consistency in existing translations, ensuring users receive accurate feedback throughout their interactions.

Copy link

vercel bot commented Sep 19, 2024

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

Name Status Preview Comments Updated (UTC)
prd-app 🔄 Building (Inspect) Visit Preview 💬 Add feedback Oct 30, 2024 2:56pm
prd-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 30, 2024 2:56pm
prd-marketing 🔄 Building (Inspect) Visit Preview 💬 Add feedback Oct 30, 2024 2:56pm
prd-openpage-api ❌ Failed (Inspect) Oct 30, 2024 2:56pm
stg-app ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 30, 2024 2:56pm
stg-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 30, 2024 2:56pm
stg-marketing ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 30, 2024 2:56pm

Copy link
Contributor

coderabbitai bot commented Sep 19, 2024

Walkthrough

The changes involve comprehensive updates to the localization files for both German and English translations within the application. These updates include the addition of new translation entries and modifications to existing ones to reflect new features and enhance clarity. The translations cover various aspects such as document management, user notifications, and team functionalities, ensuring that the user interface is accurately translated for both language audiences.

Changes

File Change Summary
packages/lib/translations/de/web.po Updated German translations with new entries and modifications for clarity, including terms related to document management and user notifications.
packages/lib/translations/en/web.po Updated English translations with new entries and refined existing messages for clarity, including terms related to team management and user interactions.
apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard.actions.ts Introduced a new asynchronous function search for fetching leaderboard data, including user session checks and error handling for admin access.
packages/lib/server-only/admin/get-signing-volume.ts Added functionality to retrieve signing volume data with defined types and a core function for querying subscriptions, including pagination and sorting options.
packages/lib/translations/de/web.js Introduced a module for German localized messages, covering various user interface prompts and error messages with support for dynamic content.
packages/lib/translations/en/web.js Introduced a module for English localized messages, encompassing user interface prompts, notifications, and error messages with dynamic content support.
apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard-fix.actions.ts Added a new asynchronous function search to retrieve leaderboard data with session checks and error handling for admin privileges.
apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx Introduced an asynchronous React component Leaderboard for rendering the leaderboard interface, including session management and data fetching.
packages/lib/server-only/admin/get-signing-volume-fix.ts Implemented functionality for retrieving signing volume data with defined types and a core function for querying subscriptions, including pagination and sorting options.

Suggested labels

version bump 👀, pkg: ee changes

Suggested reviewers

  • Mythie
  • dguyen
  • ElTimuro

🐰 In the land of words, both new and old,
Translations sprout, a story told.
From German to English, clarity reigns,
In every message, joy remains.
So hop along, let’s share the cheer,
With every update, our path is clear! 🌟📖


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Outside diff range and nitpick comments (1)
apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard.actions.ts (1)

18-20: Consider providing more specific error messages.

Instead of throwing a generic 'Unauthorized' error, consider providing more context about why the error occurred. For example:

throw new Error('Only admin users can access the leaderboard data.');

This can help with debugging and provides a clearer message to the client.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between efb2bc9 and 89154e6.

Files selected for processing (7)
  • apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx (1 hunks)
  • apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard.actions.ts (1 hunks)
  • apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx (1 hunks)
  • apps/web/src/app/(dashboard)/admin/nav.tsx (2 hunks)
  • packages/lib/server-only/admin/get-signing-volume.ts (1 hunks)
  • packages/lib/translations/de/web.po (7 hunks)
  • packages/lib/translations/en/web.po (7 hunks)
Additional comments not posted (19)
apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard.actions.ts (1)

15-25: LGTM! The function provides a secure and flexible way to retrieve leaderboard data.

The implementation looks good overall:

  • It correctly uses server-side rendering.
  • It properly checks for admin privileges before executing the main logic.
  • It uses clear parameter types for the search options.
  • It delegates data fetching to a separate function, which is a good separation of concerns.
apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx (5)

1-18: Imports and type definitions look good!

The component imports all the necessary dependencies and defines a clear type structure for its props. The AdminLeaderboardProps type provides flexibility by making all search parameters optional.


20-23: Component function signature and setup are well-structured!

The Leaderboard component is properly defined as an async default export function, accepting optional search parameters as props. Setting up internationalization and retrieving the user session are appropriate steps for an admin-facing component.


25-27: Admin access control is properly implemented!

The component correctly enforces admin access control by checking the user's admin status using the isAdmin function. Throwing an "Unauthorized" error for non-admin users is an appropriate way to handle unauthorized access attempts.


29-41: Search parameter handling and data fetching are well-implemented!

The component effectively extracts and processes the search parameters, assigning default values for pagination and sorting. This ensures a consistent and predictable behavior for the component. Using a separate search function for data fetching promotes separation of concerns and maintainability.


43-60: Component's return statement and rendering are well-structured!

The component returns a well-structured JSX with a heading and the LeaderboardTable component. Using the Trans component for the heading ensures proper internationalization. Passing the fetched data and other relevant props to the LeaderboardTable component allows for a clean separation of concerns and maintainable code structure.

apps/web/src/app/(dashboard)/admin/nav.tsx (1)

83-95: LGTM!

The new button for the leaderboard section follows the existing patterns and conventions of the AdminNav component. It is correctly linked to the "/admin/leaderboard" route, uses an appropriate icon and label, and is properly styled to highlight when active.

The use of the Trans component for the label allows for easy internationalization, which is a good practice.

Overall, this is a clean and consistent addition to the navigation.

apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx (5)

1-30: LGTM!

The imports and type definitions are appropriate and cover the necessary dependencies. The SigningVolume and LeaderboardTableProps types provide a clear structure for the data and props used in the component.


32-45: LGTM!

The component definition and state management are implemented correctly. The usage of hooks like useState, useTransition, and useDebouncedValue is appropriate for managing state and optimizing search performance. The useUpdateSearchParams custom hook is a good choice for updating the URL search params.


47-98: LGTM!

The columns definition and memoization are implemented correctly. Using useMemo is a good practice to memoize the columns array and avoid unnecessary re-renders. The column definitions are clear and include the necessary properties. The sortable columns have appropriate click handlers to trigger the sorting functionality. The usage of the satisfies keyword ensures type safety of the columns array.


100-133: LGTM!

The useEffect hook and pagination handling are implemented correctly. The useEffect hook properly updates the search params whenever the debounced search string changes. The onPaginationChange function correctly updates the page and perPage values in the search params. The handleChange function updates the search string state when the input value changes. The handleColumnSort function appropriately toggles the sort order and updates the search params when a sortable column header is clicked.


135-162: LGTM!

The rendering logic and loading state handling are implemented correctly. The search input is rendered with appropriate props and change handler. The DataTable component is rendered with the necessary props, including columns, data, pagination, and callbacks. The DataTablePagination component is correctly rendered as a child of DataTable to handle pagination. The loading indicator is conditionally rendered based on the isPending state, providing visual feedback during loading states.

packages/lib/translations/de/web.po (3)

1800-1802: LGTM!

The new translation entry for "Leaderboard" looks good. It's correctly referenced in the admin navigation component.


2819-2822: Looks good!

The new translation entry for "Signing Volume" is correctly added with references to the leaderboard components.


2832-2833: Reference update looks good!

The line number reference for the "Site Settings" translation entry has been correctly updated in apps/web/src/app/(dashboard)/admin/nav.tsx, indicating the navigation item has moved.

packages/lib/translations/en/web.po (2)

1818-1820: New translation looks good!

The new "Leaderboard" translation entry is clear, accurate and consistent.


2841-2844: New translation looks good!

The new "Signing Volume" translation entry is clear, accurate and consistent.

packages/lib/server-only/admin/get-signing-volume.ts (2)

45-46: Verify the existence and correctness of getOrderByClause function

Ensure that the getOrderByClause function is correctly defined and available in the scope before it's used.

Run the following script to check for any issues:

Verification successful

getOrderByClause function is correctly defined and used

The getOrderByClause function is properly defined and available in the scope before it's used. It's defined as a const arrow function in the same file (packages/lib/server-only/admin/get-signing-volume.ts) where it's being used. The function is called with the correct parameters (sortBy and sortOrder), and there are no naming conflicts or multiple definitions.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for the definition and export of getOrderByClause.

# Test: Search for the function definition.
# Expect: The function should be defined only once and exported if used externally.
rg --type typescript 'function getOrderByClause'

# Test: Ensure there are no naming conflicts.
# Expect: No other functions with the same name.
rg --type typescript 'getOrderByClause' | grep -v 'function getOrderByClause'

Length of output: 198


Script:

#!/bin/bash
# Search for getOrderByClause function definition
echo "Searching for getOrderByClause function definition:"
fd -e ts -e js | xargs rg 'function getOrderByClause'

# Search for getOrderByClause usage
echo "Searching for getOrderByClause usage:"
fd -e ts -e js | xargs rg 'getOrderByClause'

# Check the specific file for function usage
echo "Checking get-signing-volume.ts for getOrderByClause usage:"
rg 'getOrderByClause' packages/lib/server-only/admin/get-signing-volume.ts

# Search for imports of getOrderByClause
echo "Searching for imports of getOrderByClause:"
fd -e ts -e js | xargs rg 'import.*getOrderByClause'

Length of output: 1032


119-142: Verify the correctness of ordering by signing volume

When sorting by signingVolume, the current orderBy implementation may not work as intended because Prisma does not support ordering by the combined counts of relations directly in this manner.

Consider the following steps to verify and correct the ordering:

  1. Adjust the orderBy clause: Modify the orderBy to use _count with relation paths.

      default:
        return [
    -      {
    -        User: {
    -          Document: {
    -            _count: sortOrder,
    -          },
    -        },
    -      },
    -      {
    -        team: {
    -          document: {
    -            _count: sortOrder,
    -          },
    -        },
    -      },
    -    ];
           {
             _count: {
               User: {
                 Document: sortOrder,
               },
             },
           },
           {
             _count: {
               team: {
                 document: sortOrder,
               },
             },
           },
        ];
  2. Verify if Prisma supports this ordering: Since Prisma may not support ordering by combined counts across different relations, you might need to fetch the signing volumes first and then sort them in your application code.

    To verify, run the following script:

    Ensure you have a script verify-orderby.ts that attempts to run the query with the existing orderBy clause.

  3. Alternative solution: Fetch the counts separately and sort the leaderboardWithVolume array in your application.

    // After calculating signingVolume
    leaderboardWithVolume.sort((a, b) => {
      if (sortOrder === 'asc') {
        return a.signingVolume - b.signingVolume;
      } else {
        return b.signingVolume - a.signingVolume;
      }
    });

    Adjust your getOrderByClause to handle this case appropriately.

Verification successful

Ordering by signing volume is implemented correctly

The getOrderByClause function in packages/lib/server-only/admin/get-signing-volume.ts correctly implements the ordering for signing volume. It uses a nested structure to count documents for both User and team, which effectively represents the signing volume. This approach is valid in Prisma for ordering by related model counts.

  • For 'name' sorting, it orders by User name and team name.
  • For 'createdAt' sorting, it uses the createdAt field directly.
  • For 'signingVolume' (default case), it orders by the count of documents for both User and team.

This implementation should correctly order the results based on the total number of documents (signing volume) associated with each user or team.

Scripts executed

The following scripts were executed for the analysis:

Script:

   #!/bin/bash
   # Description: Check if ordering by combined counts works as intended.

   # Test: Attempt to query with the current orderBy clause.
   # Expect: Verify whether Prisma allows this operation or throws an error.
   npx prisma generate
   npx ts-node verify-orderby.ts
   

Length of output: 195


Script:

#!/bin/bash

# Find the implementation of getOrderByClause
ast-grep --lang typescript --pattern 'function getOrderByClause($_) {
  $$$
}'

# Search for mentions of leaderboard or signing volume
rg -i "leaderboard|signing volume" -A 5 -B 5

# Look for Prisma schema definitions
rg "model User|model Team|model Document" -A 10 schema.prisma

Length of output: 142023


Script:

#!/bin/bash

# Search for getOrderByClause function implementation
rg "function getOrderByClause" -A 10

# Examine leaderboard-related files
cat packages/lib/server-only/admin/get-signing-volume.ts

# Try to locate Prisma schema file
fd schema.prisma

Length of output: 3389

Copy link
Member

@ElTimuro ElTimuro left a comment

Choose a reason for hiding this comment

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

Looks good overall, a few points:

This commit enhances the leaderboard by adding a clickable link to each
customer's name, which directs to their Stripe subscription page. It also
removes the ID column and includes the planId in the signing volume data
for improved user experience and easier access to subscription details.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (3)
packages/lib/translations/en/web.po (2)

Line range hint 15-16: Consider using a more specific placeholder name.

The placeholder "{0}" is used here. While this works, it might be more maintainable to use a named placeholder that describes what will be inserted, e.g., "{date}" or "{formatted_date}".


Line range hint 30-31: Consider adding context for pluralization.

These translations use plural forms correctly. Consider adding a comment to explain the plural rules for translators of other languages where plural forms might be more complex.

Also applies to: 33-34

packages/lib/translations/de/web.po (1)

Line range hint 1-3524: Several translations are missing or incomplete.

There are numerous instances where translations are either missing entirely (empty msgstr) or still in English. This needs to be addressed to ensure a complete German localization. Here are a few examples:

  1. Line 1823-1825: "Leaderboard" is not translated.
  2. Line 2015-2020: "Signing Volume" is not translated.
  3. Line 2846-2850: "Signing Volume" is not translated again.

Please review the entire file and provide translations for all missing or incomplete entries.

Inconsistencies in formatting and translation style detected.

There are some inconsistencies in how certain terms or phrases are translated throughout the file. For example:

  1. "Sign in" is translated as both "Einloggen" and "Anmelden" in different places.
  2. "Dashboard" is sometimes left in English and sometimes translated to "Übersicht".

Standardize these translations for consistency across the application.

Some translations could be improved for clarity or naturalness.

While many translations are accurate, some could be improved to sound more natural in German or to better convey the original meaning. For example:

  1. Line 964: "Created" is translated as "Erstellt", which is correct but could be more specific depending on the context, such as "Erstellungsdatum" for a date field.
  2. Line 1009-1013: "Customer Type" is not translated and commented out. Consider translating this to "Kundentyp" if it's still needed.

Review these instances and consider refining the translations for better clarity and naturalness in German.

Potential functional issues in translations.

Some translations might affect functionality if not handled correctly:

  1. Lines 2859-2860: "Site Settings" is translated as "Website Einstellungen", but the space might cause issues if this is used as a key in the code. Consider using "Website-Einstellungen" instead.
  2. Throughout the file, there are instances where placeholders like {0}, {1} are used. Ensure that these are kept in the correct order in the German translations, as word order often differs between English and German.

Review these instances carefully to prevent any potential functional issues arising from the translations.

Suggestion for handling gender-neutral language.

German often requires gendered forms of words. Consider implementing a system for handling gender-neutral language or providing both masculine and feminine forms where appropriate. For example:

  1. Line 2015: "User" could be translated as "Benutzer*in" or "Benutzer/in" to be more inclusive.

This approach would make the application more inclusive and aligned with modern German language practices.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between a274e9b and 7929e2f.

📒 Files selected for processing (2)
  • packages/lib/translations/de/web.po (7 hunks)
  • packages/lib/translations/en/web.po (7 hunks)
🧰 Additional context used
🔇 Additional comments (5)
packages/lib/translations/en/web.po (5)

Line range hint 1-13: LGTM: File header and metadata look correct.

The file header contains the necessary metadata for a PO file, including the MIME type, content type, and language specification. This is correctly set up for English translations.


Line range hint 18-19: Ensure consistent use of quotation marks.

The translation uses double quotes around "{documentTitle}", which is correct. Ensure this style is consistently used throughout the file for similar placeholders.


Line range hint 21-22: LGTM: Consistent use of placeholders and parentheses.

These translations correctly use the {0} placeholder and maintain consistent formatting with parentheses.

Also applies to: 24-25, 27-28


Line range hint 36-37: LGTM: Correct use of HTML-like tags and pluralization.

These translations correctly use HTML-like tags for formatting and handle pluralization. The structure is consistent across similar messages.

Also applies to: 39-40, 42-43, 45-46, 48-49


Line range hint 51-52: Ensure placeholder consistency across similar messages.

These two messages use {0} as a placeholder for what seems to be the same type of information. Ensure that this is intentional and that the placeholder represents the same data in both cases.

Also applies to: 54-55

@ElTimuro
Copy link
Member

@ephraimduncan Looks good so far. Can you fix the merge conflicts, so I can it to to staging to test with the stripe dev account?

@ephraimduncan
Copy link
Contributor Author

working on it now

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (2)
packages/lib/translations/en/web.po (2)

1004-1008: Clean up commented out translations

These commented out translations for "Customer Type" are no longer needed and should be removed to keep the file clean.

Apply this diff to remove the commented out block:

-#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:94
-#: apps/web/src/app/(dashboard)/admin/leaderboard/leaderboard-table.tsx:94
-#~ msgid "Customer Type"
-#~ msgstr "Customer Type"
-

1822-1825: Fix inconsistent spacing

The spacing around the new "Leaderboard" translation is inconsistent with the rest of the file.

Apply this diff to fix the spacing:

-
msgid "Leaderboard"
msgstr "Leaderboard"
-
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 7929e2f and 1bcea63.

📒 Files selected for processing (2)
  • packages/lib/translations/de/web.po (7 hunks)
  • packages/lib/translations/en/web.po (7 hunks)
🧰 Additional context used
🔇 Additional comments (3)
packages/lib/translations/en/web.po (1)

959-959: ⚠️ Potential issue

Remove stray tilde character

There is a stray ~ character on this line that could cause parsing issues with the PO file.

Apply this diff to fix the issue:

 959

Likely invalid or redundant comment.

packages/lib/translations/de/web.po (2)

2863-2864: ⚠️ Potential issue

Maintain consistent terminology for "Site".

The translation alternates between "Website" and "Seite" for the word "Site", which could confuse users.

Recommend using "Website" consistently:

-msgstr "Seiteneinstellungen"
 msgstr "Website-Einstellungen"

Likely invalid or redundant comment.


964-964: ⚠️ Potential issue

Fix formatting of line number annotation.

The line number annotation is incorrectly placed within the msgid entry, which could cause parsing issues.

Move the line number annotation to its own line:

-#: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62
 #: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62

Likely invalid or redundant comment.

packages/lib/translations/de/web.po Show resolved Hide resolved
packages/lib/translations/de/web.po Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Outside diff range and nitpick comments (2)
packages/lib/translations/en/web.js (1)

1-1: Consider optimizing translation loading for better performance.

The current approach of parsing a large JSON string at runtime and bundling all translations in one file could impact performance.

Consider:

  1. Pre-parsing JSON at build time
  2. Implementing lazy loading for translations
  3. Splitting translations into smaller chunks based on usage

Example implementation using dynamic imports:

// translations/index.js
export const loadTranslation = async (key) => {
  const chunk = await import(`./chunks/${key}.json`);
  return chunk.default;
};
packages/lib/translations/de/web.js (1)

1-1: Add documentation for translation format and variables.

The translations use a specific format for variables and pluralization, but this isn't documented. Adding JSDoc comments would help maintainers understand the expected format.

Add documentation at the top of the file:

/**
 * German translations for the web application.
 * 
 * Format:
 * - Variables: ["variableName"]
 * - Pluralization: ["count", "plural", { "one": "...", "other": "..." }]
 * - Components: <0>...</0> where numbers represent React component indices
 * 
 * @example
 * "example": ["Hello ", ["name"], "!"] // Hello John!
 * "plural": [["count", "plural", { "one": "1 item", "other": ["#", " items"] }]]
 */
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 1bcea63 and d4baab1.

📒 Files selected for processing (4)
  • apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard.actions.ts (1 hunks)
  • packages/lib/server-only/admin/get-signing-volume.ts (1 hunks)
  • packages/lib/translations/de/web.js (1 hunks)
  • packages/lib/translations/en/web.js (1 hunks)
🧰 Additional context used
🔇 Additional comments (3)
apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard.actions.ts (1)

1-5: LGTM! Well-structured server action setup.

The imports and server directive are properly organized, following Next.js server component patterns and security best practices.

packages/lib/translations/en/web.js (1)

1-1: Ensure proper HTML escaping and sanitization.

The translations contain HTML-like tags and use string interpolation, which could pose security risks if not properly handled.

Run the following script to identify potentially risky translations:

✅ Verification successful

Let me try another search pattern to verify HTML and interpolation usage:

The goal is to analyze how HTML tags and string interpolations are handled in the actual web.js translations file, rather than in the translation source files.


Let me try searching in the correct path from the snippet:


Based on the search results, I can now provide a final response:

HTML tags and interpolations are properly handled through JSON.parse

The translations are stored in a JSON structure that is parsed at runtime, with proper escaping of HTML tags and interpolation variables. The implementation:

  1. Uses JSON.parse() to safely parse the translations
  2. HTML tags are stored as escaped strings (e.g. \"<0>\")
  3. Interpolation variables are stored in arrays (e.g. [\"0\"]) for safe substitution
  4. All content is properly escaped within the JSON structure

This approach provides built-in protection against XSS and interpolation attacks since:

  • JSON.parse() handles escaping
  • HTML tags and variables are treated as data rather than code
  • The framework's translation system handles safe interpolation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for translations containing HTML tags
rg -A 2 '<[0-9] >' packages/lib/translations/

# Search for translations with interpolation
rg -A 2 '\[\". ?\"\]' packages/lib/translations/

Length of output: 99537


Script:

#!/bin/bash
# Search for HTML-like tags in translations
rg -A 2 "<[^>] >" packages/lib/translations/web.js

# Search for string interpolations
rg -A 2 "\[\s*['\"][^'\"] ['\"]" packages/lib/translations/web.js

Length of output: 272


Script:

#!/bin/bash
# Search for HTML-like tags in translations
rg -A 2 "<[^>] >" packages/lib/translations/en/web.js

# Search for string interpolations in the messages
rg -A 2 '\["[^"] "\]' packages/lib/translations/en/web.js

# Also check how these translations are used
ast-grep --pattern 'messages: JSON.parse($_)' packages/lib/translations/en/web.js

Length of output: 60010

packages/lib/translations/de/web.js (1)

1-1: Verify HTML content in translations for XSS vulnerabilities.

Several translations contain HTML content (e.g., <0>, <1>, etc.). While these are likely React components, it's important to ensure proper sanitization is in place.

Let's verify the HTML handling in the codebase:

packages/lib/translations/en/web.js Show resolved Hide resolved
packages/lib/translations/de/web.js Show resolved Hide resolved
Comment on lines 140 to 155
return [
{
User: {
Document: {
_count: sortOrder,
},
},
},
{
team: {
document: {
_count: sortOrder,
},
},
},
];
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure the orderBy clause correctly sorts by signing volume

The current orderBy clause attempts to sort by counts of related documents, but Prisma may not support ordering by nested counts in this manner. Consider refactoring the query to calculate the signing volume using aggregations and order by this calculated field.

Would you like assistance in refactoring the query to correctly order by signing volume using Prisma's aggregation features?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between d4baab1 and 5b475da.

📒 Files selected for processing (3)
  • apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard-fix.actions.ts (1 hunks)
  • apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx (1 hunks)
  • packages/lib/server-only/admin/get-signing-volume-fix.ts (1 hunks)
🔇 Additional comments (2)
apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard-fix.actions.ts (1)

7-13: LGTM! Well-structured type definition.

The SearchOptions type is well-defined with appropriate typing for pagination, sorting, and search parameters.

packages/lib/server-only/admin/get-signing-volume-fix.ts (1)

86-87: Verify that documents are not double-counted in signing volume calculation

When calculating signingVolume by summing userSignedDocs and teamSignedDocs, there's a potential risk of double-counting documents if a user's documents are also associated with a team.

Consider verifying that documents are uniquely counted. If necessary, adjust the calculation to account for any overlap.

Comment on lines 15 to 25
export async function search({ search, page, perPage, sortBy, sortOrder }: SearchOptions) {
const { user } = await getRequiredServerComponentSession();

if (!isAdmin(user)) {
throw new Error('Unauthorized');
}

const results = await getSigningVolume({ search, page, perPage, sortBy, sortOrder });

return results;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider enhancing error handling and adding request validation.

While the basic authorization check is good, consider these improvements:

  1. Use a custom error type instead of generic Error
  2. Add input validation for search parameters
  3. Handle potential database errors from getSigningVolume
 export async function search({ search, page, perPage, sortBy, sortOrder }: SearchOptions) {
   const { user } = await getRequiredServerComponentSession();
 
   if (!isAdmin(user)) {
-    throw new Error('Unauthorized');
     throw new Error('You must be an admin to access the leaderboard');
   }
 
   // Validate pagination parameters
   if (page < 1 || perPage < 1 || perPage > 100) {
     throw new Error('Invalid pagination parameters');
   }
 
   try {
     const results = await getSigningVolume({ search, page, perPage, sortBy, sortOrder });
     return results;
   } catch (error) {
     console.error('Failed to fetch signing volume:', error);
     throw new Error('Failed to fetch leaderboard data');
   }
-    return results;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function search({ search, page, perPage, sortBy, sortOrder }: SearchOptions) {
const { user } = await getRequiredServerComponentSession();
if (!isAdmin(user)) {
throw new Error('Unauthorized');
}
const results = await getSigningVolume({ search, page, perPage, sortBy, sortOrder });
return results;
}
export async function search({ search, page, perPage, sortBy, sortOrder }: SearchOptions) {
const { user } = await getRequiredServerComponentSession();
if (!isAdmin(user)) {
throw new Error('You must be an admin to access the leaderboard');
}
// Validate pagination parameters
if (page < 1 || perPage < 1 || perPage > 100) {
throw new Error('Invalid pagination parameters');
}
try {
const results = await getSigningVolume({ search, page, perPage, sortBy, sortOrder });
return results;
} catch (error) {
console.error('Failed to fetch signing volume:', error);
throw new Error('Failed to fetch leaderboard data');
}
}

Comment on lines 1 to 5
'use server';

import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
import { getSigningVolume } from '@documenso/lib/server-only/admin/get-signing-volume-fix';
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Based on the gathered context, I can now provide a final response:

Remove temporary fix implementation and consolidate into a single solution

The codebase currently maintains two parallel implementations of the same functionality:

  • Original: fetch-leaderboard.actions.ts using get-signing-volume
  • Fix version: fetch-leaderboard-fix.actions.ts using get-signing-volume-fix

The page component (page.tsx) is actually running both implementations side by side, displaying two separate leaderboard tables. This appears to be a temporary testing setup rather than a permanent solution. You should:

  1. Evaluate both implementations
  2. Choose the correct implementation (likely the "-fix" version since it was added later)
  3. Consolidate into a single solution by removing the temporary files
  4. Update the page component to use only the final implementation
🔗 Analysis chain

Clarify the purpose of the "-fix" suffix.

The "-fix" suffix in both the filename and imported function suggests this might be a temporary fix. Consider documenting the issue being fixed or removing the suffix if this is intended to be a permanent solution.


Let's gather more context about these files to understand the purpose of the "-fix" suffix better.


Let's gather one final piece of information to understand the relationship between the original and fix versions.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for related files or issues that might explain the fix
rg -l "signing-volume-fix" 
rg -l "leaderboard.*fix"

Length of output: 189


Script:

#!/bin/bash
# Check the content of the related files to understand the context
echo "=== Content of page.tsx ==="
cat "apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx"

# Look for any related commits or PR descriptions
echo -e "\n=== Git history for these files ==="
git log --patch --grep="fix" -- "apps/web/src/app/(dashboard)/admin/leaderboard/"

# Check if there's an original version without the fix suffix
echo -e "\n=== Check for original version ==="
rg -l "signing-volume(?!-fix)" 
rg -l "fetch-leaderboard(?!-fix)"

Length of output: 12499


Script:

#!/bin/bash
# Check the content of both leaderboard action files
echo "=== Original leaderboard actions ==="
cat "apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard.actions.ts"

echo -e "\n=== Fix version of leaderboard actions ==="
cat "apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard-fix.actions.ts"

# Check the related volume functions
echo -e "\n=== Check volume function imports ==="
rg "get-signing-volume(?:-fix)?" -A 2

Length of output: 2725

Comment on lines 36 to 51
const { leaderboard: signingVolume, totalPages } = await search({
search: searchString,
page,
perPage,
sortBy,
sortOrder,
});

const { leaderboard: signingVolume2, totalPages: totalPagesFix2 } = await search2({
search: searchString,
page,
perPage,
sortBy,
sortOrder,
});

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider combining data fetching to reduce API calls

Fetching data separately using search and fetchFixedLeaderboard increases the number of API calls. If possible, consider combining the data fetching into a single call to improve performance.

Comment on lines 8 to 9
import { search as search2 } from './fetch-leaderboard-fix.actions';
import { search } from './fetch-leaderboard.actions';
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Clarify the naming of imported 'search' functions

Importing two functions named search from different modules and aliasing one as search2 may cause confusion. Consider renaming the imports to more descriptive names that reflect their purpose.

Apply this diff to improve clarity:

-import { search as search2 } from './fetch-leaderboard-fix.actions';
 import { search as fetchFixedLeaderboard } from './fetch-leaderboard-fix.actions';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { search as search2 } from './fetch-leaderboard-fix.actions';
import { search } from './fetch-leaderboard.actions';
import { search as fetchFixedLeaderboard } from './fetch-leaderboard-fix.actions';
import { search } from './fetch-leaderboard.actions';

Comment on lines 67 to 69
<h2 className="mt-20 text-2xl font-semibold">
<Trans>Signing Volume 2</Trans>
</h2>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update the heading to be more descriptive

Using "Signing Volume 2" might be unclear to users. Consider updating the heading to reflect the difference between the two data sets.

Suggestion:

-<Trans>Signing Volume 2</Trans>
 <Trans>Fixed Signing Volume</Trans>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<h2 className="mt-20 text-2xl font-semibold">
<Trans>Signing Volume 2</Trans>
</h2>
<h2 className="mt-20 text-2xl font-semibold">
<Trans>Fixed Signing Volume</Trans>
</h2>

Comment on lines 26 to 28
if (!isAdmin(user)) {
throw new Error('Unauthorized');
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle unauthorized access appropriately

Throwing a generic error may not be the best way to handle unauthorized access in a Next.js server component. Consider redirecting unauthorized users to the login page or rendering an appropriate error page.

Apply this diff to redirect unauthorized users:

 import { redirect } from 'next/navigation';

...

 if (!isAdmin(user)) {
-  throw new Error('Unauthorized');
   redirect('/login');
 }

Committable suggestion was skipped due to low confidence.

Comment on lines 44 to 51
const { leaderboard: signingVolume2, totalPages: totalPagesFix2 } = await search2({
search: searchString,
page,
perPage,
sortBy,
sortOrder,
});

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Rename variables for consistency and clarity

Rename variables to reflect the purpose of the data they hold, improving code readability.

Apply this diff:

-const { leaderboard: signingVolume2, totalPages: totalPagesFix2 } = await search2({
 const { leaderboard: fixedSigningVolume, totalPages: fixedTotalPages } = await fetchFixedLeaderboard({

Committable suggestion was skipped due to low confidence.

Comment on lines 54 to 60
Document: {
where: {
status: 'COMPLETED',
deletedAt: null,
},
},
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Inconsistent casing of 'document' relation may cause runtime errors

There is an inconsistency in the casing of the document relation:

  • Under User, you are using Document with an uppercase 'D'.
  • Under team, you are using document with a lowercase 'd'.

Prisma relation names are case-sensitive. This inconsistency could lead to runtime errors or unexpected behavior.

Apply this diff to ensure consistent casing:

// Under User (lines 54-60):
- Document: {
  document: {
    where: {
      status: 'COMPLETED',
      deletedAt: null,
    },
  },

// Under team (lines 64-70):
// No changes needed as 'document' is correctly cased.

Also applies to: 64-70

Comment on lines 86 to 87
const userSignedDocs = subscription.User?.Document?.length || 0;
const teamSignedDocs = subscription.team?.document?.length || 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consistent property names needed when accessing 'document' relation

In the calculation of userSignedDocs and teamSignedDocs:

  • subscription.User?.Document?.length uses Document with an uppercase 'D'.
  • subscription.team?.document?.length uses document with a lowercase 'd'.

For consistency and to prevent potential errors, both should use the same casing that matches your Prisma schema.

Apply this diff to correct the property name:

- const userSignedDocs = subscription.User?.Document?.length || 0;
  const userSignedDocs = subscription.User?.document?.length || 0;

const teamSignedDocs = subscription.team?.document?.length || 0;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const userSignedDocs = subscription.User?.Document?.length || 0;
const teamSignedDocs = subscription.team?.document?.length || 0;
const userSignedDocs = subscription.User?.document?.length || 0;
const teamSignedDocs = subscription.team?.document?.length || 0;

Comment on lines 134 to 147
User: {
Document: {
_count: sortOrder,
},
},
},
{
team: {
document: {
_count: sortOrder,
},
},
},
];
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Possible incorrect syntax for sorting by related records' count

In the getOrderByClause function, when sorting by signingVolume, you attempt to order by the count of related documents. However, the Prisma syntax used may not correctly order by the count of related records.

Adjust the orderBy clause to properly sort by the count of related documents. Here's how you can modify it:

return [
  {
    User: {
      Document: undefined, // Remove this line if unnecessary
    },
    _count: {
      Document: sortOrder,
    },
  },
  {
    team: {
      document: undefined, // Remove this line if unnecessary
    },
    _count: {
      document: sortOrder,
    },
  },
];

Alternatively, you might need to use Prisma's _count property directly:

return {
  _count: {
    // Assuming both User.Document and team.document need to be counted
    Document: sortOrder,
    document: sortOrder,
  },
};

Please refer to the Prisma documentation on ordering by relation count to ensure the syntax is correct.

Committable suggestion was skipped due to low confidence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
apps: web Issues related to the webapp
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants