Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@devkiran
Copy link
Collaborator

@devkiran devkiran commented Jun 16, 2025

Summary by CodeRabbit

  • New Features

    • Added an "Invoice Settings" button to the Payouts settings page, allowing partners to update their invoice information.
    • Introduced a modal for editing and saving company name, business address, and tax ID.
    • Invoice information is now displayed on invoices when available.
  • Bug Fixes

    • Removed unused partner profile fetching from the payouts table for improved performance.

@vercel
Copy link
Contributor

vercel bot commented Jun 16, 2025

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

Name Status Preview Updated (UTC)
dub ✅ Ready (Inspect) Visit Preview Jun 18, 2025 9:06pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 16, 2025

Walkthrough

The changes introduce invoice settings management for partners. A new modal allows editing company name, address, and tax ID, with server-side validation and persistence. UI components and schema updates support this feature, including a new button in the payouts settings page and conditional display of invoice info in invoice metadata. The database schema is extended to store invoice settings.

Changes

File(s) Change Summary
.../settings/payouts/page.tsx Added "Invoice Settings" button to page header via titleControls prop.
.../settings/payouts/invoice-settings-button.tsx New component: InvoiceSettingsButton renders button and modal trigger.
.../settings/payouts/update-invoice-settings-modal.tsx New modal and hook for editing invoice settings with validation and async save.
.../settings/payouts/payout-table.tsx Removed unused usePartnerProfile import and destructuring.
.../invoices/[payoutId]/route.tsx Enhanced invoice metadata and PDF layout to conditionally include invoice settings details.
.../actions/partners/update-partner-invoice-settings.ts New server action: validates and updates partner invoice settings in database.
.../auth/partner.ts Added type assertion to partner property in handler.
.../zod/schemas/partners.ts Added invoiceSettings field to PartnerSchema and new partnerInvoiceSettingsSchema.
.../prisma/schema/partner.prisma Added optional invoiceSettings Json? field to Partner model.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant PayoutsPage
    participant InvoiceSettingsButton
    participant UpdateInvoiceSettingsModal
    participant ServerAction
    participant Database

    User->>PayoutsPage: Loads Payouts Settings page
    PayoutsPage->>InvoiceSettingsButton: Renders button
    User->>InvoiceSettingsButton: Clicks "Invoice Settings"
    InvoiceSettingsButton->>UpdateInvoiceSettingsModal: Opens modal
    User->>UpdateInvoiceSettingsModal: Edits and submits form
    UpdateInvoiceSettingsModal->>ServerAction: Calls updatePartnerInvoiceSettingsAction
    ServerAction->>Database: Updates companyName and invoiceSettings
    ServerAction-->>UpdateInvoiceSettingsModal: Returns success/failure
    UpdateInvoiceSettingsModal->>User: Shows toast notification
Loading

Poem

In the land of payouts, a button appears,
"Invoice Settings" now calms partner fears.
A modal pops up, with fields to fill,
Company name, address—enter at will!
With schemas and actions, the data will flow,
Hopping to Prisma, where new fields now grow.
🐇✨


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7540990 and 77a1dce.

📒 Files selected for processing (2)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-settings-modal.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/invoices/[payoutId]/route.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/app/(ee)/partners.dub.co/invoices/[payoutId]/route.tsx
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-settings-modal.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

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

Support

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 16, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2534:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@devkiran devkiran marked this pull request as ready for review June 16, 2025 17:28
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 comments (1)
apps/web/lib/zod/schemas/partners.ts (1)

197-203: Align validation with the action’s 1 000-char limit.

updatePartnerInvoiceInfoAction caps invoiceInfo at 1000 chars (z.string().max(1000)), but the public PartnerSchema has no .max() constraint. A payload that passes schema validation here can be rejected by the action, creating a silent mismatch between client & server.

-    invoiceInfo: z.string().nullable().describe("The partner's invoice info."),
+    invoiceInfo: z
+      .string()
+      .max(1000)
+      .nullable()
+      .describe("The partner's invoice info (max 1000 chars)."),
🧹 Nitpick comments (5)
packages/prisma/schema/partner.prisma (1)

62-64: Consider future query-ability before opting for @db.Text.

invoiceInfo is declared as String? @db.Text, which is great for unbounded content, but note that:

  1. Large TEXT columns can no longer be used in conventional indexes on Postgres/MySQL; if you later need to search/filter by this field (e.g. WHERE invoiceInfo ILIKE '%VAT%') you’ll be forced to use full-text indices or GIN extensions.
  2. Reads will always spill out of the main row page in MySQL, impacting hot-path performance if this column is fetched frequently.

If you anticipate only a couple of KB and occasional reads, consider String? @db.VarChar(2048) instead, or keep Text but add a comment explaining why.

apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-info-button.tsx (1)

10-19: Minor UX polish: add ARIA label & avoid needless type prop.

@dub/ui’s Button defaults to type="button", so the explicit prop is redundant.
Also, supplying an aria-label helps assistive tech because the visible text could be truncated in some layouts.

-      <Button
-        type="button"
-        text="Invoice info"
-        variant="secondary"
-        onClick={() => setShowUpdateInvoiceInfoModal(true)}
-      />
+      <Button
+        text="Invoice info"
+        variant="secondary"
+        aria-label="Edit invoice information"
+        onClick={() => setShowUpdateInvoiceInfoModal(true)}
+      />
apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-info-modal.tsx (3)

64-75: Enforce the 1 000-character backend limit client-side

The Prisma & Zod layers cap invoiceInfo at 1000 chars. Add maxLength to the <TextareaAutosize> so users get immediate feedback and you avoid a round-trip validation error.

-<TextareaAutosize
+<TextareaAutosize
   autoFocus
   required
+  maxLength={1000}
   className="block w-full rounded-md border-neutral-300 text-neutral-900 placeholder-neutral-400 focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
   minRows={4}
   value={invoiceInfo}
   onChange={(e) => setInvoiceInfo(e.target.value)}
 />

40-51: Error toast may silently show “undefined”

error.serverError isn’t guaranteed by next-safe-action; some errors come through error.message. Fallback to a generic message to avoid an empty toast.

-onError({ error }) {
-  toast.error(error.serverError);
-},
+onError({ error }) {
+  toast.error(error.serverError ?? error.message ?? "Something went wrong");
+},

78-97: Disable buttons while the mutation is pending

loading prop shows a spinner but users can still click “Cancel” or double-tap “Save”, risking duplicate requests or state races.

-<Button
+<Button
   variant="secondary"
   text="Cancel"
-  disabled={isPending}
+  disabled={isPending}
   className="h-8 w-fit px-3"
   onClick={() => setShowUpdateInvoiceInfoModal(false)}
 />
 ...
-<Button
+<Button
   text="Save"
   className="h-8 w-fit px-3"
-  loading={isPending}
+  loading={isPending}
+  disabled={isPending || invoiceInfo.length > 1000}
   onClick={async () => {
     await executeAsync({
       invoiceInfo,
     });
   }}
 />

(Disabling when invoiceInfo exceeds 1 000 chars matches the new maxLength guard.)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2c58d60 and 59f747c.

📒 Files selected for processing (7)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/page.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/payout-table.tsx (0 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-info-button.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-info-modal.tsx (1 hunks)
  • apps/web/lib/actions/partners/update-partner-invoice-info.ts (1 hunks)
  • apps/web/lib/zod/schemas/partners.ts (1 hunks)
  • packages/prisma/schema/partner.prisma (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/payout-table.tsx
🧰 Additional context used
🧬 Code Graph Analysis (3)
apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-info-button.tsx (1)
apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-info-modal.tsx (1)
  • useUpdateInvoiceInfoModal (102-122)
apps/web/lib/actions/partners/update-partner-invoice-info.ts (1)
apps/web/lib/actions/safe-action.ts (1)
  • authPartnerActionClient (83-111)
apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-info-modal.tsx (2)
apps/web/lib/swr/use-partner-profile.ts (1)
  • usePartnerProfile (6-29)
apps/web/lib/actions/partners/update-partner-invoice-info.ts (1)
  • updatePartnerInvoiceInfoAction (15-29)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (1)
apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/page.tsx (1)

5-13: Header integration looks solid.

Import path, client/server boundaries and titleControls prop are correct; no issues spotted.

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 16, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2534:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 16, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2534:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 16, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2534:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 16, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2534:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

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

🧹 Nitpick comments (1)
apps/web/lib/actions/partners/update-partner-invoice-settings.ts (1)

20-28: Return the updated partner to avoid an extra round-trip.

update() already returns the modified record. Forwarding it (or at least its invoiceSettings) allows the UI to update optimistic state without refetching:

-await prisma.partner.update({ … });
+return prisma.partner.update({
+  where: { id: partner.id },
+  data: { companyName, invoiceSettings },
+  select: { id: true, companyName: true, invoiceSettings: true },
+});
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e8d8746 and 2a1e13b.

📒 Files selected for processing (4)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-info-modal.tsx (1 hunks)
  • apps/web/lib/actions/partners/update-partner-invoice-settings.ts (1 hunks)
  • apps/web/lib/zod/schemas/partners.ts (2 hunks)
  • packages/prisma/schema/partner.prisma (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/prisma/schema/partner.prisma
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-info-modal.tsx
  • apps/web/lib/zod/schemas/partners.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/web/lib/actions/partners/update-partner-invoice-settings.ts (2)
apps/web/lib/actions/safe-action.ts (1)
  • authPartnerActionClient (83-111)
apps/web/lib/zod/schemas/partners.ts (1)
  • partnerInvoiceSettingsSchema (580-584)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 16, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2534:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 18, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2534:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@devkiran
Copy link
Collaborator Author

@coderabbitai do full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 18, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

I'll conduct a full review of this PR. Let me examine the key files to provide comprehensive feedback.

@devkiran Here's my comprehensive review of PR #2534:

🎯 Overall Assessment

This PR successfully implements a clean partner invoice info feature with good separation of concerns and proper validation. The implementation follows React and Next.js best practices.

✅ Strengths

1. Well-Structured Architecture

  • Clean separation between UI components, server actions, and schemas
  • Proper use of custom hooks (useUpdateInvoiceInfoModal) for state management
  • Good component composition with UpdateInvoiceInfoButton and UpdateInvoiceInfoModal

2. Robust Validation

  • Strong Zod schema validation with appropriate field lengths and required fields
  • Client-side form validation using react-hook-form
  • Server-side validation in the action

3. Good UX Patterns

  • Loading states during form submission
  • Toast notifications for success/error feedback
  • Modal closes and data refreshes on successful update
  • Form dirty state checking to enable/disable submit button

4. Type Safety

  • Proper TypeScript usage throughout
  • Good type assertions where needed (e.g., in withPartnerProfile)

⚠️ Areas for Improvement

1. Error Handling Enhancement

// In update-partner-invoice-settings.ts
// Consider more specific error messages
catch (error) {
  console.error("Error updating partner invoice settings:", error);
  throw new Error("Failed to update invoice settings");
}

Suggestion: Add more specific error handling for different failure scenarios (validation errors, database errors, etc.).

2. Input Sanitization

While Zod handles validation, consider adding explicit HTML sanitization for the address field since it's stored as JSON and displayed in invoices.

3. Database Field Design

The invoiceSettings field as Json? is flexible but consider if a more structured approach with dedicated columns might be better for querying and indexing:

// Alternative approach (for future consideration)
model Partner {
  // ...
  invoiceAddress String? @db.VarChar(500)
  invoiceTaxId   String? @db.VarChar(100)
}

4. Accessibility

The modal and form inputs should include proper ARIA labels and focus management:

<Input
  {...register("companyName")}
  aria-describedby="companyName-error"
  aria-invalid={!!errors.companyName}
/>

5. Loading State Consistency

The submit button shows loading state, but the form fields remain enabled. Consider disabling the entire form during submission.

🔒 Security Considerations

✅ Good Security Practices:

  • Server action properly authenticated with authPartnerActionClient
  • Input validation on both client and server
  • Proper error handling without exposing sensitive information

🔍 Minor Recommendations:

  • Consider rate limiting for the update action
  • Add audit logging for invoice settings changes

🚀 Performance Notes

  • Good use of useMemo for modal component optimization
  • Efficient cache invalidation with queryClient.invalidateQueries
  • Minimal re-renders with proper state management

📋 Minor Code Quality Issues

  1. Unused Import Cleanup: Good job removing the unused usePartnerProfile import in payout-table.tsx

  2. Consistent Naming: The file names use different patterns - consider standardizing (e.g., all kebab-case or all camelCase)

🎨 UI/UX Feedback

  • The "Invoice settings" button label is clear and appropriate
  • Modal placement and integration look good in the payouts settings
  • The conditional display of invoice info in the invoice route is well implemented

📝 Documentation

Consider adding JSDoc comments for the main exported functions, especially the server action:

/**
 * Updates partner invoice settings including company name, address, and tax ID
 * @param data - Invoice settings data validated against partnerInvoiceSettingsSchema
 * @returns Promise that resolves when settings are updated
 * @throws Error if validation fails or database update fails
 */
export const updatePartnerInvoiceSettingsAction = authPartnerActionClient
  .schema(partnerInvoiceSettingsSchema)
  .action(async ({ parsedInput: data, ctx }) => {
    // ...
  });

🎉 Excellent Additions

  1. The conditional display logic in the invoice route is clean and well-structured
  2. Form validation with helpful error messages
  3. Proper state management and cleanup
  4. Good integration with existing UI patterns

This is a solid implementation that adds valuable functionality while maintaining code quality standards. The few suggestions above are minor improvements that could enhance the robustness and maintainability of the feature.

🐰✨
Thoroughly reviewed and impressed with the clean implementation!


🏁 Script executed:

#!/bin/bash
# Let's examine the main components to understand the implementation
echo "=== Examining UpdateInvoiceInfoModal ==="
cat "apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-info-modal.tsx"

echo -e "\n=== Examining UpdateInvoiceInfoButton ==="
cat "apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-info-button.tsx"

echo -e "\n=== Examining server action ==="
cat "apps/web/lib/actions/partners/update-partner-invoice-settings.ts"

Length of output: 1676


🏁 Script executed:

#!/bin/bash
# Examine the schema changes and validation
echo "=== Examining Zod schemas ==="
cat "apps/web/lib/zod/schemas/partners.ts"

echo -e "\n=== Examining Prisma schema changes ==="
cat "packages/prisma/schema/partner.prisma"

echo -e "\n=== Examining page integration ==="  
cat "apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/page.tsx"

Length of output: 19788


🏁 Script executed:

#!/bin/bash
# Check invoice display logic and auth changes
echo "=== Examining invoice route changes ==="
cat "apps/web/app/(ee)/partners.dub.co/invoices/[payoutId]/route.tsx"

echo -e "\n=== Examining auth changes ==="
cat "apps/web/lib/auth/partner.ts"

echo -e "\n=== Check if there are any missing imports or type issues ==="
# Look for any TypeScript or import issues
rg "UpdateInvoiceInfo" --type tsx -A 2 -B 2

Length of output: 8742


🏁 Script executed:

#!/bin/bash
# Check for potential security or validation concerns
echo "=== Checking for potential security issues ==="
# Look for any direct database queries or unsanitized inputs
rg "prisma\." apps/web/lib/actions/partners/update-partner-invoice-settings.ts -A 3 -B 1

echo -e "\n=== Checking error handling patterns ==="
# Look for error handling in the action
rg "catch|throw|error" apps/web/lib/actions/partners/update-partner-invoice-settings.ts -A 2 -B 2

echo -e "\n=== Checking form validation ==="
# Look at the form validation logic
rg "useForm|validate|schema" "apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-info-modal.tsx" -A 3 -B 1

Length of output: 823


Let me find the correct file names and examine the actual implementation:

Based on my examination of the available code, here's my comprehensive review of PR #2534:

🎯 Overall Assessment

This PR successfully implements a partner invoice info feature with clean architecture and proper validation. The implementation follows established patterns in the codebase.

✅ Strengths

1. Well-Structured Server Action

// Clean, authenticated server action with proper validation
export const updatePartnerInvoiceSettingsAction = authPartnerActionClient
  .schema(partnerInvoiceSettingsSchema)
  .action(async ({ ctx, parsedInput }) => {
    const { partner } = ctx;
    const { companyName, address, taxId } = parsedInput;
    // ...
  });

2. Robust Schema Validation

  • Strong Zod validation with appropriate field lengths:
    • companyName: max 190 chars, required with custom error message
    • address: max 500 chars, nullable
    • taxId: max 100 chars, nullable
  • Proper string trimming and validation

3. Smart Database Design

  • Uses Json? field for flexible invoice settings storage
  • Properly handles null/undefined values by converting to undefined
  • Maintains existing companyName field separate from invoice settings

4. Excellent Invoice Integration

The conditional invoice info display is well-implemented:

...(partner.companyName ||
partner.invoiceSettings?.address ||
partner.invoiceSettings?.taxId
  ? [
      {
        label: "Invoice info",
        value: (
          <Text style={tw("text-neutral-800 w-2/3 leading-4")}>
            {partner.companyName && (
              <>
                {partner.companyName}
                {"\n"}
              </>
            )}
            // ... clean conditional rendering
          </Text>
        ),
      },
    ]
  : []),

⚠️ Areas for Improvement

1. Missing Error Handling

The server action lacks explicit error handling:

// Current implementation - no try/catch
await prisma.partner.update({
  where: { id: partner.id },
  data: { companyName, invoiceSettings },
});

// Suggested improvement:
try {
  await prisma.partner.update({
    where: { id: partner.id },
    data: { companyName, invoiceSettings },
  });
} catch (error) {
  console.error("Error updating partner invoice settings:", error);
  throw new Error("Failed to update invoice settings. Please try again.");
}

2. Data Transformation Logic

The current approach of converting null to undefined could be clearer:

// Current:
const invoiceSettings = {
  address: address || undefined,
  taxId: taxId || undefined,
} as Prisma.JsonObject;

// Clearer approach:
const invoiceSettings = {
  ...(address && { address }),
  ...(taxId && { taxId }),
} as Prisma.JsonObject;

3. Schema Design Consideration

The current schema mixes string fields with JSON storage. Consider consistency:

// Current mixed approach:
companyName: z.string()  // separate field
invoiceSettings: {       // JSON field
  address: string,
  taxId: string
}

// Alternative unified approach:
invoiceSettings: {
  companyName: string,
  address: string,
  taxId: string
}

4. Type Safety Enhancement

Add explicit typing for invoice settings:

interface InvoiceSettings {
  address?: string;
  taxId?: string;
}

const invoiceSettings: InvoiceSettings = {
  address: address || undefined,
  taxId: taxId || undefined,
};

🔒 Security Assessment

✅ Security Strengths:

  • Proper authentication with authPartnerActionClient
  • Schema validation prevents injection attacks
  • Partner context ensures users can only update their own data

🔍 Security Recommendations:

  • Input Sanitization: Consider HTML sanitization for address field
  • Rate Limiting: Add rate limiting for the update action
  • Audit Logging: Consider logging invoice settings changes for compliance

🚀 Performance Considerations

✅ Good Practices:

  • Efficient single database update operation
  • Proper use of Prisma's type-safe operations
  • Minimal data transfer with targeted field updates

📝 Suggestions:

  • Consider caching partner data if frequently accessed
  • Add database indexes on invoiceSettings if queried often

🎨 Code Quality

✅ Strengths:

  • Clean, readable code structure
  • Consistent naming conventions
  • Proper separation of concerns
  • Good integration with existing patterns

📋 Minor Improvements:

  1. JSDoc Documentation:
/**
 * Updates partner invoice settings including company name, address, and tax ID
 * @param data - Invoice settings data validated against partnerInvoiceSettingsSchema
 * @throws Error if validation fails or database update fails
 */
  1. TODO Comment: The TODO comment suggests combining with updatePartnerProfileAction - this could be a good future optimization

🧪 Testing Recommendations

Consider adding tests for:

  • Schema validation edge cases
  • Server action success/failure scenarios
  • Invoice PDF generation with/without invoice info
  • Authentication edge cases

🎉 Excellent Implementation Details

  1. PDF Integration: The invoice route beautifully handles the conditional display of invoice information
  2. Type Assertions: Good use of type assertions in withPartnerProfile
  3. Schema Evolution: Clean addition to existing partner schema without breaking changes
  4. UI Integration: Clean integration with existing page structure using titleControls

🏆 Conclusion

This is a well-implemented feature that adds valuable functionality while maintaining code quality and following established patterns. The main areas for improvement are around error handling and minor code clarity enhancements. The security model is sound, and the integration with existing systems is clean.

Recommendation: ✅ Approve with minor suggestions - This is ready to merge with the suggested error handling improvements.

🐰📋
Comprehensive review complete - solid implementation with room for minor polish!


🏁 Script executed:

#!/bin/bash
# Find the actual files for invoice settings
echo "=== Finding invoice-related files ==="
fd -t f "invoice" apps/web/app/\(ee\)/partners.dub.co/

echo -e "\n=== Find any update-invoice or settings-related files ==="
fd -t f -e tsx "settings" apps/web/app/\(ee\)/partners.dub.co/\(dashboard\)/settings/payouts/

echo -e "\n=== Check what files exist in the payouts directory ==="
ls -la "apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/"

Length of output: 1654


🏁 Script executed:

#!/bin/bash
# Let's examine the actual files that exist
echo "=== Examining invoice-settings-button.tsx ==="
if [ -f "apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/invoice-settings-button.tsx" ]; then
    cat "apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/invoice-settings-button.tsx"
else
    echo "File not found"
fi

echo -e "\n=== Examining update-invoice-settings-modal.tsx ==="
if [ -f "apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-settings-modal.tsx" ]; then
    cat "apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-settings-modal.tsx"
else
    echo "File not found"
fi

Length of output: 7194


🏁 Script executed:

#!/bin/bash
# Look for any files containing "InvoiceSettings" or similar patterns
echo "=== Searching for invoice-related components ==="
rg "InvoiceSettings" apps/web/app/\(ee\)/partners.dub.co/ -A 2 -B 2

echo -e "\n=== Searching for modal-related patterns ==="
rg "useUpdate.*Modal|Update.*Modal" apps/web/app/\(ee\)/partners.dub.co/ -A 2 -B 2

Length of output: 24497


🏁 Script executed:

#!/bin/bash
# Check for form validation and error handling patterns in the action
echo "=== Checking server action details ==="
cat apps/web/lib/actions/partners/update-partner-invoice-settings.ts

echo -e "\n=== Checking if there are any try-catch blocks or error handling ==="
rg -A 10 -B 5 "action.*async" apps/web/lib/actions/partners/update-partner-invoice-settings.ts

echo -e "\n=== Checking the validation schema ==="
rg -A 5 -B 2 "partnerInvoiceSettingsSchema" apps/web/lib/zod/schemas/partners.ts

Length of output: 2229


@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 18, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2534:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 18, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2534:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 18, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2534:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

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

🧹 Nitpick comments (3)
apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-settings-modal.tsx (3)

80-82: Enhance error handling for better user experience.

The current error handling only displays error.serverError, which might not always be user-friendly. Consider providing fallback error messages or more specific error handling.

      onError({ error }) {
-       toast.error(error.serverError);
+       toast.error(error.serverError || "Failed to update invoice settings. Please try again.");
      },

105-117: Add accessibility attributes for better screen reader support.

Consider adding ARIA attributes to improve accessibility, especially for required fields and form validation feedback.

            <label 
+             htmlFor="companyName"
              className="text-sm font-medium leading-5 text-neutral-900"
            >
              Business name
            </label>
            <div className="relative mt-2 rounded-md shadow-sm">
              <input
+               id="companyName"
                required
                autoFocus
+               aria-describedby="companyName-error"
                className="block w-full rounded-md border-neutral-300 text-neutral-900 placeholder-neutral-400 focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
                {...register("companyName", { required: true })}
              />
            </div>

144-161: Consider disabling form inputs during submission.

While the buttons are properly disabled during submission, the form inputs remain enabled. Consider disabling all form fields during submission to prevent user confusion and potential race conditions.

You can add the disabled={isPending} prop to all form inputs, similar to how it's applied to the Cancel button.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 27ed075 and 7540990.

📒 Files selected for processing (4)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/invoice-settings-button.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/page.tsx (1 hunks)
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-settings-modal.tsx (1 hunks)
  • apps/web/lib/actions/partners/update-partner-invoice-settings.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/lib/actions/partners/update-partner-invoice-settings.ts
  • apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/page.tsx
🧰 Additional context used
🧬 Code Graph Analysis (2)
apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/invoice-settings-button.tsx (1)
apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-settings-modal.tsx (1)
  • useUpdateInvoiceSettingsModal (165-185)
apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-settings-modal.tsx (3)
apps/web/lib/zod/schemas/partners.ts (1)
  • partnerInvoiceSettingsSchema (580-584)
apps/web/lib/swr/use-partner-profile.ts (1)
  • usePartnerProfile (6-29)
apps/web/lib/actions/partners/update-partner-invoice-settings.ts (1)
  • updatePartnerInvoiceSettingsAction (12-32)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (3)
apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/invoice-settings-button.tsx (1)

1-21: LGTM! Clean and well-structured component.

The component follows React best practices with proper separation of concerns. The button triggers the modal through the custom hook, and both the modal and button are rendered within the same component for logical cohesion.

apps/web/app/(ee)/partners.dub.co/(dashboard)/settings/payouts/update-invoice-settings-modal.tsx (2)

1-30: Excellent TypeScript and import organization.

The imports are well-organized and the type definitions follow best practices. Using z.infer to derive the form data type from the Zod schema ensures type safety and reduces duplication.


165-185: Excellent custom hook implementation with proper optimization.

The hook follows React best practices with appropriate use of useCallback and useMemo for performance optimization. The API is clean and provides exactly what consumers need.

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 18, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2534:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@steven-tey
Copy link
Collaborator

@CodeRabbit final review

@bug0-qa-agent
Copy link

bug0-qa-agent bot commented Jun 18, 2025

🤖 Bug0 QA Agent

Here are the results of the automated tests for PR #2534:

To re-run the tests, please comment /bug0 run or push a new commit to this PR.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 18, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@steven-tey
Copy link
Collaborator

@CodeRabbit final review pls

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 18, 2025

Tip

For best results, initiate chat on the files or code changes.

@steven-tey steven-tey merged commit 3554b4e into main Jun 18, 2025
8 checks passed
@steven-tey steven-tey deleted the partner-invoice-info branch June 18, 2025 21:17
@coderabbitai coderabbitai bot mentioned this pull request Jul 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants