Skip to content

feat(DEV-15658): Payables remove automatic calculation of totals and allow manual edit#822

Open
dinis-monite wants to merge 21 commits into
mainfrom
feat/DEV-15658_payables-setting-edit-totals
Open

feat(DEV-15658): Payables remove automatic calculation of totals and allow manual edit#822
dinis-monite wants to merge 21 commits into
mainfrom
feat/DEV-15658_payables-setting-edit-totals

Conversation

@dinis-monite
Copy link
Copy Markdown
Collaborator

@dinis-monite dinis-monite commented Sep 8, 2025

Scope

Added a new disableAutoCalculateTotals flag in Payables componentSettings. When set to true, this allows users to manually edit the totals values of a Payable; no automatic calculation will be performed on the client side.

Jira ticket: https://monite.atlassian.net/browse/DEV-15658

Implementation

  • Added disableAutoCalculateTotals seting to Payables componentSettings.
  • Added subtotal, tax_amount, and total_amount fields to PayableDetailsFormFields interface.
  • Added Alert in PayableDetailsForm if values of Line Items or Totals have been changed.
  • Improved dirty fields detection on PayableDetailsForm to use it to display Alert for changed values and also to avoid unnecessary updates of line items if not changed.
  • Refactored PayableDetailsForm to support manual total calculations conditionally on disableAutoCalculateTotals flag.
  • Changed helper functions to conditionally prepare Payable default values or submit payload according to isCalculateTotalsEnabled flag.
  • Replaced Controller TextField with RHFTextField for improved input number handling.

Also:

  • Extracted PayableLineItems and Totals from PayableDetailsInfo into reusable components.

Remarks

  • The value of isDisableAutoCalculateTotals is hardcoded as true to simulate the setting of componentSettings with disableAutoCalculateTotals=true. This will be removed before merging.

  • The newly extracted PayableLineItems and Totals reusable components were used in a previous working version of this branch, but are no longer necessary. Decided to keep them, as the code separation can facilitate eventual refactors. I'm ok to revert this if you don't agree.

Steps to test

For testing purposes, it is simulated the setting of componentSettings with disableAutoCalculateTotals=true.

Change your partner settings to have payable.enable_line_items: false (that will be the new default). With the partner setting payable.enable_line_items: true, the saving of the payable should fail with API message.

Creating new payable, from manual input

  1. Create a new Payable.
  2. Add some line items.
  3. Totals should be manually edited, without any automatic calculation.
  4. Save the Payable, should save with success with the inputed values.

Editing Payable (either OCR or not)

  1. Open a Payable.
  2. Only change the totals values.
  3. Should see an alert about changed values.
  4. Before submit open browser devtools and check network tab that line items data is not changed.
  5. Save the Payable, should save with success with the inputed values.

And again

  1. Open a Payable that was manually created (without OCR).
  2. Only change the totals values.
  3. Should see an alert about changed values.
  4. Revert the change in totals values.
  5. Only change the line items values.
  6. Should see an alert about changed values.
  7. Before submit open browser devtools and check network tab that line items data is changed.
  8. Save the Payable, should save with success with the inputed values.

Summary by CodeRabbit

  • New Features

    • Toggle to disable automatic total calculation in Payables, allowing manual edit of subtotal, tax, and total with an in-form warning when values change.
    • Read-only components for payable line items and totals.
    • Expanded invoice experience: multi-tab details, recurrence & scheduled invoices, reminders, entity profile workflow, and enhanced invoice actions (modals, PDF download/view).
  • Refactor

    • Items and totals rendering delegated to reusable display components.
  • Chores

    • Minor SDK version bump and new theme/setting to control payables behavior.

@dinis-monite dinis-monite added pullpreview Generate a live preview for this pull request do-not-merge labels Sep 8, 2025
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Sep 8, 2025

🦋 Changeset detected

Latest commit: fb64a56

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@monite/sdk-react Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 8, 2025

Walkthrough

Adds a payables feature flag disableAutoCalculateTotals plus settings/theme plumbing, extends form types/validation/helpers to support editable subtotal/tax/total, updates PayableDetailsForm UI for manual totals (editable fields, change alerts), and introduces read-only display components for line items and totals.

Changes

Cohort / File(s) Summary of Changes
Release & settings flag
/.changeset/shaggy-eels-pretend.md
Release note and minor version bump for @monite/sdk-react announcing disableAutoCalculateTotals.
Component settings & theme props
/packages/sdk-react/src/core/componentSettings/index.ts, /packages/sdk-react/src/components/payables/hooks/usePayableDetailsThemeProps.ts
Adds disableAutoCalculateTotals?: boolean to PayableSettings and exposes it via usePayableDetailsThemeProps (derived from props or componentSettings, defaults to false).
Payable details form core
/packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
Introduces disableAutoCalculateTotals gating for edit paths, changes editability of line items/totals, computes value-change flags and shows an in-form alert when values differ, forwards calc flag to defaults/submit, and renders editable RHFTextField fields for totals/discount when manual mode enabled.
Form helpers & submit payload
/packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts
Extends prepareDefaultValues and prepareSubmit with isCalculateTotalsEnabled?; when disabled, defaults force totals to 0 and submit includes subtotal, tax_amount, total_amount (formatted to minor units); adds disableAutoCalculateTotals to MonitePayableDetailsInfoProps.
Form types
/packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/types.ts
Adds optional `subtotal?: number
Validation schema
/packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/validation.ts
Adds nullable/optional non-negative validation rules for subtotal, tax_amount, and total_amount; refactors several zod chains/enum constructions (formatting-only otherwise).
Read-only display components
/packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableLineItems.tsx, /packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableTotals.tsx
Adds DisplayPayableLineItems and DisplayPayableTotals components to render read-only line items and totals with currency formatting and i18n; both exported.
Integration & exports
/packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/PayableDetailsInfo.tsx, /packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/index.tsx
Replaces inline items/totals rendering with the new display components and re-exports them from the module index.
Line items form inputs
/packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx
Replaces Controller+TextField for quantity/price/VAT with RHFTextField wrappers, preserving behavior and currency adornments.

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly and accurately summarizes the primary change — adding a setting to disable automatic totals so Payables totals can be edited manually — and includes the ticket reference (DEV-15658). It is specific, related to the changeset, and clear enough for a reviewer scanning history.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/DEV-15658_payables-setting-edit-totals

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Sep 8, 2025

🚀 Preview URLs are now available! 🚀

Copy link
Copy Markdown
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: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/sdk-react/src/core/componentSettings/index.ts (1)

319-466: Remove temporary isDisableAutoCalculateTotals logic
Occurrences of the hard-coded isDisableAutoCalculateTotals remain in packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (lines 189–192, 200, 202, 387) and must be reverted or removed.

packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx (1)

17-21: Avoid RHF key collision with reserved id

useFieldArray reserves item.id; your data model also uses id. Set keyName to prevent collisions and use the custom key for React keys.

-  const { fields, append, remove } = useFieldArray({
-    control,
-    name: 'lineItems',
-  });
+  const { fields, append, remove } = useFieldArray({
+    control,
+    name: 'lineItems',
+    keyName: '_key',
+  });
...
-      {fields.map((item, index) => (
+      {fields.map((item, index) => (
         <Grid
           item
           container
           gap={2}
-          key={item.id}
+          key={item._key}

Also applies to: 26-36

packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts (1)

49-57: Critical: helpers default to manual-totals mode when flag is omitted (breaks back-compat).

isCalculateTotalsEnabled is optional, so undefined is treated as falsy and triggers the manual-totals path by default. This changes existing behavior: new/edit forms will initialize totals as user-editable (zeros) and include totals in submit payloads unless the caller explicitly opts in. Set a safe default of true.

Apply this diff:

 export const prepareDefaultValues = (
   formatFromMinorUnits: (
     amount: number,
     currency: CurrencyEnum | string
   ) => number | null,
   payable?: components['schemas']['PayableResponseSchema'],
   lineItems?: components['schemas']['LineItemResponse'][],
-  isCalculateTotalsEnabled?: boolean
+  isCalculateTotalsEnabled: boolean = true
 ): PayableDetailsFormFields => {
@@
 export const prepareSubmit = (
   {
     invoiceNumber,
@@
     total_amount,
   }: SubmitPayload,
-  formatToMinorUnits: (amount: number, currency: string) => number | null,
-  isCalculateTotalsEnabled?: boolean
+  formatToMinorUnits: (amount: number, currency: string) => number | null,
+  isCalculateTotalsEnabled: boolean = true
 ): components['schemas']['PayableUpdateSchema'] => {

Also applies to: 159-161, 79-87, 128-141, 175-184

🧹 Nitpick comments (14)
.changeset/shaggy-eels-pretend.md (1)

5-5: Expand the changeset with public API notes and ops caveats

  • Mention that PayableDetailsInfo now publicly re-exports DisplayPayableLineItems and DisplayPayableTotals (minor surface growth).
  • Add a brief note on required partner settings interplay (payable.enable_line_items vs manual totals) and units (totals in minor units on API, major units in form), to reduce integration surprises.
packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx (2)

96-101: Use InputAdornment for endAdornment and reuse currentCurrency

Minor MUI correctness/readability: use InputAdornment and the already watched currentCurrency.

+import InputAdornment from '@mui/material/InputAdornment';
...
-                InputProps={{
-                  endAdornment: getSymbolFromCurrency(watch('currency')),
-                }}
+                InputProps={{
+                  endAdornment: (
+                    <InputAdornment position="end">
+                      {getSymbolFromCurrency(currentCurrency) ?? ''}
+                    </InputAdornment>
+                  ),
+                }}
-                InputProps={{
-                  endAdornment: '%',
-                }}
+                InputProps={{
+                  endAdornment: <InputAdornment position="end">%</InputAdornment>,
+                }}

Also applies to: 102-112


71-77: Improve a11y label for delete action

Make the intent explicit for screen readers.

-              <IconButton
-                aria-label="delete"
+              <IconButton
+                aria-label={t(i18n)`Delete line item ${index + 1}`}
                 onClick={() => remove(index)}
                 disabled={fields.length === 1}
               >
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/types.ts (1)

20-23: Document major/minor unit conversions and consider camelCase

  • Add JSDoc to PayableDetailsFormFields (subtotal, tax_amount, total_amount) clarifying these are main-currency (major-unit) values, converted to minor units in prepareSubmit and back in prepareDefaultValues.
  • prepareDefaultValues uses formatFromMinorUnits(...) and prepareSubmit uses formatToMinorUnits(...), so unit conversions are already correctly applied.
  • Optional: rename tax_amount/total_amount to taxAmount/totalAmount in the form interface and map to API snake_case in the helpers for consistent field naming.
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableLineItems.tsx (2)

33-34: Header wording mismatch with content

Header says “Total, tax” but body shows subtotal “excl. Tax”. Make the header explicit.

-            <TableCell align="right">{t(i18n)`Total, tax`}</TableCell>
+            <TableCell align="right">{t(i18n)`Total (excl. tax)`}</TableCell>

52-55: Styling: replace MUI sx with Tailwind or theme tokens (post-migration)

When migrating to shadcn/ui, replace sx with Tailwind (e.g., mtw-text-muted-foreground).

packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableTotals.tsx (1)

34-41: Display discount row for zero or negative values
Replace the truthy check with an explicit null check so that a discount of 0 or any negative value still renders the row:

-          {payable.discount && payable.discount > 0 ? (
+          {payable.discount != null ? (
             <TableRow>
               <TableCell>{t(i18n)`Discount`}</TableCell>
               <TableCell align="right">
                 {formatCurrencyToDisplay(payable.discount, currency)}
               </TableCell>
             </TableRow>
           ) : null}
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (2)

323-348: Consider simplifying the dirty field detection logic.

The current implementation correctly identifies changes in line items and totals. However, the nested logic could be simplified for better readability.

Consider this more concise approach:

-    // Check if Line Items values have changed
-    const areLineItemsValuesChanged = (() => {
-      if (!dirtyFields.lineItems) return false;
-      const hasLengthChanged =
-        dirtyFields.lineItems.length !== (lineItems?.length || 0);
-      const hasRelevantFieldsChanged = dirtyFields.lineItems.some(
-        (lineItemDirtyFields) => {
-          if (!lineItemDirtyFields) return false;
-          return !!(
-            lineItemDirtyFields.quantity ||
-            lineItemDirtyFields.price ||
-            lineItemDirtyFields.tax
-          );
-        }
-      );
-      return hasLengthChanged || hasRelevantFieldsChanged;
-    })();
+    // Check if Line Items values have changed
+    const areLineItemsValuesChanged = useMemo(() => {
+      if (!dirtyFields.lineItems) return false;
+      
+      // Check if number of items changed
+      if (dirtyFields.lineItems.length !== (lineItems?.length || 0)) {
+        return true;
+      }
+      
+      // Check if any relevant fields changed
+      return dirtyFields.lineItems.some(item => 
+        item && (item.quantity || item.price || item.tax)
+      );
+    }, [dirtyFields.lineItems, lineItems?.length]);

684-692: Include alert for line-item changes even when totals are locked
At PayableDetailsForm.tsx:684-692 the warning is gated by isTotalsEditable && showAlertChangedValues, but showAlertChangedValues is true for line-item diffs as well. Remove the isTotalsEditable check or add a separate branch so that any line-item change always triggers the alert.

packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts (5)

79-87: Creation defaults: prefer nulls over 0 for manual totals.

Using 0 makes the UI look “valid” before the user acts and can mask validation. Using null better signals “unset”.

Apply this diff:

-    if (!isCalculateTotalsEnabled) {
-      return {
-        ...defaultValuesCreating,
-        subtotal: 0,
-        tax_amount: 0,
-        total_amount: 0,
-      };
-    }
+    if (!isCalculateTotalsEnabled) {
+      return {
+        ...defaultValuesCreating,
+        subtotal: null,
+        tax_amount: null,
+        total_amount: null,
+      };
+    }

128-141: Normalize formatter nulls to numbers for edit defaults.

Guard against formatFromMinorUnits returning null so form fields don’t get null unexpectedly.

Apply this diff:

-      subtotal:
-        subtotal && currency ? formatFromMinorUnits(subtotal, currency) : 0,
-      tax_amount:
-        tax_amount && currency ? formatFromMinorUnits(tax_amount, currency) : 0,
-      total_amount:
-        total_amount && currency
-          ? formatFromMinorUnits(total_amount, currency)
-          : 0,
+      subtotal:
+        subtotal && currency
+          ? (formatFromMinorUnits(subtotal, currency) ?? 0)
+          : 0,
+      tax_amount:
+        tax_amount && currency
+          ? (formatFromMinorUnits(tax_amount, currency) ?? 0)
+          : 0,
+      total_amount:
+        total_amount && currency
+          ? (formatFromMinorUnits(total_amount, currency) ?? 0)
+          : 0,

49-57: Naming consistency: align flag semantics.

Here we use isCalculateTotalsEnabled, while the public prop is disableAutoCalculateTotals. Consider a single positive boolean (isAutoTotalsEnabled or manualTotalsEnabled) to reduce double-negation across call sites.


321-327: Public API addition looks good; add JSDoc and default.

Add a short JSDoc and ensure default is false at the consumer boundary (e.g., theme hook) for predictable behavior.


162-174: Guard discount in payload
Only spread discount when non-null to prevent unintentionally sending 0 and clearing existing server-side values.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40dfca5 and 4e9b8c9.

📒 Files selected for processing (12)
  • .changeset/shaggy-eels-pretend.md (1 hunks)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (13 hunks)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts (7 hunks)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/types.ts (1 hunks)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/validation.ts (1 hunks)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableLineItems.tsx (1 hunks)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableTotals.tsx (1 hunks)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/PayableDetailsInfo.tsx (2 hunks)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/index.tsx (1 hunks)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx (3 hunks)
  • packages/sdk-react/src/components/payables/hooks/usePayableDetailsThemeProps.ts (1 hunks)
  • packages/sdk-react/src/core/componentSettings/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
packages/sdk-react/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/sdk-react/.cursor/rules/api-calls.mdc)

packages/sdk-react/**/*.{ts,tsx}: Use proper TypeScript types for API data and leverage type inference from the OpenAPI schema
Use custom TypeScript types (e.g., Invoice, CreateInvoiceInput) imported from '@/types/api' for Monite API data structures

Files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/index.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableTotals.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableLineItems.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/types.ts
  • packages/sdk-react/src/components/payables/hooks/usePayableDetailsThemeProps.ts
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/PayableDetailsInfo.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts
  • packages/sdk-react/src/core/componentSettings/index.ts
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/validation.ts
packages/sdk-react/**/*.tsx

📄 CodeRabbit inference engine (packages/sdk-react/.cursor/rules/ui-components.mdc)

packages/sdk-react/**/*.tsx: Always use shadcn/ui components when creating new UI elements
Import components from '@/ui/components/*' when using shadcn/ui components
All Tailwind classes should have the prefix 'mtw' (e.g., 'mtw:w-full')
Use Tailwind CSS utility classes for all styling
Avoid inline styles or styled-components
Follow Tailwind's mobile-first responsive design approach
Use Tailwind's color palette and spacing scale
Do NOT use Material UI for new components
When modifying files with Material UI: only update to shadcn/ui if the component needs significant changes; otherwise, maintain existing MUI implementation; add TODO comments when MUI components should be migrated
Always prefer shadcn/ui components such as Button, Input, Card, Dialog, etc., from '@/ui/components/[component]'
Use React Hook Form with shadcn/ui form components
Use 'lucide-react' for icons and import icons like 'import { Search, Menu, X } from "lucide-react"'
Always use named imports from the specific ui component file
Use the variant prop for different styles (e.g., variant="outline" for buttons)
Use the size prop when available (e.g., size="sm" for smaller buttons)
Extend components with className prop using Tailwind utilities
When updating Material UI components to shadcn/ui: map MUI component to shadcn/ui equivalent, convert sx props and makeStyles to Tailwind classes, update event handlers if API differs, test thoroughly, remove MUI imports only after confirming all usages are replaced
Use TypeScript for all new components
Leverage component prop types for better IntelliSense

Files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/index.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableTotals.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableLineItems.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/PayableDetailsInfo.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
🧠 Learnings (9)
📓 Common learnings
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/core/i18n/locales/en/messages.po:8470-8473
Timestamp: 2025-09-04T15:54:24.944Z
Learning: For PRs focused on the invoice preview template (feat/DEV-15554), costa-monite prefers to exclude out-of-scope tweaks like minor i18n spacing changes (e.g., "Subtotal{0}" → "Subtotal {0}") and handle them in a separate follow-up.
📚 Learning: 2025-09-08T09:09:14.252Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/InvoicePreviewMonite.tsx:320-336
Timestamp: 2025-09-08T09:09:14.252Z
Learning: In InvoicePreviewMonite component at packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/InvoicePreviewMonite.tsx, the price values from item.product.price.value are already in minor units (as defined in the PriceFloat API schema), so they can be passed directly to formatCurrencyToDisplay without conversion.

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableTotals.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableLineItems.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Use React Hook Form with shadcn/ui form components for form handling

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : Use React Hook Form with shadcn/ui form components

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-09-04T16:18:44.198Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx:6-6
Timestamp: 2025-09-04T16:18:44.198Z
Learning: For forms in monite-sdk, prefer lib-agnostic TypeScript types (e.g., PayableDetailsFormFields) for component generics and contracts; avoid coupling components to Zod-inferred types. Use Zod only at the validation layer (e.g., zodResolver with a schema typed as ZodType<FormValues>).

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/validation.ts
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Use lucide-react for icons and import icons like: import { Search, Menu, X } from 'lucide-react'

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : Use 'lucide-react' for icons and import icons like 'import { Search, Menu, X } from "lucide-react"'

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Always prefer shadcn/ui components such as Button, Input, Card, Dialog, Select, Checkbox, RadioGroup, Switch, Textarea, Label, Alert, Badge, Toast, Tabs, Table, Form, DropdownMenu, Sheet, Skeleton, Separator

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Always use shadcn/ui components when creating new UI elements

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
🧬 Code graph analysis (6)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableTotals.tsx (1)
packages/sdk-react/src/core/hooks/useCurrencies.ts (1)
  • useCurrencies (15-170)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableLineItems.tsx (1)
packages/sdk-react/src/core/hooks/useCurrencies.ts (1)
  • useCurrencies (15-170)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/PayableDetailsInfo.tsx (2)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableLineItems.tsx (1)
  • DisplayPayableLineItems (15-67)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableTotals.tsx (1)
  • DisplayPayableTotals (14-62)
packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx (1)
packages/sdk-react/src/ui/RHF/RHFTextField/RHFTextField.tsx (1)
  • RHFTextField (8-78)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (7)
packages/sdk-react/src/components/payables/hooks/usePayableDetailsThemeProps.ts (1)
  • usePayableDetailsThemeProps (4-24)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts (1)
  • prepareDefaultValues (49-142)
packages/sdk-react/src/ui/Currency/MoniteCurrency.tsx (1)
  • MoniteCurrency (57-173)
packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx (1)
  • PayableLineItemsForm (12-148)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableLineItems.tsx (1)
  • DisplayPayableLineItems (15-67)
packages/sdk-react/src/ui/components/alert.tsx (1)
  • Alert (98-98)
packages/sdk-react/src/ui/RHF/RHFTextField/RHFTextField.tsx (1)
  • RHFTextField (8-78)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts (1)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/types.ts (1)
  • PayableDetailsFormFields (10-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Build and Push sdk-playground
  • GitHub Check: Build and Push sdk-drop-in
  • GitHub Check: Build and Push sdk-demo-with-nextjs-and-clerk-auth
  • GitHub Check: Build and Push sdk-drop-in
  • GitHub Check: Build and Push sdk-playground
  • GitHub Check: Build and Push sdk-demo-with-nextjs-and-clerk-auth
  • GitHub Check: Build
🔇 Additional comments (13)
packages/sdk-react/src/core/componentSettings/index.ts (2)

350-377: LGTM on defaults and wiring

Good defaulting pattern (nullish coalescing) and placement alongside other payables flags.


367-369: disableAutoCalculateTotals is declared on MonitePayableDetailsInfoProps
Verified as an optional boolean in packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts (line 326).

packages/sdk-react/src/components/payables/hooks/usePayableDetailsThemeProps.ts (1)

20-23: Flag wiring matches componentSettings

Correct precedence (inProps over componentSettings). No further action.

packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableLineItems.tsx (1)

47-59: Confirm tax units & handle zero subtotal

  • Replace the truthy check with item.subtotal != null so a subtotal of 0 renders correctly.
  • Verify whether item.tax on LineItemResponse is expressed as a percentage (0–100) or in basis points (e.g. 1900) and adjust the display logic accordingly.
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableTotals.tsx (1)

29-56: LGTM on currency formatting and fallbacks

Using formatCurrencyToDisplay with minor-unit inputs is correct and consistent.

packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/PayableDetailsInfo.tsx (1)

7-8: LGTM! Clean refactoring to use the new display components.

The extraction of line items and totals display logic into separate components improves code organization and reusability. The components receive appropriate props with sensible defaults for currency.

Also applies to: 475-479, 482-486

packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (3)

719-742: Good implementation of conditional editing for totals fields.

The switch between editable RHFTextField components and read-only display based on isTotalsEditable is well implemented. The numeric input constraints and currency symbol display are correctly configured.

Also applies to: 789-812, 828-852


765-775: LGTM! Proper migration from Controller to RHFTextField.

The discount field has been correctly migrated to use RHFTextField with appropriate numeric constraints and currency display.


631-634: Good TypeScript practice with explicit generic parameters.

The explicit type parameters for MoniteCurrency improve type safety and code clarity.

packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/validation.ts (2)

123-139: Validation for new totals fields looks good.

The new optional fields subtotal, tax_amount, and total_amount are properly validated with non-negative constraints and appropriate error messages.


14-21: Formatting improvements enhance readability.

The multi-line chaining style for the validation schema improves code readability while maintaining the same functionality.

packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts (2)

1-1: Type-only import LGTM.

Using import type keeps bundles clean.


175-184: API supports manual totals fields
Properties subtotal, tax_amount, and total_amount are defined (and optional) in PayableUpdateSchema in schema.json, so you can safely include them in the patch payload.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (6)
packages/sdk-react/src/core/i18n/locales/en/messages.po (6)

499-501: Casing: “Add Discount” vs existing “Add discount”

UI copy style is inconsistent with the existing “Add discount” (lowercase “d”). Recommend unifying.

-msgid "Add Discount"
-msgstr "Add Discount"
+msgid "Add discount"
+msgstr "Add discount"

Note: update the corresponding usages in PayableDetailsForm to match the new msgid.


1262-1270: Duplicate strings differ only by capitalization (“Bank account” vs “Bank Account”)

Having both forces extra translations and risks drift. Prefer a single canonical form (“Bank account”) and reuse it.

- msgid "Bank Account"
- msgstr "Bank Account"
+ msgid "Bank account"
+ msgstr "Bank account"

Also align the component label to reuse the same msgid.


4024-4027: Fix label casing: “excl. Tax” → “Excl. tax”

Current casing is inconsistent with “Incl. tax”. Suggest Title-case first word only.

-msgid "excl. Tax"
-msgstr "excl. Tax"
+msgid "Excl. tax"
+msgstr "Excl. tax"

Remember to update the DisplayPayableLineItems usage.


9482-9485: Clarify “Total, tax” column header

“Total, tax” is ambiguous. Align with existing patterns (“Incl. tax” / “Excl. tax”) or use “Total incl. tax”.

-msgid "Total, tax"
-msgstr "Total, tax"
+msgid "Total incl. tax"
+msgstr "Total incl. tax"

If you actually need the generic “Incl. tax”, consider reusing the existing msgid instead of adding a near-duplicate.


10086-10093: Unify “VAT total” vs “VAT Total”

Two variants exist across Payables: “VAT total” (form) and “VAT Total” (display). Pick one (recommend “VAT total” to match “Subtotal”, “Total tax”).

- msgid "VAT Total"
- msgstr "VAT Total"
+ msgid "VAT total"
+ msgstr "VAT total"

6160-6175: Add context/explicit IDs for generic terms (Name, Price, Quantity, Subtotal, Total, Discount)

These high-frequency msgids are reused across many screens. To avoid translation collisions in some locales, add msgctxt or explicit IDs for the Payables context (e.g., “PayablesLineItems-Name”, “PayablesTotals-Subtotal”).

Example (PO):

- msgid "Name"
+ msgctxt "PayablesLineItems"
+ msgid "Name"

Or switch these to js-lingui explicit IDs in code and PO.

Also applies to: 7455-7463, 7620-7624, 8739-8747, 9430-9435, 3213-3217

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e9b8c9 and 99e463b.

📒 Files selected for processing (1)
  • packages/sdk-react/src/core/i18n/locales/en/messages.po (53 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/core/i18n/locales/en/messages.po:8470-8473
Timestamp: 2025-09-04T15:54:24.944Z
Learning: For PRs focused on the invoice preview template (feat/DEV-15554), costa-monite prefers to exclude out-of-scope tweaks like minor i18n spacing changes (e.g., "Subtotal{0}" → "Subtotal {0}") and handle them in a separate follow-up.
📚 Learning: 2025-09-04T15:54:24.944Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/core/i18n/locales/en/messages.po:8470-8473
Timestamp: 2025-09-04T15:54:24.944Z
Learning: For PRs focused on the invoice preview template (feat/DEV-15554), costa-monite prefers to exclude out-of-scope tweaks like minor i18n spacing changes (e.g., "Subtotal{0}" → "Subtotal {0}") and handle them in a separate follow-up.

Applied to files:

  • packages/sdk-react/src/core/i18n/locales/en/messages.po
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build and Push sdk-playground
  • GitHub Check: Build and Push sdk-demo-with-nextjs-and-clerk-auth
  • GitHub Check: Build and Push sdk-drop-in
  • GitHub Check: Build
🔇 Additional comments (2)
packages/sdk-react/src/core/i18n/locales/en/messages.po (2)

221-228: Validation copy LGTM

The new validation and helper texts read well and match the new manual-totals workflow.

Also applies to: 2757-2765, 2855-2858, 3222-3225, 8748-8751, 8893-8896, 9455-9457, 8965-8971


9080-9083: Confirm alert trigger conditions for “amounts modified”

Ensure this warning is shown only when totals or line items were changed while auto-calc is disabled, and it doesn’t appear on pristine loads.

If helpful, I can scan the PR for guard conditions and emit a small e2e/checklist.

Copy link
Copy Markdown
Collaborator

@costa-monite costa-monite left a comment

Choose a reason for hiding this comment

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

🚀

Copy link
Copy Markdown
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: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/sdk-react/src/components/receivables/components/EntityProfileModal.tsx (2)

50-64: Avoid possible runtime crash: guard optional address fields.

entity.address may be absent; direct property access can throw.

-    vat_country: vatId?.country || entity.address.country || undefined,
-    address_line_1: entity.address.line1 || undefined,
-    address_line_2: entity.address.line2 || undefined,
-    city: entity.address.city || undefined,
-    postal_code: entity.address.postal_code || undefined,
-    state: entity.address.state || undefined,
-    country: entity.address.country || undefined,
+    vat_country: vatId?.country || entity.address?.country || undefined,
+    address_line_1: entity.address?.line1 || undefined,
+    address_line_2: entity.address?.line2 || undefined,
+    city: entity.address?.city || undefined,
+    postal_code: entity.address?.postal_code || undefined,
+    state: entity.address?.state || undefined,
+    country: entity.address?.country || undefined,

118-136: Include country when patching address.

If users edit the country, it’s currently ignored.

           address: {
             line1: values.address_line_1 || null,
             line2: values.address_line_2 || null,
             city: values.city || null,
             postal_code: values.postal_code || null,
             state: values.state || null,
+            country: values.country || null,
           },
🧹 Nitpick comments (37)
packages/sdk-react/src/core/i18n/locales/en/messages.po (6)

4064-4066: Unify “incl./excl. tax” capitalization

Elsewhere we use “Incl. tax”. Suggest “Excl. tax” (capitalize Excl., lowercase tax) for consistency.

Apply this diff:

-msgstr "excl. Tax"
+msgstr "Excl. tax"

9592-9594: Column label reads awkwardly (“Total, tax”)

Consider “Total incl. tax” to match the “Incl./Excl. tax” pattern used elsewhere.

-msgstr "Total, tax"
+msgstr "Total incl. tax"

10194-10200: Inconsistent “VAT total/Total” casing

Two variants exist (“VAT total”, “VAT Total”). Pick one; recommend sentence case for field labels.

-#: src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableTotals.tsx:43
-msgid "VAT Total"
-msgstr "VAT Total"
+#: src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableTotals.tsx:43
+msgid "VAT Total"
+msgstr "VAT total"

2415-2417: Avoid exposing “ID” in user-facing labels

“Counterpart bank account ID” and “ID of counterpart's bank account” feel internal. Prefer “Counterpart bank account” in UI and error labels.

- msgid "Counterpart bank account ID"
- msgstr "Counterpart bank account ID"
+ msgid "Counterpart bank account ID"
+ msgstr "Counterpart bank account"
- msgid "ID of counterpart's bank account"
- msgstr "ID of counterpart's bank account"
+ msgid "ID of counterpart's bank account"
+ msgstr "Counterpart bank account"

Also applies to: 4924-4927


2773-2780: Duplicate currency labels; prefer one canonical copy

We have:

  • “Currency for the invoice amount” (likely used as a label placeholder in required errors),
  • “Invoice currency”,
  • “Currency is required”.

To keep messages consistent, translate “Currency for the invoice amount” as “Invoice currency” so generic errors read well (“Invoice currency is required”).

- msgid "Currency for the invoice amount"
- msgstr "Currency for the invoice amount"
+ msgid "Currency for the invoice amount"
+ msgstr "Invoice currency"

Also applies to: 5140-5142, 2777-2780


4502-4508: Rename misspelled file: FullfillmentSummary.tsx → FulfillmentSummary.tsx

packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/Billing/FullfillmentSummary.tsx — rename to FulfillmentSummary.tsx and update all imports/usages to match the "Fulfillment" spelling used elsewhere.

packages/sdk-react/src/ui/components/calendar.tsx (2)

215-218: z-index token may not exist — use arbitrary value syntax

z-1300 requires a custom theme scale. To avoid depending on theme tokens and ensure correctness, use z-[1300].

-        '... mtw:group-data-[focused=true]/day:z-1300 ...',
+        '... mtw:group-data-[focused=true]/day:z-[1300] ...',

37-41: Locale handling for month dropdown

toLocaleString('default') can vary between SSR and client locales. Consider sourcing the locale from DayPicker props (if provided) or your i18n context to avoid mismatches.

- formatMonthDropdown: (date) =>
-   date.toLocaleString('default', { month: 'short' }),
+ formatMonthDropdown: (date) => {
+   const locale = props.locale ?? undefined; // or from your i18n context
+   return new Intl.DateTimeFormat(locale, { month: 'short' }).format(date);
+},
packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceIterationStatusChip.stories.tsx (2)

44-47: Replace inline styles with Tailwind classes (mtw‑ prefix).

Stories live in packages/sdk-react and should avoid inline styles per guidelines. Suggest:

-    <div style={{ height: 500, padding: 20 }}>
+    <div className="mtw:h-[500px] mtw:p-5">
...
-    <div style={{ height: 500, padding: 20 }}>
+    <div className="mtw:h-[500px] mtw:p-5">
-      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
+      <div className="mtw:flex mtw:flex-col mtw:gap-3">
...
-    <div style={{ height: 500, padding: 20 }}>
+    <div className="mtw:h-[500px] mtw:p-5">

Also applies to: 53-55, 73-76


50-61: De-duplicate AllStatuses rendering.

Use a small array map for maintainability.

-      <InvoiceRecurrenceIterationStatusChipComponent status="pending" />
-      <InvoiceRecurrenceIterationStatusChipComponent status="skipped" />
-      <InvoiceRecurrenceIterationStatusChipComponent status="canceled" />
-      <InvoiceRecurrenceIterationStatusChipComponent status="issue_failed" />
-      <InvoiceRecurrenceIterationStatusChipComponent status="send_failed" />
-      <InvoiceRecurrenceIterationStatusChipComponent status="completed" />
+      {['pending','skipped','canceled','issue_failed','send_failed','completed'].map((s) => (
+        <InvoiceRecurrenceIterationStatusChipComponent key={s} status={s as any} />
+      ))}
packages/sdk-react/src/components/receivables/components/RecurrenceCancelModal.tsx (2)

25-29: Guard actions when recurrence_id is missing.

recurrence_id may be empty; calling cancel with '' will 400. Hide modal or no-op confirm when missing.

-      open={open && Boolean(invoice.id)}
+      open={open && Boolean(invoice.id) && Boolean(recurrence_id)}
...
-      onConfirm={() =>
-        cancelRecurrence(undefined, {
-          onSuccess: onClose,
-        })
-      }
+      onConfirm={() => {
+        if (!recurrence_id) return;
+        cancelRecurrence(undefined, { onSuccess: onClose });
+      }}

Also applies to: 58-66


32-38: Minor readability nit: extract chosen “source” for pending iterations.

Helps comprehension and avoids nested ternary.

-  const totalPendingInvoices =
-    (previousRecurrence?.status === 'active' &&
-    recurrence?.status === 'canceled'
-      ? previousRecurrence
-      : recurrence
-    )?.iterations?.filter(({ status }) => status === 'pending')?.length ?? 0;
+  const source = previousRecurrence?.status === 'active' && recurrence?.status === 'canceled'
+    ? previousRecurrence
+    : recurrence;
+  const totalPendingInvoices = source?.iterations?.filter(({ status }) => status === 'pending')?.length ?? 0;
packages/sdk-react/src/components/receivables/components/EntityProfileModal.tsx (2)

169-169: Tailwind class likely invalid: use arbitrary value for max-h.

mtw:max-h-4/5 isn’t a standard Tailwind utility. Prefer:

-  <DialogContent className="mtw:max-h-4/5 mtw:overflow-y-auto">
+  <DialogContent className="mtw:max-h-[80vh] mtw:overflow-y-auto">

187-193: Optional: prevent double submits while mutations are in flight.

Wire isPending flags from patchEntity, createEntityVatId, and patchEntityVat to disable the Save button during requests.

packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/validation.ts (3)

61-64: Zod v4: use required_error for numbers; message is ignored.

Ensure proper required error for tax_rate_value.

-    tax_rate_value: z
-      .number({
-        message: t(i18n)`Tax is a required field`,
-      })
+    tax_rate_value: z
+      .number({
+        required_error: t(i18n)`Tax is a required field`,
+        invalid_type_error: t(i18n)`Tax must be a number`,
+      })
       .min(0, t(i18n)`Tax rate must be 0 or greater`)
       .max(100, t(i18n)`Tax rate must be 100 or less`),

165-174: Zod v4: misuse of z.date().min(1, ...) and error message.

min(1) compares against 1970-01-01; it doesn’t express “required”. Use required_error on z.date().

-    recurrence_start_date: isRecurrenceEnabled
-      ? z.date().min(1, t(i18n)`Recurrence start date is a required field`)
+    recurrence_start_date: isRecurrenceEnabled
+      ? z.date({ required_error: t(i18n)`Recurrence start date is a required field` })
       : z.string().optional(),
-    recurrence_end_date: isRecurrenceEnabled
-      ? z.date().min(1, t(i18n)`Recurrence end date is a required field`)
+    recurrence_end_date: isRecurrenceEnabled
+      ? z.date({ required_error: t(i18n)`Recurrence end date is a required field` })
       : z.string().optional(),

70-75: Also update VAT field errors to Zod v4 option keys.

error isn’t a recognized key. Use required_error/invalid_type_error.

-    vat_rate_value: z
-      .number({ error: t(i18n)`VAT is a required field` })
+    vat_rate_value: z
+      .number({ required_error: t(i18n)`VAT is a required field`, invalid_type_error: t(i18n)`VAT must be a number` })
       .min(0),
-    vat_rate_id: z
-      .string({ error: t(i18n)`VAT is a required field` })
+    vat_rate_id: z
+      .string({ required_error: t(i18n)`VAT is a required field`, invalid_type_error: t(i18n)`VAT is a required field` })
       .min(1, t(i18n)`VAT is a required field`),
packages/sdk-react/src/components/receivables/Receivables.tsx (1)

17-19: Unify AccessRestriction import path across files.

This file uses '@/ui/accessRestriction' while CreateReceivables.tsx uses '@/ui/accessRestriction/AccessRestriction'. Prefer one path (ideally the barrel if available) to avoid duplicate bundles and confusion.

- import { AccessRestriction } from '@/ui/accessRestriction';
+ import { AccessRestriction } from '@/ui/accessRestriction/AccessRestriction';
packages/sdk-react/src/components/receivables/components/RecurrenceSection.tsx (1)

24-31: Fix Switch aria-label to match control purpose.

Currently says “Payment reminders”, but this toggle is for “Make recurring”.

- aria-label={t(i18n)`Payment reminders`}
+ aria-label={t(i18n)`Make recurring`}

Add a TODO to migrate MUI Switch to shadcn/ui when this section gets a larger refactor, per repo guidelines.

packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabOverview.tsx (2)

197-205: Simplify count check for Badge rendering.

Double-checking length is redundant.

- {creditNoteQuery?.data?.length &&
-   creditNoteQuery?.data?.length > 0 && (
+ {creditNoteQuery?.data?.length ? (
    <Badge variant="secondary" className="mtw:h-5 mtw:min-w-5 mtw:rounded-full mtw:px-1 mtw:tabular-nums">
      {creditNoteQuery?.data?.length}
    </Badge>
- )}
+ ) : null}

50-57: Guard empty dates to avoid formatting “Invalid Date”.

issueDate may resolve to ''. Provide a fallback.

- {t(i18n)`${status === 'draft' ? 'Created' : 'Issued'}`} {i18n.date(issueDate, locale.dateFormat)}
+ {t(i18n)`${status === 'draft' ? 'Created' : 'Issued'}`} {issueDate ? i18n.date(issueDate, locale.dateFormat) : '-'}

Also applies to: 211-218

packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabScheduledInvoices.tsx (2)

112-118: Use a stable, non-i18n key.

Keys should be deterministic and not localized. Prefer issued_invoice_id or iteration index as a last resort.

- key={t(i18n)`iteration-${iteration?.iteration}-${index}`}
+ key={iteration?.issued_invoice_id ?? `iter-${iteration?.iteration ?? index}`}

28-33: Avoid N+1 receivable fetches. Batch by id__in.

Fetching each issued invoice per row will not scale. Prefetch all issued IDs once and pass the map to IterationInvoice.

// Parent component (before return):
const issuedIds = (recurrence?.iterations ?? [])
  .map((it) => it.issued_invoice_id)
  .filter(Boolean) as string[];
const { data: issuedQuery } = useGetReceivables({ id__in: issuedIds }, issuedIds.length > 0);
const issuedMap = new Map(issuedQuery?.data?.map((r) => [r.id, r]) ?? []);

// Pass down:
<IterationInvoice
  ...
  issuedInvoice={issuedMap.get(iteration.issued_invoice_id ?? '')}
/>

// Child: drop useGetReceivableById and use prop instead.

Also applies to: 88-90

packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx (4)

1297-1300: Use functional state update for toggle to avoid stale closure.

- toggleRecurrence={() =>
-   setIsRecurrenceEnabled(!isRecurrenceEnabled)
- }
+ toggleRecurrence={() => setIsRecurrenceEnabled((v) => !v)}

157-159: Guard settings query on entityId availability.

If entityId can be undefined initially, add enabled to avoid a bad request.

- const { data: settings, isLoading: isSettingsLoading } =
-   api.entities.getEntitiesIdSettings.useQuery({
-     path: { entity_id: entityId },
-   });
+ const { data: settings, isLoading: isSettingsLoading } =
+   api.entities.getEntitiesIdSettings.useQuery(
+     { path: { entity_id: entityId } },
+     { enabled: Boolean(entityId) }
+   );

1321-1323: Prefer collision-resistant temp IDs.

Math.random-based IDs collide in large lists. Use crypto.randomUUID() when available.

- id: item.id || `temp-${Math.random().toString(36).substr(2, 9)}`,
+ id: item.id || (typeof crypto !== 'undefined' && 'randomUUID' in crypto
+   ? crypto.randomUUID()
+   : `temp-${Date.now()}-${Math.round(Math.random()*1e6)}`),

48-49: Align AccessRestriction import path with the rest of the app.

Receivables.tsx imports from '@/ui/accessRestriction'; prefer a single source to avoid duplication.

- import { AccessRestriction } from '@/ui/accessRestriction/AccessRestriction';
+ import { AccessRestriction } from '@/ui/accessRestriction';
packages/sdk-react/src/components/receivables/components/InvoiceDetails.tsx (2)

208-213: Remove defaultValue on controlled Tabs to avoid controlled/uncontrolled ambiguity

Tabs is controlled via value={selectedTab}. Drop defaultValue to prevent confusion.

-            <Tabs
-              defaultValue={Tab.Overview}
-              className="mtw:gap-8"
-              value={selectedTab}
+            <Tabs
+              className="mtw:gap-8"
+              value={selectedTab}
               onValueChange={handleTabChange}
             >

294-295: Align API typings with repo guideline

Casting to components['schemas']['InvoiceResponsePayload'] works, but guidelines prefer custom API types (e.g., Invoice) from @/types/api for better IntelliSense and decoupling from OpenAPI internals.

Would you like me to generate a small refactor patch switching these props to the canonical @/types/api type?

packages/sdk-react/src/components/receivables/components/InvoiceDetailsActions.tsx (4)

98-104: Prevent reverse‑tabnabbing when opening PDF

Add noopener,noreferrer to window.open.

-    if (pdfLink.data?.file_url) {
-      window.open(pdfLink.data.file_url, '_blank');
-    }
+    if (pdfLink.data?.file_url) {
+      window.open(pdfLink.data.file_url, '_blank', 'noopener,noreferrer');
+    }

286-299: Dropdown labels mismatch for non‑draft invoices

When hideSendAndPdfButtons is true on non‑draft invoices, labels show “Send draft” / “Download draft”. Use dynamic labels and disable while downloading.

-            {(isDraft || hideSendAndPdfButtons) && (
+            {(isDraft || hideSendAndPdfButtons) && (
               <>
-                <DropdownMenuItem
-                  onClick={() =>
-                    handleButtonClick(actions.onIssueAndSendButtonClick)
-                  }
-                >
-                  {t(i18n)`Send draft`}
-                </DropdownMenuItem>
-                <DropdownMenuItem onClick={handleDownloadPdf}>
-                  {t(i18n)`Download draft`}
-                </DropdownMenuItem>
+                <DropdownMenuItem
+                  onClick={() =>
+                    handleButtonClick(actions.onIssueAndSendButtonClick)
+                  }
+                >
+                  {isDraft ? t(i18n)`Send draft` : t(i18n)`Send`}
+                </DropdownMenuItem>
+                <DropdownMenuItem onClick={handleDownloadPdf} disabled={isDownloadingPdf}>
+                  {isDraft ? t(i18n)`Download draft` : t(i18n)`Download PDF`}
+                </DropdownMenuItem>
               </>
             )}

301-304: Gate “Edit template settings” behind permissions for consistency

All other mutating actions are permission‑gated; this one should be too.

-            <DropdownMenuItem onClick={actions.onTemplateSettingsButtonClick}>
+            <DropdownMenuItem
+              onClick={() => handleButtonClick(actions.onTemplateSettingsButtonClick)}
+              disabled={isUpdateAllowedLoading}
+            >
               {t(i18n)`Edit template settings`}
             </DropdownMenuItem>

240-248: Disable Duplicate button while permissions are loading

Minor UX polish: mirror other buttons’ disabled state while isUpdateAllowedLoading.

-      {isRecurring && (
+      {isRecurring && (
         <Button
           type="button"
           variant="outline"
           size="sm"
+          disabled={isUpdateAllowedLoading}
           onClick={() => handleButtonClick(handleDuplicateInvoice)}
         >
           <Copy /> {t(i18n)`Duplicate`}
         </Button>
       )}
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts (4)

49-81: Extracted defaults helper: avoid hard-coded currency/tax.

Nice extraction; this addresses the prior feedback about encapsulating defaults. To reduce coupling to EU-specific defaults, accept overridable defaults (currency, tax) instead of hard-coding 'EUR' and 19%.

Apply:

-const getDefaultValuesForCreating = (
-  isCalculateTotalsEnabled?: boolean
-): PayableDetailsFormFields => {
+const getDefaultValuesForCreating = (
+  isCalculateTotalsEnabled?: boolean,
+  defaultCurrency: CurrencyEnum = 'EUR' as CurrencyEnum,
+  defaultTaxRate = 0
+): PayableDetailsFormFields => {
   const defaultValuesCreating = {
     invoiceNumber: '',
     counterpart: '',
     counterpartBankAccount: '',
     invoiceDate: undefined,
     dueDate: undefined,
-    currency: 'EUR' as CurrencyEnum,
+    currency: defaultCurrency,
     tags: [],
     discount: null,
     lineItems: [
       {
         id: '',
         name: '',
         quantity: 1,
         price: 0,
-        tax: 19,
+        tax: defaultTaxRate,
       },
     ],
   };

Call sites keep working due to defaults; pass entity defaults later if available. Also confirm whether 19% was intentional for payables; many entities default to 0% on input.


110-133: Discount 0 becomes null in editing defaults.

Using truthiness drops legitimate 0 discounts. Switch to nullish check.

Apply:

-    discount:
-      discount && currency ? formatFromMinorUnits(discount, currency) : null,
+    discount:
+      discount != null && currency
+        ? formatFromMinorUnits(discount, currency)
+        : null,

150-191: Manual totals + discount: avoid double application.

When manual totals are submitted (!isCalculateTotalsEnabled), also sending discount may cause the backend to apply discount in addition to your provided totals. Gate discount behind the flag; keep totals as the source of truth. Also narrow the currency type in the formatter signature.

Apply:

-  formatToMinorUnits: (amount: number, currency: string) => number | null,
+  formatToMinorUnits: (amount: number, currency: CurrencyEnum | string) => number | null,
   isCalculateTotalsEnabled?: boolean
 ): components['schemas']['PayableUpdateSchema'] => {
   const submitPayload = {
     document_id: invoiceNumber,
     counterpart_id: counterpart || undefined,
     counterpart_bank_account_id: counterpartBankAccount || undefined,
     issued_at:
       invoiceDate instanceof Date ? dateToString(invoiceDate) : undefined,
     due_date: dueDate instanceof Date ? dateToString(dueDate) : undefined,
     currency: currency as CurrencyEnum,
     tag_ids: tags.map((tag) => tag.id),
     counterpart_address_id: counterpartAddressId,
-    discount: formatToMinorUnits(discount ?? 0, currency) ?? 0,
+    discount: isCalculateTotalsEnabled
+      ? formatToMinorUnits(discount ?? 0, currency) ?? 0
+      : undefined,
   };

Please confirm API behavior: if subtotal/tax/total are provided, should discount be omitted? If discount is informational, we can still send it but the backend must ignore it when totals are present.


327-333: Public prop naming consistency.

Props elsewhere often use is* for booleans (e.g., isTagsDisabled). Consider isAutoCalculateTotalsDisabled for symmetry, or at least add a JSDoc explaining polarity to avoid misuse.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 99e463b and ea64538.

📒 Files selected for processing (34)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (13 hunks)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts (6 hunks)
  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx (17 hunks)
  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/validation.ts (5 hunks)
  • packages/sdk-react/src/components/receivables/InvoiceDetails/ExistingInvoiceDetails/components/EditInvoiceDetails.tsx (3 hunks)
  • packages/sdk-react/src/components/receivables/InvoiceDetails/InvoiceDetails.tsx (2 hunks)
  • packages/sdk-react/src/components/receivables/Receivables.tsx (1 hunks)
  • packages/sdk-react/src/components/receivables/components/CustomerSection.tsx (2 hunks)
  • packages/sdk-react/src/components/receivables/components/EntityProfileModal.tsx (3 hunks)
  • packages/sdk-react/src/components/receivables/components/InvoiceDetails.tsx (11 hunks)
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsActions.tsx (9 hunks)
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsInfoBlock.tsx (1 hunks)
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsOverviewRecurrenceSection.tsx (2 hunks)
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabDetails.tsx (2 hunks)
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabOverview.tsx (3 hunks)
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabScheduledInvoices.tsx (1 hunks)
  • packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceIterationStatusChip.stories.tsx (1 hunks)
  • packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceIterationStatusChip.tsx (1 hunks)
  • packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceStatusChip.stories.tsx (1 hunks)
  • packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceStatusChip.tsx (1 hunks)
  • packages/sdk-react/src/components/receivables/components/RecurrenceCancelModal.tsx (4 hunks)
  • packages/sdk-react/src/components/receivables/components/RecurrenceFormContent.tsx (1 hunks)
  • packages/sdk-react/src/components/receivables/components/RecurrenceSection.tsx (2 hunks)
  • packages/sdk-react/src/components/receivables/components/index.ts (1 hunks)
  • packages/sdk-react/src/components/receivables/hooks/useCancelRecurrence.ts (1 hunks)
  • packages/sdk-react/src/components/receivables/hooks/useCreateReceivable.ts (1 hunks)
  • packages/sdk-react/src/components/receivables/hooks/useCreateRecurrence.ts (1 hunks)
  • packages/sdk-react/src/components/receivables/hooks/useGetInvoiceRequiredFields.ts (1 hunks)
  • packages/sdk-react/src/components/receivables/hooks/useUpdateRecurrenceById.ts (1 hunks)
  • packages/sdk-react/src/components/receivables/utils/index.ts (1 hunks)
  • packages/sdk-react/src/components/receivables/utils/invoiceStatus.ts (1 hunks)
  • packages/sdk-react/src/core/i18n/locales/en/messages.po (143 hunks)
  • packages/sdk-react/src/ui/DatePicker.tsx (2 hunks)
  • packages/sdk-react/src/ui/components/calendar.tsx (5 hunks)
✅ Files skipped from review due to trivial changes (14)
  • packages/sdk-react/src/components/receivables/components/CustomerSection.tsx
  • packages/sdk-react/src/components/receivables/InvoiceDetails/InvoiceDetails.tsx
  • packages/sdk-react/src/components/receivables/hooks/useCreateReceivable.ts
  • packages/sdk-react/src/components/receivables/hooks/useGetInvoiceRequiredFields.ts
  • packages/sdk-react/src/components/receivables/hooks/useCancelRecurrence.ts
  • packages/sdk-react/src/components/receivables/utils/index.ts
  • packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceStatusChip.stories.tsx
  • packages/sdk-react/src/components/receivables/components/index.ts
  • packages/sdk-react/src/components/receivables/hooks/useCreateRecurrence.ts
  • packages/sdk-react/src/components/receivables/utils/invoiceStatus.ts
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabDetails.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceIterationStatusChip.tsx
  • packages/sdk-react/src/ui/DatePicker.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsInfoBlock.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
🧰 Additional context used
📓 Path-based instructions (2)
packages/sdk-react/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/sdk-react/.cursor/rules/api-calls.mdc)

packages/sdk-react/**/*.{ts,tsx}: Use proper TypeScript types for API data and leverage type inference from the OpenAPI schema
Use custom TypeScript types (e.g., Invoice, CreateInvoiceInput) imported from '@/types/api' for Monite API data structures

Files:

  • packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceIterationStatusChip.stories.tsx
  • packages/sdk-react/src/components/receivables/components/RecurrenceSection.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceStatusChip.tsx
  • packages/sdk-react/src/components/receivables/Receivables.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceDetails.tsx
  • packages/sdk-react/src/components/receivables/InvoiceDetails/ExistingInvoiceDetails/components/EditInvoiceDetails.tsx
  • packages/sdk-react/src/components/receivables/hooks/useUpdateRecurrenceById.ts
  • packages/sdk-react/src/components/receivables/components/EntityProfileModal.tsx
  • packages/sdk-react/src/components/receivables/components/RecurrenceCancelModal.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabScheduledInvoices.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabOverview.tsx
  • packages/sdk-react/src/components/receivables/components/RecurrenceFormContent.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts
  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsOverviewRecurrenceSection.tsx
  • packages/sdk-react/src/ui/components/calendar.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsActions.tsx
  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/validation.ts
packages/sdk-react/**/*.tsx

📄 CodeRabbit inference engine (packages/sdk-react/.cursor/rules/ui-components.mdc)

packages/sdk-react/**/*.tsx: Always use shadcn/ui components when creating new UI elements
Import components from '@/ui/components/*' when using shadcn/ui components
All Tailwind classes should have the prefix 'mtw' (e.g., 'mtw:w-full')
Use Tailwind CSS utility classes for all styling
Avoid inline styles or styled-components
Follow Tailwind's mobile-first responsive design approach
Use Tailwind's color palette and spacing scale
Do NOT use Material UI for new components
When modifying files with Material UI: only update to shadcn/ui if the component needs significant changes; otherwise, maintain existing MUI implementation; add TODO comments when MUI components should be migrated
Always prefer shadcn/ui components such as Button, Input, Card, Dialog, etc., from '@/ui/components/[component]'
Use React Hook Form with shadcn/ui form components
Use 'lucide-react' for icons and import icons like 'import { Search, Menu, X } from "lucide-react"'
Always use named imports from the specific ui component file
Use the variant prop for different styles (e.g., variant="outline" for buttons)
Use the size prop when available (e.g., size="sm" for smaller buttons)
Extend components with className prop using Tailwind utilities
When updating Material UI components to shadcn/ui: map MUI component to shadcn/ui equivalent, convert sx props and makeStyles to Tailwind classes, update event handlers if API differs, test thoroughly, remove MUI imports only after confirming all usages are replaced
Use TypeScript for all new components
Leverage component prop types for better IntelliSense

Files:

  • packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceIterationStatusChip.stories.tsx
  • packages/sdk-react/src/components/receivables/components/RecurrenceSection.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceStatusChip.tsx
  • packages/sdk-react/src/components/receivables/Receivables.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceDetails.tsx
  • packages/sdk-react/src/components/receivables/InvoiceDetails/ExistingInvoiceDetails/components/EditInvoiceDetails.tsx
  • packages/sdk-react/src/components/receivables/components/EntityProfileModal.tsx
  • packages/sdk-react/src/components/receivables/components/RecurrenceCancelModal.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabScheduledInvoices.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabOverview.tsx
  • packages/sdk-react/src/components/receivables/components/RecurrenceFormContent.tsx
  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsOverviewRecurrenceSection.tsx
  • packages/sdk-react/src/ui/components/calendar.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsActions.tsx
🧠 Learnings (18)
📓 Common learnings
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/core/i18n/locales/en/messages.po:8470-8473
Timestamp: 2025-09-04T15:54:24.944Z
Learning: For PRs focused on the invoice preview template (feat/DEV-15554), costa-monite prefers to exclude out-of-scope tweaks like minor i18n spacing changes (e.g., "Subtotal{0}" → "Subtotal {0}") and handle them in a separate follow-up.
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : When updating Material UI components to shadcn/ui: map MUI component to shadcn/ui equivalent, convert sx props and makeStyles to Tailwind classes, update event handlers if API differs, test thoroughly, remove MUI imports only after confirming all usages are replaced

Applied to files:

  • packages/sdk-react/src/components/receivables/Receivables.tsx
  • packages/sdk-react/src/ui/components/calendar.tsx
📚 Learning: 2025-07-21T08:11:13.444Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/api-calls.mdc:0-0
Timestamp: 2025-07-21T08:11:13.444Z
Learning: Applies to packages/sdk-react/**/*.{ts,tsx} : Use custom TypeScript types (e.g., Invoice, CreateInvoiceInput) imported from '@/types/api' for Monite API data structures

Applied to files:

  • packages/sdk-react/src/components/receivables/Receivables.tsx
📚 Learning: 2025-09-05T11:02:00.731Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/InvoicePreview.tsx:34-39
Timestamp: 2025-09-05T11:02:00.731Z
Learning: In the InvoicePreview component at packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/InvoicePreview.tsx, the minScale: 1 setting in useAdaptiveScale is intentionally set to prevent downscaling below 100% to maintain invoice readability and quality, even if it requires scrolling on smaller viewports.

Applied to files:

  • packages/sdk-react/src/components/receivables/components/InvoiceDetails.tsx
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabOverview.tsx
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : Import components from '@/ui/components/*' when using shadcn/ui components

Applied to files:

  • packages/sdk-react/src/components/receivables/components/InvoiceDetails.tsx
📚 Learning: 2025-09-08T09:09:14.289Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/InvoicePreviewMonite.tsx:320-336
Timestamp: 2025-09-08T09:09:14.289Z
Learning: In InvoicePreviewMonite component at packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/InvoicePreviewMonite.tsx, the price values from item.product.price.value are already in minor units (as defined in the PriceFloat API schema), so they can be passed directly to formatCurrencyToDisplay without conversion.

Applied to files:

  • packages/sdk-react/src/components/receivables/InvoiceDetails/ExistingInvoiceDetails/components/EditInvoiceDetails.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts
  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx
📚 Learning: 2025-07-21T08:10:12.113Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/api-calls.mdc:0-0
Timestamp: 2025-07-21T08:10:12.113Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/**/*.tsx : Always destructure `api` and `entityId` from useMoniteContext.

Applied to files:

  • packages/sdk-react/src/components/receivables/InvoiceDetails/ExistingInvoiceDetails/components/EditInvoiceDetails.tsx
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : Use React Hook Form with shadcn/ui form components

Applied to files:

  • packages/sdk-react/src/components/receivables/components/EntityProfileModal.tsx
  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Use React Hook Form with shadcn/ui form components for form handling

Applied to files:

  • packages/sdk-react/src/components/receivables/components/EntityProfileModal.tsx
  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx
📚 Learning: 2025-09-04T16:18:44.198Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx:6-6
Timestamp: 2025-09-04T16:18:44.198Z
Learning: For forms in monite-sdk, prefer lib-agnostic TypeScript types (e.g., PayableDetailsFormFields) for component generics and contracts; avoid coupling components to Zod-inferred types. Use Zod only at the validation layer (e.g., zodResolver with a schema typed as ZodType<FormValues>).

Applied to files:

  • packages/sdk-react/src/components/receivables/components/EntityProfileModal.tsx
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : When modifying files with Material UI: only update to shadcn/ui if the component needs significant changes; otherwise, maintain existing MUI implementation; add TODO comments when MUI components should be migrated

Applied to files:

  • packages/sdk-react/src/components/receivables/components/RecurrenceFormContent.tsx
  • packages/sdk-react/src/ui/components/calendar.tsx
📚 Learning: 2025-09-04T15:54:24.944Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/core/i18n/locales/en/messages.po:8470-8473
Timestamp: 2025-09-04T15:54:24.944Z
Learning: For PRs focused on the invoice preview template (feat/DEV-15554), costa-monite prefers to exclude out-of-scope tweaks like minor i18n spacing changes (e.g., "Subtotal{0}" → "Subtotal {0}") and handle them in a separate follow-up.

Applied to files:

  • packages/sdk-react/src/core/i18n/locales/en/messages.po
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Always prefer shadcn/ui components such as Button, Input, Card, Dialog, Select, Checkbox, RadioGroup, Switch, Textarea, Label, Alert, Badge, Toast, Tabs, Table, Form, DropdownMenu, Sheet, Skeleton, Separator

Applied to files:

  • packages/sdk-react/src/core/i18n/locales/en/messages.po
  • packages/sdk-react/src/ui/components/calendar.tsx
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : Use the variant prop for different styles (e.g., variant="outline" for buttons)

Applied to files:

  • packages/sdk-react/src/ui/components/calendar.tsx
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Use the variant prop for different styles (e.g., variant="outline" for buttons)

Applied to files:

  • packages/sdk-react/src/ui/components/calendar.tsx
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : Always prefer shadcn/ui components such as Button, Input, Card, Dialog, etc., from '@/ui/components/[component]'

Applied to files:

  • packages/sdk-react/src/ui/components/calendar.tsx
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Use lucide-react for icons and import icons like: import { Search, Menu, X } from 'lucide-react'

Applied to files:

  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsActions.tsx
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : Use 'lucide-react' for icons and import icons like 'import { Search, Menu, X } from "lucide-react"'

Applied to files:

  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsActions.tsx
🧬 Code graph analysis (12)
packages/sdk-react/src/components/receivables/components/RecurrenceSection.tsx (1)
packages/sdk-react/src/components/receivables/components/RecurrenceFormContent.tsx (1)
  • RecurrenceFormContent (21-142)
packages/sdk-react/src/components/receivables/components/InvoiceDetails.tsx (4)
packages/sdk-react/src/components/receivables/hooks/useGetRecurrenceById.ts (1)
  • useGetRecurrenceById (3-10)
packages/sdk-react/src/core/queries/usePermissions.ts (1)
  • useIsActionAllowed (106-126)
packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabOverview.tsx (1)
  • InvoiceDetailsTabOverview (74-243)
packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabScheduledInvoices.tsx (1)
  • InvoiceDetailsTabScheduledInvoices (84-124)
packages/sdk-react/src/components/receivables/InvoiceDetails/ExistingInvoiceDetails/components/EditInvoiceDetails.tsx (2)
packages/sdk-react/src/core/queries/useMe.ts (1)
  • useMyEntity (31-68)
packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/validation.ts (2)
  • UpdateReceivablesFormProps (159-161)
  • getUpdateInvoiceValidationSchema (148-157)
packages/sdk-react/src/components/receivables/hooks/useUpdateRecurrenceById.ts (1)
packages/sdk-react/src/core/utils/getAPIErrorMessage.ts (1)
  • getAPIErrorMessage (5-22)
packages/sdk-react/src/components/receivables/components/RecurrenceCancelModal.tsx (2)
packages/sdk-react/src/components/receivables/hooks/useCancelRecurrence.ts (1)
  • useCancelRecurrence (7-36)
packages/sdk-react/src/components/receivables/hooks/useGetRecurrenceById.ts (1)
  • useGetRecurrenceById (3-10)
packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabScheduledInvoices.tsx (4)
packages/sdk-react/src/core/hooks/useCurrencies.ts (1)
  • useCurrencies (15-170)
packages/sdk-react/src/core/queries/useGetReceivableById.ts (1)
  • useGetReceivableById (8-24)
packages/sdk-react/src/components/receivables/consts.ts (1)
  • INVOICE_DOCUMENT_AUTO_ID (60-60)
packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceIterationStatusChip.tsx (1)
  • InvoiceRecurrenceIterationStatusChip (28-46)
packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabOverview.tsx (2)
packages/sdk-react/src/components/receivables/components/InvoiceDetailsOverviewRecurrenceSection.tsx (1)
  • InvoiceDetailsOverviewRecurrenceSection (35-218)
packages/sdk-react/src/components/receivables/components/index.ts (1)
  • InvoiceDetailsOverviewRecurrenceSection (52-52)
packages/sdk-react/src/components/receivables/components/RecurrenceFormContent.tsx (2)
packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/validation.ts (1)
  • CreateRecurrenceFormProps (204-206)
packages/sdk-react/src/ui/DatePicker.tsx (1)
  • DatePicker (19-54)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts (1)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/types.ts (1)
  • PayableDetailsFormFields (10-23)
packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx (4)
packages/sdk-react/src/core/queries/useCounterpart.ts (1)
  • useCounterpartContactList (342-422)
packages/sdk-react/src/components/receivables/hooks/useCreateReceivable.ts (1)
  • useCreateReceivable (7-29)
packages/sdk-react/src/components/receivables/hooks/useCreateRecurrence.ts (1)
  • useCreateRecurrence (7-20)
packages/sdk-react/src/components/receivables/hooks/useGetInvoiceRequiredFields.ts (1)
  • useGetInvoiceRequiredFields (13-41)
packages/sdk-react/src/components/receivables/components/InvoiceDetailsOverviewRecurrenceSection.tsx (5)
packages/sdk-react/src/components/receivables/hooks/useUpdateRecurrenceById.ts (1)
  • useUpdateRecurrenceById (7-48)
packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/validation.ts (2)
  • CreateRecurrenceFormProps (204-206)
  • getCreateRecurrenceValidationSchema (177-202)
packages/sdk-react/src/components/receivables/components/RecurrenceFormContent.tsx (1)
  • RecurrenceFormContent (21-142)
packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceStatusChip.tsx (1)
  • InvoiceRecurrenceStatusChip (26-44)
packages/sdk-react/src/components/receivables/components/InvoiceDetailsInfoBlock.tsx (1)
  • InvoiceDetailsInfoBlock (14-47)
packages/sdk-react/src/components/receivables/components/InvoiceDetailsActions.tsx (3)
packages/sdk-react/src/core/queries/usePermissions.ts (1)
  • useIsActionAllowed (106-126)
packages/sdk-react/src/components/receivables/hooks/useIssueReceivableById.ts (1)
  • useIssueReceivableById (12-52)
packages/sdk-react/src/components/receivables/components/RecordManualPaymentModal.tsx (1)
  • RecordManualPaymentModal (41-175)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build and Push sdk-playground
  • GitHub Check: Build and Push sdk-drop-in
  • GitHub Check: Build and Push sdk-demo-with-nextjs-and-clerk-auth
  • GitHub Check: Build
🔇 Additional comments (22)
packages/sdk-react/src/core/i18n/locales/en/messages.po (2)

9188-9191: Alert copy for manual totals — LGTM

Clear and action‑oriented. No changes needed.


8856-8859: Validation messages for negative amounts — LGTM

These cover the new manual-entry fields appropriately.

Also applies to: 9001-9004, 9563-9566

packages/sdk-react/src/ui/components/calendar.tsx (3)

11-13: Public API + sensible defaults LGTM

Type for buttonVariant is correctly inferred from Button, and defaults for captionLayout and buttonVariant are reasonable.

Also applies to: 19-21


43-66: Component overrides and classNames merging LGTM

Good use of getDefaultClassNames() and non-destructive overrides; Chevron/WeekNumber/DayButton customizations are clean and consistent with shadcn/ui.

Also applies to: 132-168, 170-178, 186-194, 199-221


31-35: Confirm Tailwind config, prefix ("mtw"), and variant plugins (RTL & data-variants) are present

  • packages/sdk-react/src/ui/components/calendar.tsx uses Tailwind v4-style arbitrary values and advanced variants (size-(--cell-size), h-(--cell-size), rtl:**, group-data, data-[...]).
  • Tailwind v4 is present in packages/sdk-playground/package.json (~4.1.11) and packages/rollup-config/package.json (~4.0.17), but repository search did not find a tailwind.config.* file or a configured prefix: 'mtw' — add/verify a workspace tailwind.config.(js|cjs|ts) that sets prefix: 'mtw'.
  • Ensure RTL and data-* variant plugins (or equivalent Tailwind v4 configuration) are enabled so rtl:** and group-data[...] selectors compile.

Files to check: packages/sdk-react/src/ui/components/calendar.tsx, packages/sdk-playground/package.json, packages/rollup-config/package.json

packages/sdk-react/src/components/receivables/InvoiceDetails/ExistingInvoiceDetails/components/EditInvoiceDetails.tsx (6)

10-14: Reminders components barrel import — LGTM.

Import consolidation is tidy and matches usages below.


17-19: New hooks imports are correctly wired.

All three hooks are used and contribute to loading state and submit flow.


86-86: Destructuring isLoading as isEntityLoading — good call.

It’s correctly included in the combined isLoading.


157-158: Dependency array includes isNonVatSupported — correct.

Keeps defaults in sync when entity VAT support changes.


23-23: Guard against missing vat_rate and confirm conversion semantics.

File: packages/sdk-react/src/components/receivables/InvoiceDetails/ExistingInvoiceDetails/components/EditInvoiceDetails.tsx — lines 23 (also applies to lines 138–143).

Accessing lineItem.product.vat_rate.value can throw if vat_rate is absent; use optional chaining + null check and verify whether rateMinorToMajor returns a percentage (e.g. 19) or a fraction (e.g. 0.19) to avoid UI/server unit mismatches.

-          tax_rate_value: isNonVatSupported
-            ? lineItem.product.vat_rate.value !== undefined
-              ? rateMinorToMajor(lineItem.product.vat_rate.value)
-              : undefined
-            : undefined,
+          // Guard against missing vat_rate and avoid hard failure in non-VAT flows
+          tax_rate_value:
+            isNonVatSupported && lineItem.product.vat_rate?.value != null
+              ? rateMinorToMajor(lineItem.product.vat_rate.value)
+              : undefined,

162-163: Validation schema conditional on isNonVatSupported is correct — no change required.

getBaseInvoiceSchema selects nonVatLineItemSchema when isNonVatSupported (tax_rate_value required) and vatLineItemSchema otherwise (vat_rate_id/vat_rate_value required); getUpdateInvoiceValidationSchema simply extends that base.

packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceIterationStatusChip.stories.tsx (1)

1-1: Import reordering looks good.

No behavioral change; fine to keep.

packages/sdk-react/src/components/receivables/hooks/useUpdateRecurrenceById.ts (1)

34-45: LGTM overall.

Invalidations and toasts are consistent with the rest of the hooks.

packages/sdk-react/src/components/receivables/Receivables.tsx (1)

5-5: LGTM on import reorder and hooks placement.

No behavioral changes introduced.

Also applies to: 22-22

packages/sdk-react/src/components/receivables/components/RecurrenceSection.tsx (1)

11-14: Signature/style changes only.

No runtime impact.

packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabOverview.tsx (1)

1-9: Recurrence and linked-docs wiring looks good.

Imports and the new Recurrence section integration are correct; props passed align with the section’s API.

Also applies to: 172-179

packages/sdk-react/src/components/receivables/components/RecurrenceFormContent.tsx (1)

31-48: General refactor/readability improvements look fine.

Date disabling logic and DatePicker wiring remain behaviorally consistent.

Also applies to: 50-141

packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx (1)

446-452: Reminder validation gates are solid.

Good user guidance and early returns to prevent invalid submissions.

Also applies to: 532-569

packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceStatusChip.tsx (1)

15-24: Don't add a "cancelled" alias — API enum uses "canceled"; add a safe default variant.

Schema (packages/sdk-react/src/api/schema.json) defines RecurrenceStatus = ["active","paused","canceled","completed"], so adding "cancelled" is unnecessary. Instead add a safe default to the cva call in packages/sdk-react/src/components/receivables/components/InvoiceRecurrenceStatusChip.tsx — e.g. include defaultVariants: { variant: 'active' } to avoid silent style omission for unexpected values.

Likely an incorrect or invalid review comment.

packages/sdk-react/src/components/receivables/components/InvoiceDetails.tsx (1)

320-321: Confirm custom z-index utility or switch to Tailwind arbitrary value

mtw:xl:z-1299 relies on a custom scale. If not configured, use mtw:xl:z-[1299].

-              className="mtw:w-full mtw:flex-row mtw:xl:max-w-[1200px] mtw:xl:z-1299"
+              className="mtw:w-full mtw:flex-row mtw:xl:max-w-[1200px] mtw:xl:z-[1299]"
packages/sdk-react/src/components/receivables/components/InvoiceDetailsActions.tsx (1)

66-71: Verify permission used for gating fits each action

You’re gating many actions with receivable:update. Some actions may require different scopes (e.g., duplicate = create, send/email = send permission). Risk: unnecessary denials.

Would you like me to map actions → permissions and wire separate checks (e.g., create, send, delete)?

packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts (1)

1-1: Type-only imports and form types: good.

Using lib-agnostic form types (PayableDetailsFormFields) aligns with our forms guidance.

Copy link
Copy Markdown
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 (11)
packages/sdk-react/src/core/i18n/locales/en/messages.po (7)

505-508: Standardize action casing: “Add Discount” → “Add discount”.

UI copy elsewhere uses sentence case for actions (“Add discount”, “Add item”, “Add new bank account”). Suggest aligning.

Apply if you also update call sites using this msgid:

-msgid "Add Discount"
-msgstr "Add Discount"
+msgid "Add discount"
+msgstr "Add discount"

1269-1277: Unify capitalization: “Bank account” vs “Bank Account”.

Two variants appear back‑to‑back; pick one (suggest sentence case).

If you choose sentence case:

- msgid "Bank Account"
- msgstr "Bank Account"
+ msgid "Bank account"
+ msgstr "Bank account"

Note: update components that reference the old msgid.


4064-4067: Fix style: “excl. Tax” → “Excl. tax” (or reuse “Excluding tax”).

  • Current casing is inconsistent with “Incl. tax”.
  • If you want to avoid abbreviations, prefer existing “Excluding tax”.

Option A (match “Incl. tax”):

-msgid "excl. Tax"
-msgstr "excl. Tax"
+msgid "Excl. tax"
+msgstr "Excl. tax"

Option B (spell out; re‑use existing key if possible):

-msgid "excl. Tax"
-msgstr "excl. Tax"
+msgid "Excluding tax"
+msgstr "Excluding tax"

9591-9594: Wording nit: “Total, tax” reads awkwardly.

Consider “Total incl. tax” to mirror “Incl. tax”.

-msgid "Total, tax"
-msgstr "Total, tax"
+msgid "Total incl. tax"
+msgstr "Total incl. tax"

10194-10200: Duplicate/inconsistent: “VAT total” vs “VAT Total”.

Please pick a single casing (suggest sentence case: “VAT total”) and de‑duplicate. If both are needed, add context or explicit ids to avoid drift.

- msgid "VAT Total"
- msgstr "VAT Total"
+ msgid "VAT total"
+ msgstr "VAT total"

Ensure all call sites are updated or switch these to explicit ids to prevent regressions.


2870-2873: Terminology check: “invoice” vs “bill” in Payables.

These strings (“Date when the invoice was issued”, “Invoice currency”, “Invoice issue date”) surface in Payables. If your UX guidelines prefer “bill” in Payables, consider aligning; if “invoice” is intentional (vendor’s invoice), ignore.

Also applies to: 5139-5142, 5177-5179


394-397: Scope reminder: avoid drive‑by i18n tweaks.

Given the PR focuses on manual totals, keep unrelated copy changes out (per costa‑monite’s preference). If any of these weren’t required for the feature, move to a follow‑up.

Also applies to: 432-435, 446-448, 509-513, 514-519, 579-582, 583-587, 655-662, 697-704, 777-783, 846-854

packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx (4)

108-118: Access gating LGTM; minor copy fix ("cannot").

Message reads better as “You cannot…”.

-          )`You can not create receivable with a type other than “${'invoice'}”`}
+          )`You cannot create receivable with a type other than “${'invoice'}”`}

Also applies to: 112-115


260-262: Reminder gating logic: good; localize toast error strings.

All user-facing errors should be wrapped with t(i18n). Keep the logic; only localize strings.

-      showErrorToast(new Error("Set your entity's Tax ID to issue invoice"));
+      showErrorToast(new Error(t(i18n)`Set your entity's Tax ID to issue invoice`));

-      showErrorToast(new Error("Set your entity's VAT ID to issue invoice"));
+      showErrorToast(new Error(t(i18n)`Set your entity's VAT ID to issue invoice`));

-      showErrorToast(
-        new Error("Set your entity's VAT ID and Tax ID to issue invoice")
-      );
+      showErrorToast(
+        new Error(t(i18n)`Set your entity's VAT ID and Tax ID to issue invoice`)
+      );

-      showErrorToast(
-        new Error('Set a Tax ID for this customer to issue invoice')
-      );
+      showErrorToast(
+        new Error(t(i18n)`Set a Tax ID for this customer to issue invoice`)
+      );

-      showErrorToast(
-        new Error('Set a VAT ID for this customer to issue invoice')
-      );
+      showErrorToast(
+        new Error(t(i18n)`Set a VAT ID for this customer to issue invoice`)
+      );

-      showErrorToast(
-        new Error('Set a VAT ID and Tax ID for this customer to issue invoice')
-      );
+      showErrorToast(
+        new Error(t(i18n)`Set a VAT ID and Tax ID for this customer to issue invoice`)
+      );

-      showErrorToast(
-        new Error(
-          'Payment reminders are disabled for this customer. Please enable them in the customer details or turn them off.'
-        )
-      );
+      showErrorToast(
+        new Error(
+          t(i18n)`Payment reminders are disabled for this customer. Please enable them in the customer details or turn them off.`
+        )
+      );

-      showErrorToast(
-        new Error(
-          'No email address is added for the selected customer. Please add it to the customer details or turn off the reminders.'
-        )
-      );
+      showErrorToast(
+        new Error(
+          t(i18n)`No email address is added for the selected customer. Please add it to the customer details or turn off the reminders.`
+        )
+      );

-      showErrorToast(
-        new Error(
-          'Reminders are disabled for this customer, and no email address has been added for it. Please update the details or turn off reminders.'
-        )
-      );
+      showErrorToast(
+        new Error(
+          t(i18n)`Reminders are disabled for this customer, and no email address has been added for it. Please update the details or turn off reminders.`
+        )
+      );

Also applies to: 446-452, 458-486, 488-520, 532-569


1320-1323: Avoid substr; prefer slice (and consider stronger IDs).

substr is non-standard. Also, Math.random() IDs can collide. At minimum, switch to slice. Optionally use crypto.randomUUID() when available.

-              id: item.id || `temp-${Math.random().toString(36).substr(2, 9)}`,
+              id: item.id || `temp-${Math.random().toString(36).slice(2, 11)}`,

Optional:

-              id: item.id || `temp-${Math.random().toString(36).slice(2, 11)}`,
+              id: item.id || `temp-${(typeof crypto !== 'undefined' && 'randomUUID' in crypto)
+                ? (crypto as any).randomUUID()
+                : Math.random().toString(36).slice(2)}`,

849-851: Currency modal: saving without selection sets currency to undefined.

If the user opens the modal and hits Save without choosing a currency, tempCurrency stays undefined, and you set actualCurrency to undefined.

-                  <DropdownMenuItem
-                    onClick={() => setIsCurrencyModalOpen(true)}
-                  >
+                  <DropdownMenuItem
+                    onClick={() => {
+                      setTempCurrency(actualCurrency);
+                      setIsCurrencyModalOpen(true);
+                    }}
+                  >
-  const handleCurrencySubmit = () => {
-    if (tempCurrency !== actualCurrency) {
+  const handleCurrencySubmit = () => {
+    if (tempCurrency && tempCurrency !== actualCurrency) {
       const validLineItems = lineItems.filter((item) => {
         return item.product?.name?.trim() !== '';
       });
       // ...
-        setActualCurrency(tempCurrency);
+        setActualCurrency(tempCurrency);

Also applies to: 721-723

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c6a34f and fc94620.

📒 Files selected for processing (3)
  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx (17 hunks)
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabOverview.tsx (3 hunks)
  • packages/sdk-react/src/core/i18n/locales/en/messages.po (143 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/sdk-react/src/components/receivables/components/InvoiceDetailsTabOverview.tsx
🧰 Additional context used
📓 Path-based instructions (2)
packages/sdk-react/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/sdk-react/.cursor/rules/api-calls.mdc)

packages/sdk-react/**/*.{ts,tsx}: Use proper TypeScript types for API data and leverage type inference from the OpenAPI schema
Use custom TypeScript types (e.g., Invoice, CreateInvoiceInput) imported from '@/types/api' for Monite API data structures

Files:

  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx
packages/sdk-react/**/*.tsx

📄 CodeRabbit inference engine (packages/sdk-react/.cursor/rules/ui-components.mdc)

packages/sdk-react/**/*.tsx: Always use shadcn/ui components when creating new UI elements
Import components from '@/ui/components/*' when using shadcn/ui components
All Tailwind classes should have the prefix 'mtw' (e.g., 'mtw:w-full')
Use Tailwind CSS utility classes for all styling
Avoid inline styles or styled-components
Follow Tailwind's mobile-first responsive design approach
Use Tailwind's color palette and spacing scale
Do NOT use Material UI for new components
When modifying files with Material UI: only update to shadcn/ui if the component needs significant changes; otherwise, maintain existing MUI implementation; add TODO comments when MUI components should be migrated
Always prefer shadcn/ui components such as Button, Input, Card, Dialog, etc., from '@/ui/components/[component]'
Use React Hook Form with shadcn/ui form components
Use 'lucide-react' for icons and import icons like 'import { Search, Menu, X } from "lucide-react"'
Always use named imports from the specific ui component file
Use the variant prop for different styles (e.g., variant="outline" for buttons)
Use the size prop when available (e.g., size="sm" for smaller buttons)
Extend components with className prop using Tailwind utilities
When updating Material UI components to shadcn/ui: map MUI component to shadcn/ui equivalent, convert sx props and makeStyles to Tailwind classes, update event handlers if API differs, test thoroughly, remove MUI imports only after confirming all usages are replaced
Use TypeScript for all new components
Leverage component prop types for better IntelliSense

Files:

  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx
🧠 Learnings (6)
📓 Common learnings
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/core/i18n/locales/en/messages.po:8470-8473
Timestamp: 2025-09-04T15:54:24.944Z
Learning: For PRs focused on the invoice preview template (feat/DEV-15554), costa-monite prefers to exclude out-of-scope tweaks like minor i18n spacing changes (e.g., "Subtotal{0}" → "Subtotal {0}") and handle them in a separate follow-up.
📚 Learning: 2025-09-04T15:54:24.944Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/core/i18n/locales/en/messages.po:8470-8473
Timestamp: 2025-09-04T15:54:24.944Z
Learning: For PRs focused on the invoice preview template (feat/DEV-15554), costa-monite prefers to exclude out-of-scope tweaks like minor i18n spacing changes (e.g., "Subtotal{0}" → "Subtotal {0}") and handle them in a separate follow-up.

Applied to files:

  • packages/sdk-react/src/core/i18n/locales/en/messages.po
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Always prefer shadcn/ui components such as Button, Input, Card, Dialog, Select, Checkbox, RadioGroup, Switch, Textarea, Label, Alert, Badge, Toast, Tabs, Table, Form, DropdownMenu, Sheet, Skeleton, Separator

Applied to files:

  • packages/sdk-react/src/core/i18n/locales/en/messages.po
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Use React Hook Form with shadcn/ui form components for form handling

Applied to files:

  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : Use React Hook Form with shadcn/ui form components

Applied to files:

  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx
📚 Learning: 2025-09-08T09:09:14.289Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/InvoicePreviewMonite.tsx:320-336
Timestamp: 2025-09-08T09:09:14.289Z
Learning: In InvoicePreviewMonite component at packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/InvoicePreviewMonite.tsx, the price values from item.product.price.value are already in minor units (as defined in the PriceFloat API schema), so they can be passed directly to formatCurrencyToDisplay without conversion.

Applied to files:

  • packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx
🧬 Code graph analysis (1)
packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx (4)
packages/sdk-react/src/core/queries/useCounterpart.ts (1)
  • useCounterpartContactList (342-422)
packages/sdk-react/src/components/receivables/hooks/useCreateReceivable.ts (1)
  • useCreateReceivable (7-29)
packages/sdk-react/src/components/receivables/hooks/useCreateRecurrence.ts (1)
  • useCreateRecurrence (7-20)
packages/sdk-react/src/components/receivables/hooks/useGetInvoiceRequiredFields.ts (1)
  • useGetInvoiceRequiredFields (13-41)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build and Push sdk-playground
  • GitHub Check: Build and Push sdk-demo-with-nextjs-and-clerk-auth
  • GitHub Check: Build and Push sdk-drop-in
  • GitHub Check: Build
🔇 Additional comments (8)
packages/sdk-react/src/core/i18n/locales/en/messages.po (2)

9188-9191: Good safety copy for manual-totals mode.

“The amounts have been modified...” is clear and action‑oriented. No changes needed.


8856-8860: Validation messages read well and are consistent.

“cannot be negative” matches other Payables validations (e.g., tax rate). Ship as is.

Also applies to: 9001-9004, 9563-9566

packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/CreateReceivables.tsx (6)

1246-1249: Double-check VAT flag semantics passed to ItemsSection.

isNonVatSupported || !isLineItemVatRateIdRequired changes behavior. Confirm ItemsSection interprets this as intended (i.e., hide/disable VAT rate when not required).


272-276: Create mutations wiring LGTM.

Using mutateAsync for receivable and mutate for recurrence is appropriate given subsequent flow control.


279-292: Required-fields query inputs LGTM.

Good defensive checks to avoid stale IDs/country. Matches useGetInvoiceRequiredFields expectations.


888-891: CTA label toggle LGTM.

Clear UX: “Activate” when recurrence is enabled, otherwise “Save and continue”.


1-6: Imports and small plumbing changes LGTM.

Additions are cohesive and correctly scoped.

Also applies to: 24-26, 29-30, 48-48, 220-223, 260-262


649-671: Wire recurrence payload from form values — don't hard-code

schema.json's CreateRecurrencePayload exposes frequency (day/week/month/quarter/year; default 'month'), interval and automation_level — source these from RecurrenceSection / RHF values and confirm AutomationLevel accepts 'issue' before defaulting to it.

-          body: {
-            invoice_id: receivableId,
-            frequency: 'month',
-            interval: 1,
+          body: {
+            invoice_id: receivableId,
+            // TODO: wire these from RecurrenceSection/RHF values if available
+            frequency: values.recurrence_frequency ?? 'month',
+            interval: values.recurrence_interval ?? 1,
             start_date: values.recurrence_start_date
               ? format(new Date(values.recurrence_start_date), 'yyyy-MM-dd')
               : undefined,
             end_date: values.recurrence_end_date
               ? format(new Date(values.recurrence_end_date), 'yyyy-MM-dd')
               : undefined,
-            automation_level: 'issue',
+            automation_level: values.recurrence_automation_level ?? 'issue',
+            // If supported by API:
+            // issue_mode: values.recurrence_issue_mode,
           },

Copy link
Copy Markdown
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

♻️ Duplicate comments (2)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (2)

179-184: Wire through disableAutoCalculateTotals (currently commented).

This prevents the feature flag from working and cascades into the debug override below.

Apply this diff:

     const {
       ocrRequiredFields,
       optionalFields,
       isTagsDisabled,
-      // disableAutoCalculateTotals, // TODO: commented for debugging (DEV-15658)
+      disableAutoCalculateTotals,
     } = usePayableDetailsThemeProps(inProps);

189-193: Remove debug override; use the incoming prop.

Hardcoding disables the feature and diverges from componentSettings.

Apply this diff:

-    // const isDisableAutoCalculateTotals = disableAutoCalculateTotals || false; // TODO: commented for debugging (DEV-15658)
-    const isDisableAutoCalculateTotals = false; // TODO: for debugging (DEV-15658)
+    const isDisableAutoCalculateTotals = !!disableAutoCalculateTotals;
     const isLineItemsEditable = isDisableAutoCalculateTotals || !payable;
     const isTotalsEditable = isDisableAutoCalculateTotals;
🧹 Nitpick comments (5)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (5)

270-285: Deduplicate reset logic by reusing memoized defaultValues.

Avoids recalculating prepareDefaultValues and keeps sources in one place.

Apply this diff:

-    useEffect(() => {
-      reset(
-        prepareDefaultValues(
-          formatFromMinorUnits,
-          payable,
-          lineItems,
-          !isDisableAutoCalculateTotals
-        )
-      );
-    }, [
-      payable,
-      formatFromMinorUnits,
-      reset,
-      lineItems,
-      isDisableAutoCalculateTotals,
-    ]);
+    useEffect(() => {
+      reset(defaultValues);
+    }, [defaultValues, reset]);

748-771: Show 0 currency amounts correctly; avoid falsy checks and use nullish coalescing.

Current truthy checks render “—” for legitimate 0 values, and || 0 masks 0.

Apply this diff:

-                              {isTotalsEditable ? (
+                              {isTotalsEditable ? (
                                 <RHFTextField
                                   name="subtotal"
                                   control={control}
                                   size="small"
                                   type="number"
-                                  inputProps={{ min: 0 }}
+                                  inputProps={{ min: 0, step: 0.01 }}
                                   sx={{ width: 150 }}
                                   InputProps={{
                                     endAdornment:
                                       getSymbolFromCurrency(currentCurrency),
                                   }}
                                 />
-                              ) : totals.subtotal && currentCurrency ? (
+                              ) : currentCurrency && totals.subtotal != null ? (
                                 formatCurrencyToDisplay(
-                                  formatToMinorUnits(
-                                    totals.subtotal,
-                                    currentCurrency
-                                  ) || 0,
+                                  formatToMinorUnits(
+                                    totals.subtotal,
+                                    currentCurrency
+                                  ) ?? 0,
                                   currentCurrency
                                 )
                               ) : (
                                 '—'
                               )}

812-842: Same 0-handling fix for VAT total; add decimal step.

Apply this diff:

-                              {isTotalsEditable ? (
+                              {isTotalsEditable ? (
                                 <RHFTextField
                                   name="tax_amount"
                                   control={control}
                                   type="number"
-                                  inputProps={{ min: 0 }}
+                                  inputProps={{ min: 0, step: 0.01 }}
                                   sx={{ width: 150 }}
                                   InputProps={{
                                     endAdornment:
                                       getSymbolFromCurrency(currentCurrency),
                                   }}
                                 />
-                              ) : totals.taxes && currentCurrency ? (
+                              ) : currentCurrency && totals.taxes != null ? (
                                 formatCurrencyToDisplay(
-                                  formatToMinorUnits(
-                                    totals.taxes,
-                                    currentCurrency
-                                  ) || 0,
+                                  formatToMinorUnits(
+                                    totals.taxes,
+                                    currentCurrency
+                                  ) ?? 0,
                                   currentCurrency
                                 )
                               ) : (
                                 '—'
                               )}

851-882: Same 0-handling fix for Total; add decimal step.

Apply this diff:

-                              {isTotalsEditable ? (
+                              {isTotalsEditable ? (
                                 <RHFTextField
                                   name="total_amount"
                                   control={control}
                                   type="number"
-                                  inputProps={{ min: 0 }}
+                                  inputProps={{ min: 0, step: 0.01 }}
                                   sx={{ width: 150 }}
                                   InputProps={{
                                     endAdornment:
                                       getSymbolFromCurrency(currentCurrency),
                                   }}
                                 />
                               ) : (
                                 <Typography variant="subtitle1">
-                                  {totals.total && currentCurrency
+                                  {currentCurrency && totals.total != null
                                     ? formatCurrencyToDisplay(
-                                        formatToMinorUnits(
-                                          totals.total,
-                                          currentCurrency
-                                        ) || 0,
+                                        formatToMinorUnits(
+                                          totals.total,
+                                          currentCurrency
+                                        ) ?? 0,
                                         currentCurrency
                                       )
                                     : '—'}
                                 </Typography>
                               )}

736-747: Prefer lucide-react icons for new UI per repo guidelines.

Use Plus/Trash2 from lucide-react for newly added controls; add TODO if deferring broader migration.

Apply these diffs within the shown ranges:

-                                <Button
-                                  startIcon={<AddIcon />}
+                                <Button
+                                  startIcon={<Plus size={16} />}
-                                <IconButton
+                                <IconButton
                                   aria-label="delete"
                                   onClick={() => {
                                     const setValue = methods.setValue;
                                     setValue('discount', null);
                                   }}
                                 >
-                                  <DeleteIcon />
+                                  <Trash2 size={18} />
                                 </IconButton>

Add imports:

// at the icons import section
import { Plus, Trash2 } from 'lucide-react';

Also applies to: 785-793

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc94620 and 953effe.

📒 Files selected for processing (1)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (14 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
packages/sdk-react/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/sdk-react/.cursor/rules/api-calls.mdc)

packages/sdk-react/**/*.{ts,tsx}: Use proper TypeScript types for API data and leverage type inference from the OpenAPI schema
Use custom TypeScript types (e.g., Invoice, CreateInvoiceInput) imported from '@/types/api' for Monite API data structures

Files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
packages/sdk-react/**/*.tsx

📄 CodeRabbit inference engine (packages/sdk-react/.cursor/rules/ui-components.mdc)

packages/sdk-react/**/*.tsx: Always use shadcn/ui components when creating new UI elements
Import components from '@/ui/components/*' when using shadcn/ui components
All Tailwind classes should have the prefix 'mtw' (e.g., 'mtw:w-full')
Use Tailwind CSS utility classes for all styling
Avoid inline styles or styled-components
Follow Tailwind's mobile-first responsive design approach
Use Tailwind's color palette and spacing scale
Do NOT use Material UI for new components
When modifying files with Material UI: only update to shadcn/ui if the component needs significant changes; otherwise, maintain existing MUI implementation; add TODO comments when MUI components should be migrated
Always prefer shadcn/ui components such as Button, Input, Card, Dialog, etc., from '@/ui/components/[component]'
Use React Hook Form with shadcn/ui form components
Use 'lucide-react' for icons and import icons like 'import { Search, Menu, X } from "lucide-react"'
Always use named imports from the specific ui component file
Use the variant prop for different styles (e.g., variant="outline" for buttons)
Use the size prop when available (e.g., size="sm" for smaller buttons)
Extend components with className prop using Tailwind utilities
When updating Material UI components to shadcn/ui: map MUI component to shadcn/ui equivalent, convert sx props and makeStyles to Tailwind classes, update event handlers if API differs, test thoroughly, remove MUI imports only after confirming all usages are replaced
Use TypeScript for all new components
Leverage component prop types for better IntelliSense

Files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
🧠 Learnings (10)
📓 Common learnings
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/core/i18n/locales/en/messages.po:8470-8473
Timestamp: 2025-09-04T15:54:24.944Z
Learning: For PRs focused on the invoice preview template (feat/DEV-15554), costa-monite prefers to exclude out-of-scope tweaks like minor i18n spacing changes (e.g., "Subtotal{0}" → "Subtotal {0}") and handle them in a separate follow-up.
📚 Learning: 2025-09-17T07:36:02.844Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#828
File: packages/sdk-react/src/components/payables/PayableDetails/PayableDetails.tsx:54-54
Timestamp: 2025-09-17T07:36:02.844Z
Learning: All payables components (Payables, PayableDetails, PayableDetailsForm, PayableDetailsInfo, PayableLineItemsForm) consistently default enableGLCodes to false, not true. The JSDoc in Payables.tsx correctly documents default false.

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-09-04T16:18:44.198Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx:6-6
Timestamp: 2025-09-04T16:18:44.198Z
Learning: For forms in monite-sdk, prefer lib-agnostic TypeScript types (e.g., PayableDetailsFormFields) for component generics and contracts; avoid coupling components to Zod-inferred types. Use Zod only at the validation layer (e.g., zodResolver with a schema typed as ZodType<FormValues>).

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : Use React Hook Form with shadcn/ui form components

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Use lucide-react for icons and import icons like: import { Search, Menu, X } from 'lucide-react'

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : Use 'lucide-react' for icons and import icons like 'import { Search, Menu, X } from "lucide-react"'

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Always prefer shadcn/ui components such as Button, Input, Card, Dialog, Select, Checkbox, RadioGroup, Switch, Textarea, Label, Alert, Badge, Toast, Tabs, Table, Form, DropdownMenu, Sheet, Skeleton, Separator

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Always use shadcn/ui components when creating new UI elements

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Use React Hook Form with shadcn/ui form components for form handling

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-09-08T09:09:14.289Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/InvoicePreviewMonite.tsx:320-336
Timestamp: 2025-09-08T09:09:14.289Z
Learning: In InvoicePreviewMonite component at packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/InvoicePreviewMonite.tsx, the price values from item.product.price.value are already in minor units (as defined in the PriceFloat API schema), so they can be passed directly to formatCurrencyToDisplay without conversion.

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
🧬 Code graph analysis (1)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (5)
packages/sdk-react/src/components/payables/hooks/usePayableDetailsThemeProps.ts (1)
  • usePayableDetailsThemeProps (4-24)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts (1)
  • prepareDefaultValues (83-148)
packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx (1)
  • PayableLineItemsForm (12-148)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsInfo/DisplayPayableLineItems.tsx (1)
  • DisplayPayableLineItems (15-67)
packages/sdk-react/src/ui/RHF/RHFTextField/RHFTextField.tsx (1)
  • RHFTextField (8-78)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build and Push sdk-playground
  • GitHub Check: Build and Push sdk-demo-with-nextjs-and-clerk-auth
  • GitHub Check: Build and Push sdk-drop-in
  • GitHub Check: Build
🔇 Additional comments (5)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (5)

194-203: Default-values memoization looks good.


335-352: Validate dirty detection for line items.

dirtyFields.lineItems can be non-array in some RHF cases (e.g., full-array changes). Please verify this logic with add/remove/edit flows.

If needed, guard with Array.isArray(dirtyFields.lineItems) before .length/.some checks.


353-379: Change‑alert gating looks solid.


690-711: Items edit/read-only switch LGTM.


660-675: MoniteCurrency generic usage is correct.

Copy link
Copy Markdown
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

♻️ Duplicate comments (1)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (1)

74-74: Fix lucide-react import/usage (build blocker).

lucide-react exports AlertCircle (no "Icon" suffix). Importing AlertCircleIcon will fail.

- import { AlertCircleIcon } from 'lucide-react';
+ import { AlertCircle } from 'lucide-react';
-                    <Alert variant="warning" icon={<AlertCircleIcon />}>
+                    <Alert variant="warning" icon={<AlertCircle />}>

Also applies to: 699-706

🧹 Nitpick comments (3)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (3)

733-746: Use MUI InputAdornment for currency suffix and set decimal input options.

Passing a raw string to InputProps.endAdornment isn’t the intended MUI API and may render incorrectly. Also, add step/inputMode for consistent decimal input.

@@
   TableRow,
   TextField,
   Typography,
 } from '@mui/material';
+import InputAdornment from '@mui/material/InputAdornment';
@@
-                              {isDisableAutoCalculateTotals ? (
+                              {isDisableAutoCalculateTotals ? (
                                 <RHFTextField
                                   name="subtotal"
                                   control={control}
                                   size="small"
-                                  type="number"
-                                  inputProps={{ min: 0 }}
+                                  type="number"
+                                  inputProps={{ min: 0, step: 0.01, inputMode: 'decimal' }}
                                   sx={{ width: 150 }}
                                   InputProps={{
-                                    endAdornment:
-                                      getSymbolFromCurrency(currentCurrency),
+                                    endAdornment: (
+                                      <InputAdornment position="end">
+                                        {getSymbolFromCurrency(currentCurrency)}
+                                      </InputAdornment>
+                                    ),
                                   }}
                                 />
                               ) : totals.subtotal && currentCurrency ? (
@@
                                 <RHFTextField
                                   name="discount"
                                   control={control}
-                                  type="number"
-                                  inputProps={{ min: 0, step: 0.01 }}
+                                  type="number"
+                                  inputProps={{ min: 0, step: 0.01, inputMode: 'decimal' }}
                                   sx={{ width: 150 }}
                                   InputProps={{
-                                    endAdornment:
-                                      getSymbolFromCurrency(currentCurrency),
+                                    endAdornment: (
+                                      <InputAdornment position="end">
+                                        {getSymbolFromCurrency(currentCurrency)}
+                                      </InputAdornment>
+                                    ),
                                   }}
                                 />
@@
-                              {isDisableAutoCalculateTotals ? (
+                              {isDisableAutoCalculateTotals ? (
                                 <RHFTextField
                                   name="tax_amount"
                                   control={control}
-                                  type="number"
-                                  inputProps={{ min: 0 }}
+                                  type="number"
+                                  inputProps={{ min: 0, step: 0.01, inputMode: 'decimal' }}
                                   sx={{ width: 150 }}
                                   InputProps={{
-                                    endAdornment:
-                                      getSymbolFromCurrency(currentCurrency),
+                                    endAdornment: (
+                                      <InputAdornment position="end">
+                                        {getSymbolFromCurrency(currentCurrency)}
+                                      </InputAdornment>
+                                    ),
                                   }}
                                 />
@@
-                              {isDisableAutoCalculateTotals ? (
+                              {isDisableAutoCalculateTotals ? (
                                 <RHFTextField
                                   name="total_amount"
                                   control={control}
-                                  type="number"
-                                  inputProps={{ min: 0 }}
+                                  type="number"
+                                  inputProps={{ min: 0, step: 0.01, inputMode: 'decimal' }}
                                   sx={{ width: 150 }}
                                   InputProps={{
-                                    endAdornment:
-                                      getSymbolFromCurrency(currentCurrency),
+                                    endAdornment: (
+                                      <InputAdornment position="end">
+                                        {getSymbolFromCurrency(currentCurrency)}
+                                      </InputAdornment>
+                                    ),
                                   }}
                                 />

Also applies to: 779-789, 803-815, 843-853, 55-72


6-6: Remove unused import.

DisplayPayableLineItems is imported but not used in this file.

-import { DisplayPayableLineItems } from '../PayableDetailsInfo';

189-199: Avoid double‑negatives; consider a positively named flag.

isDisableAutoCalculateTotals forces repeated negation. A local const like isCalculateTotalsEnabled = !disableAutoCalculateTotals would improve readability across prepareDefaultValues/prepareSubmit/UI checks.

Also applies to: 268-282, 410-414, 698-706, 733-757, 803-867

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 41687dd and fb64a56.

📒 Files selected for processing (1)
  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (14 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
packages/sdk-react/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/sdk-react/.cursor/rules/api-calls.mdc)

packages/sdk-react/**/*.{ts,tsx}: Use proper TypeScript types for API data and leverage type inference from the OpenAPI schema
Use custom TypeScript types (e.g., Invoice, CreateInvoiceInput) imported from '@/types/api' for Monite API data structures

Files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
packages/sdk-react/**/*.tsx

📄 CodeRabbit inference engine (packages/sdk-react/.cursor/rules/ui-components.mdc)

packages/sdk-react/**/*.tsx: Always use shadcn/ui components when creating new UI elements
Import components from '@/ui/components/*' when using shadcn/ui components
All Tailwind classes should have the prefix 'mtw' (e.g., 'mtw:w-full')
Use Tailwind CSS utility classes for all styling
Avoid inline styles or styled-components
Follow Tailwind's mobile-first responsive design approach
Use Tailwind's color palette and spacing scale
Do NOT use Material UI for new components
When modifying files with Material UI: only update to shadcn/ui if the component needs significant changes; otherwise, maintain existing MUI implementation; add TODO comments when MUI components should be migrated
Always prefer shadcn/ui components such as Button, Input, Card, Dialog, etc., from '@/ui/components/[component]'
Use React Hook Form with shadcn/ui form components
Use 'lucide-react' for icons and import icons like 'import { Search, Menu, X } from "lucide-react"'
Always use named imports from the specific ui component file
Use the variant prop for different styles (e.g., variant="outline" for buttons)
Use the size prop when available (e.g., size="sm" for smaller buttons)
Extend components with className prop using Tailwind utilities
When updating Material UI components to shadcn/ui: map MUI component to shadcn/ui equivalent, convert sx props and makeStyles to Tailwind classes, update event handlers if API differs, test thoroughly, remove MUI imports only after confirming all usages are replaced
Use TypeScript for all new components
Leverage component prop types for better IntelliSense

Files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
🧠 Learnings (10)
📓 Common learnings
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/core/i18n/locales/en/messages.po:8470-8473
Timestamp: 2025-09-04T15:54:24.944Z
Learning: For PRs focused on the invoice preview template (feat/DEV-15554), costa-monite prefers to exclude out-of-scope tweaks like minor i18n spacing changes (e.g., "Subtotal{0}" → "Subtotal {0}") and handle them in a separate follow-up.
📚 Learning: 2025-09-17T07:36:02.844Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#828
File: packages/sdk-react/src/components/payables/PayableDetails/PayableDetails.tsx:54-54
Timestamp: 2025-09-17T07:36:02.844Z
Learning: All payables components (Payables, PayableDetails, PayableDetailsForm, PayableDetailsInfo, PayableLineItemsForm) consistently default enableGLCodes to false, not true. The JSDoc in Payables.tsx correctly documents default false.

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-09-04T16:18:44.198Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx:6-6
Timestamp: 2025-09-04T16:18:44.198Z
Learning: For forms in monite-sdk, prefer lib-agnostic TypeScript types (e.g., PayableDetailsFormFields) for component generics and contracts; avoid coupling components to Zod-inferred types. Use Zod only at the validation layer (e.g., zodResolver with a schema typed as ZodType<FormValues>).

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : Use 'lucide-react' for icons and import icons like 'import { Search, Menu, X } from "lucide-react"'

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Use lucide-react for icons and import icons like: import { Search, Menu, X } from 'lucide-react'

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:11:43.801Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: packages/sdk-react/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:11:43.801Z
Learning: Applies to packages/sdk-react/**/*.tsx : Use React Hook Form with shadcn/ui form components

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Always prefer shadcn/ui components such as Button, Input, Card, Dialog, Select, Checkbox, RadioGroup, Switch, Textarea, Label, Alert, Badge, Toast, Tabs, Table, Form, DropdownMenu, Sheet, Skeleton, Separator

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Always use shadcn/ui components when creating new UI elements

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-07-21T08:10:47.057Z
Learnt from: CR
PR: team-monite/monite-sdk#0
File: examples/with-nextjs-and-clerk-auth/.cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-21T08:10:47.057Z
Learning: Applies to examples/with-nextjs-and-clerk-auth/src/components/**/*.tsx : Use React Hook Form with shadcn/ui form components for form handling

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
📚 Learning: 2025-09-08T09:09:14.289Z
Learnt from: costa-monite
PR: team-monite/monite-sdk#785
File: packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/InvoicePreviewMonite.tsx:320-336
Timestamp: 2025-09-08T09:09:14.289Z
Learning: In InvoicePreviewMonite component at packages/sdk-react/src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/InvoicePreviewMonite.tsx, the price values from item.product.price.value are already in minor units (as defined in the PriceFloat API schema), so they can be passed directly to formatCurrencyToDisplay without conversion.

Applied to files:

  • packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
🧬 Code graph analysis (1)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (3)
packages/sdk-react/src/components/payables/hooks/usePayableDetailsThemeProps.ts (1)
  • usePayableDetailsThemeProps (4-24)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/helpers.ts (1)
  • prepareDefaultValues (83-148)
packages/sdk-react/src/ui/RHF/RHFTextField/RHFTextField.tsx (1)
  • RHFTextField (8-78)
🔇 Additional comments (2)
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx (2)

179-184: Flag wiring looks correct.

disableAutoCalculateTotals is now properly sourced from theme props and used to drive behavior. Thanks for removing the hardcoded debug override.

If partners pass disableAutoCalculateTotals via componentSettings, verify it reaches this component:

  • componentSettings.payables.disableAutoCalculateTotals is set
  • usePayableDetailsThemeProps returns it
  • This file toggles manual totals accordingly

Also applies to: 189-199


699-706: Confirmed — Alert supports variant="warning" and icon prop.

packages/sdk-react/src/ui/components/alert.tsx defines a 'warning' variant in alertVariants and AlertProps includes icon?: React.ReactNode which is rendered when provided.

Comment on lines +746 to +756
) : totals.subtotal && currentCurrency ? (
formatCurrencyToDisplay(
formatToMinorUnits(
totals.subtotal,
currentCurrency
)
: '—'}
) || 0,
currentCurrency
)
) : (
'—'
)}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Zero values render as em dash — should render 0.00.

Truthiness checks treat 0 as falsy, so subtotal/taxes/total display “—” when they are 0.

-                              ) : totals.subtotal && currentCurrency ? (
+                              ) : (totals.subtotal !== null && totals.subtotal !== undefined) && currentCurrency ? (
@@
-                              ) : totals.taxes && currentCurrency ? (
+                              ) : (totals.taxes !== null && totals.taxes !== undefined) && currentCurrency ? (
@@
-                                  {totals.total && currentCurrency
+                                  {(totals.total !== null && totals.total !== undefined) && currentCurrency

Also applies to: 815-825, 855-866

🤖 Prompt for AI Agents
In
packages/sdk-react/src/components/payables/PayableDetails/PayableDetailsForm/PayableDetailsForm.tsx
around lines 746-756 (and similarly at 815-825 and 855-866), the render logic
uses truthiness checks like "totals.subtotal && currentCurrency" which treats 0
as falsy causing zeros to render as '—'; change the condition to explicitly test
for null/undefined (e.g., totals.subtotal != null && currentCurrency != null or
Number.isFinite(totals.subtotal) && currentCurrency) so zero values pass, then
keep the existing formatting call; apply the same null/undefined check pattern
to taxes and total blocks and only render the em dash when value or currency is
actually missing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge pullpreview Generate a live preview for this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants