Skip to content

Conversation

@rodrigopavezi
Copy link
Member

@rodrigopavezi rodrigopavezi commented Aug 30, 2024

Resolves #9

  • Add
    • Request Page
    • Request Table with pagination showing 10 requests at a time
    • Mobile first

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive layout structure for the application, including a responsive header, footer, and main content area.
    • Added a search interface to enhance user experience in the Request Network Explorer.
    • Implemented components for displaying recent requests and payments, improving data visibility.
    • Integrated a customizable dropdown menu and various UI components like buttons, cards, and tables for better interactivity.
    • Established a centralized currency management system for handling multiple currencies efficiently.
    • Added a new ESLint configuration to ensure code quality and best practices.
    • Introduced a .gitignore file to streamline version control by excluding unnecessary files.
    • Created a new ApolloWrapper component for integrating Apollo Client in the application.
  • Bug Fixes

    • Resolved issues related to data fetching and state management in various components.
  • Documentation

    • Updated README and configuration files for better clarity and usability.
  • Chores

    • Created configuration files for ESLint and PostCSS to maintain code quality and styling consistency.

@rodrigopavezi rodrigopavezi linked an issue Aug 30, 2024 that may be closed by this pull request
2 tasks
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 30, 2024

Warning

Rate limit exceeded

@rodrigopavezi has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 45 seconds before requesting another review.

How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Commits

Files that changed from the base of the PR and between 4f78ec2 and 96d33bb.

Walkthrough

The changes involve the introduction of various configuration files and a structured UI component library for a Next.js application. New React components are created for layout, navigation, and data display, alongside custom hooks for managing payment and request data. Additionally, a GraphQL client is established for data fetching, enhancing the application's organization and functionality.

Changes

File(s) Change Summary
.eslintrc.json, .gitignore, package.json New configuration files created for ESLint, git ignore patterns, and project dependencies.
src/app/page.tsx New main entry point component introduced for the homepage layout.
src/components/* New UI components added, including Footer, Header, Logo, MainNav, and various table components.
src/lib/* New files for Apollo Client integration, constants for blockchain management, and hooks for fetching data.
src/lib/hooks/use-latest-payments.tsx, src/lib/hooks/use-latest-requests.tsx New custom hooks created for managing and retrieving payment and request data.
src/lib/queries/payments.ts, src/lib/queries/transactions.ts New GraphQL queries implemented for fetching payment and transaction data from a backend service.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App
    participant GraphQLClient
    participant PaymentService

    User->>App: Requests latest payments
    App->>GraphQLClient: Fetch latest payments
    GraphQLClient->>PaymentService: Query payment data
    PaymentService-->>GraphQLClient: Return payment data
    GraphQLClient-->>App: Send payment data
    App-->>User: Display latest payments
Loading
sequenceDiagram
    participant User
    participant App
    participant GraphQLClient
    participant RequestService

    User->>App: Requests latest requests
    App->>GraphQLClient: Fetch latest requests
    GraphQLClient->>RequestService: Query request data
    RequestService-->>GraphQLClient: Return request data
    GraphQLClient-->>App: Send request data
    App-->>User: Display latest requests
Loading

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>.
    • 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @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.
Early access features: disabled

We are currently testing the following features in early access:

  • Anthropic claude-3-5-sonnet for code reviews: Anthropic claims that the new Claude model has stronger code understanding and code generation capabilities than their previous models. Note: Our default code review model was also updated late last week. Please compare the quality of the reviews between the two models by toggling the early access feature.

Note:

  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.
  • Please join our Discord Community to provide feedback and report issues on the discussion post.

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: 17

Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE

Commits

Files that changed from the base of the PR and between 0728450 and d6a99d4.

Files ignored due to path filters (6)
  • package-lock.json is excluded by !**/package-lock.json
  • public/data.svg is excluded by !**/*.svg
  • public/logo-1.svg is excluded by !**/*.svg
  • public/logo-2.svg is excluded by !**/*.svg
  • public/logo-3.svg is excluded by !**/*.svg
  • src/app/icon.ico is excluded by !**/*.ico
Files selected for processing (45)
  • .eslintrc.json (1 hunks)
  • .gitignore (1 hunks)
  • README.md (1 hunks)
  • components.json (1 hunks)
  • next.config.mjs (1 hunks)
  • package.json (1 hunks)
  • postcss.config.mjs (1 hunks)
  • src/app/globals.css (1 hunks)
  • src/app/layout.tsx (1 hunks)
  • src/app/page.tsx (1 hunks)
  • src/app/providers.tsx (1 hunks)
  • src/app/requests/page.tsx (1 hunks)
  • src/components/footer.tsx (1 hunks)
  • src/components/header.tsx (1 hunks)
  • src/components/logo.tsx (1 hunks)
  • src/components/main-nav.tsx (1 hunks)
  • src/components/recent-area.tsx (1 hunks)
  • src/components/recent-payment-table.tsx (1 hunks)
  • src/components/recent-request-table.tsx (1 hunks)
  • src/components/request-table.tsx (1 hunks)
  • src/components/search-area.tsx (1 hunks)
  • src/components/search.tsx (1 hunks)
  • src/components/socials.tsx (1 hunks)
  • src/components/stats-area.tsx (1 hunks)
  • src/components/ui/avatar.tsx (1 hunks)
  • src/components/ui/badge.tsx (1 hunks)
  • src/components/ui/button.tsx (1 hunks)
  • src/components/ui/card.tsx (1 hunks)
  • src/components/ui/dropdown-menu.tsx (1 hunks)
  • src/components/ui/input.tsx (1 hunks)
  • src/components/ui/sheet.tsx (1 hunks)
  • src/components/ui/skeleton.tsx (1 hunks)
  • src/components/ui/table.tsx (1 hunks)
  • src/lib/apollo-wrapper.tsx (1 hunks)
  • src/lib/consts.ts (1 hunks)
  • src/lib/currency-manager.ts (1 hunks)
  • src/lib/graphQlClient.ts (1 hunks)
  • src/lib/hooks/use-latest-payments.tsx (1 hunks)
  • src/lib/hooks/use-latest-requests.tsx (1 hunks)
  • src/lib/queries/payments.ts (1 hunks)
  • src/lib/queries/transactions.ts (1 hunks)
  • src/lib/types.ts (1 hunks)
  • src/lib/utils.ts (1 hunks)
  • tailwind.config.ts (1 hunks)
  • tsconfig.json (1 hunks)
Additional context used
Biome
src/lib/utils.ts

[error] 16-16: 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/app/layout.tsx

[error] 39-39: JSX elements without children should be marked as self-closing. In JSX, it is valid for any element to be self-closing.

Unsafe fix: Use a SelfClosingElement instead

(lint/style/useSelfClosingElements)

src/lib/queries/payments.ts

[error] 8-8: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)

src/components/request-table.tsx

[error] 81-81: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)

Additional comments not posted (145)
README.md (1)

1-1: LGTM!

The addition of the <!-- @format --> comment is a good practice for ensuring consistent formatting.

.eslintrc.json (1)

1-3: LGTM!

The ESLint configuration correctly extends next/core-web-vitals, which is a recommended setup for Next.js projects.

postcss.config.mjs (1)

1-8: LGTM!

The PostCSS configuration is correctly set up with Tailwind CSS as a plugin.

src/app/requests/page.tsx (1)

1-7: LGTM!

The React component is correctly implemented and renders the RequestTable component.

src/lib/currency-manager.ts (1)

1-7: LGTM!

The CurrencyManager is correctly initialized with the default list.

src/components/ui/skeleton.tsx (2)

1-1: LGTM!

The import statement is correct and follows best practices.


3-13: LGTM!

The Skeleton component is correctly implemented and follows best practices.

src/lib/graphQlClient.ts (2)

3-3: LGTM!

The import statement is correct and follows best practices.


5-13: Add error handling for missing environment variables.

The client configuration is correct but lacks error handling for missing environment variables. Consider adding error handling to ensure the environment variables are set.

-export const graphQLClient = new GraphQLClient(
-  process.env.NEXT_PUBLIC_HASURA_GRAPHQL_URL || '',
-  {
-    headers: {
-      'x-hasura-admin-secret':
-        process.env.NEXT_PUBLIC_HASURA_GRAPHQL_ADMIN_SECRET || '',
-    },
-  },
-);
+const url = process.env.NEXT_PUBLIC_HASURA_GRAPHQL_URL;
+const adminSecret = process.env.NEXT_PUBLIC_HASURA_GRAPHQL_ADMIN_SECRET;
+
+if (!url) {
+  throw new Error('NEXT_PUBLIC_HASURA_GRAPHQL_URL is not set');
+}
+
+if (!adminSecret) {
+  throw new Error('NEXT_PUBLIC_HASURA_GRAPHQL_ADMIN_SECRET is not set');
+}
+
+export const graphQLClient = new GraphQLClient(url, {
+  headers: {
+    'x-hasura-admin-secret': adminSecret,
+  },
+});
src/components/recent-area.tsx (2)

3-4: LGTM!

The import statements are correct and follow best practices.


6-13: LGTM!

The RecentArea component is correctly implemented and follows best practices.

src/app/page.tsx (2)

2-4: LGTM!

The import statements are correct and follow best practices.


6-14: LGTM!

The Home component is correctly implemented and follows best practices.

components.json (1)

1-17: LGTM!

The configuration settings are correct and follow best practices.

src/app/providers.tsx (2)

4-5: LGTM!

The import statements are correct and follow best practices.


7-17: LGTM!

The Providers component is correctly implemented and follows best practices.

.gitignore (3)

1-8: LGTM!

The entries for ignoring dependencies are standard and necessary.

The code changes are approved.


9-15: LGTM!

The entries for ignoring testing and Next.js files are standard and necessary.

The code changes are approved.


16-36: LGTM!

The entries for ignoring production, misc, debug, local env files, vercel, and typescript files are standard and necessary.

The code changes are approved.

src/components/search.tsx (1)

3-5: LGTM!

The import statements are necessary for the component to function correctly.

The code changes are approved.

src/components/footer.tsx (2)

3-5: LGTM!

The import statements are necessary for the component to function correctly.

The code changes are approved.


7-18: LGTM!

The component is well-structured and functional.

The code changes are approved.

tsconfig.json (16)

3-3: LGTM!

The lib option includes appropriate libraries for a modern web application.


4-4: LGTM!

The allowJs option is set to true, allowing JavaScript files to be compiled.


5-5: LGTM!

The skipLibCheck option is set to true, which can improve compilation speed.


6-6: LGTM!

The strict option is set to true, enabling all strict type-checking options.


7-7: LGTM!

The noEmit option is set to true, preventing the compiler from emitting output files.


8-8: LGTM!

The esModuleInterop option is set to true, enabling interoperability between CommonJS and ES Modules.


9-9: LGTM!

The module option is set to esnext, which is appropriate for modern JavaScript environments.


10-10: LGTM!

The moduleResolution option is set to bundler, which is appropriate for projects using bundlers like Webpack or Rollup.


11-11: LGTM!

The resolveJsonModule option is set to true, allowing importing JSON modules.


12-12: LGTM!

The isolatedModules option is set to true, ensuring that each file can be safely transpiled without relying on other files.


13-13: LGTM!

The jsx option is set to preserve, which is appropriate for projects using tools like Babel or a framework that handles JSX transformation.


14-14: LGTM!

The incremental option is set to true, enabling incremental compilation to improve build performance.


15-19: LGTM!

The plugins option includes the next plugin, which is appropriate for Next.js projects.


20-22: LGTM!

The paths option maps @/* to ./src/*, simplifying imports in the project.


24-24: LGTM!

The include option specifies appropriate files for a Next.js project.


25-25: LGTM!

The exclude option excludes node_modules, which is a standard practice to improve compilation performance.

src/components/search-area.tsx (2)

3-4: LGTM!

The import statements are appropriate for the component.


6-23: LGTM!

The SearchArea component is well-structured and follows best practices for React components.

src/components/logo.tsx (2)

3-6: LGTM!

The import statements are appropriate for the component.


8-33: LGTM!

The Logo component is well-structured and follows best practices for React components. The use of the cn utility and the Image component is appropriate.

src/components/header.tsx (3)

4-8: LGTM!

The import statements are correct and necessary for the component.


10-11: LGTM!

The component definition and usePathname usage are correct.


13-27: LGTM!

The JSX structure is correct and follows best practices for a responsive design.

src/lib/types.ts (2)

3-23: LGTM!

The Payment interface is well-defined and follows TypeScript best practices.


25-36: LGTM!

The Transaction interface is well-defined and follows TypeScript best practices.

src/components/main-nav.tsx (3)

3-5: LGTM!

The import statements are correct and necessary for the component.


7-11: LGTM!

The component definition is correct and follows TypeScript best practices.


13-32: LGTM!

The JSX structure is correct and follows best practices for a responsive design.

src/lib/utils.ts (2)

9-11: LGTM!

The function cn is correctly implemented and useful for merging class names.


22-30: LGTM!

The function formatTimestamp is correctly implemented and useful for formatting timestamps.

src/lib/hooks/use-latest-payments.tsx (3)

4-7: LGTM!

The import statements are correct and necessary for the functionality of the custom hook.


9-12: LGTM!

The ILatestPayments interface is correctly defined and improves type safety.


14-18: LGTM!

The Props type is correctly defined and improves type safety.

src/lib/queries/transactions.ts (1)

3-5: LGTM!

The import statements are correct and necessary for the functionality of the file.

src/lib/apollo-wrapper.tsx (1)

4-10: LGTM!

The import statements are correct and necessary for the functionality of the file.

package.json (4)

1-4: LGTM!

The metadata section is correctly defined.


5-10: LGTM!

The scripts section is correctly defined.


11-31: LGTM!

The dependencies section is correctly defined.


32-41: LGTM!

The devDependencies section is correctly defined.

src/components/ui/badge.tsx (4)

1-4: LGTM!

The import statements are correctly defined.


6-24: LGTM!

The badgeVariants definition is correctly defined.


26-28: LGTM!

The BadgeProps interface is correctly defined.


30-34: LGTM!

The Badge component is correctly defined.

src/lib/hooks/use-latest-requests.tsx (6)

1-8: LGTM!

The import statements are correctly defined.


10-15: LGTM!

The ILatestRequests interface is correctly defined.


17-21: LGTM!

The Props type is correctly defined.


35-52: LGTM!

The useMemo hook is correctly defined.


54-55: LGTM!

The return statement is correctly defined.


23-32: Remove console.log statement.

The hook implementation is correct, but the console.log statement should be removed in production code.

Apply this diff to remove the console.log statement:

-  console.log(data);

Likely invalid or redundant comment.

src/lib/consts.ts (3)

3-16: LGTM!

The CHAINS constant is well-defined and follows best practices for defining constants.


18-31: LGTM!

The CHAIN_SCAN_URLS constant is well-defined and follows best practices for defining constants.


33-46: LGTM!

The PAYMENT_CHAINS enum is well-defined and follows best practices for defining enums.

src/components/ui/avatar.tsx (3)

8-20: LGTM!

The Avatar component is well-defined and follows best practices for defining React components with forward refs.


23-33: LGTM!

The AvatarImage component is well-defined and follows best practices for defining React components with forward refs.


35-47: LGTM!

The AvatarFallback component is well-defined and follows best practices for defining React components with forward refs.

src/app/layout.tsx (4)

14-17: LGTM!

The metadata object is well-defined and follows best practices for defining metadata in Next.js.


19-52: LGTM!

The HTML structure is well-defined and follows best practices for defining the layout in Next.js.

Tools
Biome

[error] 39-39: JSX elements without children should be marked as self-closing. In JSX, it is valid for any element to be self-closing.

Unsafe fix: Use a SelfClosingElement instead

(lint/style/useSelfClosingElements)


6-6: LGTM!

The usage of the cn utility function is well-defined and follows best practices for applying class names in React components.

Also applies to: 27-30


4-4: LGTM!

The usage of the Inter font is well-defined and follows best practices for applying fonts in Next.js.

Also applies to: 12-12, 29-29

src/components/ui/button.tsx (4)

1-5: LGTM!

The imports are necessary and correctly implemented.


7-34: LGTM!

The buttonVariants constant is correctly implemented and follows best practices for managing class names.


36-40: LGTM!

The ButtonProps interface is correctly implemented and provides type safety for the Button component.


42-54: LGTM!

The Button component is correctly implemented and follows best practices for React components.

src/app/globals.css (3)

1-3: LGTM!

The Tailwind CSS imports are necessary and correctly implemented.


5-59: LGTM!

The CSS custom properties are correctly implemented and provide a flexible theming system.


62-69: LGTM!

The global element styles are correctly implemented and ensure consistent styling across the application.

src/components/ui/card.tsx (7)

1-3: LGTM!

The imports are necessary and correctly implemented.


5-18: LGTM!

The Card component is correctly implemented and follows best practices for React components.


20-30: LGTM!

The CardHeader component is correctly implemented and follows best practices for React components.


32-45: LGTM!

The CardTitle component is correctly implemented and follows best practices for React components.


47-57: LGTM!

The CardDescription component is correctly implemented and follows best practices for React components.


59-65: LGTM!

The CardContent component is correctly implemented and follows best practices for React components.


67-77: LGTM!

The CardFooter component is correctly implemented and follows best practices for React components.

tailwind.config.ts (9)

3-4: LGTM!

The import statements are correct and necessary for the configuration.


7-13: LGTM!

The darkMode and content configurations are correct and follow best practices.


16-22: LGTM!

The container configurations are correct and follow best practices.


24-57: LGTM!

The colors extension is correct and follows best practices.


59-63: LGTM!

The borderRadius extension is correct and follows best practices.


64-72: LGTM!

The keyframes extension is correct and follows best practices.


74-77: LGTM!

The animation extension is correct and follows best practices.


78-80: LGTM!

The fontFamily extension is correct and follows best practices.


83-83: LGTM!

The plugins configuration is correct and follows best practices.

src/components/stats-area.tsx (6)

3-4: LGTM!

The import statements are correct and necessary for the component.


6-8: LGTM!

The grid layout is responsive and follows best practices.


9-24: LGTM!

The first Card component is correctly implemented.


25-38: LGTM!

The second Card component is correctly implemented.


39-52: LGTM!

The third Card component is correctly implemented.


53-64: LGTM!

The fourth Card component is correctly implemented.

src/components/recent-request-table.tsx (5)

4-25: LGTM!

The import statements are correct and necessary for the component.


27-28: LGTM!

The hook usage is correct and follows best practices.


30-32: LGTM!

The loading state handling is correct and follows best practices.


34-36: LGTM!

The no data message handling is correct and follows best practices.


54-58: LGTM!

The table header is correctly implemented.

src/components/recent-payment-table.tsx (1)

1-27: LGTM!

The import statements are appropriate for the functionality provided by the component.

src/components/ui/table.tsx (9)

1-3: LGTM!

The import statements are appropriate for the functionality provided by the components.


5-16: LGTM!

The Table component is well-structured and handles overflow appropriately.


19-25: LGTM!

The TableHeader component is well-structured and applies the appropriate class for the header.


27-37: LGTM!

The TableBody component is well-structured and applies the appropriate class for the body.


39-52: LGTM!

The TableFooter component is well-structured and applies the appropriate classes for the footer.


54-67: LGTM!

The TableRow component is well-structured and applies the appropriate classes for the row.


69-82: LGTM!

The TableHead component is well-structured and applies the appropriate classes for the head.


84-94: LGTM!

The TableCell component is well-structured and applies the appropriate classes for the cell.


96-106: LGTM!

The TableCaption component is well-structured and applies the appropriate classes for the caption.

src/components/ui/sheet.tsx (11)

1-8: LGTM!

The import statements are appropriate for the functionality provided by the components.


10-10: LGTM!

The Sheet component is well-structured and serves its purpose as a wrapper.


12-12: LGTM!

The SheetTrigger component is well-structured and serves its purpose as a wrapper.


14-14: LGTM!

The SheetClose component is well-structured and serves its purpose as a wrapper.


16-16: LGTM!

The SheetPortal component is well-structured and serves its purpose as a wrapper.


18-31: LGTM!

The SheetOverlay component is well-structured and applies the appropriate classes for the overlay.


56-75: LGTM!

The SheetContent component is well-structured and applies the appropriate classes for the content.


77-89: LGTM!

The SheetHeader component is well-structured and applies the appropriate classes for the header.


91-103: LGTM!

The SheetFooter component is well-structured and applies the appropriate classes for the footer.


105-115: LGTM!

The SheetTitle component is well-structured and applies the appropriate classes for the title.


117-127: LGTM!

The SheetDescription component is well-structured and applies the appropriate classes for the description.

src/lib/queries/payments.ts (1)

8-39: LGTM!

The function is correctly implemented and constructs the query string dynamically based on the paymentChain parameter.

Tools
Biome

[error] 8-8: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)

src/components/ui/dropdown-menu.tsx (8)

21-41: LGTM!

The component is correctly implemented and follows best practices for using React.forwardRef.


43-57: LGTM!

The component is correctly implemented and follows best practices for using React.forwardRef.


59-75: LGTM!

The component is correctly implemented and follows best practices for using React.forwardRef.


77-93: LGTM!

The component is correctly implemented and follows best practices for using React.forwardRef.


95-117: LGTM!

The component is correctly implemented and follows best practices for using React.forwardRef.


119-139: LGTM!

The component is correctly implemented and follows best practices for using React.forwardRef.


141-157: LGTM!

The component is correctly implemented and follows best practices for using React.forwardRef.


159-169: LGTM!

The component is correctly implemented and follows best practices for using React.forwardRef.

Comment on lines 1 to 4
/** @type {import('next').NextConfig} */
const nextConfig = {};

export default nextConfig;
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider adding a comment explaining the minimal configuration.

The configuration file is correctly set up with a minimal configuration object. Adding a comment can help future developers understand that this is a placeholder for future configurations.

Apply this diff to add a comment:

/** @type {import('next').NextConfig} */
const nextConfig = {};
+// Placeholder for future Next.js configurations

export default nextConfig;
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
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default nextConfig;
/** @type {import('next').NextConfig} */
const nextConfig = {};
// Placeholder for future Next.js configurations
export default nextConfig;

Comment on lines 7 to 20
export function Search() {
return (
<div className="relative ml-auto flex-1 md:grow-0 w-full">
{/* TODO: add / paymentReference / CID */}
<Input
type="search"
placeholder="Search by Wallet Address / Request ID"
className="pl-8 md:w-[400px] lg:w-[800px] h-12 w-full"
/>
<Button className="absolute right-2.5 top-1" size="icon">
<SearchIcon size="20" />
</Button>
</div>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

LGTM! But address the TODO comment.

The component is well-structured and functional. Ensure to address the TODO comment for additional search parameters.

The code changes are approved.

Do you want me to help with implementing the additional search parameters?

src/lib/utils.ts Outdated
Comment on lines 13 to 20
export const groupBy = (items: any, key: any) =>
items.reduce(
(result: any, item: any) => ({
...result,
[item[key]]: [...(result[item[key]] || []), item],
}),
{},
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Refactor to avoid performance issues with the spread operator.

The function correctly groups items by a specified key but has performance issues due to the spread operator. Use push instead of the spread operator.

Apply this diff to refactor the function:

-export const groupBy = (items: any, key: any) =>
-  items.reduce(
-    (result: any, item: any) => ({
-      ...result,
-      [item[key]]: [...(result[item[key]] || []), item],
-    }),
-    {},
-  );
+export const groupBy = (items: any, key: any) =>
+  items.reduce((result: any, item: any) => {
+    (result[item[key]] = result[item[key]] || []).push(item);
+    return result;
+  }, {});
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 const groupBy = (items: any, key: any) =>
items.reduce(
(result: any, item: any) => ({
...result,
[item[key]]: [...(result[item[key]] || []), item],
}),
{},
);
export const groupBy = (items: any, key: any) =>
items.reduce((result: any, item: any) => {
(result[item[key]] = result[item[key]] || []).push(item);
return result;
}, {});
Tools
Biome

[error] 16-16: 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)

Comment on lines 5 to 30
export function Socials({ color = 'black' }: { color?: string }) {
return (
<div className="flex items-start space-x-1">
<SocialIcon
href="https://x.com/RequestNetwork"
url="https://x.com"
fgColor={color}
bgColor="transparent"
target="_blank"
/>
<SocialIcon
href="https://www.linkedin.com/company/request-network"
url="https://www.linkedin.com"
fgColor={color}
bgColor="transparent"
target="_blank"
/>
<SocialIcon
href="https://discord.gg/FsVAR3ny3f"
url="https://discord.com"
fgColor={color}
bgColor="transparent"
target="_blank"
/>
</div>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Improve prop types validation and default props.

The component is correctly implemented but can be improved by adding prop types validation and default props.

Apply this diff to add prop types validation and default props:

+import PropTypes from 'prop-types';

export function Socials({ color = 'black' }: { color?: string }) {
  return (
    <div className="flex items-start space-x-1">
      <SocialIcon
        href="https://x.com/RequestNetwork"
        url="https://x.com"
        fgColor={color}
        bgColor="transparent"
        target="_blank"
      />
      <SocialIcon
        href="https://www.linkedin.com/company/request-network"
        url="https://www.linkedin.com"
        fgColor={color}
        bgColor="transparent"
        target="_blank"
      />
      <SocialIcon
        href="https://discord.gg/FsVAR3ny3f"
        url="https://discord.com"
        fgColor={color}
        bgColor="transparent"
        target="_blank"
      />
    </div>
  );
}

+Socials.propTypes = {
+  color: PropTypes.string,
+};

+Socials.defaultProps = {
+  color: 'black',
+};
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 function Socials({ color = 'black' }: { color?: string }) {
return (
<div className="flex items-start space-x-1">
<SocialIcon
href="https://x.com/RequestNetwork"
url="https://x.com"
fgColor={color}
bgColor="transparent"
target="_blank"
/>
<SocialIcon
href="https://www.linkedin.com/company/request-network"
url="https://www.linkedin.com"
fgColor={color}
bgColor="transparent"
target="_blank"
/>
<SocialIcon
href="https://discord.gg/FsVAR3ny3f"
url="https://discord.com"
fgColor={color}
bgColor="transparent"
target="_blank"
/>
</div>
);
import PropTypes from 'prop-types';
export function Socials({ color = 'black' }: { color?: string }) {
return (
<div className="flex items-start space-x-1">
<SocialIcon
href="https://x.com/RequestNetwork"
url="https://x.com"
fgColor={color}
bgColor="transparent"
target="_blank"
/>
<SocialIcon
href="https://www.linkedin.com/company/request-network"
url="https://www.linkedin.com"
fgColor={color}
bgColor="transparent"
target="_blank"
/>
<SocialIcon
href="https://discord.gg/FsVAR3ny3f"
url="https://discord.com"
fgColor={color}
bgColor="transparent"
target="_blank"
/>
</div>
);
}
Socials.propTypes = {
color: PropTypes.string,
};
Socials.defaultProps = {
color: 'black',
};

Comment on lines 1 to 25
import * as React from "react"

import { cn } from "@/lib/utils"

export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"

export { Input }
Copy link
Contributor

Choose a reason for hiding this comment

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

Improve prop types validation and default props.

The component is correctly implemented but can be improved by adding prop types validation and default props.

Apply this diff to add prop types validation and default props:

+import PropTypes from 'prop-types';

const Input = React.forwardRef<HTMLInputElement, InputProps>(
  ({ className, type, ...props }, ref) => {
    return (
      <input
        type={type}
        className={cn(
          "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
          className
        )}
        ref={ref}
        {...props}
      />
    )
  }
)
Input.displayName = "Input"

+Input.propTypes = {
+  className: PropTypes.string,
+  type: PropTypes.string,
+};

+Input.defaultProps = {
+  className: '',
+  type: 'text',
+};

export { Input }
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 * as React from "react"
import { cn } from "@/lib/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"
export { Input }
import * as React from "react"
+import PropTypes from 'prop-types';
import { cn } from "@/lib/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"
+Input.propTypes = {
+ className: PropTypes.string,
+ type: PropTypes.string,
+};
+Input.defaultProps = {
+ className: '',
+ type: 'text',
+};
export { Input }

Comment on lines 29 to 34
export function RecentPaymentTable() {
const { payments, isLoading } = useLatestPayments();

if (isLoading) {
return <Skeleton className="h-full w-full rounded-xl" />;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Enhance Skeleton component class name.

The Skeleton component could use a more descriptive class name to indicate its purpose.

- return <Skeleton className="h-full w-full rounded-xl" />;
+ return <Skeleton className="h-full w-full rounded-xl recent-payment-skeleton" />;
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 function RecentPaymentTable() {
const { payments, isLoading } = useLatestPayments();
if (isLoading) {
return <Skeleton className="h-full w-full rounded-xl" />;
}
export function RecentPaymentTable() {
const { payments, isLoading } = useLatestPayments();
if (isLoading) {
return <Skeleton className="h-full w-full rounded-xl recent-payment-skeleton" />;
}

Comment on lines 40 to 95
return (
<Card className="xl:col-span-1 w-[95%] md:w-full">
<CardHeader className="flex flex-row items-center">
<div className="grid gap-2">
<CardTitle>Payments</CardTitle>
<CardDescription>Recent payments.</CardDescription>
</div>
<Button asChild size="sm" className="ml-auto gap-1">
<Link href="/payments">
View All
<ArrowUpRight className="h-4 w-4" />
</Link>
</Button>
</CardHeader>
<CardContent>
<Table className="overflow-x-scroll">
<TableHeader>
<TableRow>
<TableHead>Payment Reference</TableHead>
<TableHead>Transaction Hash</TableHead>
<TableHead>Blockchain</TableHead>
<TableHead className="text-right">Timestamp</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{payments.slice(0, 10).map((payment: Payment) => (
<TableRow key={payment.id}>
<TableCell>{payment.reference.slice(0, 8)}...</TableCell>
<TableCell>
<div className="font-medium text-emerald-700">
<Link
href={`${CHAIN_SCAN_URLS[payment.chain]}/tx/${payment.txHash}`}
target="_blank"
>
{payment.txHash.slice(0, 14)}...
</Link>
</div>
</TableCell>
<TableCell>{payment.chain}</TableCell>
<TableCell className="md:table-cell text-right">
<TimeAgo
datetime={payment.timestamp * 1000}
locale="en_short"
/>{' '}
({formatTimestamp(payment.timestamp)})
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
</Card>
Copy link
Contributor

Choose a reason for hiding this comment

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

Handle empty payments array.

The table rendering logic should handle cases where payments might be an empty array.

{payments.length === 0 ? (
  <div>No recent payments available.</div>
) : (
  <Table className="overflow-x-scroll">
    <TableHeader>
      <TableRow>
        <TableHead>Payment Reference</TableHead>
        <TableHead>Transaction Hash</TableHead>
        <TableHead>Blockchain</TableHead>
        <TableHead className="text-right">Timestamp</TableHead>
      </TableRow>
    </TableHeader>
    <TableBody>
      {payments.slice(0, 10).map((payment: Payment) => (
        <TableRow key={payment.id}>
          <TableCell>{payment.reference.slice(0, 8)}...</TableCell>
          <TableCell>
            <div className="font-medium text-emerald-700">
              <Link
                href={`${CHAIN_SCAN_URLS[payment.chain]}/tx/${payment.txHash}`}
                target="_blank"
              >
                {payment.txHash.slice(0, 14)}...
              </Link>
            </div>
          </TableCell>
          <TableCell>{payment.chain}</TableCell>
          <TableCell className="md:table-cell text-right">
            <TimeAgo
              datetime={payment.timestamp * 1000}
              locale="en_short"
            />{' '}
            ({formatTimestamp(payment.timestamp)})
          </TableCell>
        </TableRow>
      ))}
    </TableBody>
  </Table>
)}
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
return (
<Card className="xl:col-span-1 w-[95%] md:w-full">
<CardHeader className="flex flex-row items-center">
<div className="grid gap-2">
<CardTitle>Payments</CardTitle>
<CardDescription>Recent payments.</CardDescription>
</div>
<Button asChild size="sm" className="ml-auto gap-1">
<Link href="/payments">
View All
<ArrowUpRight className="h-4 w-4" />
</Link>
</Button>
</CardHeader>
<CardContent>
<Table className="overflow-x-scroll">
<TableHeader>
<TableRow>
<TableHead>Payment Reference</TableHead>
<TableHead>Transaction Hash</TableHead>
<TableHead>Blockchain</TableHead>
<TableHead className="text-right">Timestamp</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{payments.slice(0, 10).map((payment: Payment) => (
<TableRow key={payment.id}>
<TableCell>{payment.reference.slice(0, 8)}...</TableCell>
<TableCell>
<div className="font-medium text-emerald-700">
<Link
href={`${CHAIN_SCAN_URLS[payment.chain]}/tx/${payment.txHash}`}
target="_blank"
>
{payment.txHash.slice(0, 14)}...
</Link>
</div>
</TableCell>
<TableCell>{payment.chain}</TableCell>
<TableCell className="md:table-cell text-right">
<TimeAgo
datetime={payment.timestamp * 1000}
locale="en_short"
/>{' '}
({formatTimestamp(payment.timestamp)})
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
</Card>
return (
<Card className="xl:col-span-1 w-[95%] md:w-full">
<CardHeader className="flex flex-row items-center">
<div className="grid gap-2">
<CardTitle>Payments</CardTitle>
<CardDescription>Recent payments.</CardDescription>
</div>
<Button asChild size="sm" className="ml-auto gap-1">
<Link href="/payments">
View All
<ArrowUpRight className="h-4 w-4" />
</Link>
</Button>
</CardHeader>
<CardContent>
{payments.length === 0 ? (
<div>No recent payments available.</div>
) : (
<Table className="overflow-x-scroll">
<TableHeader>
<TableRow>
<TableHead>Payment Reference</TableHead>
<TableHead>Transaction Hash</TableHead>
<TableHead>Blockchain</TableHead>
<TableHead className="text-right">Timestamp</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{payments.slice(0, 10).map((payment: Payment) => (
<TableRow key={payment.id}>
<TableCell>{payment.reference.slice(0, 8)}...</TableCell>
<TableCell>
<div className="font-medium text-emerald-700">
<Link
href={`${CHAIN_SCAN_URLS[payment.chain]}/tx/${payment.txHash}`}
target="_blank"
>
{payment.txHash.slice(0, 14)}...
</Link>
</div>
</TableCell>
<TableCell>{payment.chain}</TableCell>
<TableCell className="md:table-cell text-right">
<TimeAgo
datetime={payment.timestamp * 1000}
locale="en_short"
/>{' '}
({formatTimestamp(payment.timestamp)})
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)}
</CardContent>
</Card>

import { graphQLClient } from '../graphQlClient';
import { CHAINS, PAYMENT_CHAINS } from '../consts';

export const getPaymentsQuery = (paymentChain: String) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

Use lowercase string instead of String.

Using String as a type is not recommended. The lowercase string should be used for consistency and to avoid potential issues.

Apply this diff to fix the type declaration:

-export const getPaymentsQuery = (paymentChain: String) =>
+export const getPaymentsQuery = (paymentChain: string) =>
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 const getPaymentsQuery = (paymentChain: String) =>
export const getPaymentsQuery = (paymentChain: string) =>
Tools
Biome

[error] 8-8: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)

Comment on lines +41 to +167
export const fetchPayments = async (variables: {
first: number;
skip: number;
}): Promise<Payment[]> => {
const mainnetData: { payment_mainnet: { payments: Payment[] } } =
await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.MAINNET),
variables,
);
const arbitrumOneData: {
payment_arbitrum_one: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.ARBITRUM_ONE),
variables,
);
const avalancheData: {
payment_avalanche: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.AVALANCHE),
variables,
);
const bscData: {
payment_bsc: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.BSC),
variables,
);
const celoData: {
payment_celo: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.CELO),
variables,
);
const fantomData: {
payment_fantom: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.FANTOM),
variables,
);
const fuseData: {
payment_fuse: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.FUSE),
variables,
);
const maticData: {
payment_matic: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.MATIC),
variables,
);
const moonbeamData: {
payment_moonbeam: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.MOONBEAM),
variables,
);
const optimismData: {
payment_optimism: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.OPTIMISM),
variables,
);
const xdaiData: {
payment_xdai: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.XDAI),
variables,
);
const zksynceraData: {
payment_zksyncera: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.ZKSYNCERA),
variables,
);

return mainnetData &&
arbitrumOneData &&
avalancheData &&
bscData &&
celoData &&
fantomData &&
fuseData &&
maticData &&
moonbeamData &&
optimismData &&
xdaiData &&
zksynceraData
? [
...mainnetData.payment_mainnet.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.MAINNET };
}),
...arbitrumOneData.payment_arbitrum_one.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.ARBITRUM_ONE };
}),
...avalancheData.payment_avalanche.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.AVALANCHE };
}),
...bscData.payment_bsc.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.BSC };
}),
...celoData.payment_celo.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.CELO };
}),
...fantomData.payment_fantom.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.FANTOM };
}),
...fuseData.payment_fuse.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.FUSE };
}),
...maticData.payment_matic.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.MATIC };
}),
...moonbeamData.payment_moonbeam.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.MOONBEAM };
}),
...optimismData.payment_optimism.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.OPTIMISM };
}),
...xdaiData.payment_xdai.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.XDAI };
}),
...zksynceraData.payment_zksyncera.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.ZKSYNCERA };
}),
].sort((a: Payment, b: Payment) => b.timestamp - a.timestamp)
: [];
Copy link
Contributor

Choose a reason for hiding this comment

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

Refactor to reduce redundancy and improve readability.

The function is correctly implemented but can be refactored to reduce redundancy and improve readability. Consider using a loop to iterate over the chains and fetch the data.

Apply this diff to refactor the function:

export const fetchPayments = async (variables: {
  first: number;
  skip: number;
}): Promise<Payment[]> => {
  const chains = [
    PAYMENT_CHAINS.MAINNET,
    PAYMENT_CHAINS.ARBITRUM_ONE,
    PAYMENT_CHAINS.AVALANCHE,
    PAYMENT_CHAINS.BSC,
    PAYMENT_CHAINS.CELO,
    PAYMENT_CHAINS.FANTOM,
    PAYMENT_CHAINS.FUSE,
    PAYMENT_CHAINS.MATIC,
    PAYMENT_CHAINS.MOONBEAM,
    PAYMENT_CHAINS.OPTIMISM,
    PAYMENT_CHAINS.XDAI,
    PAYMENT_CHAINS.ZKSYNCERA,
  ];

  const data = await Promise.all(
    chains.map(async (chain) => {
      const result = await graphQLClient.request(
        getPaymentsQuery(chain),
        variables,
      );
      return { chain, payments: result[`payment_${chain.toLowerCase()}`].payments };
    })
  );

  return data
    .flatMap(({ chain, payments }) =>
      payments.map((payment: any) => ({ ...payment, chain: CHAINS[chain] }))
    )
    .sort((a: Payment, b: Payment) => b.timestamp - a.timestamp);
};
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 const fetchPayments = async (variables: {
first: number;
skip: number;
}): Promise<Payment[]> => {
const mainnetData: { payment_mainnet: { payments: Payment[] } } =
await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.MAINNET),
variables,
);
const arbitrumOneData: {
payment_arbitrum_one: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.ARBITRUM_ONE),
variables,
);
const avalancheData: {
payment_avalanche: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.AVALANCHE),
variables,
);
const bscData: {
payment_bsc: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.BSC),
variables,
);
const celoData: {
payment_celo: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.CELO),
variables,
);
const fantomData: {
payment_fantom: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.FANTOM),
variables,
);
const fuseData: {
payment_fuse: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.FUSE),
variables,
);
const maticData: {
payment_matic: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.MATIC),
variables,
);
const moonbeamData: {
payment_moonbeam: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.MOONBEAM),
variables,
);
const optimismData: {
payment_optimism: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.OPTIMISM),
variables,
);
const xdaiData: {
payment_xdai: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.XDAI),
variables,
);
const zksynceraData: {
payment_zksyncera: { payments: Payment[] };
} = await graphQLClient.request(
getPaymentsQuery(PAYMENT_CHAINS.ZKSYNCERA),
variables,
);
return mainnetData &&
arbitrumOneData &&
avalancheData &&
bscData &&
celoData &&
fantomData &&
fuseData &&
maticData &&
moonbeamData &&
optimismData &&
xdaiData &&
zksynceraData
? [
...mainnetData.payment_mainnet.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.MAINNET };
}),
...arbitrumOneData.payment_arbitrum_one.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.ARBITRUM_ONE };
}),
...avalancheData.payment_avalanche.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.AVALANCHE };
}),
...bscData.payment_bsc.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.BSC };
}),
...celoData.payment_celo.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.CELO };
}),
...fantomData.payment_fantom.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.FANTOM };
}),
...fuseData.payment_fuse.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.FUSE };
}),
...maticData.payment_matic.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.MATIC };
}),
...moonbeamData.payment_moonbeam.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.MOONBEAM };
}),
...optimismData.payment_optimism.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.OPTIMISM };
}),
...xdaiData.payment_xdai.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.XDAI };
}),
...zksynceraData.payment_zksyncera.payments.map((payment: any) => {
return { ...payment, chain: CHAINS.ZKSYNCERA };
}),
].sort((a: Payment, b: Payment) => b.timestamp - a.timestamp)
: [];
export const fetchPayments = async (variables: {
first: number;
skip: number;
}): Promise<Payment[]> => {
const chains = [
PAYMENT_CHAINS.MAINNET,
PAYMENT_CHAINS.ARBITRUM_ONE,
PAYMENT_CHAINS.AVALANCHE,
PAYMENT_CHAINS.BSC,
PAYMENT_CHAINS.CELO,
PAYMENT_CHAINS.FANTOM,
PAYMENT_CHAINS.FUSE,
PAYMENT_CHAINS.MATIC,
PAYMENT_CHAINS.MOONBEAM,
PAYMENT_CHAINS.OPTIMISM,
PAYMENT_CHAINS.XDAI,
PAYMENT_CHAINS.ZKSYNCERA,
];
const data = await Promise.all(
chains.map(async (chain) => {
const result = await graphQLClient.request(
getPaymentsQuery(chain),
variables,
);
return { chain, payments: result[`payment_${chain.toLowerCase()}`].payments };
})
);
return data
.flatMap(({ chain, payments }) =>
payments.map((payment: any) => ({ ...payment, chain: CHAINS[chain] }))
)
.sort((a: Payment, b: Payment) => b.timestamp - a.timestamp);
};

? currencyManager.fromAddress(currencyValue)
: currencyManager.fromSymbol(currencyValue);

return `${formatUnits(row.original?.dataObject?.data?.parameters?.expectedAmount || '0', currencyDetails?.decimals!) || 'N/A'} ${currencyDetails?.symbol}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Avoid non-null assertions.

Using non-null assertions can lead to runtime errors if the value is null or undefined. Handle the case where currencyDetails might be null or undefined.

Apply this diff to handle the null or undefined case:

return `${formatUnits(
  row.original?.dataObject?.data?.parameters?.expectedAmount || '0',
-  currencyDetails?.decimals!
+  currencyDetails?.decimals || 18
) || 'N/A'} ${currencyDetails?.symbol}`;
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
return `${formatUnits(row.original?.dataObject?.data?.parameters?.expectedAmount || '0', currencyDetails?.decimals!) || 'N/A'} ${currencyDetails?.symbol}`;
return `${formatUnits(
row.original?.dataObject?.data?.parameters?.expectedAmount || '0',
currencyDetails?.decimals || 18
) || 'N/A'} ${currencyDetails?.symbol}`;
Tools
Biome

[error] 81-81: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)

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

Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE

Commits

Files that changed from the base of the PR and between d6a99d4 and 4f78ec2.

Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
Files selected for processing (2)
  • package.json (1 hunks)
  • src/components/request-table.tsx (1 hunks)
Additional context used
Biome
src/components/request-table.tsx

[error] 86-86: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)

Additional comments not posted (5)
package.json (4)

1-4: LGTM!

The metadata section is correctly defined.


5-10: LGTM!

The scripts section is correctly defined.


11-32: LGTM!

The dependencies section is correctly defined.


33-42: LGTM!

The devDependencies section is correctly defined.

src/components/request-table.tsx (1)

1-33: LGTM!

The imports section is correctly defined.

Comment on lines 34 to 89
export const columns: ColumnDef<Transaction>[] = [
{
accessorKey: 'channelId',
header: 'Request Id',
cell: ({ row }) => (
<div className="font-medium text-emerald-700">
<Link href={`/request/${row.getValue('channelId')}`}>
{String(row.getValue('channelId')).slice(0, 20)}...
</Link>
</div>
),
},
{
accessorKey: 'blockTimestamp',
header: 'Timestamp',
cell: ({ row }) => (
<div className="lowercase">
<TimeAgo
datetime={Number(row.getValue('blockTimestamp')) * 1000}
locale="en_short"
/>{' '}
({formatTimestamp(row.getValue('blockTimestamp'))})
</div>
),
},
{
accessorKey: 'payee',
header: 'Payee',
cell: ({ row }: { row: any }) => {
const address = row.original?.dataObject?.data?.parameters?.payee?.value;
return address ? truncateEthAddress(address) : 'N/A';
},
},
{
accessorKey: 'payer',
header: 'Payer',
cell: ({ row }: { row: any }) => {
const address = row.original?.dataObject?.data?.parameters?.payer?.value;
return address ? truncateEthAddress(address) : 'N/A';
},
},
{
accessorKey: 'expectedAmount',
header: 'Expected Amount',
cell: ({ row }: { row: any }) => {
const currencyValue =
row.original?.dataObject?.data?.parameters?.currency?.value;

const currencyDetails = isAddress(currencyValue)
? currencyManager.fromAddress(currencyValue)
: currencyManager.fromSymbol(currencyValue);

return `${formatUnits(row.original?.dataObject?.data?.parameters?.expectedAmount || '0', currencyDetails?.decimals!) || 'N/A'} ${currencyDetails?.symbol}`;
},
},
];
Copy link
Contributor

Choose a reason for hiding this comment

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

Avoid non-null assertions.

Using non-null assertions can lead to runtime errors if the value is null or undefined. Handle the case where currencyDetails might be null or undefined.

Apply this diff to handle the null or undefined case:

return `${formatUnits(
  row.original?.dataObject?.data?.parameters?.expectedAmount || '0',
-  currencyDetails?.decimals!
+  currencyDetails?.decimals || 18
) || 'N/A'} ${currencyDetails?.symbol}`;
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 const columns: ColumnDef<Transaction>[] = [
{
accessorKey: 'channelId',
header: 'Request Id',
cell: ({ row }) => (
<div className="font-medium text-emerald-700">
<Link href={`/request/${row.getValue('channelId')}`}>
{String(row.getValue('channelId')).slice(0, 20)}...
</Link>
</div>
),
},
{
accessorKey: 'blockTimestamp',
header: 'Timestamp',
cell: ({ row }) => (
<div className="lowercase">
<TimeAgo
datetime={Number(row.getValue('blockTimestamp')) * 1000}
locale="en_short"
/>{' '}
({formatTimestamp(row.getValue('blockTimestamp'))})
</div>
),
},
{
accessorKey: 'payee',
header: 'Payee',
cell: ({ row }: { row: any }) => {
const address = row.original?.dataObject?.data?.parameters?.payee?.value;
return address ? truncateEthAddress(address) : 'N/A';
},
},
{
accessorKey: 'payer',
header: 'Payer',
cell: ({ row }: { row: any }) => {
const address = row.original?.dataObject?.data?.parameters?.payer?.value;
return address ? truncateEthAddress(address) : 'N/A';
},
},
{
accessorKey: 'expectedAmount',
header: 'Expected Amount',
cell: ({ row }: { row: any }) => {
const currencyValue =
row.original?.dataObject?.data?.parameters?.currency?.value;
const currencyDetails = isAddress(currencyValue)
? currencyManager.fromAddress(currencyValue)
: currencyManager.fromSymbol(currencyValue);
return `${formatUnits(row.original?.dataObject?.data?.parameters?.expectedAmount || '0', currencyDetails?.decimals!) || 'N/A'} ${currencyDetails?.symbol}`;
},
},
];
export const columns: ColumnDef<Transaction>[] = [
{
accessorKey: 'channelId',
header: 'Request Id',
cell: ({ row }) => (
<div className="font-medium text-emerald-700">
<Link href={`/request/${row.getValue('channelId')}`}>
{String(row.getValue('channelId')).slice(0, 20)}...
</Link>
</div>
),
},
{
accessorKey: 'blockTimestamp',
header: 'Timestamp',
cell: ({ row }) => (
<div className="lowercase">
<TimeAgo
datetime={Number(row.getValue('blockTimestamp')) * 1000}
locale="en_short"
/>{' '}
({formatTimestamp(row.getValue('blockTimestamp'))})
</div>
),
},
{
accessorKey: 'payee',
header: 'Payee',
cell: ({ row }: { row: any }) => {
const address = row.original?.dataObject?.data?.parameters?.payee?.value;
return address ? truncateEthAddress(address) : 'N/A';
},
},
{
accessorKey: 'payer',
header: 'Payer',
cell: ({ row }: { row: any }) => {
const address = row.original?.dataObject?.data?.parameters?.payer?.value;
return address ? truncateEthAddress(address) : 'N/A';
},
},
{
accessorKey: 'expectedAmount',
header: 'Expected Amount',
cell: ({ row }: { row: any }) => {
const currencyValue =
row.original?.dataObject?.data?.parameters?.currency?.value;
const currencyDetails = isAddress(currencyValue)
? currencyManager.fromAddress(currencyValue)
: currencyManager.fromSymbol(currencyValue);
return `${formatUnits(row.original?.dataObject?.data?.parameters?.expectedAmount || '0', currencyDetails?.decimals || 18) || 'N/A'} ${currencyDetails?.symbol}`;
},
},
];
Tools
Biome

[error] 86-86: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)

Comment on lines +91 to +219
export function RequestTable() {
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});

const { requests, isLoading } = useLatestRequests({
first: pagination.pageSize,
skip: pagination.pageIndex * pagination.pageSize,
});

const table = useReactTable({
// Get only the first transaction for each request.
data: Object.values(requests).map((request) => request[0]),
columns,
pageCount: 10,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onPaginationChange: setPagination,
manualPagination: true,
state: {
pagination,
},
});

return (
<div className="w-[95%] bg-white border rounded-lg self-center md:w-full">
<div className="p-10">
<div>
<h1 className="text-2xl font-bold">Requests</h1>
</div>
<div className="flex items-center py-4">
<h1 className="text-sm text-muted-foreground">All requests.</h1>
</div>
<div className="rounded-md md:h-[600px]">
<Table className="overflow-x-scroll">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{isLoading ? (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
Loading...
</TableCell>
</TableRow>
) : table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<div className="flex-1 text-sm text-muted-foreground">
Page {table.getState().pagination.pageIndex + 1}
</div>
<div className="space-x-2">
{table.getState().pagination.pageIndex > 0 && (
<Button
variant="outline"
size="sm"
onClick={() => table.firstPage()}
>
First
</Button>
)}
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
</div>
</div>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Add error handling for data fetching.

The function is correctly implemented but can be improved by adding error handling for the data fetching process. Consider adding a try-catch block around the data fetching logic and displaying an error message if the data fetching fails.

Apply this diff to add error handling:

const { requests, isLoading, error } = useLatestRequests({
  first: pagination.pageSize,
  skip: pagination.pageIndex * pagination.pageSize,
});

return (
  <div className="w-[95%] bg-white border rounded-lg self-center md:w-full">
    <div className="p-10">
      <div>
        <h1 className="text-2xl font-bold">Requests</h1>
      </div>
      <div className="flex items-center py-4">
        <h1 className="text-sm text-muted-foreground">All requests.</h1>
      </div>
      <div className="rounded-md md:h-[600px]">
        <Table className="overflow-x-scroll">
          <TableHeader>
            {table.getHeaderGroups().map((headerGroup) => (
              <TableRow key={headerGroup.id}>
                {headerGroup.headers.map((header) => {
                  return (
                    <TableHead key={header.id}>
                      {header.isPlaceholder
                        ? null
                        : flexRender(
                            header.column.columnDef.header,
                            header.getContext(),
                          )}
                    </TableHead>
                  );
                })}
              </TableRow>
            ))}
          </TableHeader>
          <TableBody>
            {isLoading ? (
              <TableRow>
                <TableCell
                  colSpan={columns.length}
                  className="h-24 text-center"
                >
                  Loading...
                </TableCell>
              </TableRow>
            ) : error ? (
              <TableRow>
                <TableCell
                  colSpan={columns.length}
                  className="h-24 text-center"
                >
                  Error loading data.
                </TableCell>
              </TableRow>
            ) : table.getRowModel().rows?.length ? (
              table.getRowModel().rows.map((row) => (
                <TableRow
                  key={row.id}
                  data-state={row.getIsSelected() && 'selected'}
                >
                  {row.getVisibleCells().map((cell) => (
                    <TableCell key={cell.id}>
                      {flexRender(
                        cell.column.columnDef.cell,
                        cell.getContext(),
                      )}
                    </TableCell>
                  ))}
                </TableRow>
              ))
            ) : (
              <TableRow>
                <TableCell
                  colSpan={columns.length}
                  className="h-24 text-center"
                >
                  No results.
                </TableCell>
              </TableRow>
            )}
          </TableBody>
        </Table>
      </div>
      <div className="flex items-center justify-end space-x-2 py-4">
        <div className="flex-1 text-sm text-muted-foreground">
          Page {table.getState().pagination.pageIndex + 1}
        </div>
        <div className="space-x-2">
          {table.getState().pagination.pageIndex > 0 && (
            <Button
              variant="outline"
              size="sm"
              onClick={() => table.firstPage()}
            >
              First
            </Button>
          )}
          <Button
            variant="outline"
            size="sm"
            onClick={() => table.previousPage()}
            disabled={!table.getCanPreviousPage()}
          >
            Previous
          </Button>
          <Button
            variant="outline"
            size="sm"
            onClick={() => table.nextPage()}
            disabled={!table.getCanNextPage()}
          >
            Next
          </Button>
        </div>
      </div>
    </div>
  </div>
);
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 function RequestTable() {
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { requests, isLoading } = useLatestRequests({
first: pagination.pageSize,
skip: pagination.pageIndex * pagination.pageSize,
});
const table = useReactTable({
// Get only the first transaction for each request.
data: Object.values(requests).map((request) => request[0]),
columns,
pageCount: 10,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onPaginationChange: setPagination,
manualPagination: true,
state: {
pagination,
},
});
return (
<div className="w-[95%] bg-white border rounded-lg self-center md:w-full">
<div className="p-10">
<div>
<h1 className="text-2xl font-bold">Requests</h1>
</div>
<div className="flex items-center py-4">
<h1 className="text-sm text-muted-foreground">All requests.</h1>
</div>
<div className="rounded-md md:h-[600px]">
<Table className="overflow-x-scroll">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{isLoading ? (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
Loading...
</TableCell>
</TableRow>
) : table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<div className="flex-1 text-sm text-muted-foreground">
Page {table.getState().pagination.pageIndex + 1}
</div>
<div className="space-x-2">
{table.getState().pagination.pageIndex > 0 && (
<Button
variant="outline"
size="sm"
onClick={() => table.firstPage()}
>
First
</Button>
)}
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
</div>
</div>
);
export function RequestTable() {
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { requests, isLoading, error } = useLatestRequests({
first: pagination.pageSize,
skip: pagination.pageIndex * pagination.pageSize,
});
const table = useReactTable({
// Get only the first transaction for each request.
data: Object.values(requests).map((request) => request[0]),
columns,
pageCount: 10,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onPaginationChange: setPagination,
manualPagination: true,
state: {
pagination,
},
});
return (
<div className="w-[95%] bg-white border rounded-lg self-center md:w-full">
<div className="p-10">
<div>
<h1 className="text-2xl font-bold">Requests</h1>
</div>
<div className="flex items-center py-4">
<h1 className="text-sm text-muted-foreground">All requests.</h1>
</div>
<div className="rounded-md md:h-[600px]">
<Table className="overflow-x-scroll">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{isLoading ? (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
Loading...
</TableCell>
</TableRow>
) : error ? (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
Error loading data.
</TableCell>
</TableRow>
) : table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<div className="flex-1 text-sm text-muted-foreground">
Page {table.getState().pagination.pageIndex + 1}
</div>
<div className="space-x-2">
{table.getState().pagination.pageIndex > 0 && (
<Button
variant="outline"
size="sm"
onClick={() => table.firstPage()}
>
First
</Button>
)}
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
</div>
</div>
);


const inter = Inter({ subsets: ['latin'], variable: '--font-sans' });

export const metadata: Metadata = {
Copy link
Member

Choose a reason for hiding this comment

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

We should change the metadata to show Request Checkout

@rodrigopavezi rodrigopavezi merged commit 23ae369 into main Sep 2, 2024
@rodrigopavezi rodrigopavezi deleted the feat/requests-page branch September 2, 2024 12:45
@MantisClone MantisClone changed the title Feat/requests page feat: requests page Sep 3, 2024
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.

Request Scan: Requests Page

5 participants