Skip to content

Conversation

@marcuscastelo
Copy link
Owner

@marcuscastelo marcuscastelo commented Dec 3, 2025

Restructure the recent food module by moving components, simplifying data management, and enhancing real-time updates. This refactor improves code organization and reduces unnecessary exports.

Fix #1404

Copilot AI review requested due to automatic review settings December 3, 2025 18:33
@marcuscastelo marcuscastelo self-assigned this Dec 3, 2025
@vercel
Copy link

vercel bot commented Dec 3, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
macroflows Ready Ready Preview Comment Dec 5, 2025 11:33pm

@github-actions github-actions bot added backend Backend-specific issue or change complexity-medium Medium implementation complexity refactor Code refactoring required subissue labels Dec 3, 2025
Copilot finished reviewing on behalf of marcuscastelo December 3, 2025 18:37
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR restructures the recent food module by introducing a clean architecture pattern with distinct layers, moving components to more appropriate locations, and preparing the groundwork for real-time synchronization capabilities.

Key Changes

  • Introduced a service layer (recentFoodCrudService) to separate business logic from use case orchestration
  • Consolidated recent food operations into a centralized recentFoodUseCases module with consistent error handling and user feedback
  • Refactored the realtime update system to use dependency-injected callbacks instead of directly coupling to a cache store
  • Moved RemoveFromRecentButton from common components to the recent-food module for better cohesion
  • Cleaned up database schema by removing deprecated user_id_old fields

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/shared/supabase/supabase.ts Added TODO comment about implementing user-specific realtime subscriptions
src/shared/supabase/database.types.ts Removed deprecated user_id_old fields from recent_foods table and added users_duplicate table
src/sections/search/ui/openTemplateSearchModal.tsx Added template refetch on modal close to update recent foods list
src/sections/search/components/TemplateSearchResultItem.tsx Updated import path for RemoveFromRecentButton after component relocation
src/sections/search/components/TemplateSearchModal.tsx Simplified item tracking logic by delegating to recentFoodUseCases.touchRecentFoodForItem
src/modules/template-search/application/usecases/templateSearchState.ts Updated to use recentFoodUseCases.fetchUserRecentFoodsAsTemplates and added TODO for reactive signals
src/modules/template-search/application/tests/templateSearchLogic.test.ts Updated test mocks to use renamed fetchUserRecentFoodsAsTemplates method
src/modules/template-search/application/templateSearchLogic.ts Renamed dependency from fetchUserRecentFoods to fetchUserRecentFoodsAsTemplates for clarity
src/modules/recent-food/ui/tests/RemoveFromRecentButton.test.tsx Removed redundant API integration and toast promise tests, focusing on business logic tests
src/modules/recent-food/ui/RemoveFromRecentButton.tsx Simplified to use recentFoodUseCases and removed empty catch block
src/modules/recent-food/infrastructure/supabase/supabaseRecentFoodGateway.ts Added type export for RecentFoodGateway to support dependency injection
src/modules/recent-food/infrastructure/supabase/realtime.ts Refactored to accept callback functions as parameters instead of directly coupling to cache store
src/modules/recent-food/infrastructure/signals/recentFoodCacheStore.ts Deleted entire file as part of cache refactoring
src/modules/recent-food/infrastructure/recentFoodRepository.ts Converted to factory function accepting gateway as parameter; improved error log messages with [NON-FATAL] prefix
src/modules/recent-food/domain/recentFood.ts Removed comment-only line (cleanup)
src/modules/recent-food/application/usecases/recentFoodUseCases.ts New consolidated use cases module with consistent error handling, toast notifications, and touchRecentFoodForItem orchestration
src/modules/recent-food/application/usecases/recentFoodCrud.ts Deleted entire file, functionality moved to recentFoodUseCases.ts
src/modules/recent-food/application/services/recentFoodCrudService.ts New service layer that wraps repository methods without side effects (no toasts/errors)

Comment on lines +322 to +360
users_duplicate: {
Row: {
birthdate: string
created_at: string
desired_weight: number
diet: string
favorite_foods: number[] | null
gender: string
id: number
macro_profile: Json | null
name: string
uuid: string
}
Insert: {
birthdate?: string
created_at?: string
desired_weight: number
diet?: string
favorite_foods?: number[] | null
gender?: string
id?: number
macro_profile?: Json | null
name: string
uuid: string
}
Update: {
birthdate?: string
created_at?: string
desired_weight?: number
diet?: string
favorite_foods?: number[] | null
gender?: string
id?: number
macro_profile?: Json | null
name?: string
uuid?: string
}
Relationships: []
}
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The table name users_duplicate suggests this is a temporary or test table. Consider using a more descriptive name that indicates its purpose (e.g., users_migration, users_backup, or users_legacy), or remove it if it's no longer needed.

Suggested change
users_duplicate: {
Row: {
birthdate: string
created_at: string
desired_weight: number
diet: string
favorite_foods: number[] | null
gender: string
id: number
macro_profile: Json | null
name: string
uuid: string
}
Insert: {
birthdate?: string
created_at?: string
desired_weight: number
diet?: string
favorite_foods?: number[] | null
gender?: string
id?: number
macro_profile?: Json | null
name: string
uuid: string
}
Update: {
birthdate?: string
created_at?: string
desired_weight?: number
diet?: string
favorite_foods?: number[] | null
gender?: string
id?: number
macro_profile?: Json | null
name?: string
uuid?: string
}
Relationships: []
}

Copilot uses AI. Check for mistakes.
Comment on lines 28 to 32
case 'INSERT': {
if (event.new !== undefined) {
recentFoodCacheStore.upsertToCache(event.new)
callbacks.onInsert(event.new)
}
break
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The empty callbacks with unused parameter names (underscore prefix) suggest that the realtime functionality is not fully implemented yet (as confirmed by the TODO on line 27). Consider adding JSDoc comments to document what these callbacks should do when implemented, or adding logging statements to help with debugging during development.

Copilot uses AI. Check for mistakes.
Remove sections that referenced memory retrieval, memory updates, and
the requirement to begin chats with "Remembering..." from the Copilot
instructions file.
Clarify barrel file prohibition and prefer direct imports for
tree-shaking. Require verifying referenced scripts and prefer
repo-specific scripts. Add retry and escalation steps for `npm run
copilot:check` instead of blocking indefinitely. Relax `reportedBy` to
be required only for machine-readable/auditable outputs. Improve
solo-project, JSDoc, and import guidance for clearer, actionable rules.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 35 out of 36 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

src/modules/diet/recent-food/infrastructure/supabase/realtime.ts:21

  • The TODO comment at line 16 in deps.ts mentions implementing a recent food cache, but the realtime callbacks are currently using no-op functions ((_: unknown) => {}). Consider adding a TODO comment here as well to make it clear that these callbacks are intentionally stubbed out pending cache implementation, or reference the TODO in deps.ts for context.

Comment on lines +29 to +33
// TODO: Remove client-side user check after implementing row-level security (RLS)
if (currentRecentFood.user_id !== authUseCases.currentUserIdOrGuestId()) {
throw new Error('BUG: recentFood fetched does not match current user')
}

Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The TODO comment mentions implementing Row-Level Security (RLS), but in the meantime, the client-side check at line 30 throws an error if currentRecentFood.user_id doesn't match the current user. However, this check happens after the database fetch has already occurred. This means a malicious user could potentially fetch another user's recent food data before the error is thrown. While the data won't be used, it's a minor information leak. Consider:

  1. Prioritizing the RLS implementation for proper server-side security
  2. Moving this validation earlier in the flow, or
  3. Adding a server-side filter in the fetch query itself to ensure only the current user's data is returned

The current implementation provides defense-in-depth against bugs but not against intentional attacks.

Suggested change
// TODO: Remove client-side user check after implementing row-level security (RLS)
if (currentRecentFood.user_id !== authUseCases.currentUserIdOrGuestId()) {
throw new Error('BUG: recentFood fetched does not match current user')
}
// Ensure recentFoodCrudService.fetchRecentFoodByUserTypeAndReferenceId enforces user-level filtering at the query level.

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +28
* For GroupItem: returns the reference of the first trackable child (food or recipe)
*
* @param item - The item to extract reference from
* @returns The recent food reference, or null if the item cannot be tracked
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The JSDoc comment describes the function as returning "The recent food reference, or null if the item cannot be tracked", but the actual return type is RecentFoodReference[] (an array that can be empty). The documentation should be updated to reflect that this function now returns an array of references and returns an empty array (not null) when nothing can be tracked:

/**
 * Extracts the recent food reference (type and referenceId) from an item.
 * This is used to track recently used foods/recipes.
 *
 * For FoodItem: returns the food reference directly
 * For RecipeItem: returns the recipe reference directly
 * For GroupItem: returns references for all trackable children (foods and recipes)
 *
 * @param item - The item to extract reference from
 * @returns An array of recent food references, or an empty array if the item cannot be tracked
 */
Suggested change
* For GroupItem: returns the reference of the first trackable child (food or recipe)
*
* @param item - The item to extract reference from
* @returns The recent food reference, or null if the item cannot be tracked
* For GroupItem: returns references for all trackable children (foods and recipes)
*
* @param item - The item to extract reference from
* @returns An array of recent food references, or an empty array if the item cannot be tracked

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +20
const [recentFoodRef] = extractRecentFoodReferenceFromItem(item)
if (recentFoodRef === undefined) {
logging.warn(
'Cannot touch recent food for item - no trackable reference found',
{ item },
)
showError('Não foi possível adicionar alimento aos alimentos recentes.')
return
}

for (const recentFoodRef of extractRecentFoodReferenceFromItem(item)) {
await touchRecentFood(recentFoodRef)
}
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The first parameter is destructured but immediately checked. The logic extracts the first element and then checks if it's undefined, but subsequently iterates over all elements using the full array again. This creates redundant extraction and iteration.

Consider simplifying to:

export async function touchRecentFoodForItem(item: Item) {
  const references = extractRecentFoodReferenceFromItem(item)
  if (references.length === 0) {
    logging.warn(
      'Cannot touch recent food for item - no trackable reference found',
      { item },
    )
    showError('Não foi possível adicionar alimento aos alimentos recentes.')
    return
  }

  for (const recentFoodRef of references) {
    await touchRecentFood(recentFoodRef)
  }
}

Copilot uses AI. Check for mistakes.
Comment on lines +322 to +360
users_duplicate: {
Row: {
birthdate: string
created_at: string
desired_weight: number
diet: string
favorite_foods: number[] | null
gender: string
id: number
macro_profile: Json | null
name: string
uuid: string
}
Insert: {
birthdate?: string
created_at?: string
desired_weight: number
diet?: string
favorite_foods?: number[] | null
gender?: string
id?: number
macro_profile?: Json | null
name: string
uuid: string
}
Update: {
birthdate?: string
created_at?: string
desired_weight?: number
diet?: string
favorite_foods?: number[] | null
gender?: string
id?: number
macro_profile?: Json | null
name?: string
uuid?: string
}
Relationships: []
}
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

A users_duplicate table has been added to the database types, which suggests a data migration or temporary structure. This table appears to be unrelated to the "recent food" refactoring mentioned in the PR description. If this is temporary migration infrastructure, it should be documented or removed after migration completion. If it's permanent, it needs proper documentation explaining its purpose and relationship to the main users table.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Backend-specific issue or change complexity-medium Medium implementation complexity refactor Code refactoring required subissue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

recent-food: Encapsulate infra exports — export only factories

2 participants