filter menu option#25
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds URL-backed table column filtering and pinning: new UncontrolledInput component, trimmed-string parser, hooks to read/write debounced filter query state and active-count, new header menu pin/filter option components, header/menu integration showing active badges, and related server/schema filters and column-ordinal adjustments. Changes
Sequence DiagramsequenceDiagram
participant User
participant UI as Filter UI
participant Hook as useTableFilterMenuFilterByQuery
participant Parser as parseAsTrimmedString
participant QueryState as URL Query State
participant ActiveHook as useTableFilterMenuActive
User->>UI: Type value
UI->>Hook: setFilterValueWithOptions(value)
Hook->>Parser: trim input
Parser-->>Hook: trimmed value
Hook->>Hook: debounce 500ms (unless immediate)
Hook->>QueryState: update query param (trimmed or null)
QueryState->>ActiveHook: search params changed
ActiveHook->>Parser: read & trim param
ActiveHook-->>UI: hasActive / activeCount (badge)
User->>UI: Press Enter
UI->>Hook: submit (immediate)
Hook->>QueryState: immediate update
User->>UI: Click clear
UI->>Hook: onClear -> set null
Hook->>QueryState: set param null
QueryState->>ActiveHook: cleared -> update badge
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
Comment Tip CodeRabbit can enforce grammar and style rules using `languagetool`.Configure the |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/hook-form/UnInput.tsx`:
- Around line 8-15: The component and its props type are misnamed:
UncontrolledInput is actually a controlled input (uses value + onChange) and
should be renamed to reflect that API; rename the component UncontrolledInput to
ClearableInput and rename UncontrolledInputProps to ClearableInputProps, update
the default export to ClearableInput, and update any internal references such as
handleChange and its prop usage accordingly; also update all imports/usages
across the codebase to import ClearableInput instead of UncontrolledInput so
call sites reflect the controlled, clearable behavior.
In `@shared/table/FormattedTableHeader.tsx`:
- Around line 88-89: The code is calling getIsColumnFilterable and passing the
raw/cast columnId (which can be 'actions') instead of the normalized id used
elsewhere; change showFilterOptions to call getIsColumnFilterable with the
normalized column id and pass that same normalized id into
FormattedTableHeadMenu so pinning and menu logic use the canonical id (reference
getIsColumnFilterable, FormattedTableHeadMenu, and the normalized column id
variable used earlier in this file) — add a guard/early return if the normalized
id is missing/invalid to satisfy the edge-case handling guideline.
In `@shared/table/FormattedTableHeadMenu.tsx`:
- Around line 21-23: The component is unsafely asserting columnId to AppColumnId
in FormattedTableHeadMenu (casts around useTableFilterMenuActive and two other
sites) which bypasses compiler checks; fix it by changing the
FormattedTableHeadMenu prop type so columnId is typed as AppColumnId (or a union
that includes all valid search table columns) and update callers to pass the
narrowed type, then remove the three casts to AppColumnId and let
useTableFilterMenuActive accept the strongly-typed columnId; alternatively, if
broader coverage is intended, expand the AppColumnId type definition so it
actually includes the additional columns (also apply the same change in
FormattedTableHeader where the same cast occurs).
In `@shared/table/FormattedTableHeadMenuFilterOption.tsx`:
- Around line 69-80: The Label and UncontrolledInput in
FormattedTableHeadMenuFilterOption are siblings and need to be programmatically
linked: add a stable id (e.g., generate via React's useId or accept a prop) and
pass it as the id prop to UncontrolledInput (symbol: UncontrolledInput) and set
the Label's htmlFor to that same id (symbol: Label); ensure any existing
handlers (handleOnChange, handleOnClear, handleOnKeyDown) remain unchanged and
stopPropagation/onFocus behavior is preserved.
- Around line 57-62: handleOnKeyDown currently calls setFilterValueWithOptions
which routes through the debounced path in useTableFilterMenuFilterByQuery, so
Enter doesn’t submit immediately; change the Enter branch to call a
non-debounced immediate updater instead (either an existing exported function
like setFilterValueImmediate or add/export a new immediate setter from
useTableFilterMenuFilterByQuery) passing (e.target as HTMLInputElement).value,
or alter setFilterValueWithOptions to accept an options flag (e.g., { immediate:
true }) and honor it to skip debounce; update both handleOnKeyDown and the hook
(useTableFilterMenuFilterByQuery) accordingly so Enter triggers an immediate
update.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 1f4af71a-9e6c-4ba6-b5cc-eb2c1828831e
📒 Files selected for processing (10)
components/hook-form/UnInput.tsxhooks/useTableFilterMenuActive.tshooks/useTableFilterMenuFilterByQuery.tsnuqs-parsers/string.parser.tsshared/table/FormattedTableHeadMenu.tsxshared/table/FormattedTableHeadMenuFilterOption.tsxshared/table/FormattedTableHeadMenuPinOption.tsxshared/table/FormattedTableHeader.tsxshared/table/table.utils.tsstore/subscriptions/table.store.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
**/*.{ts,tsx}: Write concise, technical TypeScript code with accurate examples using functional and declarative programming patterns; avoid classes
Minimize the use of'use client',useEffect, andsetState; favor React Server Components (RSC) and Next.js SSR features
Implement dynamic imports for code splitting and optimization
Prioritize error handling and edge cases: use early returns for error conditions, implement guard clauses to handle preconditions and invalid states early, use custom error types for consistent error handling
Structure files with exported components, subcomponents, helpers, static content, and types
Use modern state management solutions (e.g., Zustand, TanStack React Query) for global state and data fetching
Implement validation using Zod for schema validation
Implement proper error handling, user input validation, and secure coding practices
Files:
store/subscriptions/table.store.tsshared/table/FormattedTableHeadMenuFilterOption.tsxshared/table/FormattedTableHeadMenuPinOption.tsxhooks/useTableFilterMenuFilterByQuery.tsnuqs-parsers/string.parser.tscomponents/hook-form/UnInput.tsxshared/table/FormattedTableHeadMenu.tsxhooks/useTableFilterMenuActive.tsshared/table/table.utils.tsshared/table/FormattedTableHeader.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Use descriptive variable names with auxiliary verbs (e.g.,
isLoading,hasError)
Files:
store/subscriptions/table.store.tsshared/table/FormattedTableHeadMenuFilterOption.tsxshared/table/FormattedTableHeadMenuPinOption.tsxhooks/useTableFilterMenuFilterByQuery.tsnuqs-parsers/string.parser.tscomponents/hook-form/UnInput.tsxshared/table/FormattedTableHeadMenu.tsxhooks/useTableFilterMenuActive.tsshared/table/table.utils.tsshared/table/FormattedTableHeader.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Optimize images: use WebP format, include size data, implement lazy loading
Files:
store/subscriptions/table.store.tsshared/table/FormattedTableHeadMenuFilterOption.tsxshared/table/FormattedTableHeadMenuPinOption.tsxhooks/useTableFilterMenuFilterByQuery.tsnuqs-parsers/string.parser.tscomponents/hook-form/UnInput.tsxshared/table/FormattedTableHeadMenu.tsxhooks/useTableFilterMenuActive.tsshared/table/table.utils.tsshared/table/FormattedTableHeader.tsx
**/*.{tsx,css,scss}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Use responsive design with a mobile-first approach
Files:
shared/table/FormattedTableHeadMenuFilterOption.tsxshared/table/FormattedTableHeadMenuPinOption.tsxcomponents/hook-form/UnInput.tsxshared/table/FormattedTableHeadMenu.tsxshared/table/FormattedTableHeader.tsx
**/*.{tsx,css,config.*}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Use modern UI frameworks (e.g., Tailwind CSS, Shadcn UI, Radix UI) for styling
Files:
shared/table/FormattedTableHeadMenuFilterOption.tsxshared/table/FormattedTableHeadMenuPinOption.tsxcomponents/hook-form/UnInput.tsxshared/table/FormattedTableHeadMenu.tsxshared/table/FormattedTableHeader.tsx
🧬 Code graph analysis (6)
shared/table/FormattedTableHeadMenuFilterOption.tsx (6)
store/subscriptions/table.store.ts (2)
TableId(4-4)AppColumnId(34-34)hooks/useTableFilterMenuFilterByQuery.ts (1)
useTableFilterMenuFilterByQuery(6-26)components/ui/dropdown-menu.tsx (6)
DropdownMenuSeparator(259-259)DropdownMenuGroup(252-252)DropdownMenuLabel(253-253)DropdownMenuSub(250-250)DropdownMenuSubTrigger(261-261)DropdownMenuSubContent(262-262)shared/components/ColumnStack.tsx (1)
ColumnStack(10-16)components/ui/label.tsx (1)
Label(25-25)components/hook-form/UnInput.tsx (1)
UncontrolledInput(14-38)
hooks/useTableFilterMenuFilterByQuery.ts (2)
store/subscriptions/table.store.ts (2)
TableId(4-4)AppColumnId(34-34)nuqs-parsers/string.parser.ts (1)
parseAsTrimmedString(3-11)
components/hook-form/UnInput.tsx (2)
components/ui/input.tsx (1)
Input(33-33)components/ui/button.tsx (1)
Button(104-104)
hooks/useTableFilterMenuActive.ts (1)
store/subscriptions/table.store.ts (2)
TableId(4-4)AppColumnId(34-34)
shared/table/table.utils.ts (1)
store/subscriptions/table.store.ts (1)
AppColumnId(34-34)
shared/table/FormattedTableHeader.tsx (3)
shared/table/table.utils.ts (1)
getIsColumnFilterable(343-358)store/subscriptions/table.store.ts (1)
AppColumnId(34-34)shared/table/FormattedTableHeadMenu.tsx (1)
FormattedTableHeadMenu(21-64)
🔇 Additional comments (2)
nuqs-parsers/string.parser.ts (1)
3-10: LGTM.Trim + empty-to-null normalization is centralized here, which keeps the query-string contract consistent across the filter hooks.
hooks/useTableFilterMenuFilterByQuery.ts (1)
7-20: LGTM.Debouncing non-empty updates while clearing immediately is a good fit for URL-backed filter state.
| type UncontrolledInputProps = Omit<React.ComponentProps<'input'>, 'value' | 'onChange'> & { | ||
| onClear?: () => void; | ||
| onChange?: (_e: React.ChangeEvent<HTMLInputElement>) => void; | ||
| value: string; | ||
| }; | ||
|
|
||
| export default function UncontrolledInput({ className, onClear, onChange, value = '', ...props }: UncontrolledInputProps) { | ||
| const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Rename this to match the API.
This component is controlled (value + onChange), so UncontrolledInput is misleading at call sites. A name like ClearableInput would better match the contract.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/hook-form/UnInput.tsx` around lines 8 - 15, The component and its
props type are misnamed: UncontrolledInput is actually a controlled input (uses
value + onChange) and should be renamed to reflect that API; rename the
component UncontrolledInput to ClearableInput and rename UncontrolledInputProps
to ClearableInputProps, update the default export to ClearableInput, and update
any internal references such as handleChange and its prop usage accordingly;
also update all imports/usages across the codebase to import ClearableInput
instead of UncontrolledInput so call sites reflect the controlled, clearable
behavior.
| const showFilterOptions = getIsColumnFilterable(columnId as AppColumnId); | ||
|
|
There was a problem hiding this comment.
Pass the normalized column id into the menu flow.
columnId can still be 'actions' here — Line 60 already proves that by normalizing it for store lookups. Casting the raw value to AppColumnId lets the actions header reach the menu with an invalid id, so pinning can target actions instead of tableActions. Use the normalized id for both getIsColumnFilterable(...) and FormattedTableHeadMenu.
Suggested fix
- const showFilterOptions = getIsColumnFilterable(columnId as AppColumnId);
+ const normalizedColumnId = storeColumnId as AppColumnId;
+ const showFilterOptions = getIsColumnFilterable(normalizedColumnId);
...
- <FormattedTableHeadMenu columnId={ columnId } tableId={ tableId } showFilterOptions={ showFilterOptions } />
+ <FormattedTableHeadMenu columnId={ normalizedColumnId } tableId={ tableId } showFilterOptions={ showFilterOptions } />Also applies to: 147-147
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@shared/table/FormattedTableHeader.tsx` around lines 88 - 89, The code is
calling getIsColumnFilterable and passing the raw/cast columnId (which can be
'actions') instead of the normalized id used elsewhere; change showFilterOptions
to call getIsColumnFilterable with the normalized column id and pass that same
normalized id into FormattedTableHeadMenu so pinning and menu logic use the
canonical id (reference getIsColumnFilterable, FormattedTableHeadMenu, and the
normalized column id variable used earlier in this file) — add a guard/early
return if the normalized id is missing/invalid to satisfy the edge-case handling
guideline.
| return ( | ||
| <ColumnStack className="gap-y-2"> | ||
| <Label className="text-sm font-medium">Filter value</Label> | ||
| <UncontrolledInput | ||
| placeholder="Enter filter text..." | ||
| onKeyDown={ handleOnKeyDown } | ||
| onPointerDown={ (e) => e.stopPropagation() } | ||
| onFocus={ (e) => e.target.select() } | ||
| onChange={ handleOnChange } | ||
| value={ filterValue } | ||
| className={ filterValue ? 'pr-8' : '' } | ||
| onClear={ handleOnClear } |
There was a problem hiding this comment.
Associate the label with the input.
Label is rendered as a sibling of the input, so without htmlFor/id the field has no programmatic label. Add an input id and wire it through Label htmlFor.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@shared/table/FormattedTableHeadMenuFilterOption.tsx` around lines 69 - 80,
The Label and UncontrolledInput in FormattedTableHeadMenuFilterOption are
siblings and need to be programmatically linked: add a stable id (e.g., generate
via React's useId or accept a prop) and pass it as the id prop to
UncontrolledInput (symbol: UncontrolledInput) and set the Label's htmlFor to
that same id (symbol: Label); ensure any existing handlers (handleOnChange,
handleOnClear, handleOnKeyDown) remain unchanged and stopPropagation/onFocus
behavior is preserved.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/`(base)/search/_components/SearchTableParent.tsx:
- Line 17: The code defines columnsSorted from BILLS_TABLE_COLUMNS but then
mixes usages; replace the intermediate name to a clear, accurate identifier and
use it everywhere: rename columnsSorted to columns (or similar) and initialize
it from BILLS_TABLE_COLUMNS (e.g., const columns = [...BILLS_TABLE_COLUMNS] as
string[]), then update all references in this component — including the header
rendering and the body (places currently referencing BILLS_TABLE_COLUMNS) — to
use the single columns symbol so the same list is used consistently; ensure the
new name is used in functions/JSX that previously referenced columnsSorted or
BILLS_TABLE_COLUMNS.
In
`@app/`(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsx:
- Line 19: The variable name columnsSorted is misleading because it’s just a
shallow mutable copy of BILLS_TABLE_COLUMNS; rename columnsSorted to columns
(keep the type string[] and the copy operation [...BILLS_TABLE_COLUMNS]) and
update every usage inside SubscriptionDetailsBillsTable (and any local helper
functions or JSX) to use columns instead so intent matches the code (search for
columnsSorted, replace with columns and ensure imports/exports and prop names
remain consistent).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 033dc3bb-eb8f-4735-ab5f-daf37f9fc142
📒 Files selected for processing (2)
app/(base)/search/_components/SearchTableParent.tsxapp/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsx
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
**/*.{ts,tsx}: Write concise, technical TypeScript code with accurate examples using functional and declarative programming patterns; avoid classes
Minimize the use of'use client',useEffect, andsetState; favor React Server Components (RSC) and Next.js SSR features
Implement dynamic imports for code splitting and optimization
Prioritize error handling and edge cases: use early returns for error conditions, implement guard clauses to handle preconditions and invalid states early, use custom error types for consistent error handling
Structure files with exported components, subcomponents, helpers, static content, and types
Use modern state management solutions (e.g., Zustand, TanStack React Query) for global state and data fetching
Implement validation using Zod for schema validation
Implement proper error handling, user input validation, and secure coding practices
Files:
app/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsxapp/(base)/search/_components/SearchTableParent.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Use descriptive variable names with auxiliary verbs (e.g.,
isLoading,hasError)
Files:
app/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsxapp/(base)/search/_components/SearchTableParent.tsx
**/*.{tsx,css,scss}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Use responsive design with a mobile-first approach
Files:
app/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsxapp/(base)/search/_components/SearchTableParent.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Optimize images: use WebP format, include size data, implement lazy loading
Files:
app/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsxapp/(base)/search/_components/SearchTableParent.tsx
**/*.{tsx,css,config.*}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Use modern UI frameworks (e.g., Tailwind CSS, Shadcn UI, Radix UI) for styling
Files:
app/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsxapp/(base)/search/_components/SearchTableParent.tsx
🧬 Code graph analysis (2)
app/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsx (1)
store/subscriptions/table.store.ts (1)
BILLS_TABLE_COLUMNS(22-32)
app/(base)/search/_components/SearchTableParent.tsx (1)
store/subscriptions/table.store.ts (1)
BILLS_TABLE_COLUMNS(22-32)
| const sortData: SortDataModel | null = await getSortDataForPageIdCached(SORT_DATA_PAGE_IDS.search); | ||
| const billDues: BillDueWithSubscriptionAndSortData = await getAllBillsCached(sortData, null); | ||
| const columnsSorted: string[] = BILLS_TABLE_COLUMNS; | ||
| const columnsSorted: string[] = [...BILLS_TABLE_COLUMNS] as string[]; |
There was a problem hiding this comment.
Inconsistent usage: columnsSorted vs BILLS_TABLE_COLUMNS.
The header (line 25) uses columnsSorted, but the body (line 43) still references BILLS_TABLE_COLUMNS directly. If the goal is immutability or consistency, use the same reference in both places.
Also, columnsSorted is misleading—no sorting occurs. Consider renaming to columns.
Proposed fix
- const columnsSorted: string[] = [...BILLS_TABLE_COLUMNS] as string[];
+ const columns: string[] = [...BILLS_TABLE_COLUMNS] as string[];And update usages:
- { columnsSorted.map((column: string, index: number, array: string[]) => {
+ { columns.map((column: string, index: number, array: string[]) => {- { BILLS_TABLE_COLUMNS.map((column: string) => (
+ { columns.map((column: string) => (Also applies to: 43-45
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/`(base)/search/_components/SearchTableParent.tsx at line 17, The code
defines columnsSorted from BILLS_TABLE_COLUMNS but then mixes usages; replace
the intermediate name to a clear, accurate identifier and use it everywhere:
rename columnsSorted to columns (or similar) and initialize it from
BILLS_TABLE_COLUMNS (e.g., const columns = [...BILLS_TABLE_COLUMNS] as
string[]), then update all references in this component — including the header
rendering and the body (places currently referencing BILLS_TABLE_COLUMNS) — to
use the single columns symbol so the same list is used consistently; ensure the
new name is used in functions/JSX that previously referenced columnsSorted or
BILLS_TABLE_COLUMNS.
|
|
||
| export default function SubscriptionDetailsBillsTable({ billDues }: SubscriptionDetailsBillsTableProps) { | ||
| const columnsSorted: string[] = BILLS_TABLE_COLUMNS; | ||
| const columnsSorted: string[] = [...BILLS_TABLE_COLUMNS] as string[]; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Misleading variable name.
columnsSorted implies the array is sorted, but it's just a shallow copy for mutability. Rename to columns for clarity.
Proposed fix
- const columnsSorted: string[] = [...BILLS_TABLE_COLUMNS] as string[];
+ const columns: string[] = [...BILLS_TABLE_COLUMNS] as string[];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@app/`(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsx
at line 19, The variable name columnsSorted is misleading because it’s just a
shallow mutable copy of BILLS_TABLE_COLUMNS; rename columnsSorted to columns
(keep the type string[] and the copy operation [...BILLS_TABLE_COLUMNS]) and
update every usage inside SubscriptionDetailsBillsTable (and any local helper
functions or JSX) to use columns instead so intent matches the code (search for
columnsSorted, replace with columns and ensure imports/exports and prop names
remain consistent).
There was a problem hiding this comment.
♻️ Duplicate comments (2)
shared/table/FormattedTableHeadMenuFilterOption.tsx (1)
69-80:⚠️ Potential issue | 🟡 MinorAssociate the label with the input.
The field still has no programmatic label because
Labelis missinghtmlForandUncontrolledInputhas no matchingid. Screen readers will announce this as an unlabeled input.Proposed fix
+import { useId } from 'react'; import { ListFilter } from 'lucide-react'; @@ function FormattedTableHeadMenuFilterOptionContent({ columnId, tableId }: Props) { + const filterInputId = useId(); const { filterValue, setFilterValueWithOptions } = useTableFilterMenuFilterByQuery(tableId, columnId); @@ - <Label className="text-sm font-medium">Filter value</Label> + <Label htmlFor={ filterInputId } className="text-sm font-medium">Filter value</Label> <UncontrolledInput + id={ filterInputId } placeholder="Enter filter text..." onKeyDown={ handleOnKeyDown }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shared/table/FormattedTableHeadMenuFilterOption.tsx` around lines 69 - 80, The Label and UncontrolledInput are not programmatically associated; update the FormattedTableHeadMenuFilterOption component to generate and pass a stable id to the input and set Label's htmlFor to that id (e.g., use React's useId or build one from a stable key), then pass that id into the UncontrolledInput via its id prop and set Label htmlFor to the same value so screen readers correctly announce the field; adjust references around the existing Label and UncontrolledInput usage (and ensure any onFocus/select logic still works with the id change).shared/table/FormattedTableHeader.tsx (1)
87-87:⚠️ Potential issue | 🟠 MajorPass the normalized column id into the menu flow.
columnIdcan be'actions'at runtime, but downstream consumers (like pinning logic inFormattedTableHeadMenu) may expect'tableActions'. The type castcolumnId as AppColumnIdonly asserts the type—it doesn't transform the value. BothgetIsColumnFilterable(...)andFormattedTableHeadMenushould receive a normalized id.Consider adding a guard or mapping for special column ids to prevent invalid ids reaching the menu.
Suggested approach
+ // Normalize columnId for store/menu operations + const normalizedColumnId = (columnId === 'actions' ? 'tableActions' : columnId) as AppColumnId; - const showFilterOptions = getIsColumnFilterable(columnId as AppColumnId); + const showFilterOptions = getIsColumnFilterable(normalizedColumnId); ... - <FormattedTableHeadMenu columnId={ columnId } tableId={ tableId } showFilterOptions={ showFilterOptions } /> + <FormattedTableHeadMenu columnId={ normalizedColumnId } tableId={ tableId } showFilterOptions={ showFilterOptions } />As per coding guidelines, "Prioritize error handling and edge cases: use early returns for error conditions, implement guard clauses to handle preconditions and invalid states early."
Also applies to: 146-146
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shared/table/FormattedTableHeader.tsx` at line 87, Normalize columnId before using it in the menu flow: introduce a small guard/mapping that converts runtime special ids (e.g. 'actions') to the canonical AppColumnId (e.g. 'tableActions'), assign it to a local normalizedColumnId, and use that normalizedColumnId when calling getIsColumnFilterable(...) and when passing props into FormattedTableHeadMenu; ensure the mapping/guard lives near the top of FormattedTableHeader and returns early or falls back to a safe default if normalization fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@shared/table/FormattedTableHeader.tsx`:
- Line 87: Normalize columnId before using it in the menu flow: introduce a
small guard/mapping that converts runtime special ids (e.g. 'actions') to the
canonical AppColumnId (e.g. 'tableActions'), assign it to a local
normalizedColumnId, and use that normalizedColumnId when calling
getIsColumnFilterable(...) and when passing props into FormattedTableHeadMenu;
ensure the mapping/guard lives near the top of FormattedTableHeader and returns
early or falls back to a safe default if normalization fails.
In `@shared/table/FormattedTableHeadMenuFilterOption.tsx`:
- Around line 69-80: The Label and UncontrolledInput are not programmatically
associated; update the FormattedTableHeadMenuFilterOption component to generate
and pass a stable id to the input and set Label's htmlFor to that id (e.g., use
React's useId or build one from a stable key), then pass that id into the
UncontrolledInput via its id prop and set Label htmlFor to the same value so
screen readers correctly announce the field; adjust references around the
existing Label and UncontrolledInput usage (and ensure any onFocus/select logic
still works with the id change).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: d993ffa6-69da-441f-bf8d-da89a24adef1
📒 Files selected for processing (9)
app/(base)/add/_components/AddedBillDues.tsxapp/(base)/bills/_components/BillsTableParentContent.tsxapp/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsxapp/(base)/subscriptions/_components/SubscriptionsTableParentWrapper.tsxhooks/useTableFilterMenuFilterByQuery.tsshared/table/FormattedTableHeadMenuFilterOption.tsxshared/table/FormattedTableHeader.tsxshared/table/SearchTableHeaderDisplay.tsxstore/subscriptions/table.store.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
**/*.{ts,tsx}: Write concise, technical TypeScript code with accurate examples using functional and declarative programming patterns; avoid classes
Minimize the use of'use client',useEffect, andsetState; favor React Server Components (RSC) and Next.js SSR features
Implement dynamic imports for code splitting and optimization
Prioritize error handling and edge cases: use early returns for error conditions, implement guard clauses to handle preconditions and invalid states early, use custom error types for consistent error handling
Structure files with exported components, subcomponents, helpers, static content, and types
Use modern state management solutions (e.g., Zustand, TanStack React Query) for global state and data fetching
Implement validation using Zod for schema validation
Implement proper error handling, user input validation, and secure coding practices
Files:
app/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsxstore/subscriptions/table.store.tsapp/(base)/add/_components/AddedBillDues.tsxshared/table/SearchTableHeaderDisplay.tsxapp/(base)/subscriptions/_components/SubscriptionsTableParentWrapper.tsxhooks/useTableFilterMenuFilterByQuery.tsapp/(base)/bills/_components/BillsTableParentContent.tsxshared/table/FormattedTableHeadMenuFilterOption.tsxshared/table/FormattedTableHeader.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Use descriptive variable names with auxiliary verbs (e.g.,
isLoading,hasError)
Files:
app/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsxstore/subscriptions/table.store.tsapp/(base)/add/_components/AddedBillDues.tsxshared/table/SearchTableHeaderDisplay.tsxapp/(base)/subscriptions/_components/SubscriptionsTableParentWrapper.tsxhooks/useTableFilterMenuFilterByQuery.tsapp/(base)/bills/_components/BillsTableParentContent.tsxshared/table/FormattedTableHeadMenuFilterOption.tsxshared/table/FormattedTableHeader.tsx
**/*.{tsx,css,scss}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Use responsive design with a mobile-first approach
Files:
app/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsxapp/(base)/add/_components/AddedBillDues.tsxshared/table/SearchTableHeaderDisplay.tsxapp/(base)/subscriptions/_components/SubscriptionsTableParentWrapper.tsxapp/(base)/bills/_components/BillsTableParentContent.tsxshared/table/FormattedTableHeadMenuFilterOption.tsxshared/table/FormattedTableHeader.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Optimize images: use WebP format, include size data, implement lazy loading
Files:
app/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsxstore/subscriptions/table.store.tsapp/(base)/add/_components/AddedBillDues.tsxshared/table/SearchTableHeaderDisplay.tsxapp/(base)/subscriptions/_components/SubscriptionsTableParentWrapper.tsxhooks/useTableFilterMenuFilterByQuery.tsapp/(base)/bills/_components/BillsTableParentContent.tsxshared/table/FormattedTableHeadMenuFilterOption.tsxshared/table/FormattedTableHeader.tsx
**/*.{tsx,css,config.*}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Use modern UI frameworks (e.g., Tailwind CSS, Shadcn UI, Radix UI) for styling
Files:
app/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsxapp/(base)/add/_components/AddedBillDues.tsxshared/table/SearchTableHeaderDisplay.tsxapp/(base)/subscriptions/_components/SubscriptionsTableParentWrapper.tsxapp/(base)/bills/_components/BillsTableParentContent.tsxshared/table/FormattedTableHeadMenuFilterOption.tsxshared/table/FormattedTableHeader.tsx
🧬 Code graph analysis (5)
app/(base)/subscriptions/[subscriptionId]/_components/SubscriptionDetailsBillsTable.tsx (4)
models/bills/bills.model.ts (1)
BillDueWithSubscription(72-75)store/subscriptions/table.store.ts (1)
BILLS_TABLE_COLUMNS(22-32)app/(base)/bills/_components/BillsTableParentRow.tsx (1)
BillsTableParentRow(9-30)shared/table/BillsDueTableParentRow.tsx (1)
BillsTableParentRow(10-18)
app/(base)/add/_components/AddedBillDues.tsx (1)
store/subscriptions/table.store.ts (1)
BILLS_TABLE_COLUMNS(22-32)
shared/table/SearchTableHeaderDisplay.tsx (2)
store/subscriptions/table.store.ts (2)
useTableColumn(248-251)useTableColumnsActions(275-275)hooks/useColumnResize.ts (1)
useColumnResize(37-108)
hooks/useTableFilterMenuFilterByQuery.ts (2)
store/subscriptions/table.store.ts (2)
TableId(4-4)AppColumnId(34-34)nuqs-parsers/string.parser.ts (1)
parseAsTrimmedString(3-11)
shared/table/FormattedTableHeadMenuFilterOption.tsx (6)
store/subscriptions/table.store.ts (2)
TableId(4-4)AppColumnId(34-34)hooks/useTableFilterMenuFilterByQuery.ts (1)
useTableFilterMenuFilterByQuery(6-25)components/ui/dropdown-menu.tsx (5)
DropdownMenuGroup(252-252)DropdownMenuLabel(253-253)DropdownMenuSub(250-250)DropdownMenuSubTrigger(261-261)DropdownMenuSubContent(262-262)shared/components/ColumnStack.tsx (1)
ColumnStack(10-16)components/ui/label.tsx (1)
Label(25-25)components/hook-form/UnInput.tsx (1)
UncontrolledInput(14-38)
🔇 Additional comments (4)
shared/table/SearchTableHeaderDisplay.tsx (1)
53-60: The column ID scheme is correct as-is. All SearchTable column references use'tableActions'(not'actions'), the store persists column widths under thetableActionskey, anduseTableColumn()reads from the correct state property. No remaining'actions'callers exist that would trigger the fallback behavior described.shared/table/FormattedTableHeader.tsx (3)
9-9: LGTM!Import additions for
AppColumnIdandgetIsColumnFilterablealign with the new filtering functionality.Also applies to: 16-16
60-63: LGTM!Using
columnIddirectly foruseTableColumnand the resize hook is cleaner.
118-118: LGTM!RowStack className consolidation looks clean.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@server/bills/bills.server.ts`:
- Around line 359-383: The parsing for bills__cost currently uses
Number.parseFloat which accepts partial strings (e.g., "10abc"); update the
logic that processes costRaw/numericPart (in the block that sets operator and
numericPart and pushes into whereClause.AND) to first trim numericPart, convert
it with Number(numericPartTrimmed) and only accept it when
Number.isFinite(costValue) is true (also guard against empty numericPart after
slicing operators); replace the Number.parseFloat check with this strict numeric
validation so cost: { [operator]: costValue } is only added for valid finite
numbers.
In `@validators/bills/bill.schema.ts`:
- Around line 61-63: The three filter fields in billSearchParamsSchema need
tighter Zod validators: replace the loose z.string().optional() for
['bills__cost'] with a validator that only accepts numeric/decimal strings
(e.g., a regex or z.coerce.number() pattern as string) so downstream parsing
never yields NaN, and for ['bills__subscription'] and ['bills__frequency']
replace the open strings with trimmed, bounded strings (use .trim().max(N) and
optionally a safe-character regex) to prevent oversized or untrimmed free-text
from being passed to Prisma; update billSearchParamsSchema to use these
validators for the keys 'bills__cost', 'bills__subscription', and
'bills__frequency'.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: b360ef68-4bd5-4648-b413-f463b7caed05
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (3)
package.jsonserver/bills/bills.server.tsvalidators/bills/bill.schema.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
**/*.{ts,tsx}: Write concise, technical TypeScript code with accurate examples using functional and declarative programming patterns; avoid classes
Minimize the use of'use client',useEffect, andsetState; favor React Server Components (RSC) and Next.js SSR features
Implement dynamic imports for code splitting and optimization
Prioritize error handling and edge cases: use early returns for error conditions, implement guard clauses to handle preconditions and invalid states early, use custom error types for consistent error handling
Structure files with exported components, subcomponents, helpers, static content, and types
Use modern state management solutions (e.g., Zustand, TanStack React Query) for global state and data fetching
Implement validation using Zod for schema validation
Implement proper error handling, user input validation, and secure coding practices
Files:
server/bills/bills.server.tsvalidators/bills/bill.schema.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Use descriptive variable names with auxiliary verbs (e.g.,
isLoading,hasError)
Files:
server/bills/bills.server.tsvalidators/bills/bill.schema.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Optimize images: use WebP format, include size data, implement lazy loading
Files:
server/bills/bills.server.tsvalidators/bills/bill.schema.ts
🔇 Additional comments (4)
package.json (1)
63-63: Dependency versions confirmed. Bothlucide-react@0.577.0andzustand@5.0.12are latest stable releases; patch-level updates supporting the new filtering features with no breaking changes expected.server/bills/bills.server.ts (3)
326-326: No actionable change here.
386-402: Good addition for nested text filters.The
subscription.nameandsubscription.billCycleDurationcase-insensitive filters are wired correctly and compose cleanly with the existingANDclause.
762-762: No actionable change here.
| [`bills__cost`]: z.string().optional(), | ||
| ['bills__subscription']: z.string().optional(), | ||
| ['bills__frequency']: z.string().optional(), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, locate and read the validation file
find . -name "bill.schema.ts" -o -name "bill.schema.ts" | head -20Repository: yiqu/tb
Length of output: 84
🏁 Script executed:
# Search for the bill schema file more broadly
fd "bill.*schema" --type fRepository: yiqu/tb
Length of output: 82
🏁 Script executed:
# List files in validators directory to understand structure
find . -path "*/validators/bills/*" -type fRepository: yiqu/tb
Length of output: 84
🏁 Script executed:
cat -n validators/bills/bill.schema.tsRepository: yiqu/tb
Length of output: 3329
🏁 Script executed:
# Search for usages of the filter field names
rg "bills__cost|bills__subscription|bills__frequency" --type ts --type tsxRepository: yiqu/tb
Length of output: 78
🏁 Script executed:
# Search for where billSearchParamsSchema is used
rg "billSearchParamsSchema" --type ts --type tsxRepository: yiqu/tb
Length of output: 78
🏁 Script executed:
# Search for usages of the filter field names without type restriction
rg "bills__cost|bills__subscription|bills__frequency"Repository: yiqu/tb
Length of output: 956
🏁 Script executed:
# Search for where billSearchParamsSchema is used
rg "billSearchParamsSchema"Repository: yiqu/tb
Length of output: 9963
🏁 Script executed:
cat -n server/bills/bills.server.ts | head -150Repository: yiqu/tb
Length of output: 6147
🏁 Script executed:
# Search for the actual parsing logic in bills.server.ts
rg -A 5 "bills__cost" server/bills/bills.server.tsRepository: yiqu/tb
Length of output: 367
🏁 Script executed:
# Get more context around the cost parsing logic
rg -B 2 -A 15 "bills__cost" server/bills/bills.server.tsRepository: yiqu/tb
Length of output: 721
🏁 Script executed:
# Get the complete cost parsing logic
rg -B 2 -A 30 "bills__cost" server/bills/bills.server.ts | head -60Repository: yiqu/tb
Length of output: 1213
🏁 Script executed:
# Get the frequency parsing logic
rg -B 2 -A 10 "bills__frequency" server/bills/bills.server.tsRepository: yiqu/tb
Length of output: 534
Add constraints to filter parameters in billSearchParamsSchema.
These query filter fields lack validation. The bills__cost filter has downstream parsing logic that silently ignores malformed input (returns NaN), while bills__subscription and bills__frequency pass untrimmed, unbounded strings directly to Prisma queries. Add Zod validation to enforce format consistency and prevent oversized free-text filters that could stress the database.
Suggested schema hardening
+const billCostFilterSchema = z
+ .string()
+ .trim()
+ .refine((value) => value === '' || /^(?:[<>]=?|)?\s*-?\d+(?:\.\d+)?$/.test(value), {
+ message: 'Invalid cost filter format',
+ });
+
export const billSearchParamsSchema = z.object({
frequency: z.string().optional(),
@@
- [`bills__cost`]: z.string().optional(),
- ['bills__subscription']: z.string().optional(),
- ['bills__frequency']: z.string().optional(),
+ [`bills__cost`]: billCostFilterSchema.optional(),
+ ['bills__subscription']: z.string().trim().max(120).optional(),
+ ['bills__frequency']: z.string().trim().max(64).optional(),
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@validators/bills/bill.schema.ts` around lines 61 - 63, The three filter
fields in billSearchParamsSchema need tighter Zod validators: replace the loose
z.string().optional() for ['bills__cost'] with a validator that only accepts
numeric/decimal strings (e.g., a regex or z.coerce.number() pattern as string)
so downstream parsing never yields NaN, and for ['bills__subscription'] and
['bills__frequency'] replace the open strings with trimmed, bounded strings (use
.trim().max(N) and optionally a safe-character regex) to prevent oversized or
untrimmed free-text from being passed to Prisma; update billSearchParamsSchema
to use these validators for the keys 'bills__cost', 'bills__subscription', and
'bills__frequency'.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
validators/subscriptions/subscriptions.schema.ts (1)
47-54:⚠️ Potential issue | 🟠 MajorMatch the schema keys to the filter menu contract.
The menu hook writes
subscriptions__${columnId}, but this schema only declaressubscription_costand omits the other subscription-column filters added in this PR. Those params will be stripped by Zod or remain untyped, so the new server-side filters will not receive them reliably.Suggested fix
export const subscriptionSearchParamsSchema = z.object({ frequency: z.string().optional(), subscriptions: z.string().optional(), page: z.number().optional(), addBillDueSubscriptionId: z.string().optional(), year: z.string().optional(), - subscription_cost: z.string().optional(), + subscriptions__cost: z.string().optional(), + subscriptions__name: z.string().optional(), + subscriptions__billCycleDuration: z.string().optional(), + subscriptions__description: z.string().optional(), + subscriptions__url: z.string().optional(), });As per coding guidelines, "Implement validation using Zod for schema validation".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@validators/subscriptions/subscriptions.schema.ts` around lines 47 - 54, subscriptionSearchParamsSchema currently only lists fixed keys (like subscription_cost) and will strip or reject dynamic filter keys emitted by the menu hook (subscriptions__${columnId}); update subscriptionSearchParamsSchema to accept those dynamic keys by adding a catchall validator (e.g., .catchall(z.string().optional())) to the existing z.object(...) so any subscriptions__* params are preserved and typed as optional strings.
♻️ Duplicate comments (1)
shared/table/FormattedTableHeader.tsx (1)
60-63:⚠️ Potential issue | 🟠 MajorNormalize
actionsto the canonical column key before using it here.This path now threads raw
columnIdthrough width state and the menu. When the header id isactions, the store/text helpers still key that column astableActions, so the actions header falls back to the default width and pinning targets an unknown key.Suggested fix
+ const normalizedColumnId = columnId === 'actions' ? 'tableActions' : columnId; - const columnWidth = useTableColumn(columnId); + const columnWidth = useTableColumn(normalizedColumnId); const { currentWidth, isResizing, handleResizePointerDown } = useColumnResize({ - columnId, + columnId: normalizedColumnId, initialWidth: columnWidth, minWidth: 80, maxWidth: 1200, onWidthChange: setColumnWidth, }); @@ - const showFilterOptions = getIsColumnFilterable(columnId as AppColumnId); + const showFilterOptions = getIsColumnFilterable(normalizedColumnId as AppColumnId); @@ - { SEARCH_TABLE_COLUMN_TEXT[columnId] ?? columnId } + { SEARCH_TABLE_COLUMN_TEXT[normalizedColumnId] ?? normalizedColumnId } @@ - <FormattedTableHeadMenu columnId={ columnId } tableId={ tableId } showFilterOptions={ showFilterOptions } columnIndex={ index } /> + <FormattedTableHeadMenu + columnId={ normalizedColumnId } + tableId={ tableId } + showFilterOptions={ showFilterOptions } + columnIndex={ index } + />Also applies to: 87-87, 146-146
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shared/table/FormattedTableHeader.tsx` around lines 60 - 63, The header should normalize the special-case id 'actions' to the canonical key 'tableActions' before passing it into width and menu hooks; create a normalized id (e.g., normalizedColumnId = columnId === 'actions' ? 'tableActions' : columnId) and use normalizedColumnId wherever columnId is passed to useTableColumn, useTableColumnsActions, useColumnResize and any menu/pinning/width logic (references: columnId, useTableColumn, useTableColumnsActions, useColumnResize); apply the same normalization to the other occurrences noted (lines around the other header usages).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/`(base)/_dashboard/_components/BillsTableHasParamsSection.tsx:
- Around line 15-27: The component currently always returns null, disabling the
params action section; restore the original conditional that checks
hasSearchParams using isSearchParamsExist(searchParams) and render the
Suspense-wrapped BillsActionBarClearAllFilters plus the Separator when true.
Re-enable (or re-add) the commented block that computes hasSearchParams and the
JSX with Suspense(fallback=ActionBarButtonSkeleton) and Separator so the
clear-filters UI appears when searchParams exist; ensure the referenced
identifiers (isSearchParamsExist, searchParams, BillsActionBarClearAllFilters,
ActionBarButtonSkeleton, Suspense, Separator) are imported/available in
BillsTableHasParamsSection.tsx.
In `@app/`(base)/_dashboard/_components/dashboard.utils.ts:
- Around line 13-19: Rename the local variable paramsWithOutInitialParams to
paramsWithoutInitialParams (fix the lowercase 'o' typo) and simplify the
function by returning the boolean expression directly: replace the if-statement
that checks paramsWithOutInitialParams.length > 0 and returns true/false with a
single return of paramsWithoutInitialParams.length > 0; update any references in
the same scope to the new name.
In `@hooks/useDashboardRangeSelect.ts`:
- Around line 10-17: The helper named clearParams only resets
monthDueBillsNavigation (selectedMonthYear) but its name implies a full filter
reset; either rename clearParams to clearSelectedMonthYear and update all
callers and the returned property to that new name, or expand its implementation
to clear every filter/query state used by this hook (e.g., reset
monthDueBillsNavigation plus any other active filter state) and keep the name;
update the returned object (currently returning selectedMonthYear,
setMonthDueBillsNavigation, clearParams) to match the chosen change so callers
use a descriptive, accurate symbol.
In `@server/bills/bills.server.ts`:
- Around line 359-384: The filter currently only matches billDue.cost; update
the whereClause modification so it filters by the effective cost (billDue.cost
when present, otherwise billDue.subscription.cost). Instead of pushing { cost: {
[operator]: costValue } }, push an OR condition that matches either billDue.cost
with the operator or when billDue.cost is null matches subscription.cost with
the same operator (use whereClause.AND.push with an object like { OR: [ {
billDue: { cost: { [operator]: costValue } } }, { billDue: { cost: null },
subscription: { cost: { [operator]: costValue } } } ] } ), using the existing
variables costRaw, operator and costValue and the whereClause and
whereClause.AND.push call to locate the change.
---
Outside diff comments:
In `@validators/subscriptions/subscriptions.schema.ts`:
- Around line 47-54: subscriptionSearchParamsSchema currently only lists fixed
keys (like subscription_cost) and will strip or reject dynamic filter keys
emitted by the menu hook (subscriptions__${columnId}); update
subscriptionSearchParamsSchema to accept those dynamic keys by adding a catchall
validator (e.g., .catchall(z.string().optional())) to the existing z.object(...)
so any subscriptions__* params are preserved and typed as optional strings.
---
Duplicate comments:
In `@shared/table/FormattedTableHeader.tsx`:
- Around line 60-63: The header should normalize the special-case id 'actions'
to the canonical key 'tableActions' before passing it into width and menu hooks;
create a normalized id (e.g., normalizedColumnId = columnId === 'actions' ?
'tableActions' : columnId) and use normalizedColumnId wherever columnId is
passed to useTableColumn, useTableColumnsActions, useColumnResize and any
menu/pinning/width logic (references: columnId, useTableColumn,
useTableColumnsActions, useColumnResize); apply the same normalization to the
other occurrences noted (lines around the other header usages).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: acbf63b4-3f83-4c35-8dd3-07980c458eb8
📒 Files selected for processing (13)
app/(base)/_dashboard/_components/BillsActionBarClearAllFilters.tsxapp/(base)/_dashboard/_components/BillsActivityMonthSelectPrevious.tsxapp/(base)/_dashboard/_components/BillsTableHasParamsSection.tsxapp/(base)/_dashboard/_components/dashboard.utils.tshooks/useDashboardRangeSelect.tshooks/useTableFilterMenuFilterByQuery.tsserver/bills/bills.server.tsserver/subscriptions/subscriptions.server.tsshared/table/FormattedTableHeadMenu.tsxshared/table/FormattedTableHeadMenuPinOption.tsxshared/table/FormattedTableHeader.tsxshared/table/table.utils.tsvalidators/subscriptions/subscriptions.schema.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
**/*.{ts,tsx}: Write concise, technical TypeScript code with accurate examples using functional and declarative programming patterns; avoid classes
Minimize the use of'use client',useEffect, andsetState; favor React Server Components (RSC) and Next.js SSR features
Implement dynamic imports for code splitting and optimization
Prioritize error handling and edge cases: use early returns for error conditions, implement guard clauses to handle preconditions and invalid states early, use custom error types for consistent error handling
Structure files with exported components, subcomponents, helpers, static content, and types
Use modern state management solutions (e.g., Zustand, TanStack React Query) for global state and data fetching
Implement validation using Zod for schema validation
Implement proper error handling, user input validation, and secure coding practices
Files:
validators/subscriptions/subscriptions.schema.tshooks/useTableFilterMenuFilterByQuery.tshooks/useDashboardRangeSelect.tsshared/table/table.utils.tsapp/(base)/_dashboard/_components/BillsTableHasParamsSection.tsxshared/table/FormattedTableHeadMenu.tsxapp/(base)/_dashboard/_components/BillsActivityMonthSelectPrevious.tsxshared/table/FormattedTableHeadMenuPinOption.tsxapp/(base)/_dashboard/_components/dashboard.utils.tsserver/subscriptions/subscriptions.server.tsapp/(base)/_dashboard/_components/BillsActionBarClearAllFilters.tsxshared/table/FormattedTableHeader.tsxserver/bills/bills.server.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Use descriptive variable names with auxiliary verbs (e.g.,
isLoading,hasError)
Files:
validators/subscriptions/subscriptions.schema.tshooks/useTableFilterMenuFilterByQuery.tshooks/useDashboardRangeSelect.tsshared/table/table.utils.tsapp/(base)/_dashboard/_components/BillsTableHasParamsSection.tsxshared/table/FormattedTableHeadMenu.tsxapp/(base)/_dashboard/_components/BillsActivityMonthSelectPrevious.tsxshared/table/FormattedTableHeadMenuPinOption.tsxapp/(base)/_dashboard/_components/dashboard.utils.tsserver/subscriptions/subscriptions.server.tsapp/(base)/_dashboard/_components/BillsActionBarClearAllFilters.tsxshared/table/FormattedTableHeader.tsxserver/bills/bills.server.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Optimize images: use WebP format, include size data, implement lazy loading
Files:
validators/subscriptions/subscriptions.schema.tshooks/useTableFilterMenuFilterByQuery.tshooks/useDashboardRangeSelect.tsshared/table/table.utils.tsapp/(base)/_dashboard/_components/BillsTableHasParamsSection.tsxshared/table/FormattedTableHeadMenu.tsxapp/(base)/_dashboard/_components/BillsActivityMonthSelectPrevious.tsxshared/table/FormattedTableHeadMenuPinOption.tsxapp/(base)/_dashboard/_components/dashboard.utils.tsserver/subscriptions/subscriptions.server.tsapp/(base)/_dashboard/_components/BillsActionBarClearAllFilters.tsxshared/table/FormattedTableHeader.tsxserver/bills/bills.server.ts
**/*.{tsx,css,scss}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Use responsive design with a mobile-first approach
Files:
app/(base)/_dashboard/_components/BillsTableHasParamsSection.tsxshared/table/FormattedTableHeadMenu.tsxapp/(base)/_dashboard/_components/BillsActivityMonthSelectPrevious.tsxshared/table/FormattedTableHeadMenuPinOption.tsxapp/(base)/_dashboard/_components/BillsActionBarClearAllFilters.tsxshared/table/FormattedTableHeader.tsx
**/*.{tsx,css,config.*}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-rules.mdc)
Use modern UI frameworks (e.g., Tailwind CSS, Shadcn UI, Radix UI) for styling
Files:
app/(base)/_dashboard/_components/BillsTableHasParamsSection.tsxshared/table/FormattedTableHeadMenu.tsxapp/(base)/_dashboard/_components/BillsActivityMonthSelectPrevious.tsxshared/table/FormattedTableHeadMenuPinOption.tsxapp/(base)/_dashboard/_components/BillsActionBarClearAllFilters.tsxshared/table/FormattedTableHeader.tsx
🧠 Learnings (2)
📚 Learning: 2026-03-09T04:12:00.060Z
Learnt from: CR
Repo: yiqu/tb PR: 0
File: .cursor/rules/nextjs-rules.mdc:0-0
Timestamp: 2026-03-09T04:12:00.060Z
Learning: Applies to **/*.{ts,tsx} : Implement proper error handling, user input validation, and secure coding practices
Applied to files:
shared/table/FormattedTableHeadMenu.tsx
📚 Learning: 2026-03-09T04:12:00.060Z
Learnt from: CR
Repo: yiqu/tb PR: 0
File: .cursor/rules/nextjs-rules.mdc:0-0
Timestamp: 2026-03-09T04:12:00.060Z
Learning: Applies to **/*.{ts,tsx} : Use modern state management solutions (e.g., Zustand, TanStack React Query) for global state and data fetching
Applied to files:
app/(base)/_dashboard/_components/BillsActivityMonthSelectPrevious.tsx
🧬 Code graph analysis (7)
hooks/useTableFilterMenuFilterByQuery.ts (2)
store/subscriptions/table.store.ts (2)
TableId(4-4)AppColumnId(34-34)nuqs-parsers/string.parser.ts (1)
parseAsTrimmedString(3-11)
shared/table/table.utils.ts (1)
store/subscriptions/table.store.ts (1)
AppColumnId(34-34)
shared/table/FormattedTableHeadMenu.tsx (4)
store/subscriptions/table.store.ts (2)
TableId(4-4)AppColumnId(34-34)hooks/useTableFilterMenuActive.ts (1)
useTableFilterMenuActive(5-17)shared/table/FormattedTableHeadMenuPinOption.tsx (1)
FormattedTableHeadMenuPinOption(13-31)shared/table/FormattedTableHeadMenuFilterOption.tsx (1)
FormattedTableHeadMenuFilterOption(24-49)
app/(base)/_dashboard/_components/BillsActivityMonthSelectPrevious.tsx (1)
hooks/useDashboardRangeSelect.ts (1)
useDashboardRangeSelect(3-19)
shared/table/FormattedTableHeadMenuPinOption.tsx (2)
store/subscriptions/table.store.ts (3)
TableId(4-4)AppColumnId(34-34)useTableColumnsActions(275-275)components/ui/dropdown-menu.tsx (3)
DropdownMenuGroup(252-252)DropdownMenuLabel(253-253)DropdownMenuItem(251-251)
app/(base)/_dashboard/_components/dashboard.utils.ts (1)
validators/bills/bill.schema.ts (1)
billSearchParamsSchema(44-64)
shared/table/FormattedTableHeader.tsx (4)
store/subscriptions/table.store.ts (3)
useTableColumn(248-251)useTableColumnsActions(275-275)AppColumnId(34-34)shared/table/table.utils.ts (1)
getIsColumnFilterable(343-370)shared/components/RowStack.tsx (1)
RowStack(9-15)shared/table/FormattedTableHeadMenu.tsx (1)
FormattedTableHeadMenu(22-70)
🔇 Additional comments (3)
app/(base)/_dashboard/_components/BillsActionBarClearAllFilters.tsx (1)
7-14: Good simplification with shared hook usage.Using
useDashboardRangeSelecton Line 10 keeps this action component focused and removes router-specific coupling.app/(base)/_dashboard/_components/BillsActivityMonthSelectPrevious.tsx (1)
18-25: Branch handling is clear and correct here.The Line 21–25 split cleanly handles “back to current month” vs “navigate to previous month” with the shared hook API.
hooks/useTableFilterMenuFilterByQuery.ts (1)
7-12: Reconsider in context of codebase-wide filter pattern.
history: 'push'does create entries during debounced pauses, but this pattern is intentional and consistent across all 50+ filter implementations in the codebase (pagination, subscriptions, bills, etc.), where it semantically distinguishes persistent state changes from temporary dialog operations that usehistory: 'replace'. Changing only this file creates an inconsistency. If this UX concern applies here, it should be addressed as a codebase-wide refactor of all filter components, not in isolation.
| // const hasSearchParams: boolean = isSearchParamsExist(searchParams); | ||
| // if (hasSearchParams) { | ||
| // return ( | ||
| // <> | ||
| // <Suspense fallback={ <ActionBarButtonSkeleton /> }> | ||
| // <BillsActionBarClearAllFilters /> | ||
| // </Suspense> | ||
| // <Separator orientation="vertical" className="h-[1.2rem]!" /> | ||
| // </> | ||
| // ); | ||
| // } | ||
|
|
||
| return null; |
There was a problem hiding this comment.
This currently disables the params action section entirely.
Line 27 always returns null, so the clear-filters UI never renders even when params exist. If this is not intentional, restore the conditional block instead of commenting it out.
Proposed fix
export default function BillsTableHasParamsSection({ searchParams }: Props) {
- // const hasSearchParams: boolean = isSearchParamsExist(searchParams);
- // if (hasSearchParams) {
- // return (
- // <>
- // <Suspense fallback={ <ActionBarButtonSkeleton /> }>
- // <BillsActionBarClearAllFilters />
- // </Suspense>
- // <Separator orientation="vertical" className="h-[1.2rem]!" />
- // </>
- // );
- // }
-
- return null;
+ const hasSearchParams: boolean = isSearchParamsExist(searchParams);
+ if (!hasSearchParams) return null;
+
+ return (
+ <>
+ <Suspense fallback={ <ActionBarButtonSkeleton /> }>
+ <BillsActionBarClearAllFilters />
+ </Suspense>
+ <Separator orientation="vertical" className="h-[1.2rem]!" />
+ </>
+ );
}📝 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.
| // const hasSearchParams: boolean = isSearchParamsExist(searchParams); | |
| // if (hasSearchParams) { | |
| // return ( | |
| // <> | |
| // <Suspense fallback={ <ActionBarButtonSkeleton /> }> | |
| // <BillsActionBarClearAllFilters /> | |
| // </Suspense> | |
| // <Separator orientation="vertical" className="h-[1.2rem]!" /> | |
| // </> | |
| // ); | |
| // } | |
| return null; | |
| const hasSearchParams: boolean = isSearchParamsExist(searchParams); | |
| if (!hasSearchParams) return null; | |
| return ( | |
| <> | |
| <Suspense fallback={ <ActionBarButtonSkeleton /> }> | |
| <BillsActionBarClearAllFilters /> | |
| </Suspense> | |
| <Separator orientation="vertical" className="h-[1.2rem]!" /> | |
| </> | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/`(base)/_dashboard/_components/BillsTableHasParamsSection.tsx around
lines 15 - 27, The component currently always returns null, disabling the params
action section; restore the original conditional that checks hasSearchParams
using isSearchParamsExist(searchParams) and render the Suspense-wrapped
BillsActionBarClearAllFilters plus the Separator when true. Re-enable (or
re-add) the commented block that computes hasSearchParams and the JSX with
Suspense(fallback=ActionBarButtonSkeleton) and Separator so the clear-filters UI
appears when searchParams exist; ensure the referenced identifiers
(isSearchParamsExist, searchParams, BillsActionBarClearAllFilters,
ActionBarButtonSkeleton, Suspense, Separator) are imported/available in
BillsTableHasParamsSection.tsx.
| const paramsWithOutInitialParams = searchParamsKeys.filter((key) => key !== 'month' && key !== 'year'); | ||
|
|
||
| if ( | ||
| (searchParamsKeys.length === 3 && | ||
| searchParamsKeys.includes('month') && | ||
| searchParamsKeys.includes('year') && | ||
| searchParamsKeys.includes('page')) || | ||
| searchParamsKeys.includes('selectedMonthYear') || | ||
| searchParamsKeys.includes('editBillId') | ||
| ) { | ||
| return false; | ||
| if (paramsWithOutInitialParams.length > 0) { | ||
| return true; | ||
| } | ||
|
|
||
| return hasSearchParams; | ||
| return false; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Typo in variable name and simplify return.
paramsWithOutInitialParams → paramsWithoutInitialParams (lowercase 'o' in "without"). Also, the conditional can be reduced to a direct return.
♻️ Proposed simplification
- const paramsWithOutInitialParams = searchParamsKeys.filter((key) => key !== 'month' && key !== 'year');
+ const paramsWithoutInitialParams = searchParamsKeys.filter((key) => key !== 'month' && key !== 'year');
- if (paramsWithOutInitialParams.length > 0) {
- return true;
- }
-
- return false;
+ return paramsWithoutInitialParams.length > 0;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/`(base)/_dashboard/_components/dashboard.utils.ts around lines 13 - 19,
Rename the local variable paramsWithOutInitialParams to
paramsWithoutInitialParams (fix the lowercase 'o' typo) and simplify the
function by returning the boolean expression directly: replace the if-statement
that checks paramsWithOutInitialParams.length > 0 and returns true/false with a
single return of paramsWithoutInitialParams.length > 0; update any references in
the same scope to the new name.
| const clearParams = () => { | ||
| setMonthDueBillsNavigation(null); | ||
| }; | ||
|
|
||
| return { | ||
| selectedMonthYear: monthDueBillsNavigation, | ||
| setMonthDueBillsNavigation, | ||
| clearParams, |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Rename clearParams or clear all relevant query params.
On Line 10, clearParams suggests a full filter reset, but it only clears selectedMonthYear (Line 11). This can cause incorrect usage in callers. Either rename it to something scoped (e.g., clearSelectedMonthYear) or extend it to clear all active filter query keys.
As per coding guidelines: "**/*.{ts,tsx,js,jsx}: Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@hooks/useDashboardRangeSelect.ts` around lines 10 - 17, The helper named
clearParams only resets monthDueBillsNavigation (selectedMonthYear) but its name
implies a full filter reset; either rename clearParams to clearSelectedMonthYear
and update all callers and the returned property to that new name, or expand its
implementation to clear every filter/query state used by this hook (e.g., reset
monthDueBillsNavigation plus any other active filter state) and keep the name;
update the returned object (currently returning selectedMonthYear,
setMonthDueBillsNavigation, clearParams) to match the chosen change so callers
use a descriptive, accurate symbol.
| if (searchParams?.['bills__cost'] && searchParams['bills__cost'].trim() !== '') { | ||
| const costRaw: string = searchParams['bills__cost'].trim(); | ||
| let operator: 'equals' | 'gte' | 'lte' | 'gt' | 'lt' = 'equals'; | ||
| let numericPart: string = costRaw; | ||
|
|
||
| if (costRaw.startsWith('>=')) { | ||
| operator = 'gte'; | ||
| numericPart = costRaw.slice(2); | ||
| } else if (costRaw.startsWith('<=')) { | ||
| operator = 'lte'; | ||
| numericPart = costRaw.slice(2); | ||
| } else if (costRaw.startsWith('>')) { | ||
| operator = 'gt'; | ||
| numericPart = costRaw.slice(1); | ||
| } else if (costRaw.startsWith('<')) { | ||
| operator = 'lt'; | ||
| numericPart = costRaw.slice(1); | ||
| } | ||
|
|
||
| const trimmed = numericPart.trim(); | ||
| const costValue = Number(trimmed); | ||
| if (trimmed !== '' && Number.isFinite(costValue)) { | ||
| whereClause.AND.push({ | ||
| cost: { [operator]: costValue }, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Filter against the effective bill cost.
This only matches billDue.cost. The rest of this file already falls back to billDue.subscription.cost when billDue.cost is null, so matching rows can disappear from the filtered result even though the table still shows the same cost.
Suggested fix
const trimmed = numericPart.trim();
const costValue = Number(trimmed);
if (trimmed !== '' && Number.isFinite(costValue)) {
whereClause.AND.push({
- cost: { [operator]: costValue },
+ OR: [
+ { cost: { [operator]: costValue } },
+ {
+ AND: [
+ { cost: null },
+ { subscription: { cost: { [operator]: costValue } } },
+ ],
+ },
+ ],
});
}📝 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.
| if (searchParams?.['bills__cost'] && searchParams['bills__cost'].trim() !== '') { | |
| const costRaw: string = searchParams['bills__cost'].trim(); | |
| let operator: 'equals' | 'gte' | 'lte' | 'gt' | 'lt' = 'equals'; | |
| let numericPart: string = costRaw; | |
| if (costRaw.startsWith('>=')) { | |
| operator = 'gte'; | |
| numericPart = costRaw.slice(2); | |
| } else if (costRaw.startsWith('<=')) { | |
| operator = 'lte'; | |
| numericPart = costRaw.slice(2); | |
| } else if (costRaw.startsWith('>')) { | |
| operator = 'gt'; | |
| numericPart = costRaw.slice(1); | |
| } else if (costRaw.startsWith('<')) { | |
| operator = 'lt'; | |
| numericPart = costRaw.slice(1); | |
| } | |
| const trimmed = numericPart.trim(); | |
| const costValue = Number(trimmed); | |
| if (trimmed !== '' && Number.isFinite(costValue)) { | |
| whereClause.AND.push({ | |
| cost: { [operator]: costValue }, | |
| }); | |
| } | |
| if (searchParams?.['bills__cost'] && searchParams['bills__cost'].trim() !== '') { | |
| const costRaw: string = searchParams['bills__cost'].trim(); | |
| let operator: 'equals' | 'gte' | 'lte' | 'gt' | 'lt' = 'equals'; | |
| let numericPart: string = costRaw; | |
| if (costRaw.startsWith('>=')) { | |
| operator = 'gte'; | |
| numericPart = costRaw.slice(2); | |
| } else if (costRaw.startsWith('<=')) { | |
| operator = 'lte'; | |
| numericPart = costRaw.slice(2); | |
| } else if (costRaw.startsWith('>')) { | |
| operator = 'gt'; | |
| numericPart = costRaw.slice(1); | |
| } else if (costRaw.startsWith('<')) { | |
| operator = 'lt'; | |
| numericPart = costRaw.slice(1); | |
| } | |
| const trimmed = numericPart.trim(); | |
| const costValue = Number(trimmed); | |
| if (trimmed !== '' && Number.isFinite(costValue)) { | |
| whereClause.AND.push({ | |
| OR: [ | |
| { cost: { [operator]: costValue } }, | |
| { | |
| AND: [ | |
| { cost: null }, | |
| { subscription: { cost: { [operator]: costValue } } }, | |
| ], | |
| }, | |
| ], | |
| }); | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@server/bills/bills.server.ts` around lines 359 - 384, The filter currently
only matches billDue.cost; update the whereClause modification so it filters by
the effective cost (billDue.cost when present, otherwise
billDue.subscription.cost). Instead of pushing { cost: { [operator]: costValue }
}, push an OR condition that matches either billDue.cost with the operator or
when billDue.cost is null matches subscription.cost with the same operator (use
whereClause.AND.push with an object like { OR: [ { billDue: { cost: {
[operator]: costValue } } }, { billDue: { cost: null }, subscription: { cost: {
[operator]: costValue } } } ] } ), using the existing variables costRaw,
operator and costValue and the whereClause and whereClause.AND.push call to
locate the change.
There was a problem hiding this comment.
2 issues found across 27 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="validators/subscriptions/subscriptions.schema.ts">
<violation number="1" location="validators/subscriptions/subscriptions.schema.ts:53">
P3: Naming inconsistency: `subscription_cost` uses snake_case while every other key in this schema is camelCase (e.g. `addBillDueSubscriptionId`). Consider renaming to `subscriptionCost` for consistency.</violation>
</file>
<file name="app/(base)/_dashboard/_components/dashboard.utils.ts">
<violation number="1" location="app/(base)/_dashboard/_components/dashboard.utils.ts:13">
P2: `page` and `selectedMonthYear` are no longer excluded from the filter check. When a user paginates or selects a different month, `isSearchParamsExist` will now return `true`, which may incorrectly indicate active filters in the pagination component.</violation>
</file>
Since this is your first cubic review, here's how it works:
- cubic automatically reviews your code and comments on bugs and improvements
- Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
- Add one-off context when rerunning by tagging
@cubic-dev-aiwith guidance or docs links (includingllms.txt) - Ask questions if you need clarification on any suggestion
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| if (searchParamsKeys.length === 2 && searchParamsKeys.includes('month') && searchParamsKeys.includes('year')) { | ||
| return false; | ||
| } | ||
| const paramsWithOutInitialParams = searchParamsKeys.filter((key) => key !== 'month' && key !== 'year'); |
There was a problem hiding this comment.
P2: page and selectedMonthYear are no longer excluded from the filter check. When a user paginates or selects a different month, isSearchParamsExist will now return true, which may incorrectly indicate active filters in the pagination component.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/(base)/_dashboard/_components/dashboard.utils.ts, line 13:
<comment>`page` and `selectedMonthYear` are no longer excluded from the filter check. When a user paginates or selects a different month, `isSearchParamsExist` will now return `true`, which may incorrectly indicate active filters in the pagination component.</comment>
<file context>
@@ -5,31 +5,18 @@ import { EST_TIME_ZONE } from '@/lib/general.utils';
- if (searchParamsKeys.length === 2 && searchParamsKeys.includes('month') && searchParamsKeys.includes('year')) {
- return false;
- }
+ const paramsWithOutInitialParams = searchParamsKeys.filter((key) => key !== 'month' && key !== 'year');
- if (
</file context>
| const paramsWithOutInitialParams = searchParamsKeys.filter((key) => key !== 'month' && key !== 'year'); | |
| const paramsWithOutInitialParams = searchParamsKeys.filter((key) => key !== 'month' && key !== 'year' && key !== 'page' && key !== 'selectedMonthYear'); |
| page: z.number().optional(), | ||
| addBillDueSubscriptionId: z.string().optional(), | ||
| year: z.string().optional(), | ||
| subscription_cost: z.string().optional(), |
There was a problem hiding this comment.
P3: Naming inconsistency: subscription_cost uses snake_case while every other key in this schema is camelCase (e.g. addBillDueSubscriptionId). Consider renaming to subscriptionCost for consistency.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At validators/subscriptions/subscriptions.schema.ts, line 53:
<comment>Naming inconsistency: `subscription_cost` uses snake_case while every other key in this schema is camelCase (e.g. `addBillDueSubscriptionId`). Consider renaming to `subscriptionCost` for consistency.</comment>
<file context>
@@ -50,4 +50,5 @@ export const subscriptionSearchParamsSchema = z.object({
page: z.number().optional(),
addBillDueSubscriptionId: z.string().optional(),
year: z.string().optional(),
+ subscription_cost: z.string().optional(),
});
</file context>
Summary by CodeRabbit
New Features
Refactor
UX