Feature/chef overflow - #36
Conversation
…vive-Front-End into feature/chef-overflow
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 20
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (14)
src/components/Dashboard/RecipeBuilderView.jsx-358-358 (1)
358-358:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix user-facing typo in nutrient label.
Line 358 uses
"Suger"; this should be"Sugar".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/RecipeBuilderView.jsx` at line 358, Fix the typo in the nutrient label configuration for the sugar item in RecipeBuilderView.jsx. In the object where the key is 'sugar', change the label value from "Suger" to "Sugar". This is a simple string correction to ensure the user-facing text displays the correct spelling of the nutrient name.src/components/Dashboard/MenuManagementView.jsx-157-157 (1)
157-157:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAlign supported-file hint with actual validation rules.
Line 157 says
xlsx,csv, but Lines 52-53 and 153 also allowxls.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/MenuManagementView.jsx` at line 157, The text hint displayed on the file upload component indicates only "xlsx,csv" are supported, but the actual validation logic allows three file types including xls as well. Update the hint text in the paragraph element with className containing "text-[12px]" to include "xls" in the supported file formats list so it accurately reflects what the validation on the upload handler accepts.src/components/Dashboard/IngredientsView.jsx-246-247 (1)
246-247:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUse the correct
colSpanfor the empty table row.Line 255 uses
colSpan={10}, but the header defines 9 columns (Line 246), which can cause layout/semantics mismatch.Suggested fix
- <tr><td colSpan={10}><EmptyState title="No ingredients found" description="Adjust your search or add a new ingredient." /></td></tr> + <tr><td colSpan={9}><EmptyState title="No ingredients found" description="Adjust your search or add a new ingredient." /></td></tr>Also applies to: 255-255
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/IngredientsView.jsx` around lines 246 - 247, The header row in the IngredientsView component defines 9 columns by mapping over an array containing ["Name", "Category", "Fat", "Cal", "Pro", "Sug", "Stock", "Price", "Actions"], but the empty table row around line 255 uses colSpan={10}. Update the colSpan value from 10 to 9 to match the actual number of header columns defined in the component to ensure proper table semantics and layout consistency.src/components/Dashboard/IngredientsView.jsx-14-14 (1)
14-14:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove unused variables to resolve current lint errors.
Line 14 (
color) and Line 257 (i) are unused and currently flagged by ESLint.Suggested fix
-function CircleMetric({ pct, color, value, label, badge, change = 0 }) { +function CircleMetric({ pct, value, label, badge, change = 0 }) { ... - filtered.map((item, i) => ( + filtered.map((item) => (Also applies to: 257-257
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/IngredientsView.jsx` at line 14, Remove the unused `color` parameter from the CircleMetric function signature since it is not referenced anywhere in the function body. Additionally, locate and remove the unused variable `i` around line 257 that is flagged by ESLint as unused. Both of these unused variables are causing lint errors and should be deleted entirely from their respective locations.Source: Linters/SAST tools
src/components/Dashboard/IngredientsView.jsx-184-184 (1)
184-184:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix the metric label typo shown to users.
Line 184 uses
"Protien"; this should be"Protein".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/IngredientsView.jsx` at line 184, The metric label for protein is misspelled as "Protien" instead of "Protein" in the object being defined on line 184. Fix this typo by correcting the label property value from "Protien" to "Protein" in the line that contains getCategoryCount("Protein") and getCategoryPct("Protein") calls. This ensures the displayed label matches the actual category name being queried.src/components/Dashboard/MenuManagementView.jsx-71-75 (1)
71-75:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winClear file input value so the same file can be selected again.
After a successful upload,
selectedFileis reset (Line 83) but the input value remains unchanged (Line 153), so choosing the same file again may not fireonChange.Suggested fix
-import { useState, useMemo } from "react"; +import { useState, useMemo, useRef } from "react"; ... +const fileInputRef = useRef(null); ... const handleUpload = () => { if (!selectedFile) return; uploadFile(selectedFile, { onSuccess: () => { addToast("Menu file uploaded and processed successfully!", "success"); setSelectedFile(null); + if (fileInputRef.current) fileInputRef.current.value = ""; }, ... - <input id="menu-file-upload" type="file" accept=".csv,.xlsx,.xls" className="hidden" onChange={handleChange} /> + <input id="menu-file-upload" ref={fileInputRef} type="file" accept=".csv,.xlsx,.xls" className="hidden" onChange={handleChange} />Also applies to: 81-84, 153-153
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/MenuManagementView.jsx` around lines 71 - 75, After a successful file upload where selectedFile is reset around line 83, you also need to clear the file input element's value to empty string. This allows the onChange handler in handleChange to fire when the same file is selected again, since the input value will change from empty to the file. Add a statement to reset the file input's value property (likely by accessing the input element reference at line 153) whenever the upload completes successfully.src/components/Dashboard/IngredientsView.jsx-199-202 (1)
199-202:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winPass
changeintoCircleMetricto avoid always showing0.00%.Line 201 omits
change, but Line 42 renders it; all cards currently display the default0.00%instead of the metric-specific value.Suggested fix
- {metricRows.map((m, i) => ( - <CircleMetric key={i} pct={m.pct} value={m.value} label={m.label} badge={m.badge} /> - ))} + {metricRows.map((m) => ( + <CircleMetric + key={m.label} + pct={m.pct} + value={m.value} + label={m.label} + badge={m.badge} + change={m.change} + /> + ))}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/IngredientsView.jsx` around lines 199 - 202, The CircleMetric component is missing the change prop when it is being rendered in the map function on line 201. Add the change prop to the CircleMetric component call and pass m.change as its value, so that each metric card displays its specific change percentage instead of defaulting to 0.00%.src/mocks/dashboardMock.js-15-15 (1)
15-15:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove unused catch binding to satisfy linting.
Line 15 defines
ebut never uses it, matching the reportedno-unused-varserror.Suggested fix
- } catch (e) { + } catch { console.warn(`[MOCK] Corrupted localStorage data for mock_${key}. Resetting.`); localStorage.removeItem(`mock_${key}`); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/mocks/dashboardMock.js` at line 15, The catch block on line 15 defines an unused variable binding `e` that triggers a no-unused-vars linting error. Remove the unused variable `e` from the catch statement by changing `catch (e)` to `catch ()` to satisfy the linter and match the modern JavaScript convention for unused error bindings.Source: Linters/SAST tools
src/index.css-3-6 (1)
3-6:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAvoid pointer cursor on disabled buttons.
Line 4 applies
cursor: pointerto all buttons, including disabled ones, which makes non-interactive controls appear clickable.Suggested fix
`@layer` base { - button { + button:not(:disabled) { cursor: pointer; } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/index.css` around lines 3 - 6, The button selector within the `@layer` base block currently applies cursor: pointer to all buttons, including disabled ones, making non-interactive disabled buttons appear clickable. Modify the button selector to exclude disabled buttons by using the :not(:disabled) pseudo-class selector, so that the cursor: pointer style is only applied to enabled interactive buttons.src/components/Dashboard/shared/IngredientModal.jsx-85-90 (1)
85-90:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRevoke
createObjectURLpreviews to avoid client-side memory leaks.Each new image selection creates a blob URL that is never revoked.
Suggested patch
+import { useState, useEffect, useRef } from "react"; @@ const [formData, setFormData] = useState(EMPTY_FORM); + const previewUrlRef = useRef(null); @@ const handleImageChange = (e) => { const file = e.target.files[0]; if (file) { + if (previewUrlRef.current) URL.revokeObjectURL(previewUrlRef.current); const url = URL.createObjectURL(file); + previewUrlRef.current = url; setFormData((prev) => ({ ...prev, image: url })); } }; + + useEffect(() => { + return () => { + if (previewUrlRef.current) URL.revokeObjectURL(previewUrlRef.current); + }; + }, []);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/shared/IngredientModal.jsx` around lines 85 - 90, The handleImageChange function creates blob URLs using URL.createObjectURL but never revokes the previous URL, causing memory leaks. Before updating the form data with a new blob URL, first revoke the old image URL (if one exists) using URL.revokeObjectURL() on the previous image value, then create and set the new blob URL. This ensures each old blob URL is properly released from memory when a new image is selected.src/components/Dashboard/shared/OrdersOverviewChart.jsx-18-18 (1)
18-18:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove unused
maxValueto avoid lint failure.
maxValueis assigned but never read, which matches theno-unused-varserror and can block strict CI lint checks.Suggested fix
- const maxValue = Math.max(...data.map(d => d.orders));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/shared/OrdersOverviewChart.jsx` at line 18, Remove the unused maxValue variable assignment on line 18 in OrdersOverviewChart.jsx. The variable is declared using Math.max(...data.map(d => d.orders)) but is never referenced anywhere in the component, causing a no-unused-vars lint error. Simply delete the entire line that assigns maxValue to resolve the lint failure.Source: Linters/SAST tools
src/components/Dashboard/shared/DishDetailsModal.jsx-52-65 (1)
52-65:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winPreserve valid
0nutrition values in the UI.Using
|| "-"hides real zeroes (for example,0sugar becomes"-"). Use nullish coalescing so onlynull/undefinedfallback.💡 Suggested fix
-<span className="block text-[13px] font-bold text-[`#1a1a1a`] mt-0.5">{dish.calories || "-"}</span> +<span className="block text-[13px] font-bold text-[`#1a1a1a`] mt-0.5">{dish.calories ?? "-"}</span> -<span className="block text-[13px] font-bold text-[`#1a1a1a`] mt-0.5">{dish.protein || "-"}</span> +<span className="block text-[13px] font-bold text-[`#1a1a1a`] mt-0.5">{dish.protein ?? "-"}</span> -<span className="block text-[13px] font-bold text-[`#1a1a1a`] mt-0.5">{dish.fat || "-"}</span> +<span className="block text-[13px] font-bold text-[`#1a1a1a`] mt-0.5">{dish.fat ?? "-"}</span> -<span className="block text-[13px] font-bold text-[`#1a1a1a`] mt-0.5">{dish.sugar || "-"}</span> +<span className="block text-[13px] font-bold text-[`#1a1a1a`] mt-0.5">{dish.sugar ?? "-"}</span>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/shared/DishDetailsModal.jsx` around lines 52 - 65, The nutrition value display in DishDetailsModal is using the logical OR operator (||) which treats zero as a falsy value and displays it as a dash instead of showing the actual zero. Replace all instances of || "-" with ?? "-" (nullish coalescing operator) for the nutrition fields: dish.calories, dish.protein, dish.fat, and dish.sugar. This ensures that only null or undefined values fall back to the dash, while legitimate zero values are displayed correctly.src/components/Dashboard/MetricCards.jsx-11-11 (1)
11-11:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix KPI label grammar.
Line 11 should be plural:
"Total Customers".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/MetricCards.jsx` at line 11, In the MetricCards.jsx file, update the totalCustomers property label from "Total Customer" to "Total Customers" to fix the grammar and use the correct plural form for the KPI label.src/components/Dashboard/InventoryAlerts.jsx-11-11 (1)
11-11:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix days-left label text formatting.
Line 11 renders
"Day"for all values and misses spacing. Use pluralization (day/days) for clearer UI text.Suggested fix
- <span className="text-[9px] text-gray-500">({daysLeft}Day left)</span> + <span className="text-[9px] text-gray-500"> + ({daysLeft} {daysLeft === 1 ? "day" : "days"} left) + </span>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/InventoryAlerts.jsx` at line 11, The span element displaying the days-left label in the InventoryAlerts component is missing spacing and does not account for proper pluralization. Fix the text in the span by adding a space between the daysLeft variable and the word "Day", and implement conditional logic to use "Day" when daysLeft equals 1 and "Days" for all other values. This will ensure the label reads correctly as either "1 Day left" or "X Days left" depending on the actual value.
🧹 Nitpick comments (6)
src/components/Dashboard/LiveKitchenView.jsx (1)
31-34: ⚡ Quick winMake kitchen cards keyboard-operable for “view details.”
Both active and done cards open details via click-only containers. Please add keyboard interaction (or explicit buttons) so the details view is accessible without a mouse.
Also applies to: 222-226
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/LiveKitchenView.jsx` around lines 31 - 34, The kitchen card containers in the LiveKitchenView component are currently divs with only onClick handlers, making them inaccessible to keyboard users. Replace the div element containing the onViewOrder click handler with a button element (or add keyboard event handling such as onKeyDown to detect Enter/Space keys) to make the cards keyboard-operable. This needs to be applied to both the active cards section (around line 31-34) and the done cards section (around line 222-226) to ensure consistent accessibility across all kitchen cards that open detail views.src/components/Dashboard/OrdersView.jsx (2)
81-86: ⚡ Quick winPrefer React Query refetch over
window.location.reload()for retry.Reloading the whole page discards local UI state and is heavier than refetching the failed queries. Hook-level
refetch(or query invalidation) gives a smoother, scoped retry path.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/OrdersView.jsx` around lines 81 - 86, In the OrdersView component's hasError block, replace the window.location.reload() call in the ErrorState component's onRetry prop with React Query's refetch function. First, obtain the refetch function from your React Query hook (likely from useQuery) that fetches the orders data, then pass a callback to onRetry that calls this refetch function instead of reloading the entire page. This preserves local UI state and provides a scoped, smoother retry experience.
238-253: ⚡ Quick winMake order-row “view details” interaction keyboard-accessible.
The details modal is opened via
<tr onClick>only, so keyboard users can’t trigger the same action. Please expose a focusable control (e.g., explicit “View details” button) or add keyboard handlers with proper semantics.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/OrdersView.jsx` around lines 238 - 253, The `<tr>` element rendering order data is only clickable via mouse, making it inaccessible to keyboard users. Add keyboard support by implementing an onKeyDown event handler on the table row that responds to Enter or Space keys to trigger setViewingOrder, or alternatively expose a focusable button control (such as the existing StatusBadge or a dedicated "View details" button) that users can tab to and activate. Additionally, add appropriate ARIA attributes (like role="button" and tabIndex="0") to make the interactive intent clear to assistive technologies.src/components/Dashboard/ChefMenuView.jsx (1)
260-264: ⚡ Quick winAdd keyboard-accessible path for opening dish details.
The details modal is opened by clicking the table row only. Please add a focusable button (or equivalent keyboard interaction) so non-pointer users can open details too.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/ChefMenuView.jsx` around lines 260 - 264, The table row element with the onClick handler for setViewingItem(item) is not accessible to keyboard users. Add keyboard support by including a tabIndex attribute to make the row focusable and an onKeyDown handler that detects Enter or Space key presses and calls the same setViewingItem(item) function. This ensures both pointer and keyboard users can open the dish details modal.src/components/Dashboard/shared/DashboardSkeleton.jsx (1)
75-84: ⚡ Quick winMake skeleton layout responsive for smaller viewports.
DashboardPageSkeletoncurrently hard-codes desktop grids, which can break loading UX on narrow screens. Switching to responsive grid classes keeps the loading state usable across breakpoints.Suggested diff
export function DashboardPageSkeleton() { return ( <div className="flex flex-col gap-5 p-8"> - <div className="grid grid-cols-3 gap-4"> + <div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4"> <MetricCardSkeleton /> <MetricCardSkeleton /> <MetricCardSkeleton /> </div> - <div className="grid gap-4" style={{ gridTemplateColumns: "2fr 1fr" }}> + <div className="grid grid-cols-1 xl:grid-cols-[2fr_1fr] gap-4"> <ChartSkeleton height={280} /> <ChartSkeleton height={280} /> </div> </div> ); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/shared/DashboardSkeleton.jsx` around lines 75 - 84, The DashboardSkeleton component uses hard-coded grid layouts that do not adapt to smaller viewports. Replace the fixed grid-cols-3 class on the first grid containing the three MetricCardSkeleton components with responsive Tailwind breakpoint classes (such as grid-cols-1 for mobile, grid-cols-2 for tablets, and grid-cols-3 for larger screens). Similarly, update the second grid containing the ChartSkeleton components by replacing the inline style with gridTemplateColumns with responsive Tailwind grid column classes instead of a fixed 2fr 1fr layout. This ensures the skeleton loading state displays appropriately across all screen sizes.src/components/Dashboard/DashboardHeader.jsx (1)
33-36: ⚡ Quick winAdd an accessible name to the notification button.
The bell button is icon-only; add
aria-labelso screen readers can identify its action.💡 Suggested fix
<button type="button" + aria-label="Open notifications" className="w-[46px] h-[46px] rounded-2xl bg-white flex items-center justify-center cursor-pointer relative hover:shadow-md transition-shadow shadow-sm border-none" >🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/Dashboard/DashboardHeader.jsx` around lines 33 - 36, The notification button in DashboardHeader.jsx is missing an accessibility label. Add an aria-label attribute to the button element that describes its purpose for screen readers, such as "View notifications" or "Notifications", to ensure users with assistive technologies can understand the button's function.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/Dashboard/ChefMenuView.jsx`:
- Around line 146-149: The issue in the categoryCounts mapping in
ChefMenuView.jsx is that the expression using `length || cat.count` treats zero
as falsy, causing empty categories to incorrectly display stale fallback count
values. Replace the logical OR operator with the nullish coalescing operator by
changing the count assignment to use `??` instead of `||`, or better yet, remove
the fallback entirely and use only the filtered length since the filter
operation always returns a valid count. This ensures that categories with zero
items display zero instead of reverting to outdated fallback values.
In `@src/components/Dashboard/DashboardHeader.jsx`:
- Around line 53-56: The onError handler in the avatar error handling is using
e.currentTarget.nextSibling which is fragile because nextSibling can be any node
type (text nodes, comments) and may be null. Replace this with
e.currentTarget.nextElementSibling to safely access only element nodes, or
better yet, refactor to use a ref or querySelector to directly target the
fallback avatar element instead of relying on sibling traversal. This ensures
the error handler won't throw when the DOM structure changes or contains
non-element nodes.
In `@src/components/Dashboard/LiveKitchenView.jsx`:
- Around line 91-97: Remove the unused `revertingOrder` state variable from the
LiveKitchenView component. Wire up the UI actions in the component to actually
set the `orderToCancel` and `orderToRevert` state variables by connecting them
to appropriate button click handlers or menu actions. Enable the currently
disabled button in the Done section that should trigger the "Not Done" revert
flow. Ensure that the confirmation modals referenced at lines 125-130, 221-261,
and 279-303 have corresponding UI triggers that properly set these state
variables so the cancel and revert workflows are reachable from the user
interface.
In `@src/components/Dashboard/MetricCards.jsx`:
- Around line 23-35: The code in the MetricCards map function accesses
icons[key] without validating that the key exists in the icons object, which
causes a render crash when unexpected API keys are encountered. Add a guard
condition after assigning Icon to check if it is undefined and either skip
rendering that metric card or provide a fallback icon. Alternatively, filter
Object.entries(metrics) to only include keys that have corresponding entries in
the icons object before mapping over them.
In `@src/components/Dashboard/RecipeBuilderView.jsx`:
- Around line 47-50: The component returns an ErrorState at line 149 whenever
errIngredients exists, but the useEffect hook at line 47 shows that
initialIngredients is only needed when !editMeal. Modify the condition at line
149 that checks errIngredients to also verify !editMeal, so the error is only
shown in create mode and does not block users from editing and saving updates
when editMeal data is present.
In `@src/components/Dashboard/shared/ConfirmModal.jsx`:
- Around line 7-43: The ConfirmModal component is missing critical accessibility
attributes and keyboard support. Add role="dialog" and aria-modal="true" to the
outer container div that wraps the modal, and add an aria-labelledby attribute
referencing the title h2 element. Implement keyboard event handling using a
useEffect hook to listen for the Escape key press and call onClose when
detected. This ensures keyboard users and assistive technology users can
properly interact with and dismiss the modal dialog.
In `@src/components/Dashboard/shared/IngredientModal.jsx`:
- Line 7: The VALIDATORS.costPerUnit and FILTERS.costPerUnit regex patterns
currently allow an optional trailing `$` symbol (via `\$?`), which permits
invalid formats like "40$" to be sent to the mutation payload and violate
backend numeric contracts. Remove the optional `\$?` portion from the regex
pattern in VALIDATORS.costPerUnit and from the FILTERS.costPerUnit pattern so
only numeric formats like "40" or "40.50" are accepted. Additionally, verify
that the submit handler around line 118 passes only the validated numeric value
to the mutation payload without any currency symbol processing.
In `@src/components/Dashboard/shared/MenuModal.jsx`:
- Around line 80-85: The handleImageChange function creates blob URLs with
URL.createObjectURL that are never revoked, causing memory leaks when images are
repeatedly changed. Before creating a new blob URL in handleImageChange, check
if formData.image is an existing blob URL (by checking if it starts with
"blob:") and revoke it using URL.revokeObjectURL() to free the memory.
Additionally, add a cleanup mechanism to revoke the blob URL when the component
unmounts or when the form is reset to ensure no orphaned URLs remain in memory.
In `@src/components/Dashboard/shared/OrderDetailsModal.jsx`:
- Around line 28-46: The modal component OrderDetailsModal.jsx is missing
essential accessibility attributes for dialog semantics. Add role="dialog" and
aria-modal="true" to the main modal container div, assign an id to the h2
element containing "Order {order.id}" and link it to the modal using
aria-labelledby attribute. Additionally, implement an Escape key event listener
on the modal container (or use useEffect hook) that calls the onClose function
when the Escape key is pressed, allowing keyboard users to dismiss the modal.
In `@src/components/Dashboard/shared/useToast.jsx`:
- Around line 20-43: The FiX icon component is used in the ToastContainer
component's JSX but is not imported at the top of the file. Add an import
statement for FiX from the appropriate icon library (likely react-icons, based
on the naming convention matching the Icon component and cfg.icon pattern used
in the map function) alongside the existing import of useToastStore and
TOAST_STYLES so that the close button icon renders correctly without throwing a
runtime error.
In `@src/components/Dashboard/TrendingMenus.jsx`:
- Line 64: The revenue display in the span element divides the item.revenue by
1000 to scale it down, but there is no unit indicator to show users that the
value represents thousands of dollars. Add a 'k' suffix after the formatted
revenue value to explicitly indicate the thousands unit, or alternatively render
the full unscaled currency amount without division. This will prevent ambiguity
about whether the displayed value represents actual dollars or scaled thousands.
In `@src/components/ProtectedRoute.jsx`:
- Around line 1-2: The ProtectedRoute component has an unused useEffect hook and
does not implement the documented 2-second auto-redirect for unauthenticated
users. In the unauthenticated path (the section rendering the friendly message),
add a useEffect hook that sets up a timer to automatically redirect to the login
page after 2 seconds using the useNavigate hook. The useEffect should depend on
the navigation state and clean up the timer if the component unmounts before the
redirect occurs.
In `@src/components/StaffRoute.jsx`:
- Around line 23-28: The staff authorization check in the StaffRoute component
is commented out, creating an authorization gap where any authenticated user can
access protected routes. Uncomment the authorization block that checks if the
user exists and validates their role is either "chef" or "admin", and uncomment
the corresponding navigate and console.warn statements within that block. Apply
the same fix to the similar commented authorization check mentioned in the range
50-54 of the same file to ensure consistent protection across all
staff-protected routes.
In `@src/hooks/dashboard/useDashboardRealtime.js`:
- Line 38: The socket event listeners for "orders:new", and the other two
listeners mentioned (at lines 45 and 67) have unused callback parameters that
trigger lint errors. Fix these by prefixing each unused parameter with an
underscore (e.g., rename newOrder to _newOrder, and apply the same pattern to
the other unused parameters in the event handlers at lines 45 and 67). This
indicates to the linter that these parameters are intentionally unused.
In `@src/hooks/dashboard/useOrders.js`:
- Around line 36-43: The optimistic status update in the updater function passed
to setQueriesData is checking for the structure { orders: [...] }, but the
getOrders query returns an array directly. Modify the updater logic within the
setQueriesData call to handle both cases: when old is an array (the actual
cached data from getOrders) by mapping over it directly, and when old is an
object with an orders property by mapping over old.orders. This ensures the
optimistic update applies correctly to the active orders list query regardless
of the data structure in the cache.
In `@src/mocks/handlers.js`:
- Around line 432-447: In the handler function for the POST /menu/upload
endpoint, after the line that calls dash.mockMenuUploads.unshift(newUpload) to
add the new upload entry, add a call to dash.saveMock() immediately following it
to persist the changes to storage so that uploaded menu entries are retained
after page reload.
- Around line 454-534: The menu and ingredients CRUD handlers are mutating mock
arrays in memory without persisting the state. Add a `dash.saveMock()` call at
the end of each handler function that modifies state to ensure durability across
sessions. Specifically, add this call to the POST handler for `/menu/items`, the
PATCH handler for `/menu/items/{id}`, the DELETE handler for `/menu/items/{id}`,
the POST handler for `/recipes`, the POST handler for `/ingredients`, the PATCH
handler for `/ingredients/{id}`, and the DELETE handler for `/ingredients/{id}`.
Each handler should call `dash.saveMock()` immediately before returning the
response object to persist the mutations.
In `@src/services/mappers/dashboardMappers.js`:
- Around line 12-28: The mapDashboardMetrics function (and similarly the
functions at lines 94 and 186) directly dereferences the data parameter without
checking if it is null or undefined, which will cause the dashboard rendering to
break if a null/undefined payload is received. Add a guard clause at the
beginning of each mapper function that checks if data is null or undefined and
returns a default object with zero/default values for all metrics if the data is
falsy, otherwise proceed with the existing destructuring logic.
In `@src/store/orderStore.js`:
- Around line 358-360: The newOrder object in orderStore.js is generating a
random fallback ID when response.data.id is missing, and this fabricated ID gets
persisted to payment history, causing desynchronization with the backend. Remove
the random ID generation fallback (the Math.floor(10000 + Math.random() *
90000).toString() expression) from the id property assignment and instead handle
the missing ID case by throwing an error or rejecting the operation to ensure
only real backend order IDs are used in the order object creation and subsequent
persistence.
- Around line 347-356: The axios client instance created in src/services/api.js
lacks a timeout configuration, which can cause indefinite loading states if
network requests hang. Add a timeout property to the axios.create() call in
src/services/api.js (around lines 50–58) and set it to an appropriate value such
as 30000 milliseconds. This ensures that requests will abort if they exceed the
specified duration, preventing the UI from remaining frozen during network
issues.
---
Minor comments:
In `@src/components/Dashboard/IngredientsView.jsx`:
- Around line 246-247: The header row in the IngredientsView component defines 9
columns by mapping over an array containing ["Name", "Category", "Fat", "Cal",
"Pro", "Sug", "Stock", "Price", "Actions"], but the empty table row around line
255 uses colSpan={10}. Update the colSpan value from 10 to 9 to match the actual
number of header columns defined in the component to ensure proper table
semantics and layout consistency.
- Line 14: Remove the unused `color` parameter from the CircleMetric function
signature since it is not referenced anywhere in the function body.
Additionally, locate and remove the unused variable `i` around line 257 that is
flagged by ESLint as unused. Both of these unused variables are causing lint
errors and should be deleted entirely from their respective locations.
- Line 184: The metric label for protein is misspelled as "Protien" instead of
"Protein" in the object being defined on line 184. Fix this typo by correcting
the label property value from "Protien" to "Protein" in the line that contains
getCategoryCount("Protein") and getCategoryPct("Protein") calls. This ensures
the displayed label matches the actual category name being queried.
- Around line 199-202: The CircleMetric component is missing the change prop
when it is being rendered in the map function on line 201. Add the change prop
to the CircleMetric component call and pass m.change as its value, so that each
metric card displays its specific change percentage instead of defaulting to
0.00%.
In `@src/components/Dashboard/InventoryAlerts.jsx`:
- Line 11: The span element displaying the days-left label in the
InventoryAlerts component is missing spacing and does not account for proper
pluralization. Fix the text in the span by adding a space between the daysLeft
variable and the word "Day", and implement conditional logic to use "Day" when
daysLeft equals 1 and "Days" for all other values. This will ensure the label
reads correctly as either "1 Day left" or "X Days left" depending on the actual
value.
In `@src/components/Dashboard/MenuManagementView.jsx`:
- Line 157: The text hint displayed on the file upload component indicates only
"xlsx,csv" are supported, but the actual validation logic allows three file
types including xls as well. Update the hint text in the paragraph element with
className containing "text-[12px]" to include "xls" in the supported file
formats list so it accurately reflects what the validation on the upload handler
accepts.
- Around line 71-75: After a successful file upload where selectedFile is reset
around line 83, you also need to clear the file input element's value to empty
string. This allows the onChange handler in handleChange to fire when the same
file is selected again, since the input value will change from empty to the
file. Add a statement to reset the file input's value property (likely by
accessing the input element reference at line 153) whenever the upload completes
successfully.
In `@src/components/Dashboard/MetricCards.jsx`:
- Line 11: In the MetricCards.jsx file, update the totalCustomers property label
from "Total Customer" to "Total Customers" to fix the grammar and use the
correct plural form for the KPI label.
In `@src/components/Dashboard/RecipeBuilderView.jsx`:
- Line 358: Fix the typo in the nutrient label configuration for the sugar item
in RecipeBuilderView.jsx. In the object where the key is 'sugar', change the
label value from "Suger" to "Sugar". This is a simple string correction to
ensure the user-facing text displays the correct spelling of the nutrient name.
In `@src/components/Dashboard/shared/DishDetailsModal.jsx`:
- Around line 52-65: The nutrition value display in DishDetailsModal is using
the logical OR operator (||) which treats zero as a falsy value and displays it
as a dash instead of showing the actual zero. Replace all instances of || "-"
with ?? "-" (nullish coalescing operator) for the nutrition fields:
dish.calories, dish.protein, dish.fat, and dish.sugar. This ensures that only
null or undefined values fall back to the dash, while legitimate zero values are
displayed correctly.
In `@src/components/Dashboard/shared/IngredientModal.jsx`:
- Around line 85-90: The handleImageChange function creates blob URLs using
URL.createObjectURL but never revokes the previous URL, causing memory leaks.
Before updating the form data with a new blob URL, first revoke the old image
URL (if one exists) using URL.revokeObjectURL() on the previous image value,
then create and set the new blob URL. This ensures each old blob URL is properly
released from memory when a new image is selected.
In `@src/components/Dashboard/shared/OrdersOverviewChart.jsx`:
- Line 18: Remove the unused maxValue variable assignment on line 18 in
OrdersOverviewChart.jsx. The variable is declared using Math.max(...data.map(d
=> d.orders)) but is never referenced anywhere in the component, causing a
no-unused-vars lint error. Simply delete the entire line that assigns maxValue
to resolve the lint failure.
In `@src/index.css`:
- Around line 3-6: The button selector within the `@layer` base block currently
applies cursor: pointer to all buttons, including disabled ones, making
non-interactive disabled buttons appear clickable. Modify the button selector to
exclude disabled buttons by using the :not(:disabled) pseudo-class selector, so
that the cursor: pointer style is only applied to enabled interactive buttons.
In `@src/mocks/dashboardMock.js`:
- Line 15: The catch block on line 15 defines an unused variable binding `e`
that triggers a no-unused-vars linting error. Remove the unused variable `e`
from the catch statement by changing `catch (e)` to `catch ()` to satisfy the
linter and match the modern JavaScript convention for unused error bindings.
---
Nitpick comments:
In `@src/components/Dashboard/ChefMenuView.jsx`:
- Around line 260-264: The table row element with the onClick handler for
setViewingItem(item) is not accessible to keyboard users. Add keyboard support
by including a tabIndex attribute to make the row focusable and an onKeyDown
handler that detects Enter or Space key presses and calls the same
setViewingItem(item) function. This ensures both pointer and keyboard users can
open the dish details modal.
In `@src/components/Dashboard/DashboardHeader.jsx`:
- Around line 33-36: The notification button in DashboardHeader.jsx is missing
an accessibility label. Add an aria-label attribute to the button element that
describes its purpose for screen readers, such as "View notifications" or
"Notifications", to ensure users with assistive technologies can understand the
button's function.
In `@src/components/Dashboard/LiveKitchenView.jsx`:
- Around line 31-34: The kitchen card containers in the LiveKitchenView
component are currently divs with only onClick handlers, making them
inaccessible to keyboard users. Replace the div element containing the
onViewOrder click handler with a button element (or add keyboard event handling
such as onKeyDown to detect Enter/Space keys) to make the cards
keyboard-operable. This needs to be applied to both the active cards section
(around line 31-34) and the done cards section (around line 222-226) to ensure
consistent accessibility across all kitchen cards that open detail views.
In `@src/components/Dashboard/OrdersView.jsx`:
- Around line 81-86: In the OrdersView component's hasError block, replace the
window.location.reload() call in the ErrorState component's onRetry prop with
React Query's refetch function. First, obtain the refetch function from your
React Query hook (likely from useQuery) that fetches the orders data, then pass
a callback to onRetry that calls this refetch function instead of reloading the
entire page. This preserves local UI state and provides a scoped, smoother retry
experience.
- Around line 238-253: The `<tr>` element rendering order data is only clickable
via mouse, making it inaccessible to keyboard users. Add keyboard support by
implementing an onKeyDown event handler on the table row that responds to Enter
or Space keys to trigger setViewingOrder, or alternatively expose a focusable
button control (such as the existing StatusBadge or a dedicated "View details"
button) that users can tab to and activate. Additionally, add appropriate ARIA
attributes (like role="button" and tabIndex="0") to make the interactive intent
clear to assistive technologies.
In `@src/components/Dashboard/shared/DashboardSkeleton.jsx`:
- Around line 75-84: The DashboardSkeleton component uses hard-coded grid
layouts that do not adapt to smaller viewports. Replace the fixed grid-cols-3
class on the first grid containing the three MetricCardSkeleton components with
responsive Tailwind breakpoint classes (such as grid-cols-1 for mobile,
grid-cols-2 for tablets, and grid-cols-3 for larger screens). Similarly, update
the second grid containing the ChartSkeleton components by replacing the inline
style with gridTemplateColumns with responsive Tailwind grid column classes
instead of a fixed 2fr 1fr layout. This ensures the skeleton loading state
displays appropriately across all screen sizes.
🪄 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 Plus
Run ID: 307dc8cc-0e00-47de-8251-d3f39583f07c
⛔ Files ignored due to path filters (2)
package-lock.jsonis excluded by!**/package-lock.jsonpublic/images/chef-avatar.pngis excluded by!**/*.png
📒 Files selected for processing (62)
package.jsonsrc/App.jsxsrc/Layout/DashboardLayout.jsxsrc/Layout/index.jssrc/components/Dashboard/ChefMenuView.jsxsrc/components/Dashboard/CustomerReviews.jsxsrc/components/Dashboard/DashboardHeader.jsxsrc/components/Dashboard/DashboardSidebar.jsxsrc/components/Dashboard/DashboardView.jsxsrc/components/Dashboard/IngredientsView.jsxsrc/components/Dashboard/InventoryAlerts.jsxsrc/components/Dashboard/LiveKitchenView.jsxsrc/components/Dashboard/MenuManagementView.jsxsrc/components/Dashboard/MetricCards.jsxsrc/components/Dashboard/OrderTypes.jsxsrc/components/Dashboard/OrdersView.jsxsrc/components/Dashboard/RecentActivity.jsxsrc/components/Dashboard/RecipeBuilderView.jsxsrc/components/Dashboard/RevenueChart.jsxsrc/components/Dashboard/TopCategories.jsxsrc/components/Dashboard/TrendingMenus.jsxsrc/components/Dashboard/shared/ConfirmModal.jsxsrc/components/Dashboard/shared/DashboardSkeleton.jsxsrc/components/Dashboard/shared/DishDetailsModal.jsxsrc/components/Dashboard/shared/EmptyState.jsxsrc/components/Dashboard/shared/ErrorState.jsxsrc/components/Dashboard/shared/IngredientModal.jsxsrc/components/Dashboard/shared/MenuModal.jsxsrc/components/Dashboard/shared/OrderDetailsModal.jsxsrc/components/Dashboard/shared/OrdersOverviewChart.jsxsrc/components/Dashboard/shared/SortMenu.jsxsrc/components/Dashboard/shared/StatusBadge.jsxsrc/components/Dashboard/shared/TimeFilter.jsxsrc/components/Dashboard/shared/useToast.jsxsrc/components/ProtectedRoute.jsxsrc/components/StaffRoute.jsxsrc/components/ui/RegularFoodCard.jsxsrc/hooks/dashboard/useDashboard.jssrc/hooks/dashboard/useDashboardRealtime.jssrc/hooks/dashboard/useIngredients.jssrc/hooks/dashboard/useKitchenOrders.jssrc/hooks/dashboard/useMenuItems.jssrc/hooks/dashboard/useMenuUploads.jssrc/hooks/dashboard/useOrders.jssrc/index.csssrc/lib/queryClient.jssrc/main.jsxsrc/mocks/dashboardMock.jssrc/mocks/handlers.jssrc/pages/Dashboard/ChefMenu.jsxsrc/pages/Dashboard/Dashboard.jsxsrc/pages/Dashboard/Ingredients.jsxsrc/pages/Dashboard/LiveKitchen.jsxsrc/pages/Dashboard/MenuManagement.jsxsrc/pages/Dashboard/Orders.jsxsrc/pages/Dashboard/RecipeBuilder.jsxsrc/pages/Home/Home.jsxsrc/pages/index.jssrc/services/dashboardService.jssrc/services/mappers/dashboardMappers.jssrc/store/orderStore.jssrc/store/toastStore.js
💤 Files with no reviewable changes (1)
- src/pages/Home/Home.jsx
| const categoryCounts = categoryItems.map(cat => ({ | ||
| ...cat, | ||
| count: allItems.filter(i => i.category?.toLowerCase() === cat.name?.toLowerCase()).length || cat.count, | ||
| })); |
There was a problem hiding this comment.
Fix category count fallback: zero-count categories are currently misreported.
length || cat.count treats 0 as falsy, so empty categories can incorrectly show stale fallback values.
Suggested fix
const categoryCounts = categoryItems.map(cat => ({
...cat,
- count: allItems.filter(i => i.category?.toLowerCase() === cat.name?.toLowerCase()).length || cat.count,
+ count: allItems.filter(i => i.category?.toLowerCase() === cat.name?.toLowerCase()).length,
}));📝 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 categoryCounts = categoryItems.map(cat => ({ | |
| ...cat, | |
| count: allItems.filter(i => i.category?.toLowerCase() === cat.name?.toLowerCase()).length || cat.count, | |
| })); | |
| const categoryCounts = categoryItems.map(cat => ({ | |
| ...cat, | |
| count: allItems.filter(i => i.category?.toLowerCase() === cat.name?.toLowerCase()).length, | |
| })); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/Dashboard/ChefMenuView.jsx` around lines 146 - 149, The issue
in the categoryCounts mapping in ChefMenuView.jsx is that the expression using
`length || cat.count` treats zero as falsy, causing empty categories to
incorrectly display stale fallback count values. Replace the logical OR operator
with the nullish coalescing operator by changing the count assignment to use
`??` instead of `||`, or better yet, remove the fallback entirely and use only
the filtered length since the filter operation always returns a valid count.
This ensures that categories with zero items display zero instead of reverting
to outdated fallback values.
| onError={(e) => { | ||
| e.currentTarget.style.display = "none"; | ||
| e.currentTarget.nextSibling.style.display = "flex"; | ||
| }} |
There was a problem hiding this comment.
Avoid brittle sibling DOM mutation in avatar error handling.
e.currentTarget.nextSibling.style can be null or a non-element node, which can throw when the avatar fails to load.
💡 Suggested fix
onError={(e) => {
e.currentTarget.style.display = "none";
- e.currentTarget.nextSibling.style.display = "flex";
+ const fallback = e.currentTarget.nextElementSibling;
+ if (fallback instanceof HTMLElement) {
+ fallback.style.display = "flex";
+ }
}}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/Dashboard/DashboardHeader.jsx` around lines 53 - 56, The
onError handler in the avatar error handling is using
e.currentTarget.nextSibling which is fragile because nextSibling can be any node
type (text nodes, comments) and may be null. Replace this with
e.currentTarget.nextElementSibling to safely access only element nodes, or
better yet, refactor to use a ref or querySelector to directly target the
fallback avatar element instead of relying on sibling traversal. This ensures
the error handler won't throw when the DOM structure changes or contains
non-element nodes.
| const [revertingOrder, setRevertingOrder] = useState(null); | ||
| const [viewingOrder, setViewingOrder] = useState(null); | ||
| const { boards, isFetching, error, refetch } = useRealtimeKitchen(); | ||
| const { mutate: updateStatus } = useUpdateKitchenStatus(); | ||
| const [orderToCancel, setOrderToCancel] = useState(null); | ||
| const [orderToMarkDone, setOrderToMarkDone] = useState(null); | ||
| const [orderToRevert, setOrderToRevert] = useState(null); |
There was a problem hiding this comment.
Cancel/Revert confirmation flows are currently unreachable from UI.
orderToCancel and orderToRevert are never set from visible actions in this component, so both related confirmation modals/handlers are dead paths. The Done section also renders a disabled button, so “Not Done” revert cannot be triggered. This leaves intended state transitions incomplete.
Also, revertingOrder state at Line 91 is unused (matches static analysis).
Also applies to: 99-107, 125-130, 221-261, 279-303
🧰 Tools
🪛 ESLint
[error] 91-91: 'revertingOrder' is assigned a value but never used. Allowed unused vars must match /^[A-Z_]/u.
(no-unused-vars)
[error] 91-91: 'setRevertingOrder' is assigned a value but never used. Allowed unused vars must match /^[A-Z_]/u.
(no-unused-vars)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/Dashboard/LiveKitchenView.jsx` around lines 91 - 97, Remove
the unused `revertingOrder` state variable from the LiveKitchenView component.
Wire up the UI actions in the component to actually set the `orderToCancel` and
`orderToRevert` state variables by connecting them to appropriate button click
handlers or menu actions. Enable the currently disabled button in the Done
section that should trigger the "Not Done" revert flow. Ensure that the
confirmation modals referenced at lines 125-130, 221-261, and 279-303 have
corresponding UI triggers that properly set these state variables so the cancel
and revert workflows are reachable from the user interface.
Source: Linters/SAST tools
| {Object.entries(metrics).map(([key, { value, change, trend }]) => { | ||
| const Icon = icons[key]; | ||
| const isUp = trend === "up"; | ||
| const TrendIcon = isUp ? FiTrendingUp : FiTrendingDown; | ||
|
|
||
| return ( | ||
| <div | ||
| key={key} | ||
| className="bg-white rounded-[20px] p-5 shadow-sm flex items-center gap-4 hover:-translate-y-0.5 hover:shadow-md transition-all duration-200" | ||
| > | ||
| <div className="w-[52px] h-[52px] rounded-2xl bg-[#F97316] flex items-center justify-center shrink-0"> | ||
| <Icon size={24} className="text-white" /> | ||
| </div> |
There was a problem hiding this comment.
Guard unknown metric keys before rendering icons.
At Line 24/34, icons[key] can be undefined for unexpected API keys, which will crash render (Element type is invalid). Filter to known keys or provide a fallback icon before rendering.
Suggested fix
- {Object.entries(metrics).map(([key, { value, change, trend }]) => {
+ {Object.entries(metrics)
+ .filter(([key]) => icons[key] && labels[key])
+ .map(([key, { value = 0, change = 0, trend = "down" }]) => {
const Icon = icons[key];🧰 Tools
🪛 ast-grep (0.43.0)
[warning] 32-34: A list component should have a key to prevent re-rendering
Context:
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(list-component-needs-key)
[warning] 33-33: A list component should have a key to prevent re-rendering
Context:
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(list-component-needs-key)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/Dashboard/MetricCards.jsx` around lines 23 - 35, The code in
the MetricCards map function accesses icons[key] without validating that the key
exists in the icons object, which causes a render crash when unexpected API keys
are encountered. Add a guard condition after assigning Icon to check if it is
undefined and either skip rendering that metric card or provide a fallback icon.
Alternatively, filter Object.entries(metrics) to only include keys that have
corresponding entries in the icons object before mapping over them.
| if (initialIngredients && localIngredients.length === 0 && !editMeal) { | ||
| setLocalIngredients(initialIngredients); | ||
| } | ||
| }, [initialIngredients, localIngredients.length, editMeal]); |
There was a problem hiding this comment.
Do not block edit mode when the seed-ingredients query fails.
Line 149 returns ErrorState for any errIngredients, but Line 47 already shows this query is only required when !editMeal. In edit mode, this can prevent users from saving updates even with editMeal data present.
Suggested fix
- if (errIngredients) return <div><DashboardHeader title={editMeal ? "Edit Meal" : "Recipe Builder"} /><ErrorState message="Failed to load recipe data." onRetry={() => window.location.reload()} /></div>;
+ if (errIngredients && !editMeal) return <div><DashboardHeader title={editMeal ? "Edit Meal" : "Recipe Builder"} /><ErrorState message="Failed to load recipe data." onRetry={() => window.location.reload()} /></div>;Also applies to: 149-149
🧰 Tools
🪛 ast-grep (0.43.0)
[warning] 47-47: Avoid using the initial state variable in setState
Context: setLocalIngredients(initialIngredients)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(setstate-same-var)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/Dashboard/RecipeBuilderView.jsx` around lines 47 - 50, The
component returns an ErrorState at line 149 whenever errIngredients exists, but
the useEffect hook at line 47 shows that initialIngredients is only needed when
!editMeal. Modify the condition at line 149 that checks errIngredients to also
verify !editMeal, so the error is only shown in create mode and does not block
users from editing and saving updates when editMeal data is present.
| { method: "post", match: (url) => url.endsWith("/menu/upload"), handler: (config) => { | ||
| const now = new Date(); | ||
| const dateStr = `${now.getDate()} ${now.toLocaleString("en", { month: "short" })} ${now.getFullYear()}`; | ||
| const timeStr = now.toLocaleTimeString("en", { hour: "2-digit", minute: "2-digit" }); | ||
| const newUpload = { | ||
| id: Date.now(), | ||
| filename: config.headers?.["X-File-Name"] || "Uploaded_file.xlsx", | ||
| date: dateStr, | ||
| time: timeStr, | ||
| added: Math.floor(Math.random() * 30) + 1, | ||
| updated: Math.floor(Math.random() * 10), | ||
| status: "Success", | ||
| }; | ||
| dash.mockMenuUploads.unshift(newUpload); | ||
| return { status: 201, data: newUpload }; | ||
| }}, |
There was a problem hiding this comment.
Persist uploaded menu history after POST /menu/upload.
This handler mutates dash.mockMenuUploads but never calls dash.saveMock, so uploaded entries disappear after reload.
💡 Suggested fix
dash.mockMenuUploads.unshift(newUpload);
+ dash.saveMock("menuUploads", dash.mockMenuUploads);
return { status: 201, data: newUpload };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/mocks/handlers.js` around lines 432 - 447, In the handler function for
the POST /menu/upload endpoint, after the line that calls
dash.mockMenuUploads.unshift(newUpload) to add the new upload entry, add a call
to dash.saveMock() immediately following it to persist the changes to storage so
that uploaded menu entries are retained after page reload.
| { method: "post", match: (url) => url.endsWith("/menu/items"), handler: (config) => { | ||
| const body = JSON.parse(config.data || "{}"); | ||
| const newItem = { | ||
| id: Date.now(), | ||
| name: body.name || "Unnamed Meal", | ||
| image: body.image || "", | ||
| category: body.category || "Mixed", | ||
| price: parseFloat(body.price) || 0, | ||
| calories: body.calories || "0g", | ||
| protein: body.protein || "0g", | ||
| fat: body.fat || "0g", | ||
| sugar: body.sugar || "0g", | ||
| rating: 0, | ||
| status: "Active" | ||
| }; | ||
| dash.mockMenuItems.push(newItem); | ||
| return { status: 201, data: newItem }; | ||
| }}, | ||
| { method: "patch", match: (url) => url.match(/\/menu\/items\/\d+/), handler: (config) => { | ||
| const body = JSON.parse(config.data || "{}"); | ||
| const id = parseInt(config.url?.match(/\/menu\/items\/(\d+)/)?.[1]); | ||
| const idx = dash.mockMenuItems.findIndex(i => i.id === id); | ||
| if (idx !== -1) dash.mockMenuItems[idx] = { ...dash.mockMenuItems[idx], ...body }; | ||
| return { status: 200, data: idx !== -1 ? dash.mockMenuItems[idx] : body }; | ||
| }}, | ||
| { method: "delete", match: (url) => url.match(/\/menu\/items\/\d+/), handler: (config) => { | ||
| const id = parseInt(config.url?.match(/\/menu\/items\/(\d+)/)?.[1]); | ||
| const idx = dash.mockMenuItems.findIndex(i => i.id === id); | ||
| if (idx !== -1) dash.mockMenuItems.splice(idx, 1); | ||
| return { status: 200, data: { message: "Deleted" } }; | ||
| }}, | ||
|
|
||
| // ────────────────────────────────────────────── | ||
| // RECIPE BUILDER | ||
| // ────────────────────────────────────────────── | ||
| { method: "get", match: (url) => url.endsWith("/recipes/ingredients"), handler: () => ({ status: 200, data: dash.mockRecipeIngredients }) }, | ||
| { method: "post", match: (url) => url.endsWith("/recipes"), handler: (config) => { | ||
| const body = JSON.parse(config.data || "{}"); | ||
| const newItem = { | ||
| id: Date.now(), | ||
| name: body.name || "Unnamed Meal", | ||
| category: body.category || "Mixed", | ||
| price: parseFloat(body.price) || 0, | ||
| calories: parseInt(body.calories) || 0, | ||
| protein: body.protein || "0g", | ||
| fat: body.fat || "0g", | ||
| sugar: body.sugar || "0g", | ||
| rating: 0, | ||
| status: "Active" | ||
| }; | ||
| dash.mockMenuItems.push(newItem); | ||
| return { status: 201, data: newItem }; | ||
| }}, | ||
|
|
||
|
|
||
|
|
||
| // ────────────────────────────────────────────── | ||
| // INGREDIENTS | ||
| // ────────────────────────────────────────────── | ||
| { method: "get", match: (url) => url.endsWith("/ingredients/metrics"), handler: () => ({ status: 200, data: dash.mockIngredientsMetrics }) }, | ||
| { method: "get", match: (url) => url.includes("/ingredients") && !url.includes("metrics") && !url.match(/\/ingredients\/\d+/), handler: () => ({ status: 200, data: dash.mockIngredients }) }, | ||
| { method: "post", match: (url) => url.endsWith("/ingredients"), handler: (config) => { | ||
| const body = JSON.parse(config.data || "{}"); | ||
| const newIngredient = { id: Date.now(), ...body }; | ||
| dash.mockIngredients.push(newIngredient); | ||
| return { status: 201, data: newIngredient }; | ||
| }}, | ||
| { method: "patch", match: (url) => url.match(/\/ingredients\/\d+/), handler: (config) => { | ||
| const body = JSON.parse(config.data || "{}"); | ||
| const id = parseInt(config.url?.match(/\/ingredients\/(\d+)/)?.[1]); | ||
| const idx = dash.mockIngredients.findIndex(i => i.id === id); | ||
| if (idx !== -1) dash.mockIngredients[idx] = { ...dash.mockIngredients[idx], ...body }; | ||
| return { status: 200, data: idx !== -1 ? dash.mockIngredients[idx] : body }; | ||
| }}, | ||
| { method: "delete", match: (url) => url.match(/\/ingredients\/\d+/), handler: (config) => { | ||
| const id = parseInt(config.url?.match(/\/ingredients\/(\d+)/)?.[1]); | ||
| const idx = dash.mockIngredients.findIndex(i => i.id === id); | ||
| if (idx !== -1) dash.mockIngredients.splice(idx, 1); | ||
| return { status: 200, data: { message: "Deleted" } }; | ||
| }}, | ||
| { method: "post", match: (url) => url.endsWith("/ingredients/upload"), handler: () => ({ status: 200, data: { success: true, count: 15 } }) }, |
There was a problem hiding this comment.
Persist menu/ingredients mutations to keep mock state durable.
Menu and ingredient create/update/delete handlers mutate arrays in memory only. Without dash.saveMock, state is not durable across refresh/session.
💡 Suggested fix pattern
{ method: "post", match: (url) => url.endsWith("/menu/items"), handler: (config) => {
...
dash.mockMenuItems.push(newItem);
+ dash.saveMock("menuItems", dash.mockMenuItems);
return { status: 201, data: newItem };
}},
{ method: "patch", match: (url) => url.match(/\/menu\/items\/\d+/), handler: (config) => {
...
if (idx !== -1) dash.mockMenuItems[idx] = { ...dash.mockMenuItems[idx], ...body };
+ dash.saveMock("menuItems", dash.mockMenuItems);
return { status: 200, data: idx !== -1 ? dash.mockMenuItems[idx] : body };
}},
{ method: "delete", match: (url) => url.match(/\/menu\/items\/\d+/), handler: (config) => {
...
if (idx !== -1) dash.mockMenuItems.splice(idx, 1);
+ dash.saveMock("menuItems", dash.mockMenuItems);
return { status: 200, data: { message: "Deleted" } };
}},
{ method: "post", match: (url) => url.endsWith("/ingredients"), handler: (config) => {
...
dash.mockIngredients.push(newIngredient);
+ dash.saveMock("ingredients", dash.mockIngredients);
return { status: 201, data: newIngredient };
}},
{ method: "patch", match: (url) => url.match(/\/ingredients\/\d+/), handler: (config) => {
...
if (idx !== -1) dash.mockIngredients[idx] = { ...dash.mockIngredients[idx], ...body };
+ dash.saveMock("ingredients", dash.mockIngredients);
return { status: 200, data: idx !== -1 ? dash.mockIngredients[idx] : body };
}},
{ method: "delete", match: (url) => url.match(/\/ingredients\/\d+/), handler: (config) => {
...
if (idx !== -1) dash.mockIngredients.splice(idx, 1);
+ dash.saveMock("ingredients", dash.mockIngredients);
return { status: 200, data: { message: "Deleted" } };
}},🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/mocks/handlers.js` around lines 454 - 534, The menu and ingredients CRUD
handlers are mutating mock arrays in memory without persisting the state. Add a
`dash.saveMock()` call at the end of each handler function that modifies state
to ensure durability across sessions. Specifically, add this call to the POST
handler for `/menu/items`, the PATCH handler for `/menu/items/{id}`, the DELETE
handler for `/menu/items/{id}`, the POST handler for `/recipes`, the POST
handler for `/ingredients`, the PATCH handler for `/ingredients/{id}`, and the
DELETE handler for `/ingredients/{id}`. Each handler should call
`dash.saveMock()` immediately before returning the response object to persist
the mutations.
| export const mapDashboardMetrics = (data) => ({ | ||
| totalOrders: { | ||
| value: data.totalOrders?.value || 0, | ||
| change: data.totalOrders?.change || 0, | ||
| trend: data.totalOrders?.trend || "up", | ||
| }, | ||
| totalCustomers: { | ||
| value: data.totalCustomers?.value || 0, | ||
| change: data.totalCustomers?.change || 0, | ||
| trend: data.totalCustomers?.trend || "down", | ||
| }, | ||
| totalRevenue: { | ||
| value: data.totalRevenue?.value || 0, | ||
| change: data.totalRevenue?.change || 0, | ||
| trend: data.totalRevenue?.trend || "up", | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Harden top-level object mappers against null payloads.
At Line 12, Line 94, and Line 186, data is dereferenced without a fallback. A null/undefined response body will throw and break dashboard rendering.
Suggested fix
-export const mapDashboardMetrics = (data) => ({
+export const mapDashboardMetrics = (data = {}) => ({
totalOrders: {
value: data.totalOrders?.value || 0,
@@
-export const mapOrdersMetrics = (data) => ({
+export const mapOrdersMetrics = (data = {}) => ({
totalOrders: data.totalOrders || 0,
@@
-export const mapIngredientsMetrics = (data) => ({
+export const mapIngredientsMetrics = (data = {}) => ({
total: data.total || 0,Also applies to: 94-107, 186-193
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/services/mappers/dashboardMappers.js` around lines 12 - 28, The
mapDashboardMetrics function (and similarly the functions at lines 94 and 186)
directly dereferences the data parameter without checking if it is null or
undefined, which will cause the dashboard rendering to break if a null/undefined
payload is received. Add a guard clause at the beginning of each mapper function
that checks if data is null or undefined and returns a default object with
zero/default values for all metrics if the data is falsy, otherwise proceed with
the existing destructuring logic.
| // Make real API request to place the order | ||
| const response = await placeOrder({ | ||
| items: [...state.items], | ||
| totalAmount: state.totalAmount, | ||
| deliveryFee: state.getDeliveryFee(), | ||
| finalTotal: totalWithDelivery, | ||
| customerDetails: state.customerDetails, | ||
| paymentMethod: state.paymentMethod, | ||
| note: state.note | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Locate placeOrder implementation
fd -i 'order.service*' src/services
rg -n -C4 '\bplaceOrder\b' src/services
# 2) Inspect return shape (AxiosResponse vs already-unwrapped data)
rg -n -C6 'placeOrder|api\.post\(|axios\.post\(|\.then\(' src/services
# 3) Check whether axios client sets a timeout
rg -n -C4 'axios\.create|timeout' src/services src/libRepository: Revive-Graduation-Project/Revive-Front-End
Length of output: 10914
🏁 Script executed:
# Get the full api.js file to see complete axios configuration including timeout
cat -n src/services/api.jsRepository: Revive-Graduation-Project/Revive-Front-End
Length of output: 6028
🏁 Script executed:
# Get the context around line 347-356 in orderStore.js to see how the response is used
sed -n '347,370p' src/store/orderStore.jsRepository: Revive-Graduation-Project/Revive-Front-End
Length of output: 1068
Add a timeout to the axios client to prevent indefinite loading states.
The code correctly accesses response.data (line 359 confirms this pattern). However, src/services/api.js does not configure a timeout on the axios instance (line 50–58). This risks indefinite requests if the network hangs, leaving the UI in a frozen state.
Add a timeout property to axios.create() in src/services/api.js:
Example fix
export const api = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || 'https://api.example.com',
withCredentials: true,
headers: {
'Content-Type': 'application/json',
},
timeout: 30000, // 30 seconds
...(USE_MOCK && { adapter: mockAdapter }),
});🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/store/orderStore.js` around lines 347 - 356, The axios client instance
created in src/services/api.js lacks a timeout configuration, which can cause
indefinite loading states if network requests hang. Add a timeout property to
the axios.create() call in src/services/api.js (around lines 50–58) and set it
to an appropriate value such as 30000 milliseconds. This ensures that requests
will abort if they exceed the specified duration, preventing the UI from
remaining frozen during network issues.
| const newOrder = { | ||
| id: orderId, | ||
| date: new Date().toISOString(), | ||
| id: response.data.id || Math.floor(10000 + Math.random() * 90000).toString(), | ||
| date: response.data.createdAt || new Date().toISOString(), |
There was a problem hiding this comment.
Do not fabricate order IDs when API omits them.
Line 359 generates a random fallback ID, then Line 373 persists it to payment history. That can desynchronize transaction records from the real backend order identity.
Suggested fix
- const newOrder = {
- id: response.data.id || Math.floor(10000 + Math.random() * 90000).toString(),
+ if (!response?.data?.id) {
+ throw new Error("Invalid order response: missing order id");
+ }
+
+ const newOrder = {
+ id: String(response.data.id),
date: response.data.createdAt || new Date().toISOString(),
items: [...state.items],Also applies to: 372-374
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/store/orderStore.js` around lines 358 - 360, The newOrder object in
orderStore.js is generating a random fallback ID when response.data.id is
missing, and this fabricated ID gets persisted to payment history, causing
desynchronization with the backend. Remove the random ID generation fallback
(the Math.floor(10000 + Math.random() * 90000).toString() expression) from the
id property assignment and instead handle the missing ID case by throwing an
error or rejecting the operation to ensure only real backend order IDs are used
in the order object creation and subsequent persistence.
Summary by CodeRabbit