Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Apply Template to All Categories in Group for Mobile #3737

Open
wants to merge 30 commits into
base: master
Choose a base branch
from

Conversation

Dreptschar
Copy link
Contributor

Adds Feature from #3040 and expands #3666

dreptschar and others added 27 commits October 14, 2024 18:02
…emplate-apply

# Conflicts:
#	packages/desktop-client/src/components/budget/index.tsx
…emplate-apply

# Conflicts:
#	packages/loot-core/src/server/api.ts
#	packages/loot-core/src/types/api-handlers.d.ts
#	packages/loot-core/src/types/server-handlers.d.ts
@actual-github-bot actual-github-bot bot changed the title Apply Template to All Categories in Group for Mobile [WIP] Apply Template to All Categories in Group for Mobile Oct 25, 2024
Copy link

netlify bot commented Oct 25, 2024

Deploy Preview for actualbudget ready!

Name Link
🔨 Latest commit 22d7e0d
🔍 Latest deploy log https://app.netlify.com/sites/actualbudget/deploys/671c0779a80f260008db3da3
😎 Deploy Preview https://deploy-preview-3737.demo.actualbudget.org
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@Dreptschar Dreptschar changed the title [WIP] Apply Template to All Categories in Group for Mobile Apply Template to All Categories in Group for Mobile Oct 25, 2024
Copy link
Contributor

github-actions bot commented Oct 25, 2024

Bundle Stats — desktop-client

Hey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle.

As this PR is updated, I'll keep you updated on how the bundle size is impacted.

Total

Files count Total bundle size % Changed
9 5.32 MB → 5.32 MB (+5.22 kB) +0.10%
Changeset
File Δ Size
src/components/modals/EnvelopeBudgetGroupMenuModal.tsx 🆕 +2.17 kB 0 B → 2.17 kB
src/components/budget/envelope/BudgetMenuGroup.tsx 🆕 +678 B 0 B → 678 B
src/components/budget/SidebarGroup.tsx 📈 +553 B (+9.59%) 5.63 kB → 6.17 kB
src/components/budget/ExpenseGroup.tsx 📈 +74 B (+2.91%) 2.49 kB → 2.56 kB
src/components/budget/index.tsx 📈 +232 B (+2.57%) 8.8 kB → 9.03 kB
home/runner/work/actual/actual/packages/loot-core/src/client/actions/queries.ts 📈 +205 B (+2.50%) 8.01 kB → 8.21 kB
src/components/Modals.tsx 📈 +388 B (+2.46%) 15.39 kB → 15.76 kB
src/components/mobile/budget/BudgetTable.jsx 📈 +847 B (+1.55%) 53.35 kB → 54.17 kB
src/components/budget/BudgetTable.jsx 📈 +78 B (+1.27%) 6.01 kB → 6.08 kB
src/components/budget/BudgetCategories.jsx 📈 +74 B (+0.85%) 8.53 kB → 8.6 kB
View detailed bundle breakdown

Added

No assets were added

Removed

No assets were removed

Bigger

Asset File Size % Changed
static/js/index.js 3.33 MB → 3.34 MB (+3.41 kB) +0.10%
static/js/wide.js 232.16 kB → 233.15 kB (+1011 B) +0.43%
static/js/narrow.js 82.57 kB → 83.39 kB (+847 B) +1.00%

Smaller

No assets were smaller

Unchanged

Asset File Size % Changed
static/js/indexeddb-main-thread-worker-e59fee74.js 13.5 kB 0%
static/js/resize-observer.js 18.37 kB 0%
static/js/usePreviewTransactions.js 1.64 kB 0%
static/js/BackgroundImage.js 122.29 kB 0%
static/js/AppliedFilters.js 21.3 kB 0%
static/js/ReportRouter.js 1.5 MB 0%

Copy link
Contributor

github-actions bot commented Oct 25, 2024

Bundle Stats — loot-core

Hey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle.

As this PR is updated, I'll keep you updated on how the bundle size is impacted.

Total

Files count Total bundle size % Changed
1 1.27 MB → 1.27 MB (+438 B) +0.03%
Changeset
File Δ Size
packages/loot-core/src/server/budget/app.ts 📈 +110 B (+6.04%) 1.78 kB → 1.89 kB
packages/loot-core/src/server/budget/goaltemplates.ts 📈 +1.09 kB (+5.27%) 20.61 kB → 21.7 kB
View detailed bundle breakdown

Added

No assets were added

Removed

No assets were removed

Bigger

Asset File Size % Changed
kcab.worker.js 1.27 MB → 1.27 MB (+438 B) +0.03%

Smaller

No assets were smaller

Unchanged

No assets were unchanged

@Dreptschar
Copy link
Contributor Author

Ive add a new menu to groups. Idk if thats a good solution would love to hear some opinions

Copy link
Contributor

coderabbitai bot commented Oct 25, 2024

Walkthrough

The pull request introduces several modifications across various components within the desktop client of the application, primarily focused on enhancing budget management functionalities. A new modal component, EnvelopeBudgetGroupMenuModal, is added to handle budget group templates, integrated into the existing Modals component. The BudgetCategories, BudgetTable, and ExpenseGroup components are updated to include a new prop, onApplyBudgetTemplatesInGroup, which allows for the application of budget templates within a specific group context. Additionally, the SidebarGroup component is enhanced with a new callback function to facilitate the application of budget templates from the sidebar. A new component, BudgetMenuGroup, is introduced to extend menu functionalities related to budget templates. The server-side code is also updated to support applying multiple budget templates through new methods and action types. Overall, these changes aim to improve user interaction and functionality related to budget management within the application.

Possibly related PRs

Suggested labels

:sparkles: Merged

Suggested reviewers

  • youngcw
  • MikesGlitch

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🧹 Outside diff range and nitpick comments (13)
packages/desktop-client/src/components/budget/envelope/BudgetMenuGroup.tsx (1)

39-39: Add translation context for better i18n.

The translation key 'Apply budget template' might need additional context for translators to understand its usage in a group menu context.

-text: t('Apply budget template'),
+text: t('Apply budget template', {
+  context: 'budgetGroupMenu'
+}),
packages/loot-core/src/server/budget/types/handlers.d.ts (1)

83-86: LGTM! Consider adding JSDoc documentation.

The new method signature is well-structured and consistent with existing template-related methods. Consider adding JSDoc comments to document the purpose and parameters.

+  /**
+   * Applies budget templates to multiple categories within a specified month
+   * @param {Object} arg - The arguments object
+   * @param {string} arg.month - The target month for applying templates
+   * @param {string[]} arg.categoryIds - Array of category IDs to apply templates to
+   * @returns {Promise<Notification>} Notification indicating success or failure
+   */
   'budget/apply-multiple-templates': (arg: {
     month: string;
     categoryIds: string[];
   }) => Promise<Notification>;
packages/desktop-client/src/components/modals/EnvelopeBudgetGroupMenuModal.tsx (1)

64-71: Add accessibility label for budgeted amount.

The "Budgeted" text should be properly associated with its value for screen readers.

 <Text
+  accessibilityRole="label"
+  accessibilityHint="Shows the total budgeted amount for this group"
   style={{
     fontSize: 17,
     fontWeight: 400,
   }}
 >
   Budgeted
 </Text>
packages/desktop-client/src/components/budget/ExpenseGroup.tsx (1)

132-132: Consider state management optimization for mobile performance.

While the implementation is correct, consider using a state management solution (e.g., Context API or Redux) to avoid prop drilling, especially for mobile performance optimization. This would be particularly beneficial as the application grows and more template-related features are added.

packages/desktop-client/src/components/budget/SidebarGroup.tsx (2)

129-134: Consider adding error handling for empty category lists.

While the implementation is correct, consider adding a guard clause or user feedback when there are no visible categories in the group.

 } else if (type === 'apply-multiple-category-template') {
+  const visibleCategories = group.categories
+    .filter(c => !c['hidden'])
+    .map(c => c['id']);
+  if (visibleCategories.length === 0) {
+    // Show user feedback that no categories are available
+    return;
+  }
   onApplyBudgetTemplatesInGroup?.(
-    group.categories
-      .filter(c => !c['hidden'])
-      .map(c => c['id']),
+    visibleCategories
   );
 }

146-153: Consider using type-safe menu item structure.

The menu items implementation is good, but consider defining a type for menu items to ensure type safety across the application.

type MenuItem = {
  name: string;
  text: string;
  disabled?: boolean;
};
packages/desktop-client/src/components/budget/BudgetTable.jsx (1)

31-31: LGTM! Consider adding PropTypes validation.

The new prop follows consistent naming conventions and is well-placed in the props list.

Consider adding PropTypes validation for the new callback prop:

BudgetTable.propTypes = {
  // ... other props
  onApplyBudgetTemplatesInGroup: PropTypes.func
};
packages/loot-core/src/client/state-types/modals.d.ts (1)

177-181: LGTM! The new modal type is well-structured and consistent.

The new envelope-budget-group-menu modal type follows the established patterns and is properly integrated into the type system. It appropriately extends the budget management functionality to work at the group level.

Consider adding JSDoc comments to document:

  • The purpose of this modal
  • The relationship between month and group
  • Expected behavior of onApplyGroupTemplate

This would improve maintainability and help other developers understand the intended usage.

packages/loot-core/src/client/actions/queries.ts (1)

Line range hint 13-108: LGTM! Consider adding JSDoc comments.

The new case integrates well with the existing applyBudgetAction function. The implementation maintains consistency with the overall function structure and follows the established patterns for budget actions.

To improve maintainability, consider adding JSDoc comments to document the expected shape of args for each action type.

Example documentation:

/**
 * Applies budget actions based on the specified type
 * @param month The target month
 * @param type The action type
 * @param args The action arguments
 * @param args.categories For 'apply-multiple-templates': Array of category IDs
 * @param args.category For single category actions: The target category ID
 * @param args.amount For amount-related actions: The amount to set/transfer
 */
export function applyBudgetAction(month, type, args) {
packages/desktop-client/src/components/budget/BudgetCategories.jsx (1)

31-31: Consider adding PropTypes definition for the new callback prop.

To improve type safety and documentation, consider adding PropTypes definition for onApplyBudgetTemplatesInGroup.

BudgetCategories.propTypes = {
+ onApplyBudgetTemplatesInGroup: PropTypes.func,
};
packages/desktop-client/src/components/budget/index.tsx (2)

268-275: Add TypeScript types and error handling.

The function implementation looks good but could benefit from some improvements:

  1. Add type safety for the categories parameter
  2. Add error handling for failed dispatch
  3. Add validation for the categories array

Consider this implementation:

-  const onApplyBudgetTemplatesInGroup = async categories => {
+  const onApplyBudgetTemplatesInGroup = async (categories: string[]) => {
+    if (!Array.isArray(categories) || categories.length === 0) {
+      return;
+    }
+
+    try {
       dispatch(
         applyBudgetAction(startMonth, 'apply-multiple-templates', {
           month: startMonth,
           categories,
         }),
       );
+    } catch (error) {
+      dispatch(
+        addNotification({
+          type: 'error',
+          message: t('Failed to apply budget templates'),
+        }),
+      );
+    }
   };

268-275: Add test coverage for the new template application feature.

Consider adding tests to verify:

  1. Successful template application to multiple categories
  2. Error handling scenarios
  3. Integration with both mobile and desktop interfaces

Would you like me to help create test cases for this new functionality?

Also applies to: 378-378

packages/loot-core/src/server/budget/goaltemplates.ts (1)

206-206: Ensure consistent variable usage in processTemplate

Assigning categories = category; may cause confusion since category can be either a single category or an array of categories. To improve code clarity, consider ensuring that the variable names accurately reflect their content.

If category can be an array, consider renaming the parameter to categories for consistency.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f25dc1f and 9e7e11c.

⛔ Files ignored due to path filters (1)
  • upcoming-release-notes/3666.md is excluded by !**/*.md
📒 Files selected for processing (14)
  • packages/desktop-client/src/components/Modals.tsx (2 hunks)
  • packages/desktop-client/src/components/budget/BudgetCategories.jsx (2 hunks)
  • packages/desktop-client/src/components/budget/BudgetTable.jsx (2 hunks)
  • packages/desktop-client/src/components/budget/ExpenseGroup.tsx (3 hunks)
  • packages/desktop-client/src/components/budget/SidebarGroup.tsx (5 hunks)
  • packages/desktop-client/src/components/budget/envelope/BudgetMenuGroup.tsx (1 hunks)
  • packages/desktop-client/src/components/budget/index.tsx (2 hunks)
  • packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx (6 hunks)
  • packages/desktop-client/src/components/modals/EnvelopeBudgetGroupMenuModal.tsx (1 hunks)
  • packages/loot-core/src/client/actions/queries.ts (1 hunks)
  • packages/loot-core/src/client/state-types/modals.d.ts (1 hunks)
  • packages/loot-core/src/server/budget/app.ts (1 hunks)
  • packages/loot-core/src/server/budget/goaltemplates.ts (4 hunks)
  • packages/loot-core/src/server/budget/types/handlers.d.ts (1 hunks)
🧰 Additional context used
📓 Learnings (1)
packages/desktop-client/src/components/modals/EnvelopeBudgetGroupMenuModal.tsx (2)
Learnt from: UnderKoen
PR: actualbudget/actual#3381
File: packages/desktop-client/src/components/budget/envelope/EnvelopeBudgetComponents.tsx:274-274
Timestamp: 2024-09-24T20:27:51.684Z
Learning: UnderKoen prefers not to include changes that would significantly alter many files in a PR focusing on specific functionality.
Learnt from: UnderKoen
PR: actualbudget/actual#3381
File: packages/desktop-client/src/components/budget/envelope/EnvelopeBudgetComponents.tsx:274-274
Timestamp: 2024-10-08T15:46:15.739Z
Learning: UnderKoen prefers not to include changes that would significantly alter many files in a PR focusing on specific functionality.
🪛 GitHub Check: lint
packages/desktop-client/src/components/modals/EnvelopeBudgetGroupMenuModal.tsx

[warning] 39-39:
'amountFocused' is assigned a value but never used. Allowed unused vars must match /^(_|React)/u

🔇 Additional comments (20)
packages/loot-core/src/server/budget/app.ts (1)

32-35: LGTM! The new method follows established patterns.

The new budget/apply-multiple-templates method is well-integrated, following the consistent pattern of using mutator(undoable()) for state management and is appropriately positioned among other template-related methods.

Let's verify the implementation of the underlying action:

✅ Verification successful

Implementation verified and follows best practices

The applyMultipleCategoryTemplates function is properly implemented in goaltemplates.ts with appropriate database handling and follows the expected pattern for template operations. The function:

  • Takes required parameters (month and categoryIds)
  • Uses safe SQL query construction with placeholders
  • Integrates with existing template management functions (storeTemplates, getTemplates)
  • Is correctly exported and imported in the app configuration
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation of applyMultipleCategoryTemplates
# Expected: Find the implementation and its usage

# Look for the implementation
echo "Searching for applyMultipleCategoryTemplates implementation:"
ast-grep --pattern 'export const applyMultipleCategoryTemplates = $_'

# Look for tests
echo -e "\nSearching for related tests:"
rg -l "applyMultipleCategoryTemplates" -g "*test*"

Length of output: 3225


Script:

#!/bin/bash
# Let's try a different approach to find the implementation

# Search for the implementation using ripgrep
echo "Searching for applyMultipleCategoryTemplates implementation:"
rg -A 5 "applyMultipleCategoryTemplates" --type ts

# Look for the file where it's imported from
echo -e "\nSearching for imports:"
rg "import.*applyMultipleCategoryTemplates"

# Search for any references to this function
echo -e "\nSearching for all references:"
rg "applyMultipleCategoryTemplates"

Length of output: 1774

packages/desktop-client/src/components/modals/EnvelopeBudgetGroupMenuModal.tsx (2)

1-23: LGTM! Well-organized imports and type definitions.

The imports are properly structured and the type definitions are clear and concise.


50-50: Verify modal registration and mobile back handling.

Ensure this modal is properly registered in the modal system and handles mobile back button/gesture appropriately.

✅ Verification successful

Let me gather more information about the modal system implementation to verify the mobile back handling.


Let me try to locate the modal component and its implementation first.


Let me check the modal state management and back handling implementation.


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

Modal is properly integrated with the system

The modal is correctly registered in the modal system and uses the common Modal component which:

  • Is managed through the global modal state system (useModalState)
  • Automatically handles modal stack management
  • Closes on location changes (route navigation)
  • Uses the standard modal lifecycle hooks
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the modal is properly registered and handles mobile back
rg -A 5 "envelope-budget-group-menu" packages/desktop-client/src/components/Modals.tsx

Length of output: 288


Script:

#!/bin/bash
# Check modal system implementation and back handling
ast-grep --pattern 'function $_($$_) {
  $$$
  case "envelope-budget-group-menu":
  $$$
}'

# Check for mobile back handling patterns
rg -B 3 -A 5 "handleBackButton|onBackButtonPress" packages/desktop-client/src/components/modals/

# Check the parent component implementation
rg -B 3 -A 10 "class Modal|function Modal" packages/desktop-client/src/components/modals/Modal.tsx

Length of output: 388


Script:

#!/bin/bash
# Find the Modal component location
fd -e tsx -e ts -e js Modal

# Check modal system implementation
rg -B 3 -A 10 "class Modal|function Modal|const Modal"

# Look for back button handling in the codebase
rg -B 2 -A 5 "handleBackButton|onBackButtonPress|useBackHandler"

Length of output: 11257


Script:

#!/bin/bash
# Check modal state management
rg -B 3 -A 5 "useModalState|closeModal|pushModal" packages/desktop-client/src/hooks/useModalState.ts

# Check if there's a global back handler
rg -B 3 -A 5 "BackHandler|useEffect.*back" packages/desktop-client/src/components/common/Modal.tsx

# Look for mobile-specific handlers
rg -B 3 -A 5 "mobile.*back|platform.*back" packages/desktop-client/src/

Length of output: 580

packages/desktop-client/src/components/budget/ExpenseGroup.tsx (3)

28-30: LGTM! Type definition is well-structured.

The type definition correctly leverages ComponentProps for type safety and maintains consistency with the existing prop types pattern.


49-49: LGTM! Props interface is properly updated.

The new callback prop is well-positioned among other similar props, maintaining the component's interface consistency.


Line range hint 1-137: Verify mobile-specific requirements and responsiveness.

Since this change is part of mobile template functionality:

  1. Ensure the component handles touch events appropriately
  2. Verify that the layout works well on different screen sizes
  3. Consider adding mobile-specific styles or breakpoints if needed
packages/desktop-client/src/components/budget/SidebarGroup.tsx (3)

6-6: LGTM: Clean implementation of new feature flag hook and prop type.

The new import and prop type are well-structured and properly typed.

Also applies to: 36-36


52-52: LGTM: Props destructuring is clean and well-organized.

The new callback prop is properly integrated into the component's props destructuring.


58-58: Verify feature flag configuration.

The goalTemplatesEnabled feature flag controls the visibility of budget template functionality. Ensure this flag is properly configured in the feature flags system.

✅ Verification successful

The feature flag goalTemplatesEnabled appears to be properly configured in the codebase. Let me break down the findings:

  1. The flag is defined in the preferences type system (packages/loot-core/src/types/prefs.d.ts)
  2. It has a default value of false in packages/desktop-client/src/hooks/useFeatureFlag.ts
  3. It's accessible through the Experimental settings UI component (packages/desktop-client/src/components/settings/Experimental.tsx)
  4. It's consistently used across multiple components to conditionally render goal template functionality

Feature flag configuration is verified and properly implemented.

  • Default state is disabled (false) by default
  • Configurable through the Experimental settings UI
  • Used consistently across the codebase for goal template related features
  • Properly typed in the preferences system
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for feature flag configuration and usage
# Test: Search for goalTemplatesEnabled flag configuration
rg -A 5 "goalTemplatesEnabled"

Length of output: 7451

packages/desktop-client/src/components/budget/BudgetTable.jsx (1)

239-239: LGTM! Verify BudgetCategories implementation.

The prop is correctly forwarded to the BudgetCategories component.

Let's verify that the BudgetCategories component properly implements this prop:

✅ Verification successful

Prop implementation verified successfully

The onApplyBudgetTemplatesInGroup prop is properly implemented throughout the component hierarchy:

  • Correctly declared and passed through BudgetCategories component
  • Properly forwarded to ExpenseGroup component
  • Finally used in SidebarGroup where it's called with filtered categories
  • Implementation in index.tsx shows it dispatches an 'apply-multiple-templates' budget action
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify BudgetCategories component implementation of onApplyBudgetTemplatesInGroup

# Test: Check if BudgetCategories component properly declares and uses the prop
ast-grep --pattern 'function BudgetCategories({ $$$, onApplyBudgetTemplatesInGroup, $$$ }) {
  $$$
}'

# Test: Look for actual usage of the callback within BudgetCategories
rg "onApplyBudgetTemplatesInGroup\(" packages/desktop-client/src/components/budget/BudgetCategories.jsx

Length of output: 209


Script:

#!/bin/bash
# Let's try a broader search to understand the BudgetCategories component structure

# First, let's check if the file exists
fd "BudgetCategories.jsx" packages/desktop-client/src/components/budget/

# If it exists, let's look at its content
fd "BudgetCategories.jsx" packages/desktop-client/src/components/budget/ -x cat {}

# Let's also try a more flexible pattern match for the prop usage
rg "onApplyBudgetTemplatesInGroup" packages/desktop-client/src/components/budget/ -A 2

Length of output: 15126

packages/loot-core/src/client/state-types/modals.d.ts (1)

177-181: Verify the completeness of the modal interface.

The modal interface seems minimal compared to other budget-related modals (e.g., envelope-budget-menu). Consider whether additional operations might be needed at the group level.

Let's analyze other group-level operations in the codebase:

✅ Verification successful

Let me try a different search strategy to analyze the group-level operations and modal interfaces.


Let me try one more search to specifically look at the modal interfaces and group-level operations.


Based on the analysis of the codebase, I can now provide a final response:

The modal interface is appropriately complete for its purpose.

The envelope-budget-group-menu modal interface contains all necessary operations at the group level, as evidenced by:

  1. It includes the essential group-level budgeting operations:

    • Viewing group's total budgeted amount
    • Applying budget templates at the group level
    • Month-specific operations (via the month context)
  2. Additional group operations are handled through other interfaces:

    • Group visibility toggling
    • Group deletion
    • Adding categories
    • Group notes management
    • Group renaming

The current interface aligns well with the envelope budgeting system's group-level operations and doesn't require additional fields.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for patterns of group-level budget operations
echo "Searching for group-level budget operations..."
rg -l "group.*budget|budget.*group" --type ts --type tsx

# Search for similar modal definitions
echo "Searching for similar modal patterns..."
ast-grep --pattern 'type FinanceModals = {
  $$$
  $name: {
    group: $_
    $$$
  }
  $$$
}'

Length of output: 375


Script:

#!/bin/bash
# Search for files containing budget group related code
echo "Searching for budget group related files..."
rg -l "CategoryGroupEntity|BudgetGroup" --type ts

# Look for group-level operations in these files
echo "Searching for group operations..."
rg "group\." -A 5 --type ts

# Search for modal interfaces
echo "Looking for modal interfaces..."
rg "type.*Modal.*=.*{" -A 10 --type ts

Length of output: 49009


Script:

#!/bin/bash
# Search for modal interfaces in the codebase
echo "Looking for modal interfaces..."
rg "envelope-budget.*menu|budget.*menu" -A 5 --type ts

# Look for group-level operations in the modal components
echo "Looking for group-level operations in modals..."
rg -l "EnvelopeBudgetGroupMenuModal|CategoryGroupMenuModal" --type ts

# Check the implementation of these modal components
echo "Checking modal implementations..."
fd -e tsx "EnvelopeBudgetGroupMenuModal|CategoryGroupMenuModal" -x cat {}

Length of output: 24770

packages/loot-core/src/client/actions/queries.ts (1)

99-108: ⚠️ Potential issue

Consider improving error handling and consistency.

The new case for applying multiple templates has some potential improvements to consider:

  1. Error handling should be added to maintain robustness
  2. The notification dispatch pattern differs from similar template-related cases
  3. Type safety could be improved

Consider applying this improvement:

       case 'apply-multiple-templates':
-        dispatch(
-          addNotification(
-            await send('budget/apply-multiple-templates', {
-              month,
-              categoryIds: args.categories,
-            }),
-          ),
-        );
+        const result = await send('budget/apply-multiple-templates', {
+          month,
+          categoryIds: args.categories,
+        });
+        if (result.error) {
+          dispatch(addGenericErrorNotification());
+          throw new Error(result.error);
+        }
         break;

Let's verify the error handling pattern in similar cases:

packages/desktop-client/src/components/budget/BudgetCategories.jsx (2)

249-249: Verify if budget templates should apply to income groups.

I notice that the onApplyBudgetTemplatesInGroup prop is only passed to ExpenseGroup and not to IncomeGroup. Please confirm if this is intentional and aligns with the requirements.

#!/bin/bash
# Description: Check if there are any references to budget templates with income groups
# Expected: No references to budget templates with income

# Search for any mentions of templates with income
rg -i "template.*income|income.*template"

# Search for related issues or discussions
gh issue list --search "income template"

249-249: Verify ExpenseGroup component's prop definition.

The prop is correctly passed down, but let's ensure it's properly defined in the ExpenseGroup component's interface.

✅ Verification successful

Let me generate additional scripts to properly verify the prop definition.


Prop is properly defined and used correctly

The onApplyBudgetTemplatesInGroup prop is correctly defined in both the ExpenseGroup and SidebarGroup components:

  • In ExpenseGroup.tsx, it's defined as an optional prop using TypeScript's ComponentProps
  • In SidebarGroup.tsx, it's properly typed as (categories: object[]) => void
  • The prop is correctly passed through the component hierarchy: BudgetCategories → ExpenseGroup → SidebarGroup
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the prop is properly defined in ExpenseGroup component
# Expected: Find the prop definition in ExpenseGroup's interface/props

# Search for ExpenseGroup's prop definitions
ast-grep --pattern 'interface ExpenseGroupProps {
  $$$
}'

# Search for PropTypes definition
rg "ExpenseGroup.propTypes" -A 10

Length of output: 881


Script:

#!/bin/bash
# Search for ExpenseGroup component definition and its props
ast-grep --pattern 'const ExpenseGroup = ({ $$ }) => {
  $$$
}'

# Search for ExpenseGroup component with PropTypes
rg "ExpenseGroup.*propTypes|PropTypes.*ExpenseGroup" -A 10

# Search for the specific prop usage within ExpenseGroup
rg "onApplyBudgetTemplatesInGroup" -B 2 -A 2

Length of output: 6322

packages/desktop-client/src/components/budget/index.tsx (1)

378-378: Update TypeScript interface for DynamicBudgetTable.

The new prop is correctly added, but we should ensure:

  1. The DynamicBudgetTable component's TypeScript interface includes this prop
  2. Document why this prop is only needed for envelope budgets and not tracking budgets

Let's check if the interface exists:

packages/desktop-client/src/components/Modals.tsx (2)

33-33: LGTM!

The import statement follows the established patterns and conventions.


407-418: Verify month prop usage in EnvelopeBudgetGroupMenuModal.

The implementation follows the established patterns and correctly integrates with the modal system. However, while month is used for the namespace context, it's not passed to the modal component.

Let's verify if the modal component needs the month prop:

packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx (2)

Line range hint 753-764: LGTM: Props added for template functionality.

The new props month and onBudgetAction are properly integrated to support the group template feature.


890-913: LGTM: Well-implemented UI enhancement.

The budgeted amount is now properly wrapped in an interactive button that:

  • Follows existing UI patterns
  • Maintains privacy filtering
  • Handles text sizing appropriately
  • Improves accessibility
packages/loot-core/src/server/budget/goaltemplates.ts (1)

68-68: Update applySingleCategoryTemplate to pass categories

The addition of categories as an argument to processTemplate ensures consistent handling of category data and aligns with the updated function signature.

Comment on lines +20 to +28
const onMenuSelect = (name: string) => {
switch (name) {
case 'apply-group-category-template':
onApplyGroupTemplate?.();
break;
default:
throw new Error(`Unrecognized menu item: ${name}`);
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider more graceful error handling in production.

The error throwing for unrecognized menu items might be too aggressive for production. Consider logging the error and providing a fallback behavior.

 const onMenuSelect = (name: string) => {
   switch (name) {
     case 'apply-group-category-template':
       onApplyGroupTemplate?.();
       break;
     default:
-      throw new Error(`Unrecognized menu item: ${name}`);
+      console.error(`Unrecognized menu item: ${name}`);
   }
 };
📝 Committable suggestion

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

Suggested change
const onMenuSelect = (name: string) => {
switch (name) {
case 'apply-group-category-template':
onApplyGroupTemplate?.();
break;
default:
throw new Error(`Unrecognized menu item: ${name}`);
}
};
const onMenuSelect = (name: string) => {
switch (name) {
case 'apply-group-category-template':
onApplyGroupTemplate?.();
break;
default:
console.error(`Unrecognized menu item: ${name}`);
}
};

Comment on lines +7 to +12
type BudgetMenuGroupProps = Omit<
ComponentPropsWithoutRef<typeof Menu>,
'onMenuSelect' | 'items'
> & {
onApplyGroupTemplate: () => void;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider improving the type definition for better type safety.

The onApplyGroupTemplate callback could be enhanced:

  1. Make it optional since you use optional chaining in the implementation
  2. Include error handling in the type signature
 type BudgetMenuGroupProps = Omit<
   ComponentPropsWithoutRef<typeof Menu>,
   'onMenuSelect' | 'items'
 > & {
-  onApplyGroupTemplate: () => void;
+  onApplyGroupTemplate?: () => void | Promise<void>;
 };
📝 Committable suggestion

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

Suggested change
type BudgetMenuGroupProps = Omit<
ComponentPropsWithoutRef<typeof Menu>,
'onMenuSelect' | 'items'
> & {
onApplyGroupTemplate: () => void;
};
type BudgetMenuGroupProps = Omit<
ComponentPropsWithoutRef<typeof Menu>,
'onMenuSelect' | 'items'
> & {
onApplyGroupTemplate?: () => void | Promise<void>;
};

Comment on lines +34 to +43
items={[
...(isGoalTemplatesEnabled
? [
{
name: 'apply-group-category-template',
text: t('Apply budget template'),
},
]
: []),
]}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Based on the Menu component implementation and usage context, I can now provide the final response:

Consider conditionally rendering the Menu component when empty

The Menu component doesn't have built-in handling for empty states, and rendering it with an empty array will result in a clickable menu button that opens an empty menu. To improve the user experience:

return isGoalTemplatesEnabled ? (
  <Menu
    {...props}
    onMenuSelect={onMenuSelect}
    items={[
      {
        name: 'apply-group-category-template',
        text: t('Apply budget template'),
      },
    ]}
  />
) : null;
🔗 Analysis chain

Handle empty menu state when feature is disabled.

When isGoalTemplatesEnabled is false, the menu will be empty. Consider:

  1. Either hiding the menu completely when empty
  2. Or providing a disabled state explaining why features are unavailable
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are other places in the codebase handling empty menus
rg -l "items={(\[\])}" --type ts --type tsx

Length of output: 149


Script:

#!/bin/bash
# Search for empty menu items array patterns with proper escaping
rg "items=\{\[\]\}" --type ts --type tsx

# Search for Menu component usage to understand empty state handling
rg -A 3 "<Menu" --type ts --type tsx

# Look for the Menu component definition
ast-grep --pattern 'interface $_Props {
  $$$
  items: $_
  $$$
}'

Length of output: 206


Script:

#!/bin/bash
# Search for Menu component usage with correct file extension
rg -A 3 "<Menu" .

# Look for the Menu component definition
rg -A 10 "interface.*Menu.*Props" .

# Search for empty menu items array patterns
rg "items=\{(\[\]|[])" .

Length of output: 17212


Script:

#!/bin/bash
# Look for Menu component definition and its props
rg -A 5 "export.*Menu.*=.*" ./packages/desktop-client/src/components/common/Menu.tsx

# Check if there's any handling of empty items
rg -A 5 "items\.length.*===.*0" .

# Look for conditional rendering of Menu component
rg "{\s*items.*length.*[><=].*0.*}" .

Length of output: 963


Script:

#!/bin/bash
# Look at the Menu component implementation
cat ./packages/desktop-client/src/components/common/Menu.tsx

# Look for conditional rendering patterns around Menu
rg -B 2 -A 2 "<Menu.*" ./packages/desktop-client/src/components/budget/envelope/BudgetMenuGroup.tsx

Length of output: 6928

Comment on lines +83 to +86
'budget/apply-multiple-templates': (arg: {
month: string;
categoryIds: string[];
}) => Promise<Notification>;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding type validation for the month parameter.

The month parameter could benefit from a more specific type to ensure it follows a consistent format (e.g., 'YYYY-MM').

+  // Define a specific month format type
+  type YearMonth = `${number}-${number}`;
+
   'budget/apply-multiple-templates': (arg: {
-    month: string;
+    month: YearMonth;
     categoryIds: string[];
   }) => Promise<Notification>;

Committable suggestion was skipped due to low confidence.

Comment on lines 35 to 43
const budgeted = useEnvelopeSheetValue(
envelopeBudget.groupBudgeted(group.id),
);
const value = integerToAmount(budgeted || 0);
const [amountFocused, setAmountFocused] = useState(false);

useEffect(() => {
setAmountFocused(true);
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove unused state and effect.

The amountFocused state and its corresponding effect are not used in the component. Consider removing them to improve code clarity.

-  const [amountFocused, setAmountFocused] = useState(false);
-
-  useEffect(() => {
-    setAmountFocused(true);
-  }, []);
📝 Committable suggestion

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

Suggested change
const budgeted = useEnvelopeSheetValue(
envelopeBudget.groupBudgeted(group.id),
);
const value = integerToAmount(budgeted || 0);
const [amountFocused, setAmountFocused] = useState(false);
useEffect(() => {
setAmountFocused(true);
}, []);
const budgeted = useEnvelopeSheetValue(
envelopeBudget.groupBudgeted(group.id),
);
const value = integerToAmount(budgeted || 0);
🧰 Tools
🪛 GitHub Check: lint

[warning] 39-39:
'amountFocused' is assigned a value but never used. Allowed unused vars must match /^(_|React)/u

Comment on lines +79 to +91
<Text
style={{
...makeAmountFullStyle(value),
userSelect: 'none',
fontSize: 30,
fontWeight: 600,
textAlign: 'center',
}}
>
{amountToCurrency(Math.abs(value))}
</Text>
</View>
</View>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance amount display accessibility and loading state.

The amount display should be accessible and handle loading states gracefully.

 <Text
   style={{
     ...makeAmountFullStyle(value),
     userSelect: 'none',
     fontSize: 30,
     fontWeight: 600,
     textAlign: 'center',
   }}
+  accessibilityRole="text"
+  accessibilityLabel={`Budgeted amount ${amountToCurrency(Math.abs(value))}`}
 >
-  {amountToCurrency(Math.abs(value))}
+  {budgeted === undefined ? (
+    <ActivityIndicator size="small" />
+  ) : (
+    amountToCurrency(Math.abs(value))
+  )}
 </Text>

Committable suggestion was skipped due to low confidence.

@@ -236,6 +236,7 @@ function BudgetCell({
const categoryNotes = useNotes(category.id);

const onOpenCategoryBudgetMenu = () => {
console.log('open');
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove debug logging statement.

Debug logging statements should not be committed to production code.

-    console.log('open');
📝 Committable suggestion

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

Suggested change
console.log('open');

Comment on lines +766 to +783
const dispatch = useDispatch();
const onOpenGroupBudgetMenu = () => {
console.log('OPEN Group header menu' + month);
dispatch(
pushModal('envelope-budget-group-menu', {
month,
group,
onApplyGroupTemplate: () => {
onBudgetAction(month, 'apply-multiple-templates', {
month,
categories: group.categories
.filter(c => !c['hidden'])
.map(c => c['id']),
});
},
}),
);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Clean up the implementation.

The function has several issues that should be addressed:

  1. Remove the debug logging statement
  2. Remove redundant month parameter in the action payload
  3. Simplify the hidden categories filter
-    console.log('OPEN Group header menu' + month);
     dispatch(
       pushModal('envelope-budget-group-menu', {
         month,
         group,
         onApplyGroupTemplate: () => {
           onBudgetAction(month, 'apply-multiple-templates', {
-            month,
             categories: group.categories
-              .filter(c => !c['hidden'])
-              .map(c => c['id']),
+              .filter(c => !c.hidden)
+              .map(c => c.id),
           });
         },
       }),
     );
📝 Committable suggestion

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

Suggested change
const dispatch = useDispatch();
const onOpenGroupBudgetMenu = () => {
console.log('OPEN Group header menu' + month);
dispatch(
pushModal('envelope-budget-group-menu', {
month,
group,
onApplyGroupTemplate: () => {
onBudgetAction(month, 'apply-multiple-templates', {
month,
categories: group.categories
.filter(c => !c['hidden'])
.map(c => c['id']),
});
},
}),
);
};
const dispatch = useDispatch();
const onOpenGroupBudgetMenu = () => {
dispatch(
pushModal('envelope-budget-group-menu', {
month,
group,
onApplyGroupTemplate: () => {
onBudgetAction(month, 'apply-multiple-templates', {
categories: group.categories
.filter(c => !c.hidden)
.map(c => c.id),
});
},
}),
);
};

Comment on lines +155 to +167
if (Array.isArray(category)) {
const multipleCategoryTemplates = [];
for (let dd = 0; dd < category.length; dd++) {
const categoryId = category[dd].id;
if (templates[categoryId] !== undefined) {
multipleCategoryTemplates[categoryId] = templates[categoryId];
multipleCategoryTemplates[categoryId] = multipleCategoryTemplates[
categoryId
].filter(t => t.directive === directive);
}
}
return multipleCategoryTemplates;
} else if (category) {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use an object instead of an array for multipleCategoryTemplates

multipleCategoryTemplates is initialized as an empty array but is used as an object with category IDs as keys. This can lead to confusion since arrays are intended for sequential numeric indexes. It's better to use an object {} when keys are non-numeric.

Apply this diff to fix the issue:

-const multipleCategoryTemplates = [];
+const multipleCategoryTemplates = {};

Update the rest of the code accordingly:

for (let dd = 0; dd < category.length; dd++) {
  const categoryId = category[dd].id;
  if (templates[categoryId] !== undefined) {
-    multipleCategoryTemplates[categoryId] = templates[categoryId];
-    multipleCategoryTemplates[categoryId] = multipleCategoryTemplates[
-      categoryId
-    ].filter(t => t.directive === directive);
+    multipleCategoryTemplates[categoryId] = templates[categoryId].filter(
+      t => t.directive === directive
+    );
  }
}
📝 Committable suggestion

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

Suggested change
if (Array.isArray(category)) {
const multipleCategoryTemplates = [];
for (let dd = 0; dd < category.length; dd++) {
const categoryId = category[dd].id;
if (templates[categoryId] !== undefined) {
multipleCategoryTemplates[categoryId] = templates[categoryId];
multipleCategoryTemplates[categoryId] = multipleCategoryTemplates[
categoryId
].filter(t => t.directive === directive);
}
}
return multipleCategoryTemplates;
} else if (category) {
if (Array.isArray(category)) {
const multipleCategoryTemplates = {};
for (let dd = 0; dd < category.length; dd++) {
const categoryId = category[dd].id;
if (templates[categoryId] !== undefined) {
multipleCategoryTemplates[categoryId] = templates[categoryId].filter(
t => t.directive === directive
);
}
}
return multipleCategoryTemplates;
} else if (category) {

Comment on lines +40 to +55
export async function applyMultipleCategoryTemplates({ month, categoryIds }) {
const placeholders = categoryIds.map(() => '?').join(', ');
const query = `SELECT * FROM v_categories WHERE id IN (${placeholders})`;
const categories = await db.all(query, categoryIds);
await storeTemplates();
const category_templates = await getTemplates(categories, 'template');
const category_goals = await getTemplates(categories, 'goal');
const ret = await processTemplate(
month,
true,
category_templates,
categories,
);
await processGoals(category_goals, month);
return ret;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle empty categoryIds to prevent SQL syntax errors

If categoryIds is an empty array, the constructed SQL query will be invalid (WHERE id IN ()), leading to a syntax error when executing the query. Please add a check to handle this case appropriately.

Apply this diff to fix the issue:

export async function applyMultipleCategoryTemplates({ month, categoryIds }) {
+  if (categoryIds.length === 0) {
+    return;
+  }
  const placeholders = categoryIds.map(() => '?').join(', ');
  const query = `SELECT * FROM v_categories WHERE id IN (${placeholders})`;
  const categories = await db.all(query, categoryIds);
📝 Committable suggestion

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

Suggested change
export async function applyMultipleCategoryTemplates({ month, categoryIds }) {
const placeholders = categoryIds.map(() => '?').join(', ');
const query = `SELECT * FROM v_categories WHERE id IN (${placeholders})`;
const categories = await db.all(query, categoryIds);
await storeTemplates();
const category_templates = await getTemplates(categories, 'template');
const category_goals = await getTemplates(categories, 'goal');
const ret = await processTemplate(
month,
true,
category_templates,
categories,
);
await processGoals(category_goals, month);
return ret;
}
export async function applyMultipleCategoryTemplates({ month, categoryIds }) {
if (categoryIds.length === 0) {
return;
}
const placeholders = categoryIds.map(() => '?').join(', ');
const query = `SELECT * FROM v_categories WHERE id IN (${placeholders})`;
const categories = await db.all(query, categoryIds);
await storeTemplates();
const category_templates = await getTemplates(categories, 'template');
const category_goals = await getTemplates(categories, 'goal');
const ret = await processTemplate(
month,
true,
category_templates,
categories,
);
await processGoals(category_goals, month);
return ret;
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (1)
packages/desktop-client/src/components/modals/EnvelopeBudgetGroupMenuModal.tsx (1)

20-23: Consider enhancing type safety of the callback prop.

The onApplyGroupTemplate callback type could be more specific about what data it receives when invoked.

Consider updating the type definition:

 type EnvelopeBudgetGroupMenuModalProps = {
   group: CategoryGroupEntity;
-  onApplyGroupTemplate: () => void;
+  onApplyGroupTemplate: (groupId: string, templateId?: string) => void;
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 9e7e11c and 22d7e0d.

⛔ Files ignored due to path filters (1)
  • upcoming-release-notes/3737.md is excluded by !**/*.md
📒 Files selected for processing (2)
  • packages/desktop-client/src/components/modals/EnvelopeBudgetGroupMenuModal.tsx (1 hunks)
  • packages/loot-core/src/client/actions/queries.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/loot-core/src/client/actions/queries.ts
🔇 Additional comments (3)
packages/desktop-client/src/components/modals/EnvelopeBudgetGroupMenuModal.tsx (3)

1-19: LGTM! Well-organized imports.

The imports are properly organized, with clear separation between external dependencies, internal modules, and type imports.


29-34: Enhance mobile UI patterns.

The current menu item styles could be improved for better mobile user experience.


74-86: Enhance amount display accessibility and loading state.

The amount display should handle loading states and be accessible.

Comment on lines +87 to +90
<BudgetMenuGroup
getItemStyle={() => defaultMenuItemStyle}
onApplyGroupTemplate={onApplyGroupTemplate}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding error boundary.

The BudgetMenuGroup component might throw errors that should be caught to prevent the entire modal from crashing.

Consider wrapping the component with an error boundary:

+import { ErrorBoundary } from '../common/ErrorBoundary';

-          <BudgetMenuGroup
-            getItemStyle={() => defaultMenuItemStyle}
-            onApplyGroupTemplate={onApplyGroupTemplate}
-          />
+          <ErrorBoundary fallback={<Text>Failed to load menu options</Text>}>
+            <BudgetMenuGroup
+              getItemStyle={() => defaultMenuItemStyle}
+              onApplyGroupTemplate={onApplyGroupTemplate}
+            />
+          </ErrorBoundary>

Committable suggestion was skipped due to low confidence.

Comment on lines +35 to +42
const budgeted = useEnvelopeSheetValue(
envelopeBudget.groupBudgeted(group.id),
);
const value = integerToAmount(budgeted || 0);

if (!group) {
return null;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Move null check before hook usage.

The null check for group should be before using it in the useEnvelopeSheetValue hook to prevent potential runtime errors.

Apply this change:

 export function EnvelopeBudgetGroupMenuModal({
   group,
   onApplyGroupTemplate,
 }: EnvelopeBudgetGroupMenuModalProps) {
+  if (!group) {
+    return null;
+  }
+
   const defaultMenuItemStyle: CSSProperties = {
     ...styles.mobileMenuItem,
     color: theme.menuItemText,
     borderRadius: 0,
     borderTop: `1px solid ${theme.pillBorder}`,
   };
   const budgeted = useEnvelopeSheetValue(
     envelopeBudget.groupBudgeted(group.id),
   );
   const value = integerToAmount(budgeted || 0);

-  if (!group) {
-    return null;
-  }
📝 Committable suggestion

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

Suggested change
const budgeted = useEnvelopeSheetValue(
envelopeBudget.groupBudgeted(group.id),
);
const value = integerToAmount(budgeted || 0);
if (!group) {
return null;
}
if (!group) {
return null;
}
const defaultMenuItemStyle: CSSProperties = {
...styles.mobileMenuItem,
color: theme.menuItemText,
borderRadius: 0,
borderTop: `1px solid ${theme.pillBorder}`,
};
const budgeted = useEnvelopeSheetValue(
envelopeBudget.groupBudgeted(group.id),
);
const value = integerToAmount(budgeted || 0);

Comment on lines +35 to +39
const budgeted = useEnvelopeSheetValue(
envelopeBudget.groupBudgeted(group.id),
);
const value = integerToAmount(budgeted || 0);

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for data fetching.

The component should handle potential errors from useEnvelopeSheetValue and provide appropriate feedback to users.

Consider implementing error handling:

 const budgeted = useEnvelopeSheetValue(
   envelopeBudget.groupBudgeted(group.id),
 );
+const [error, setError] = useState<Error | null>(null);
+
+if (error) {
+  return (
+    <View style={{ padding: 16, alignItems: 'center' }}>
+      <Text style={{ color: theme.errorText }}>
+        Failed to load budget data. Please try again.
+      </Text>
+    </View>
+  );
+}

Committable suggestion was skipped due to low confidence.

@youngcw
Copy link
Member

youngcw commented Oct 25, 2024

I don't love that its in a new modal vs one of the existing ones

@Dreptschar
Copy link
Contributor Author

Agreed. But I couldn't think of a way to put the new button in one of the existing menus. It's to big of a word for the remove Menu and when I add a third button to the one below the notes it looks awful. I could adjust the width of the buttons so three can fit in one row.
One the other hand, I always thought it's weird that you can't click the budget of a group idk 😅

@Dreptschar
Copy link
Contributor Author

How should i proceed ?

@youngcw
Copy link
Member

youngcw commented Nov 4, 2024

First off this will need to be updated and conflicts fixed. I would look at adding the action to the existing group modal since you aren't budgeting a group, you are budgeting categories, it doesn't make sense to click on the group budget total. The other modals have a few different options for actions to look at.

@joel-jeremy
Copy link
Contributor

I agree that this should not be in a new modal. The button is also not obvious that it can be clicked. We can instead add this on the existing category group modal but have a collapsible Actions section. Please see the existing month menu modal:
image
image

Copy link
Contributor

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions bot added the Stale label Dec 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants