Implement order creation API, UI components, and filters - #7
Conversation
- Added API route for creating orders with validation and error handling. - Created CreateOrderModal component for user input with form validation using react-hook-form and zod. - Implemented styles for the CreateOrderModal component. - Added tests for CreateOrderModal to ensure proper functionality and validation. - Defined schema for order creation using zod to enforce input constraints.
… and improve styles
…button icon styles for consistency
…add styles and tests
- Add FilterSelect component for selecting filter options. - Introduce FilterTextInput component for text-based filters. - Create FiltersActions component for applying and clearing filters. - Implement FiltersBar to manage filter fields and actions. - Add FiltersFields to render various filter inputs based on configuration. - Create Head component for rendering table headers with sorting functionality. - Implement Pagination component for navigating through paginated data. - Add Root and Table components for structuring the orders grid layout. - Create Toolbar for actions and pagination at the top of the grid. - Add tests for ActionButton, Body, FilterSelect, FiltersBar, Head, and Toolbar components. - Define filters configuration and utility functions for managing filter states. - Implement useFiltersBarController for handling filter state and navigation. - Create useOrderGridWithPaginationController for managing orders and selection state.
- Removed old test files for FiltersBar and Toolbar. - Deleted filters configuration and controller logic. - Introduced new Filter components including FilterSelect, FilterDatePicker, and FiltersBar. - Implemented FiltersActions for applying and clearing filters. - Created ActionButton and Toolbar components for better action handling. - Added Pagination component for navigation. - Updated styles for new filter components. - Added tests for new FilterSelect and ActionButton components.
…tersFields and update styles
…, update styles and validation messages
… better readability
📝 WalkthroughWalkthroughThis PR introduces a comprehensive order-creation feature and expands the UI component library with Storybook integration. It adds an API route for order submission, a modal-based form with validation, reusable UI components (table, inputs, buttons, date picker), and filter/pagination controls for the order grid. The changes include component tests, Storybook stories, new design tokens, and component documentation. Changes
Sequence DiagramssequenceDiagram
participant User
participant Modal as CreateOrderModal
participant Form as useCreateOrderForm
participant API as /api/orders
participant Upstream as Upstream API
participant Grid as OrderGrid
participant Router as router.refresh()
User->>Modal: Click "Criar ordem"
User->>Modal: Fill form (instrument, side, price, quantity)
User->>Modal: Click "Criar"
Modal->>Form: onSubmit()
Form->>Form: Validate with createOrderSchema
alt Validation fails
Form->>Modal: Set field errors & root error
Modal->>User: Display error messages
else Validation passes
Form->>API: POST validated data
API->>API: Generate clientOrderId (CL-timestamp-uuid)
API->>API: Construct order payload
API->>Upstream: POST to upstream API
alt Upstream error
Upstream-->>API: Error response
API-->>Form: HTTP error status
Form->>Modal: Set root error message
Modal->>User: Display error
else Upstream success
Upstream-->>API: Order created
API-->>Form: HTTP 201 + response JSON
Form->>Modal: Call onCreated callback
Modal->>Grid: onCreated triggered
Grid->>Router: router.refresh()
Router->>Grid: Reload order data
Modal->>User: Close modal
end
end
sequenceDiagram
participant User
participant Toolbar as OrdersGridToolbar
participant FiltersBar as OrdersGridFiltersBar
participant Controller as useFiltersBarController
participant Router as router.push()
User->>Toolbar: Click "Filtro" button
Toolbar->>FiltersBar: Display filter form
User->>FiltersBar: Select "Venda" option
FiltersBar->>Controller: setFieldValue('side', 'Venda')
Controller->>Controller: Update formState.side
User->>FiltersBar: Click "Aplicar"
FiltersBar->>Controller: applyFilters(event)
Controller->>Controller: Extract filter values from formState
Controller->>Controller: Build URL search params
Controller->>Controller: Reset page to 1
Controller->>Router: router.push(url with updated filters)
Router->>User: Navigate with new filter query params
alt User clicks "Limpar"
User->>FiltersBar: Click "Limpar"
FiltersBar->>Controller: clearFilters()
Controller->>Controller: Reset formState to empty
Controller->>Controller: Remove all filter params
Controller->>Router: router.push(url without filters)
Router->>User: Navigate with cleared filters
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 15
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (9)
docs/patterns/components.md-217-241 (1)
217-241:⚠️ Potential issue | 🟡 MinorFix section hierarchy/numbering for Zustand guidance.
## 10) State Management (Zustand)is followed by## 11) When to use Zustandand then## When NOT to use Zustand(unnumbered). Make these heading levels/numbering consistent for readability and navigation.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/patterns/components.md` around lines 217 - 241, The headings under "## 10) State Management (Zustand)" are inconsistent—"## 11) When to use Zustand (✅)" and an unnumbered "## When NOT to use Zustand" break the hierarchy; update them to be consistent subheadings (e.g., change "## 11) When to use Zustand (✅)" to "### When to use Zustand (✅)" and change "## When NOT to use Zustand" to "### When NOT to use Zustand (❌)" or alternatively renumber as "10.1" / "10.2" if you prefer numeric subsections so that the section titles (State Management (Zustand), When to use Zustand, When NOT to use Zustand) follow a consistent heading level and numbering scheme.components/OrderGrid-bkp/order-grid-empty-state.ts-6-12 (1)
6-12:⚠️ Potential issue | 🟡 MinorUser-facing Portuguese copy has missing accents.
At Line 7 and Line 11,
"nao"should be"não"for correct PT-BR text quality.✍️ Proposed fix
- return `${filters.instrument} nao encontrado.`; + return `${filters.instrument} não encontrado.`; ... - return `ID ${filters.id} nao encontrado.`; + return `ID ${filters.id} não encontrado.`;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/OrderGrid-bkp/order-grid-empty-state.ts` around lines 6 - 12, Update the Portuguese messages that use "nao" to the correct accented form "não" in the return strings for the branches checking filters.instrument and filters.id (i.e., the conditional blocks referencing filters.instrument and filters.id), and ensure the file is saved with UTF-8 encoding so the "ã" character is preserved.components/ui/inputs/TextInput/TextInput.stories.tsx-32-36 (1)
32-36:⚠️ Potential issue | 🟡 MinorFix Portuguese copy accents in user-facing story text.
"Campo obrigatorio"and"Entrada invalida"should use proper accents.Suggested copy update
export const Invalid: Story = { args: { - errorMessage: 'Campo obrigatorio', + errorMessage: 'Campo obrigatório', invalid: true, label: 'Instrumento', - value: 'Entrada invalida', + value: 'Entrada inválida', }, };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/inputs/TextInput/TextInput.stories.tsx` around lines 32 - 36, The story contains Portuguese strings with missing accents: update the user-facing props in TextInput.stories.tsx (specifically the errorMessage and value fields) to use proper accents — change errorMessage from "Campo obrigatorio" to "Campo obrigatório" and value from "Entrada invalida" to "Entrada inválida"; verify the label "Instrumento" is correct and adjust any other Portuguese strings in the same story if needed.components/OrderGrid/components/ActionButton.tsx-16-23 (1)
16-23:⚠️ Potential issue | 🟡 MinorAvoid always setting
aria-pressedon non-toggle actions.
aria-pressedis currently always present becauseisActivedefaults tofalse. That can incorrectly announce non-toggle buttons as toggle buttons.✅ Suggested fix
export const OrdersGridActionButton = ({ label, iconAlt, iconSrc, onClick, - isActive = false, + isActive, }: OrdersGridActionButtonProps) => { return ( <ButtonIcon aria-label={label} - aria-pressed={isActive} + aria-pressed={typeof isActive === 'boolean' ? isActive : undefined} className={`orders-grid__action-button ${ isActive ? 'orders-grid__action-button--active' : '' }`.trim()}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/OrderGrid/components/ActionButton.tsx` around lines 16 - 23, The component always renders aria-pressed because isActive defaults to false; change OrdersGridActionButtonProps and the ActionButton component so isActive is optional (no default false) and only pass aria-pressed to ButtonIcon when the action is a toggle: conditionally include aria-pressed (e.g., only set aria-pressed={isActive} when isActive is not undefined or when a new isToggle prop is true). Update the ButtonIcon prop usage in ActionButton to omit aria-pressed for non-toggle actions so non-toggle buttons are not announced as toggle controls.components/OrderGrid/components/Filter/Filter.styles.css-27-40 (1)
27-40:⚠️ Potential issue | 🟡 MinorAdd explicit keyboard focus styling for text inputs to match select/date inputs.
The text input does not have explicit
:focus/:focus-visiblestyles in this file, while select and date inputs do. Although global styles provide a:focus-visibleoutline, the text input will show a different visual appearance (the default outline) compared to the green outline used by select/date inputs. Adding explicit styles ensures consistent focus styling across all filter input types.Suggested patch
.orders-grid__filter-select-input:focus, .orders-grid__filter-select-input:focus-visible, .orders-grid__filter-date-input:focus, -.orders-grid__filter-date-input:focus-visible { +.orders-grid__filter-date-input:focus-visible, +.orders-grid__filter-field .ui-text-input__input:focus, +.orders-grid__filter-field .ui-text-input__input:focus-visible { `@apply` outline-2 outline-green; outline-offset: 2px; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/OrderGrid/components/Filter/Filter.styles.css` around lines 27 - 40, The text input selector ".orders-grid__filter-field .ui-text-input__input" lacks explicit :focus/:focus-visible rules like the select/date inputs; update the CSS so ".orders-grid__filter-field .ui-text-input__input" receives the same focus styles as ".orders-grid__filter-select-input" and ".orders-grid__filter-date-input" by adding matching :focus and :focus-visible rules that apply the same outline (outline-2 outline-green) and outline-offset: 2px to ensure consistent keyboard focus styling.components/CreateOrderModal/index.tsx-51-59 (1)
51-59:⚠️ Potential issue | 🟡 MinorReset form state on every modal close path, not only Cancel/success.
At Line 98,onOpenChangeis passed directly toModal.Root; closing via overlay/Escape can leave stale values/errors for the next open.Suggested fix
const CreateOrderModal = ({ open, onOpenChange, onCreated, }: CreateOrderModalProps) => { + const resetToDefaults = () => + reset({ + instrument: '', + side: 'Compra', + price: 0, + quantity: 0, + } as CreateOrderFormDefaults); + const closeModal = () => { - reset({ - instrument: '', - side: 'Compra', - price: 0, - quantity: 0, - } as CreateOrderFormDefaults); + resetToDefaults(); onOpenChange(false); }; + + const handleOpenChange = (nextOpen: boolean) => { + if (!nextOpen) resetToDefaults(); + onOpenChange(nextOpen); + }; return ( - <Modal.Root open={open} onOpenChange={onOpenChange}> + <Modal.Root open={open} onOpenChange={handleOpenChange}>Also applies to: 98-98
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/CreateOrderModal/index.tsx` around lines 51 - 59, The modal currently passes onOpenChange directly to Modal.Root, so closing via overlay/Escape skips your reset logic; create a wrapper handler (e.g., handleOpenChange) that accepts the new open state, and when it becomes false call the existing closeModal (which resets the form and calls onOpenChange(false)), otherwise forward true to onOpenChange; replace the direct use of onOpenChange on Modal.Root with this wrapper so all close paths (Cancel, success, overlay, Escape) run the same reset logic.components/OrderGrid/components/Filter/useFiltersBarController.ts-46-55 (1)
46-55:⚠️ Potential issue | 🟡 MinorForm state may become stale if
filtersprop changes.The
formStateis initialized once fromfiltersviauseState. If thefiltersprop changes (e.g., via browser back/forward navigation), the form inputs will show stale values. Consider using auseEffectto sync or derive state directly from props.🔧 Proposed fix to sync form state with filter prop changes
export const useFiltersBarController = ({ filters, sortState, }: UseFiltersBarControllerArgs) => { const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); const [formState, setFormState] = useState<FiltersFormState>( toFiltersFormState(filters), ); + + // Sync form state when filters change (e.g., browser navigation) + useEffect(() => { + setFormState(toFiltersFormState(filters)); + }, [filters]);Also add the import:
-import { useState } from 'react'; +import { useState, useEffect } from 'react';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/OrderGrid/components/Filter/useFiltersBarController.ts` around lines 46 - 55, useFiltersBarController initializes formState from filters once, which can become stale when filters prop changes; add a useEffect inside useFiltersBarController that watches filters and updates formState by calling setFormState(toFiltersFormState(filters)) so the form reflects prop changes (reference: useFiltersBarController, formState, setFormState, toFiltersFormState, filters); ensure you import useEffect if not already imported.components/ui/inputs/SelectInput/SelectInput.stories.tsx-36-43 (1)
36-43:⚠️ Potential issue | 🟡 MinorTypo in user-facing text.
The error message
'Campo obrigatorio'is missing the accent mark. The correct Portuguese spelling is'Campo obrigatório'.✏️ Suggested fix
export const Invalid: Story = { args: { - errorMessage: 'Campo obrigatorio', + errorMessage: 'Campo obrigatório', invalid: true, label: 'Lado', value: '', }, }; export const Composed: Story = { render: (args) => ( <SelectInput.Root {...args}> <SelectInput.Label /> <SelectInput.Trigger /> <SelectInput.Menu /> <SelectInput.Error /> </SelectInput.Root> ), args: { label: 'Lado', - errorMessage: 'Campo obrigatorio', + errorMessage: 'Campo obrigatório', invalid: true, value: 'Compra', }, };Also applies to: 54-59
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/inputs/SelectInput/SelectInput.stories.tsx` around lines 36 - 43, Update the user-facing typo in the story exports so the Portuguese error message includes the accent: change the errorMessage value from 'Campo obrigatorio' to 'Campo obrigatório' in the SelectInput.stories.tsx story export named Invalid and also fix the other occurrence noted (the second story block around lines 54-59) so both errorMessage strings use 'Campo obrigatório'.components/OrderGrid/components/Filter/FilterSelect.tsx-57-101 (1)
57-101:⚠️ Potential issue | 🟡 MinorThe ARIA roles don't match the interaction model.
This popup advertises a
listbox, but eachoptioncontains a nested<button>and there is no listbox keyboard handling. Assistive tech will get listbox semantics that the widget doesn't actually support. Either implement a real listbox pattern here or drop thelistbox/optionroles and treat it as a regular popup list.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/OrderGrid/components/Filter/FilterSelect.tsx` around lines 57 - 101, The component currently advertises a listbox but renders each choice as a nested <button> without listbox keyboard handling (see isOpen, hasOptions, options.map, selectedOption, effectiveValue, onChange, setIsOpen), which misleads assistive tech; fix by dropping the listbox/option roles and using proper popup semantics instead—change the <ul aria-labelledby={id} role="listbox"> to a popup/menu role (e.g., role="menu" or remove role) and change each option element to use role="menuitem" (or leave as native <button> without option role), ensure aria-haspopup and aria-expanded remain on the trigger and keep the existing onClick handlers (onChange and setIsOpen) intact so behavior doesn't change.
🧹 Nitpick comments (16)
styles/theme.css (1)
33-33: Token duplication can drift; alias the value instead.Line 33 duplicates Line 32’s clamp value. Consider mapping
--text-size-16to--font-size-baseto keep a single source of truth.♻️ Proposed refactor
- --text-size-16: clamp(1.1rem, 1rem + 0.5vw, 1.6rem); + --text-size-16: var(--font-size-base);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@styles/theme.css` at line 33, The CSS variable --text-size-16 duplicates the clamp expression used elsewhere; change it to reference the canonical token --font-size-base instead (i.e., set --text-size-16 to var(--font-size-base)) so the value is aliased to a single source of truth and future drift is prevented; update any usages relying on --text-size-16 accordingly to inherit the centralized value.components/CreateOrderModal/CreateOrderModal.styles.css (1)
24-30: Button width/margin styles are duplicated.Line 24–30 can be merged into a shared selector to reduce repetition.
♻️ Proposed refactor
-.create-order-modal__submit-button { - `@apply` w-[11.6rem] mt-4 self-start; -} - -.create-order-modal__cancel-button { +.create-order-modal__submit-button, +.create-order-modal__cancel-button { `@apply` w-[11.6rem] mt-4 self-start; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/CreateOrderModal/CreateOrderModal.styles.css` around lines 24 - 30, The two duplicated rules for .create-order-modal__submit-button and .create-order-modal__cancel-button should be merged into a single, shared selector to avoid repetition; update the CSS by combining the selectors (e.g., ".create-order-modal__submit-button, .create-order-modal__cancel-button") and keep the `@apply` w-[11.6rem] mt-4 self-start declaration in that single rule so both buttons inherit the same width and margin.components/ui/inputs/DatePickerInput/DatePickerInput.styles.css (1)
82-84: Inconsistent color usage for error state.Line 79 uses the custom
border-red-alertcolor, but line 83 uses the default Tailwindtext-red-600. Consider using a consistent custom color token for error states.♻️ Use consistent error color
.ui-date-picker-input__error { - `@apply` text-grid-sm text-red-600 font-semibold; + `@apply` text-grid-sm text-red-alert font-semibold; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/inputs/DatePickerInput/DatePickerInput.styles.css` around lines 82 - 84, The error text class .ui-date-picker-input__error uses the default Tailwind color text-red-600 while the component uses the custom token border-red-alert elsewhere; update .ui-date-picker-input__error to use the same custom token (e.g., replace text-red-600 with text-red-alert or the project’s text-red-alert utility) so error text and border colors are consistent across the DatePickerInput styles.components/OrderGrid-bkp/OrderGrid.styles.css (1)
1-5: Consider removing backup stylesheet after migration is complete.The
-bkpsuffix indicates this is a legacy/backup file. The AI summary mentions this overlaps with the newer non-bkp implementation. Consider removing this file once the migration to the newOrderGridcomponent is verified and stable.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/OrderGrid-bkp/OrderGrid.styles.css` around lines 1 - 5, This backup stylesheet (components/OrderGrid-bkp/OrderGrid.styles.css) with the -bkp suffix and the .orders-grid rule appears to overlap the migrated OrderGrid styles; remove this legacy file once the new OrderGrid implementation is verified stable, and ensure no imports reference components/OrderGrid-bkp/OrderGrid.styles.css (search for any imports of OrderGrid-bkp or .orders-grid); if any remain, update them to point to the new OrderGrid styles or component to avoid broken imports.lib/orders/create-order.schema.ts (1)
10-17: Contradictory validation pattern forsidefield.The combination of
.enum(),.optional(), and.refine()creates confusing validation logic. The.optional()allowsundefined, but the subsequent.refine()will rejectundefinedsince it doesn't equal'Compra'or'Venda'. Ifsideis required, remove.optional():♻️ Simplified required enum validation
side: z .enum(['Compra', 'Venda'], { error: 'Lado e obrigatorio', - }) - .optional() - .refine((val) => val === 'Compra' || val === 'Venda', { - message: 'Lado e obrigatorio', }),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/orders/create-order.schema.ts` around lines 10 - 17, The `side` schema currently mixes .enum(), .optional(), and .refine(), causing contradictory validation: .optional() allows undefined but .refine() rejects it. Fix by making `side` required: remove .optional() and also remove the redundant .refine() (z.enum(['Compra','Venda'], { error: 'Lado e obrigatorio' }) is sufficient) so validation is consistent; if instead you intend `side` to be optional, keep .optional() and change the .refine() predicate to allow undefined (e.g., val === undefined || val === 'Compra' || val === 'Venda').components/ui/buttons/ButtonIcon/ButtonIcon.styles.css (1)
23-45: Consolidate duplicated primary/secondary modifier rules.Line 23 through Line 45 repeats the same declarations for both variants; collapsing this will reduce maintenance drift risk.
♻️ Suggested refactor
-.ui-button-icon--primary { - `@apply` font-semibold; -} - -.ui-button-icon--primary:hover { - `@apply` opacity-80; -} - -.ui-button-icon--primary:active { - `@apply` opacity-70; -} - -.ui-button-icon--secondary { +.ui-button-icon--primary, +.ui-button-icon--secondary { `@apply` font-semibold; } -.ui-button-icon--secondary:hover { +.ui-button-icon--primary:hover, +.ui-button-icon--secondary:hover { `@apply` opacity-80; } -.ui-button-icon--secondary:active { +.ui-button-icon--primary:active, +.ui-button-icon--secondary:active { `@apply` opacity-70; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/buttons/ButtonIcon/ButtonIcon.styles.css` around lines 23 - 45, The .ui-button-icon--primary and .ui-button-icon--secondary blocks duplicate identical declarations (font-semibold, :hover opacity-80, :active opacity-70); refactor by extracting the common styles into a shared selector (e.g., .ui-button-icon--primary, .ui-button-icon--secondary { ... }) and likewise combine the hover and active state rules into shared selectors (.ui-button-icon--primary:hover, .ui-button-icon--secondary:hover { ... } and .ui-button-icon--primary:active, .ui-button-icon--secondary:active { ... }) so the three duplicated rule sets are consolidated while preserving the same Tailwind `@apply` utilities.components/ui/Table/__tests__/Table.test.tsx (1)
51-56: Prefer a null-safe row assertion overas Elementcasting.The cast can mask a null path and make failures less actionable.
Refactor suggestion
const firstRowCell = screen.getByText('PETR4'); const firstRow = firstRowCell.closest('tr'); - expect(firstRow).toBeTruthy(); - - fireEvent.click(firstRow as Element); - fireEvent.keyDown(firstRow as Element, { key: 'Enter' }); + expect(firstRow).not.toBeNull(); + if (!firstRow) return; + + fireEvent.click(firstRow); + fireEvent.keyDown(firstRow, { key: 'Enter' });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/Table/__tests__/Table.test.tsx` around lines 51 - 56, The test currently casts firstRow with `as Element`, which can hide null issues; instead assert the row exists using a null-safe assertion (e.g., `expect(firstRow).not.toBeNull()` or `expect(firstRow).toBeTruthy()`) right after `const firstRow = firstRowCell.closest('tr')`, then call `fireEvent.click(firstRow)` and `fireEvent.keyDown(firstRow, { key: 'Enter' })` using the asserted `firstRow` (referencing `firstRowCell.closest('tr')`, `firstRow`, and the `fireEvent.click` / `fireEvent.keyDown` calls) so the test fails clearly if the row is missing rather than silently casting a null.components/OrderGrid/components/Filter/FiltersBar.tsx (1)
23-26: Add useEffect to controller to sync form state when filters prop changes, then remove the key remount.The
JSON.stringify(filters)key currently forces component remount to reset form state when the filters prop changes. Instead, add a useEffect inuseFiltersBarControllerto syncformStatewhenever thefiltersprop changes:useEffect(() => { setFormState(toFiltersFormState(filters)); }, [filters]);Then remove the key from the form element. This eliminates unnecessary remounts and avoids focus/state churn while maintaining proper synchronization.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/OrderGrid/components/Filter/FiltersBar.tsx` around lines 23 - 26, The form is currently forced to remount via key={JSON.stringify(filters)}; instead update the controller to sync state on prop changes: inside useFiltersBarController add a useEffect that calls setFormState(toFiltersFormState(filters)) whenever the filters prop changes, ensuring formState tracks filters; then remove the JSON.stringify(filters) key from the <form> so the component no longer remounts while still keeping state in sync.components/OrderGrid-bkp/order-grid-with-pagination.presenter.ts (1)
26-33: Consider error handling for the data fetch.If
getPaginatedOrdersForGridthrows (e.g., network error, service unavailable), the error will bubble up unhandled. Depending on your error boundary setup, you may want to catch and transform this into a user-friendly error state within the view model.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/OrderGrid-bkp/order-grid-with-pagination.presenter.ts` around lines 26 - 33, Wrap the call to getPaginatedOrdersForGrid(...) in a try/catch inside the presenter (the code that builds the order grid view model in order-grid-with-pagination.presenter.ts); on error catch the thrown exception and either set a clear error state on the view model (e.g., errorMessage / hasError) or return a safe fallback (empty items, currentPage=0, totalPages=0) and log the original error for diagnostics, ensuring you include the original parameters (requestedPage, pageSize, sortBy, sortDir, filters) in the log so the failure can be reproduced.components/ui/inputs/DatePickerInput/__tests__/DatePickerInput.test.tsx (1)
67-84: Consider adding a test for popover close after date selection.The compound composition test validates error/invalid state rendering, but there's no explicit test verifying that selecting a date closes the popover. If that's expected behavior, adding a test would prevent regressions.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/inputs/DatePickerInput/__tests__/DatePickerInput.test.tsx` around lines 67 - 84, Add a test to verify the popover closes after selecting a date: render the same compound composition (DatePickerInput.Root with DatePickerInput.Trigger and DatePickerInput.Popover), open the popover by interacting with DatePickerInput.Trigger (e.g., userEvent.click on the trigger), simulate selecting a date inside DatePickerInput.Popover (click the date element rendered by the calendar), and then assert the popover is no longer in the document or not visible (e.g., expect(queryByRole('dialog' or appropriate role)).toBeNull()/not.toBeInTheDocument()). Use the existing helpers like screen/getByRole and userEvent to perform interactions and reference DatePickerInput.Trigger and DatePickerInput.Popover in the test.components/ui/Table/components/Body.tsx (1)
42-56: Consider addingrole="row"orrole="button"for accessible clickable rows.When
onRowClickis provided, the<tr>becomes interactive. For better screen reader support, consider addingrole="button"(or keepingrole="row"while addingaria-pressedor similar) so assistive technologies announce the element as actionable.♿ Proposed accessibility enhancement
<tr className={rowClassName} key={rowKey} onClick={onRowClick ? () => onRowClick(row) : undefined} onKeyDown={ onRowClick ? (event) => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); onRowClick(row); } } : undefined } tabIndex={onRowClick ? 0 : undefined} + role={onRowClick ? 'button' : undefined} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/Table/components/Body.tsx` around lines 42 - 56, The clickable table row rendered in Body.tsx becomes interactive when onRowClick is present but lacks an accessibility role; update the <tr> element (where rowClassName, rowKey and onRowClick are used) to include role="button" when onRowClick is provided, add an appropriate aria-* state such as aria-pressed={false} or aria-label if needed, and ensure tabIndex remains set to 0 and the existing onKeyDown handler is kept so screen readers and keyboard users recognize the row as actionable.components/ui/buttons/Button/Button.styles.css (1)
27-49: Add disabled and focus-visible styles for accessibility and UX completeness.The button variants define
:hoverand:activestates but lack:
:disabledstate (to visually indicate non-interactivity):focus-visiblestate (for keyboard navigation accessibility)♿ Proposed additions for disabled and focus states
.ui-button--primary:active { `@apply` border-green-dark bg-green-dark text-white; } +.ui-button--primary:disabled { + `@apply` opacity-50 cursor-not-allowed; +} + +.ui-button--primary:focus-visible { + `@apply` outline-2 outline-offset-2 outline-green; +} .ui-button--secondary { `@apply` border-gray-dark bg-gray-dark text-white font-semibold; } .ui-button--secondary:hover { `@apply` border-gray-darker bg-gray-darker text-white; } .ui-button--secondary:active { `@apply` border-gray-darker bg-gray-darker text-white; } + +.ui-button--secondary:disabled { + `@apply` opacity-50 cursor-not-allowed; +} + +.ui-button--secondary:focus-visible { + `@apply` outline-2 outline-offset-2 outline-gray-dark; +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/buttons/Button/Button.styles.css` around lines 27 - 49, Add explicit :disabled and :focus-visible rules for both .ui-button--primary and .ui-button--secondary to improve accessibility: for :disabled apply non-interactive visuals (muted background/border/text via existing utility tokens, reduced opacity or specific bg/border text classes, and cursor-not-allowed), and ensure hover/active styles do not override disabled; for :focus-visible add a clear keyboard focus indicator (outline/ring or visible border change using your design tokens) so keyboard users see focus on .ui-button--primary and .ui-button--secondary. Reference the existing class names (.ui-button--primary, .ui-button--secondary) and their :hover/:active blocks when adding these new :disabled and :focus-visible rules so styles are consistent and precedence prevents hover/active from applying when disabled.components/ui/inputs/DatePickerInput/components/DatePickerInputPopover.tsx (1)
29-107: Consider focus management for the dialog popover.The popover uses
role="dialog"and properly closes on Escape key press. However, it lacks focus management: when the dialog opens, focus should move into it (typically to the first day button or clear button), and when it closes, focus should return to the trigger button. This improves keyboard navigation experience for users.app/api/orders/route.ts (1)
80-92: Consider adding idempotency handling for order creation.If the upstream POST succeeds but the response fails to reach the client (network issues, timeouts), a retry would create a duplicate order. Consider:
- Accepting a client-generated idempotency key
- Using
clientOrderIdas an idempotency key and checking for existing orders before creating🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/orders/route.ts` around lines 80 - 92, The POST handling currently proxies order creation via fetchWithTimeout and will create duplicates on client retry; add idempotency handling by accepting an Idempotency-Key header (or using payload.clientOrderId) and checking for an existing order before creating: extract the header or payload.clientOrderId in route.ts, query the upstream or your DB for an existing order with that key, return the existing order if found, otherwise include the Idempotency-Key when calling fetchWithTimeout (or persist the key to map to the created order after success) and only create once; update error/response flows around fetchWithTimeout and NextResponse.json to return the original order when deduplicated.components/ui/inputs/TextInput/TextInput.styles.css (1)
21-27: Inconsistent error color tokens.The invalid border uses
border-red-alert(line 22) while the error text usestext-red-600(line 26). Consider using the same color token for both to maintain visual consistency in error states.♻️ Suggested fix
.ui-text-input__error { - `@apply` text-grid-sm text-red-600 font-semibold; + `@apply` text-grid-sm text-red-alert font-semibold; }Or alternatively, update the invalid border to use
border-red-600ifred-600is the intended error color.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/inputs/TextInput/TextInput.styles.css` around lines 21 - 27, The error styles are inconsistent: .ui-text-input--invalid uses border-red-alert while .ui-text-input__error uses text-red-600; pick one token and make both use it for consistent error styling. Update either .ui-text-input--invalid to use border-red-600 to match text-red-600, or change .ui-text-input__error to text-red-alert so both classes (.ui-text-input--invalid and .ui-text-input__error) use the same color token across border and text.components/OrderGrid/components/Filter/filters.config.ts (1)
12-18: Reduce duplicated filter-shape declarations to prevent drift.
FILTER_FORM_KEYS,toFiltersFormState, andemptyFiltersFormStaterepeat the same field list. Consider deriving them from a single source constant.♻️ Suggested refactor
+const EMPTY_FILTERS_FORM_STATE: FiltersFormState = { + id: '', + instrument: '', + status: '', + side: '', + date: '', +}; -export const FILTER_FORM_KEYS: Array<keyof FiltersFormState> = [ - 'id', - 'instrument', - 'status', - 'side', - 'date', -]; +export const FILTER_FORM_KEYS = Object.keys( + EMPTY_FILTERS_FORM_STATE, +) as Array<keyof FiltersFormState>; export const toFiltersFormState = ( filters: OrdersGridFilterState, ): FiltersFormState => ({ - id: filters.id ?? '', - instrument: filters.instrument ?? '', - status: filters.status ?? '', - side: filters.side ?? '', - date: filters.date ?? '', + ...EMPTY_FILTERS_FORM_STATE, + id: filters.id ?? EMPTY_FILTERS_FORM_STATE.id, + instrument: filters.instrument ?? EMPTY_FILTERS_FORM_STATE.instrument, + status: filters.status ?? EMPTY_FILTERS_FORM_STATE.status, + side: filters.side ?? EMPTY_FILTERS_FORM_STATE.side, + date: filters.date ?? EMPTY_FILTERS_FORM_STATE.date, }); -export const emptyFiltersFormState = (): FiltersFormState => ({ - id: '', - instrument: '', - status: '', - side: '', - date: '', -}); +export const emptyFiltersFormState = (): FiltersFormState => ({ + ...EMPTY_FILTERS_FORM_STATE, +});Also applies to: 86-102
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/OrderGrid/components/Filter/filters.config.ts` around lines 12 - 18, FILTER_FORM_KEYS, toFiltersFormState, and emptyFiltersFormState duplicate the same field list and should be derived from one source to avoid drift; create a single constant (e.g., FILTER_FIELD_NAMES or FILTER_FORM_SCHEMA) that lists the keys and then build FILTER_FORM_KEYS from that constant and implement toFiltersFormState and emptyFiltersFormState by mapping over that same constant so all three always come from the same source; update any type assertions or casts in toFiltersFormState/emptyFiltersFormState to use the new constant and adjust usages of FILTER_FORM_KEYS to reference the derived value.
🤖 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/api/orders/route.ts`:
- Around line 12-32: getNextOrderId() computes a sequential ID by reading
existing orders, which risks race conditions when multiple requests create
orders concurrently; update the create-order flow to stop relying on that
function for unique IDs by either delegating ID generation to the upstream API
or generating a collision-resistant ID locally (e.g., crypto.randomUUID()) and
stop calling getNextOrderId() in the POST handler, and if you keep client-side
IDs add an idempotency/retry strategy: catch conflict responses (HTTP 409) from
the POST, regenerate a new UUID (or re-request the upstream-generated ID) and
retry a limited number of times; refer to getNextOrderId and the POST request
handling code where the new order is posted to implement these changes.
In `@components/CreateOrderModal/__tests__/CreateOrderModal.test.tsx`:
- Around line 46-47: The test overrides global.fetch with
jest.fn().mockResolvedValue(...) which can leak into other tests; capture the
original global.fetch at the start of the test file (e.g., const originalFetch =
global.fetch) and restore it in an afterEach/afterAll hook, or call global.fetch
= originalFetch after the test that sets global.fetch, ensuring the mocked
jest.fn() used in CreateOrderModal.test.tsx is cleaned up and the original fetch
is reinstated.
In `@components/CreateOrderModal/index.tsx`:
- Around line 80-91: The fetch call that posts to '/api/orders' can throw
network errors which are not currently caught; wrap the await fetch(...) in a
try/catch (around the code in CreateOrderModal/index.tsx where you call fetch)
and in the catch block call setError('root', { message: 'Nao foi possivel criar
a ordem. Verifique sua conexao e tente novamente.' }) (or include the caught
error.message for more detail), then return to prevent further processing; keep
the existing response.ok handling inside the try.
In `@components/OrderGrid-bkp/order-grid-with-pagination.presenter.ts`:
- Around line 1-54: The `components/OrderGrid-bkp/` backup directory (e.g., file
OrderGrid-bkp/order-grid-with-pagination.presenter.ts and related files) is dead
code and should be removed before merging; delete the entire OrderGrid-bkp
folder and ensure no imports reference it (search for any usages of
OrderGrid-bkp or its exported symbols like
buildOrderGridWithPaginationViewModel) so the repo only contains the active
components in components/OrderGrid/.
In `@components/OrderGrid-bkp/useOrderGridWithPaginationController.ts`:
- Around line 8-11: Remove the dataToken symbol from the selection machinery:
change the SelectionState type to only include orderId and update all places
that create, set, compare, or validate SelectionState (references around the
SelectionState declaration and the blocks noted at lines 22-37 and 58-62) to
stop generating or checking dataToken; instead resolve selection solely by
orderId against the current orders array (e.g., in the selectedOrder resolver
and any setSelectedOrder logic) so that harmless rerenders which recreate
Symbols no longer clear the selection.
In `@components/OrderGrid/components/Filter/FilterDatePicker.tsx`:
- Around line 12-81: Remove the duplicated declarations (CalendarCell,
WEEK_DAYS, parseIsoDate, toIsoDate, formatInputDate, getMonthLabel,
buildCalendarCells) from FilterDatePicker.tsx and replace them with imports from
the shared date-picker utilities module; ensure you import the exact symbols
(CalendarCell, WEEK_DAYS, parseIsoDate, toIsoDate, formatInputDate,
getMonthLabel, buildCalendarCells) and update any references in FilterDatePicker
to use the imported symbols, adding/adjusting exports in the shared utils if any
symbol is not yet exported.
In `@components/ui/inputs/RadioInput/index.tsx`:
- Around line 49-56: The visible label element (class "ui-radio-input__label")
isn't associated with the radiogroup and the group can be unnamed when both
label and aria-label are missing; update the RadioInput component to
generate/accept a stable id for the label (e.g., `${id}-label`), give the <p
className="ui-radio-input__label"> that id when label exists, and
replace/augment the radiogroup attributes to use aria-labelledby={labelId} when
label exists (falling back to aria-label={ariaLabel || fallbackLabel} when it
doesn't) so the radiogroup always has an accessible name and the visible label
is linked via aria-labelledby.
In `@components/ui/inputs/SelectInput/components/SelectInputRoot.tsx`:
- Around line 75-79: The hidden input in SelectInputRoot is always a successful
form control even when the trigger is disabled; update the hidden input
rendering so it mirrors the disabled state (e.g., add disabled={disabled} or
disabled={contextValue.disabled} to the <input name={name} type="hidden"
value={selectedValue} />) so a visually disabled select does not submit a value.
In `@components/ui/inputs/SelectInput/SelectInput.styles.css`:
- Around line 47-49: The invalid state currently applies border-red-alert to the
root wrapper (.ui-select-input--invalid) but the visible border is on the inner
element .ui-select-input__field; update the stylesheet so that when the root has
the invalid class the border-red-alert is applied to .ui-select-input__field
(e.g., use a selector like .ui-select-input--invalid .ui-select-input__field) or
duplicate the border rule on .ui-select-input__field to ensure the visible field
shows the invalid border.
In `@components/ui/inputs/SelectInput/useSelectInputController.ts`:
- Around line 21-31: The current logic lets selectedValue keep a missing value
even after selectedOption falls back to options[0], causing the visible label
and the submitted value to diverge; to fix, first derive a resolvedValue (use
isControlled ? String(value ?? '') : internalValue), then compute selectedOption
via useMemo(() => options.find(o => o.value === resolvedValue) ?? options[0],
[options, resolvedValue]), and finally normalize selectedValue to
selectedOption.value (e.g., const selectedValue = selectedOption?.value ?? ''),
updating references to initialValue, internalValue, selectedValue and
selectedOption accordingly so the visible option and the actual value always
match.
In `@docs/patterns/components.md`:
- Around line 21-24: Update the guidance in docs/patterns/components.md to align
with Next.js colocation best practices: remove the "must contain **only** route
files" wording and instead state that route files (page.tsx, layout.tsx,
loading.tsx, error.tsx, not-found.tsx, route.ts) define routes while
non‑routable utilities may be colocated in app/ using private folders (example:
app/blog/_components/Post.tsx) or kept outside app/ for shared UI logic to avoid
app-router coupling; mention using leading-underscore folders (e.g.,
_components/) for private utilities to prevent them from becoming routes.
- Around line 67-73: Update the "Rule: no Tailwind utility strings inline in
JSX" guidance to align with Tailwind v4 best practices: change the rule text in
the "no Tailwind utility strings inline in JSX" section to recommend using
inline Tailwind utility classes in JSX (e.g., className="flex gap-2 ...") as the
default pattern for component styling, and state that component-scoped CSS
(.styles.css and `@apply`) should be reserved for third-party overrides or truly
custom CSS needs; update examples and the bullet list to show inline utilities
as preferred, keep semantic className usage as an option for larger structural
hooks, and explicitly document the exception criteria where `@apply` or CSS
modules are still appropriate.
In `@package.json`:
- Around line 25-30: The package "storybook" is currently listed in dependencies
but is a development-only tool; move the "storybook": "^10.3.1" entry out of the
top-level "dependencies" block and add it to the "devDependencies" block (where
"@storybook/nextjs" already lives) so it is installed only in dev environments;
ensure you remove the original "storybook" entry from dependencies and keep the
same version string when adding it to devDependencies.
In `@server/db.json`:
- Around line 2-1048: The seeded orders array returns two different shapes
because only recent entries include the fields clientOrderId, exchangeOrderId,
and executionId; fix by making the order shape uniform: either backfill every
object in the /orders collection to include clientOrderId, exchangeOrderId, and
executionId (set to null or empty string) so all fixtures share the same keys,
or update the Order type/schema and any serializers/parsers (the order consumer,
order creation flow, and any presenters reading /orders) to mark clientOrderId,
exchangeOrderId, and executionId as optional and handle their absence
consistently across reads/writes; pick one approach and apply it consistently to
the seeded data and to the Order contract used by the API/clients.
In `@styles/globals.css`:
- Around line 4-5: Remove the external `@import` line from globals.css and instead
self-host the font using next/font/local: add the font files under public/fonts
(e.g., WixMadeforText-Regular.woff2), create a localFont import in your Next.js
root layout or _app (using next/font/local with weight/style and preload),
export its className or CSS variable, and apply that className/variable to the
html/body element (and delete the original `@import` in styles/globals.css);
ensure you configure fallback fonts and any required subsets in the localFont
call to retain optimization and CLS prevention.
---
Minor comments:
In `@components/CreateOrderModal/index.tsx`:
- Around line 51-59: The modal currently passes onOpenChange directly to
Modal.Root, so closing via overlay/Escape skips your reset logic; create a
wrapper handler (e.g., handleOpenChange) that accepts the new open state, and
when it becomes false call the existing closeModal (which resets the form and
calls onOpenChange(false)), otherwise forward true to onOpenChange; replace the
direct use of onOpenChange on Modal.Root with this wrapper so all close paths
(Cancel, success, overlay, Escape) run the same reset logic.
In `@components/OrderGrid-bkp/order-grid-empty-state.ts`:
- Around line 6-12: Update the Portuguese messages that use "nao" to the correct
accented form "não" in the return strings for the branches checking
filters.instrument and filters.id (i.e., the conditional blocks referencing
filters.instrument and filters.id), and ensure the file is saved with UTF-8
encoding so the "ã" character is preserved.
In `@components/OrderGrid/components/ActionButton.tsx`:
- Around line 16-23: The component always renders aria-pressed because isActive
defaults to false; change OrdersGridActionButtonProps and the ActionButton
component so isActive is optional (no default false) and only pass aria-pressed
to ButtonIcon when the action is a toggle: conditionally include aria-pressed
(e.g., only set aria-pressed={isActive} when isActive is not undefined or when a
new isToggle prop is true). Update the ButtonIcon prop usage in ActionButton to
omit aria-pressed for non-toggle actions so non-toggle buttons are not announced
as toggle controls.
In `@components/OrderGrid/components/Filter/Filter.styles.css`:
- Around line 27-40: The text input selector ".orders-grid__filter-field
.ui-text-input__input" lacks explicit :focus/:focus-visible rules like the
select/date inputs; update the CSS so ".orders-grid__filter-field
.ui-text-input__input" receives the same focus styles as
".orders-grid__filter-select-input" and ".orders-grid__filter-date-input" by
adding matching :focus and :focus-visible rules that apply the same outline
(outline-2 outline-green) and outline-offset: 2px to ensure consistent keyboard
focus styling.
In `@components/OrderGrid/components/Filter/FilterSelect.tsx`:
- Around line 57-101: The component currently advertises a listbox but renders
each choice as a nested <button> without listbox keyboard handling (see isOpen,
hasOptions, options.map, selectedOption, effectiveValue, onChange, setIsOpen),
which misleads assistive tech; fix by dropping the listbox/option roles and
using proper popup semantics instead—change the <ul aria-labelledby={id}
role="listbox"> to a popup/menu role (e.g., role="menu" or remove role) and
change each option element to use role="menuitem" (or leave as native <button>
without option role), ensure aria-haspopup and aria-expanded remain on the
trigger and keep the existing onClick handlers (onChange and setIsOpen) intact
so behavior doesn't change.
In `@components/OrderGrid/components/Filter/useFiltersBarController.ts`:
- Around line 46-55: useFiltersBarController initializes formState from filters
once, which can become stale when filters prop changes; add a useEffect inside
useFiltersBarController that watches filters and updates formState by calling
setFormState(toFiltersFormState(filters)) so the form reflects prop changes
(reference: useFiltersBarController, formState, setFormState,
toFiltersFormState, filters); ensure you import useEffect if not already
imported.
In `@components/ui/inputs/SelectInput/SelectInput.stories.tsx`:
- Around line 36-43: Update the user-facing typo in the story exports so the
Portuguese error message includes the accent: change the errorMessage value from
'Campo obrigatorio' to 'Campo obrigatório' in the SelectInput.stories.tsx story
export named Invalid and also fix the other occurrence noted (the second story
block around lines 54-59) so both errorMessage strings use 'Campo obrigatório'.
In `@components/ui/inputs/TextInput/TextInput.stories.tsx`:
- Around line 32-36: The story contains Portuguese strings with missing accents:
update the user-facing props in TextInput.stories.tsx (specifically the
errorMessage and value fields) to use proper accents — change errorMessage from
"Campo obrigatorio" to "Campo obrigatório" and value from "Entrada invalida" to
"Entrada inválida"; verify the label "Instrumento" is correct and adjust any
other Portuguese strings in the same story if needed.
In `@docs/patterns/components.md`:
- Around line 217-241: The headings under "## 10) State Management (Zustand)"
are inconsistent—"## 11) When to use Zustand (✅)" and an unnumbered "## When NOT
to use Zustand" break the hierarchy; update them to be consistent subheadings
(e.g., change "## 11) When to use Zustand (✅)" to "### When to use Zustand (✅)"
and change "## When NOT to use Zustand" to "### When NOT to use Zustand (❌)" or
alternatively renumber as "10.1" / "10.2" if you prefer numeric subsections so
that the section titles (State Management (Zustand), When to use Zustand, When
NOT to use Zustand) follow a consistent heading level and numbering scheme.
---
Nitpick comments:
In `@app/api/orders/route.ts`:
- Around line 80-92: The POST handling currently proxies order creation via
fetchWithTimeout and will create duplicates on client retry; add idempotency
handling by accepting an Idempotency-Key header (or using payload.clientOrderId)
and checking for an existing order before creating: extract the header or
payload.clientOrderId in route.ts, query the upstream or your DB for an existing
order with that key, return the existing order if found, otherwise include the
Idempotency-Key when calling fetchWithTimeout (or persist the key to map to the
created order after success) and only create once; update error/response flows
around fetchWithTimeout and NextResponse.json to return the original order when
deduplicated.
In `@components/CreateOrderModal/CreateOrderModal.styles.css`:
- Around line 24-30: The two duplicated rules for
.create-order-modal__submit-button and .create-order-modal__cancel-button should
be merged into a single, shared selector to avoid repetition; update the CSS by
combining the selectors (e.g., ".create-order-modal__submit-button,
.create-order-modal__cancel-button") and keep the `@apply` w-[11.6rem] mt-4
self-start declaration in that single rule so both buttons inherit the same
width and margin.
In `@components/OrderGrid-bkp/order-grid-with-pagination.presenter.ts`:
- Around line 26-33: Wrap the call to getPaginatedOrdersForGrid(...) in a
try/catch inside the presenter (the code that builds the order grid view model
in order-grid-with-pagination.presenter.ts); on error catch the thrown exception
and either set a clear error state on the view model (e.g., errorMessage /
hasError) or return a safe fallback (empty items, currentPage=0, totalPages=0)
and log the original error for diagnostics, ensuring you include the original
parameters (requestedPage, pageSize, sortBy, sortDir, filters) in the log so the
failure can be reproduced.
In `@components/OrderGrid-bkp/OrderGrid.styles.css`:
- Around line 1-5: This backup stylesheet
(components/OrderGrid-bkp/OrderGrid.styles.css) with the -bkp suffix and the
.orders-grid rule appears to overlap the migrated OrderGrid styles; remove this
legacy file once the new OrderGrid implementation is verified stable, and ensure
no imports reference components/OrderGrid-bkp/OrderGrid.styles.css (search for
any imports of OrderGrid-bkp or .orders-grid); if any remain, update them to
point to the new OrderGrid styles or component to avoid broken imports.
In `@components/OrderGrid/components/Filter/filters.config.ts`:
- Around line 12-18: FILTER_FORM_KEYS, toFiltersFormState, and
emptyFiltersFormState duplicate the same field list and should be derived from
one source to avoid drift; create a single constant (e.g., FILTER_FIELD_NAMES or
FILTER_FORM_SCHEMA) that lists the keys and then build FILTER_FORM_KEYS from
that constant and implement toFiltersFormState and emptyFiltersFormState by
mapping over that same constant so all three always come from the same source;
update any type assertions or casts in toFiltersFormState/emptyFiltersFormState
to use the new constant and adjust usages of FILTER_FORM_KEYS to reference the
derived value.
In `@components/OrderGrid/components/Filter/FiltersBar.tsx`:
- Around line 23-26: The form is currently forced to remount via
key={JSON.stringify(filters)}; instead update the controller to sync state on
prop changes: inside useFiltersBarController add a useEffect that calls
setFormState(toFiltersFormState(filters)) whenever the filters prop changes,
ensuring formState tracks filters; then remove the JSON.stringify(filters) key
from the <form> so the component no longer remounts while still keeping state in
sync.
In `@components/ui/buttons/Button/Button.styles.css`:
- Around line 27-49: Add explicit :disabled and :focus-visible rules for both
.ui-button--primary and .ui-button--secondary to improve accessibility: for
:disabled apply non-interactive visuals (muted background/border/text via
existing utility tokens, reduced opacity or specific bg/border text classes, and
cursor-not-allowed), and ensure hover/active styles do not override disabled;
for :focus-visible add a clear keyboard focus indicator (outline/ring or visible
border change using your design tokens) so keyboard users see focus on
.ui-button--primary and .ui-button--secondary. Reference the existing class
names (.ui-button--primary, .ui-button--secondary) and their :hover/:active
blocks when adding these new :disabled and :focus-visible rules so styles are
consistent and precedence prevents hover/active from applying when disabled.
In `@components/ui/buttons/ButtonIcon/ButtonIcon.styles.css`:
- Around line 23-45: The .ui-button-icon--primary and .ui-button-icon--secondary
blocks duplicate identical declarations (font-semibold, :hover opacity-80,
:active opacity-70); refactor by extracting the common styles into a shared
selector (e.g., .ui-button-icon--primary, .ui-button-icon--secondary { ... })
and likewise combine the hover and active state rules into shared selectors
(.ui-button-icon--primary:hover, .ui-button-icon--secondary:hover { ... } and
.ui-button-icon--primary:active, .ui-button-icon--secondary:active { ... }) so
the three duplicated rule sets are consolidated while preserving the same
Tailwind `@apply` utilities.
In `@components/ui/inputs/DatePickerInput/__tests__/DatePickerInput.test.tsx`:
- Around line 67-84: Add a test to verify the popover closes after selecting a
date: render the same compound composition (DatePickerInput.Root with
DatePickerInput.Trigger and DatePickerInput.Popover), open the popover by
interacting with DatePickerInput.Trigger (e.g., userEvent.click on the trigger),
simulate selecting a date inside DatePickerInput.Popover (click the date element
rendered by the calendar), and then assert the popover is no longer in the
document or not visible (e.g., expect(queryByRole('dialog' or appropriate
role)).toBeNull()/not.toBeInTheDocument()). Use the existing helpers like
screen/getByRole and userEvent to perform interactions and reference
DatePickerInput.Trigger and DatePickerInput.Popover in the test.
In `@components/ui/inputs/DatePickerInput/DatePickerInput.styles.css`:
- Around line 82-84: The error text class .ui-date-picker-input__error uses the
default Tailwind color text-red-600 while the component uses the custom token
border-red-alert elsewhere; update .ui-date-picker-input__error to use the same
custom token (e.g., replace text-red-600 with text-red-alert or the project’s
text-red-alert utility) so error text and border colors are consistent across
the DatePickerInput styles.
In `@components/ui/inputs/TextInput/TextInput.styles.css`:
- Around line 21-27: The error styles are inconsistent: .ui-text-input--invalid
uses border-red-alert while .ui-text-input__error uses text-red-600; pick one
token and make both use it for consistent error styling. Update either
.ui-text-input--invalid to use border-red-600 to match text-red-600, or change
.ui-text-input__error to text-red-alert so both classes (.ui-text-input--invalid
and .ui-text-input__error) use the same color token across border and text.
In `@components/ui/Table/__tests__/Table.test.tsx`:
- Around line 51-56: The test currently casts firstRow with `as Element`, which
can hide null issues; instead assert the row exists using a null-safe assertion
(e.g., `expect(firstRow).not.toBeNull()` or `expect(firstRow).toBeTruthy()`)
right after `const firstRow = firstRowCell.closest('tr')`, then call
`fireEvent.click(firstRow)` and `fireEvent.keyDown(firstRow, { key: 'Enter' })`
using the asserted `firstRow` (referencing `firstRowCell.closest('tr')`,
`firstRow`, and the `fireEvent.click` / `fireEvent.keyDown` calls) so the test
fails clearly if the row is missing rather than silently casting a null.
In `@components/ui/Table/components/Body.tsx`:
- Around line 42-56: The clickable table row rendered in Body.tsx becomes
interactive when onRowClick is present but lacks an accessibility role; update
the <tr> element (where rowClassName, rowKey and onRowClick are used) to include
role="button" when onRowClick is provided, add an appropriate aria-* state such
as aria-pressed={false} or aria-label if needed, and ensure tabIndex remains set
to 0 and the existing onKeyDown handler is kept so screen readers and keyboard
users recognize the row as actionable.
In `@lib/orders/create-order.schema.ts`:
- Around line 10-17: The `side` schema currently mixes .enum(), .optional(), and
.refine(), causing contradictory validation: .optional() allows undefined but
.refine() rejects it. Fix by making `side` required: remove .optional() and also
remove the redundant .refine() (z.enum(['Compra','Venda'], { error: 'Lado e
obrigatorio' }) is sufficient) so validation is consistent; if instead you
intend `side` to be optional, keep .optional() and change the .refine()
predicate to allow undefined (e.g., val === undefined || val === 'Compra' || val
=== 'Venda').
In `@styles/theme.css`:
- Line 33: The CSS variable --text-size-16 duplicates the clamp expression used
elsewhere; change it to reference the canonical token --font-size-base instead
(i.e., set --text-size-16 to var(--font-size-base)) so the value is aliased to a
single source of truth and future drift is prevented; update any usages relying
on --text-size-16 accordingly to inherit the centralized value.
| const response = await fetch('/api/orders', { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify(parsed.data), | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| setError('root', { | ||
| message: 'Nao foi possivel criar a ordem. Tente novamente.', | ||
| }); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Handle thrown network errors from fetch to avoid silent submit failures.
At Line 80, fetch can reject (offline, timeout, DNS), and the current flow only handles non-OK HTTP responses.
Suggested fix
- const response = await fetch('/api/orders', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(parsed.data),
- });
-
- if (!response.ok) {
- setError('root', {
- message: 'Nao foi possivel criar a ordem. Tente novamente.',
- });
- return;
- }
+ try {
+ const response = await fetch('/api/orders', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(parsed.data),
+ });
+
+ if (!response.ok) {
+ setError('root', {
+ message: 'Nao foi possivel criar a ordem. Tente novamente.',
+ });
+ return;
+ }
+ } catch {
+ setError('root', {
+ message: 'Nao foi possivel criar a ordem. Tente novamente.',
+ });
+ return;
+ }📝 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 response = await fetch('/api/orders', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(parsed.data), | |
| }); | |
| if (!response.ok) { | |
| setError('root', { | |
| message: 'Nao foi possivel criar a ordem. Tente novamente.', | |
| }); | |
| return; | |
| } | |
| try { | |
| const response = await fetch('/api/orders', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(parsed.data), | |
| }); | |
| if (!response.ok) { | |
| setError('root', { | |
| message: 'Nao foi possivel criar a ordem. Tente novamente.', | |
| }); | |
| return; | |
| } | |
| } catch { | |
| setError('root', { | |
| message: 'Nao foi possivel criar a ordem. Tente novamente.', | |
| }); | |
| return; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/CreateOrderModal/index.tsx` around lines 80 - 91, The fetch call
that posts to '/api/orders' can throw network errors which are not currently
caught; wrap the await fetch(...) in a try/catch (around the code in
CreateOrderModal/index.tsx where you call fetch) and in the catch block call
setError('root', { message: 'Nao foi possivel criar a ordem. Verifique sua
conexao e tente novamente.' }) (or include the caught error.message for more
detail), then return to prevent further processing; keep the existing
response.ok handling inside the try.
| - `app/` must contain **only** route files: | ||
| - `page.tsx`, `layout.tsx`, `loading.tsx`, `error.tsx`, `not-found.tsx` | ||
| - `route.ts` (handlers) | ||
| - Avoid reusable UI logic inside `app/` |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Next.js App Router, can non-route files (e.g., colocated components/helpers) exist inside the app/ directory, and what does official guidance recommend?
💡 Result:
Yes, non-route files such as colocated components and helpers can exist inside the Next.js App Router's app/ directory. Only specific special files (page.js/tsx, layout.js/tsx, loading.js/tsx, error.js/tsx, route.js/tsx) define routes; other files are safely colocated without becoming publicly accessible routes. Official Next.js documentation explicitly states: "project files can be safely colocated inside route segments in the app directory without accidentally being routable" and provides examples like app/blog/_components/Post.tsx for UI utilities. Recommendations include: - Colocate freely by default, as non-special files do not create routes. - Use private folders prefixed with _ (e.g., _components) to explicitly opt out of routing and organize non-routable files. - Route groups with (e.g., (marketing)) for organization without URL impact. - Alternatives: store files outside app/ (e.g., src/components), in top-level app/ folders, or in src/ for separation. Next.js is unopinionated about organization but supports colocation natively in app/.
Citations:
- 1: https://nextjs.org/docs/app/building-your-application/routing/colocation
- 2: https://nextjs.org/docs/app/getting-started/project-structure
- 3: https://nextjs.org/docs/14/app/building-your-application/routing
- 4: https://nextjs.org/docs/13/app/building-your-application/routing/colocation
Align app/ structure guidance with Next.js colocation best practices.
The current "must contain only route files" rule contradicts official Next.js documentation. Per Next.js App Router guidance, non-route files safely colocate in app/ without becoming publicly accessible routes. Colocate route-segment utilities using private folders (e.g., app/blog/_components/Post.tsx) to keep related code organized. Reword to: "Route files (page.tsx, layout.tsx, etc.) define routes; colocate non-routable utilities using private folders (e.g., _components/), or store shared UI logic outside app/ to avoid app-router dependencies."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/patterns/components.md` around lines 21 - 24, Update the guidance in
docs/patterns/components.md to align with Next.js colocation best practices:
remove the "must contain **only** route files" wording and instead state that
route files (page.tsx, layout.tsx, loading.tsx, error.tsx, not-found.tsx,
route.ts) define routes while non‑routable utilities may be colocated in app/
using private folders (example: app/blog/_components/Post.tsx) or kept outside
app/ for shared UI logic to avoid app-router coupling; mention using
leading-underscore folders (e.g., _components/) for private utilities to prevent
them from becoming routes.
| ### Rule: no Tailwind utility strings inline in JSX | ||
|
|
||
| - **Do not** write `className="flex gap-2 ..."`. | ||
| - Components must use a **component-scoped CSS Style** (`<Component>.styles.css`). | ||
| - Tailwind utilities must be composed inside the CSS Style using `@apply`. | ||
| - Semantic class names in JSX are allowed and expected (for example: `className="create-order-modal__field"`). | ||
| - The restriction applies only to raw Tailwind utility strings inline in JSX. |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What do Tailwind CSS v4 docs and community best practices recommend regarding inline utility classes in JSX vs extracting to CSS with @apply?
💡 Result:
Tailwind CSS v4 docs and community best practices strongly recommend using inline utility classes in JSX over extracting to CSS with @apply. The core philosophy is utility-first CSS: compose styles directly in HTML/JSX using utility classes like "flex p-4 bg-blue-500". This keeps all styling co-located, leverages Tailwind's purging for minimal CSS, enables variants (hover:, dark:, responsive), and avoids custom CSS maintenance. @apply is supported in v4 docs as an escape hatch for specific cases: overriding third-party library styles or when you must write custom CSS but want Tailwind design tokens. Example from docs: .select2-dropdown { @apply rounded-b-lg shadow-md; }. It requires @reference in scoped styles (Vue/Svelte/CSS modules) and can cause issues like unexpected purging or context problems in v4. For JSX/React, no official guidance promotes @apply; instead, docs emphasize complete static class names (use clsx/twMerge for conditionals), components for reuse, and avoiding dynamic concatenation. Adam Wathan (creator) advises against @apply, favoring theme vars or utilities for simplicity/performance. Community (shadcn/ui, guides) echoes: extract React components, not CSS classes.
Citations:
- 1: https://tailwindcss.com/docs/styling-with-utility-classes
- 2: https://tailwindcss.com/docs/functions-and-directives
- 3: https://tailwindcss.com/docs/upgrade-guide
- 4: https://tailwindcss.com/docs/content-configuration
- 5: Strategies for Using @apply vs. theme() tailwindlabs/tailwindcss#13064
- 6: https://devtoolbox-blue.vercel.app/en/blog/tailwind-css-guide/
- 7: https://infinum.com/handbook/frontend/react/tailwind/best-practices
Update the "no inline Tailwind utilities in JSX" rule to align with official Tailwind v4 best practices.
The current rule contradicts Tailwind CSS v4 official documentation and community best practices. Tailwind's utility-first philosophy recommends composing styles directly in JSX using inline utility classes (e.g., className="flex gap-2 ..."), not extracting them to CSS with @apply. According to Tailwind docs, @apply is an escape hatch for third-party library overrides or custom CSS requirements—not the recommended pattern for React components. Community practices (shadcn/ui, community guides) consistently favor extracting React components for reuse rather than extracting CSS classes.
Revise the rule to recommend inline utilities for component styling, with CSS modules reserved for overriding third-party styles or truly custom CSS needs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/patterns/components.md` around lines 67 - 73, Update the "Rule: no
Tailwind utility strings inline in JSX" guidance to align with Tailwind v4 best
practices: change the rule text in the "no Tailwind utility strings inline in
JSX" section to recommend using inline Tailwind utility classes in JSX (e.g.,
className="flex gap-2 ...") as the default pattern for component styling, and
state that component-scoped CSS (.styles.css and `@apply`) should be reserved for
third-party overrides or truly custom CSS needs; update examples and the bullet
list to show inline utilities as preferred, keep semantic className usage as an
option for larger structural hooks, and explicitly document the exception
criteria where `@apply` or CSS modules are still appropriate.
| @import url('https://db.onlinewebfonts.com/c/6c7eafed0f5bba0ad722f5373ecd4a54?family=Wix+Madefor+Text+Regular'); | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For Next.js 16, what are the recommended practices for loading custom fonts (next/font/local vs CSS @import from external domains), including privacy and performance trade-offs?
💡 Result:
For Next.js 16, the recommended practice for loading custom fonts is to use next/font/local, which self-hosts the fonts as static assets for optimal performance and privacy. Avoid CSS @import from external domains. How to use next/font/local: Import from 'next/font/local' and configure with src pointing to local font files (e.g., WOFF2 in app/fonts/ or public/): import localFont from 'next/font/local' const myFont = localFont({ src: './fonts/my-font.woff2', display: 'swap', // or 'block', etc. variable: '--font-myfont', // optional for CSS vars }) Apply via className on or body in root layout: For multiple weights/styles: src: [ { path: './fonts/Regular.woff2', weight: '400' }, { path: './fonts/Bold.woff2', weight: '700' }, ] Use variable fonts where possible for best flexibility. Privacy and performance trade-offs: next/font/local (recommended): - Performance: Fonts downloaded at build time, served from same domain as static assets. Automatic subsetting, preloading, size-adjust for zero layout shift (CLS). Reduces LCP/FCP. - Privacy: No external requests; fully self-hosted. GDPR-compliant, no third-party tracking. CSS @import from external domains (e.g., Google Fonts): - Performance: External network requests cause delays, potential FOIT/FOUT/CLS. No automatic optimization/subsetting/preloading. Slower render. - Privacy: Sends requests to external domains (e.g., fonts.googleapis.com), leaking user IP/visits. Privacy concerns, potential GDPR issues. Official docs emphasize next/font removes external requests for improved privacy/performance. Self-host even Google Fonts via next/font/google similarly. Manual @font-face in CSS possible but lacks next/font optimizations; still prefer next/font/local for customs.
Citations:
- 1: https://nextjs.org/docs/app/getting-started/fonts
- 2: https://beta.nextjs.org/docs/optimizing/fonts
- 3: https://nextjs.org/docs/app/api-reference/components/font
- 4: https://nextjs.org/docs/14/app/building-your-application/optimizing/fonts
- 5: https://thelinuxcode.com/fonts-in-nextjs-2026-nextfont-patterns-performance-and-production-pitfalls/
- 6: https://contentful.com/blog/next-js-fonts
Replace external font @import with next/font/local.
The runtime @import from db.onlinewebfonts.com is render-blocking, leaks user data to third parties, and prevents automatic font optimization. Use next/font/local to self-host fonts with automatic subsetting, preloading, and CLS prevention—improving performance and privacy.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@styles/globals.css` around lines 4 - 5, Remove the external `@import` line from
globals.css and instead self-host the font using next/font/local: add the font
files under public/fonts (e.g., WixMadeforText-Regular.woff2), create a
localFont import in your Next.js root layout or _app (using next/font/local with
weight/style and preload), export its className or CSS variable, and apply that
className/variable to the html/body element (and delete the original `@import` in
styles/globals.css); ensure you configure fallback fonts and any required
subsets in the localFont call to retain optimization and CLS prevention.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
app/api/orders/route.ts (1)
43-55: Consider wrapping the upstream call in a try-catch for explicit error handling.If
fetchWithTimeoutthrows (e.g., network failure, timeout), the error will propagate as a generic 500 without a structured response. While acceptable, explicit handling would provide clearer error messages to clients.♻️ Optional improvement
+ let response; + try { + response = await fetchWithTimeout(`${env.apiBaseUrl}/orders`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + cache: 'no-store', + }); + } catch (error) { + return NextResponse.json( + { message: 'Falha ao comunicar com o servidor de ordens.' }, + { status: 503 }, + ); + } - const response = await fetchWithTimeout(`${env.apiBaseUrl}/orders`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(payload), - cache: 'no-store', - });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/orders/route.ts` around lines 43 - 55, The upstream POST using fetchWithTimeout to `${env.apiBaseUrl}/orders` is not wrapped in try-catch, so thrown errors (network/timeouts) leak as generic 500s; add a try-catch around the call and subsequent response.ok check in the handler that calls fetchWithTimeout, catch any Error, log or capture it, and return a structured NextResponse.json with a clear message and an appropriate status (e.g., 502 or 504) including error.message; update the block that constructs the request (references: fetchWithTimeout, env.apiBaseUrl, payload, NextResponse.json) to ensure both non-ok responses and thrown exceptions produce consistent JSON responses.
🤖 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/api/orders/route.ts`:
- Line 26: Replace the insecure Math.random()-based uuidv4() usage with the
runtime-provided crypto.randomUUID(): update the code in route.ts where uuidv4()
is called (symbol: uuidv4) to call crypto.randomUUID() instead, remove or stop
importing the custom uuid helper from "@/lib/shared/uuid", and ensure any
callers expecting the same string ID type continue to work; if targeting
environments where crypto.randomUUID may be unavailable, add a small runtime
fallback that throws or polyfills explicitly rather than using Math.random().
---
Nitpick comments:
In `@app/api/orders/route.ts`:
- Around line 43-55: The upstream POST using fetchWithTimeout to
`${env.apiBaseUrl}/orders` is not wrapped in try-catch, so thrown errors
(network/timeouts) leak as generic 500s; add a try-catch around the call and
subsequent response.ok check in the handler that calls fetchWithTimeout, catch
any Error, log or capture it, and return a structured NextResponse.json with a
clear message and an appropriate status (e.g., 502 or 504) including
error.message; update the block that constructs the request (references:
fetchWithTimeout, env.apiBaseUrl, payload, NextResponse.json) to ensure both
non-ok responses and thrown exceptions produce consistent JSON responses.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 887c8d98-b494-446b-80ac-93e1f336e374
📒 Files selected for processing (3)
.vscode/settings.jsonapp/api/orders/route.tslib/shared/uuid.ts
✅ Files skipped from review due to trivial changes (2)
- .vscode/settings.json
- lib/shared/uuid.ts
…improve error messages
…or better readability
- Deleted FilterSelect, FilterTextInput, FiltersActions, FiltersBar, FiltersFields, Head, Pagination, Root, Table, Toolbar components. - Removed associated tests for the deleted components. - Cleaned up filters configuration and filter controller logic. - Updated package.json to ensure consistent dependencies.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
app/api/orders/route.ts (1)
12-13: Consider returning a clearer error for malformed JSON.When
request.json()fails,bodybecomesnull, andsafeParsewill produce generic validation errors. For better API ergonomics, detect this case explicitly.💡 Suggested improvement
- const body = await request.json().catch(() => null); - const parsedBody = createOrderSchema.safeParse(body); + const body = await request.json().catch(() => null); + if (body === null) { + return NextResponse.json( + { message: 'Corpo da requisição inválido ou ausente.' }, + { status: 400 }, + ); + } + const parsedBody = createOrderSchema.safeParse(body);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/orders/route.ts` around lines 12 - 13, The handler currently sets body = await request.json().catch(() => null) and then calls createOrderSchema.safeParse(body), which yields generic validation errors when JSON parsing failed; update this to detect the parse failure explicitly (e.g., catch the JSON parse error from request.json() and return or throw a 400 response with a clear "Malformed JSON" message) before calling createOrderSchema.safeParse so that malformed JSON returns a distinct, user-friendly error instead of generic schema errors.components/CreateOrderModal/index.tsx (1)
38-38: Minor: Unusual indentation beforereturnstatement.The
returnstatement has extra leading whitespace. This appears to be a formatting inconsistency.🧹 Fix indentation
- return ( + return (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/CreateOrderModal/index.tsx` at line 38, The return statement in the CreateOrderModal component has extra leading whitespace causing inconsistent indentation; open the component function (CreateOrderModal) and adjust the indentation so the line with "return (" aligns with the surrounding block (same indentation level as the opening of the function or preceding JSX/logic), ensuring consistent spacing for the entire render block.server/db.json (2)
1113-1114: Consider cleaning up test artifacts in seed data.Some entries have placeholder-like instrument values (
"ddd","ERE","AAA","WEA") that appear to be manual test artifacts. While harmless for development, consider either removing them or using realistic instrument symbols for consistency with the rest of the dataset.Also applies to: 1132-1133, 1151-1152, 1170-1171, 1189-1190
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/db.json` around lines 1113 - 1114, Replace or remove the test-artifact instrument values ("ddd", "ERE", "AAA", "WEA") in the seed JSON entries that currently set "instrument": "ddd"/"ERE"/"AAA"/"WEA" (the objects with keys "instrument" and "side") and either delete those test records or replace them with realistic instrument symbols consistent with the rest of the dataset; ensure the modified JSON objects remain valid and retain their original structure (same keys like "instrument" and "side") so seeding/parsing code continues to work.
1203-1204:$schemaconventionally appears at the top of JSON files.While valid at any position, placing
$schemaat the top of the file is the common convention for better discoverability.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/db.json` around lines 1203 - 1204, Move the "$schema" property to the top of server/db.json for better discoverability; locate the "$schema" entry (currently near the end of the file) and cut/paste it so it becomes the first property in the root JSON object, ensuring you preserve JSON syntax (commas/whitespace) when relocating the "$schema" entry.components/CreateOrderModal/useCreateOrderForm.ts (1)
63-91: Nested try-catch structure can be simplified.The outer try-catch (lines 63, 86-91) only catches errors that occur after the inner try-catch completes. Since
closeModal()andonCreated?.()are unlikely to throw, this catch block may never execute. Consider flattening the structure.♻️ Suggested simplification
- try { - let response; - try { - response = await fetch('/api/orders', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(parsed.data), - }); - } catch (networkError) { - setError('root', { - message: 'Erro de rede. Verifique sua conexão e tente novamente.', - }); - onError?.(networkError); - return; - } - if (!response.ok) { - setError('root', { - message: 'Não foi possível criar a ordem. Tente novamente.', - }); - return; - } - closeModal(); - onCreated?.(); - } catch (err) { - setError('root', { - message: 'Erro inesperado. Tente novamente.', - }); - onError?.(err); - } + let response; + try { + response = await fetch('/api/orders', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(parsed.data), + }); + } catch (networkError) { + setError('root', { + message: 'Erro de rede. Verifique sua conexão e tente novamente.', + }); + onError?.(networkError); + return; + } + if (!response.ok) { + setError('root', { + message: 'Não foi possível criar a ordem. Tente novamente.', + }); + return; + } + closeModal(); + onCreated?.();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/CreateOrderModal/useCreateOrderForm.ts` around lines 63 - 91, The current nested try/catch is unnecessary—remove the inner try and use a single try around the await fetch and subsequent logic (response.ok check, closeModal(), onCreated?.()). On fetch failure the catch should setError('root', { message: 'Erro de rede. Verifique sua conexão e tente novamente.' }) and call onError?.(err); if fetch succeeds but response.ok is false, call setError('root', { message: 'Não foi possível criar a ordem. Tente novamente.' }) and return without calling onError; otherwise proceed to closeModal() and onCreated?.(). Keep references to fetch('/api/orders'), setError('root', …), closeModal(), onCreated?.(), and onError?.() so you update those exact call sites.
🤖 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/api/orders/route.ts`:
- Around line 43-48: The call to fetchWithTimeout (the code assigning to
response) can throw on timeout or network errors; wrap that fetchWithTimeout
invocation in a try-catch around the POST to `${env.apiBaseUrl}/orders` (the
block that creates `response` from `payload`) and handle errors by returning a
clear HTTP Response instead of letting exceptions bubble: catch
AbortError/timeouts separately if possible (or check error.name ===
'AbortError') and return a 504 with a JSON/plain message indicating an upstream
timeout, and for other network errors return a 502 with a JSON/plain message
indicating upstream/network failure; ensure you still handle non-2xx responses
from `response` after the try and propagate them appropriately.
---
Nitpick comments:
In `@app/api/orders/route.ts`:
- Around line 12-13: The handler currently sets body = await
request.json().catch(() => null) and then calls
createOrderSchema.safeParse(body), which yields generic validation errors when
JSON parsing failed; update this to detect the parse failure explicitly (e.g.,
catch the JSON parse error from request.json() and return or throw a 400
response with a clear "Malformed JSON" message) before calling
createOrderSchema.safeParse so that malformed JSON returns a distinct,
user-friendly error instead of generic schema errors.
In `@components/CreateOrderModal/index.tsx`:
- Line 38: The return statement in the CreateOrderModal component has extra
leading whitespace causing inconsistent indentation; open the component function
(CreateOrderModal) and adjust the indentation so the line with "return (" aligns
with the surrounding block (same indentation level as the opening of the
function or preceding JSX/logic), ensuring consistent spacing for the entire
render block.
In `@components/CreateOrderModal/useCreateOrderForm.ts`:
- Around line 63-91: The current nested try/catch is unnecessary—remove the
inner try and use a single try around the await fetch and subsequent logic
(response.ok check, closeModal(), onCreated?.()). On fetch failure the catch
should setError('root', { message: 'Erro de rede. Verifique sua conexão e tente
novamente.' }) and call onError?.(err); if fetch succeeds but response.ok is
false, call setError('root', { message: 'Não foi possível criar a ordem. Tente
novamente.' }) and return without calling onError; otherwise proceed to
closeModal() and onCreated?.(). Keep references to fetch('/api/orders'),
setError('root', …), closeModal(), onCreated?.(), and onError?.() so you update
those exact call sites.
In `@server/db.json`:
- Around line 1113-1114: Replace or remove the test-artifact instrument values
("ddd", "ERE", "AAA", "WEA") in the seed JSON entries that currently set
"instrument": "ddd"/"ERE"/"AAA"/"WEA" (the objects with keys "instrument" and
"side") and either delete those test records or replace them with realistic
instrument symbols consistent with the rest of the dataset; ensure the modified
JSON objects remain valid and retain their original structure (same keys like
"instrument" and "side") so seeding/parsing code continues to work.
- Around line 1203-1204: Move the "$schema" property to the top of
server/db.json for better discoverability; locate the "$schema" entry (currently
near the end of the file) and cut/paste it so it becomes the first property in
the root JSON object, ensuring you preserve JSON syntax (commas/whitespace) when
relocating the "$schema" entry.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ded01310-a0df-4a53-9429-4aadce40eb9d
📒 Files selected for processing (10)
.gitignoreapp/api/orders/route.tscomponents/CreateOrderModal/__tests__/CreateOrderModal.test.tsxcomponents/CreateOrderModal/index.tsxcomponents/CreateOrderModal/useCreateOrderForm.tscomponents/OrderGrid/components/Filter/__tests__/FilterSelect.test.tsxlib/orders/create-order.schema.tslib/shared/uuid.tspackage.jsonserver/db.json
✅ Files skipped from review due to trivial changes (4)
- .gitignore
- package.json
- lib/orders/create-order.schema.ts
- components/CreateOrderModal/tests/CreateOrderModal.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- components/OrderGrid/components/Filter/tests/FilterSelect.test.tsx
- lib/shared/uuid.ts
| const response = await fetchWithTimeout(`${env.apiBaseUrl}/orders`, { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify(payload), | ||
| cache: 'no-store', | ||
| }); |
There was a problem hiding this comment.
Wrap upstream fetch in try-catch to handle network/timeout errors.
fetchWithTimeout can throw (timeout abort, DNS failure, network offline). Currently, these exceptions propagate uncaught, resulting in an opaque 500 response. Handle the exception to return a meaningful error.
🛡️ Proposed fix
- const response = await fetchWithTimeout(`${env.apiBaseUrl}/orders`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(payload),
- cache: 'no-store',
- });
+ let response: Response;
+ try {
+ response = await fetchWithTimeout(`${env.apiBaseUrl}/orders`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payload),
+ cache: 'no-store',
+ });
+ } catch {
+ return NextResponse.json(
+ { message: 'Falha ao conectar com o servidor. Tente novamente.' },
+ { status: 503 },
+ );
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/orders/route.ts` around lines 43 - 48, The call to fetchWithTimeout
(the code assigning to response) can throw on timeout or network errors; wrap
that fetchWithTimeout invocation in a try-catch around the POST to
`${env.apiBaseUrl}/orders` (the block that creates `response` from `payload`)
and handle errors by returning a clear HTTP Response instead of letting
exceptions bubble: catch AbortError/timeouts separately if possible (or check
error.name === 'AbortError') and return a 504 with a JSON/plain message
indicating an upstream timeout, and for other network errors return a 502 with a
JSON/plain message indicating upstream/network failure; ensure you still handle
non-2xx responses from `response` after the try and propagate them
appropriately.
Summary by CodeRabbit