Skip to content

Feature/chef overflow - #36

Merged
ibrahim607 merged 11 commits into
devfrom
feature/chef-overflow
Jun 26, 2026
Merged

Feature/chef overflow#36
ibrahim607 merged 11 commits into
devfrom
feature/chef-overflow

Conversation

@M7mednsr

@M7mednsr M7mednsr commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Added comprehensive staff/chef dashboard with authenticated access for authorized users.
    • Implemented dashboard overview with key metrics, revenue charts, and category analytics.
    • Added Orders management view with status tracking and filtering capabilities.
    • Created Chef Menu management section for menu item creation, editing, and deletion.
    • Introduced Live Kitchen view with real-time order status management via Kanban board.
    • Added Ingredients inventory management with upload functionality.
    • Created Menu Management section supporting spreadsheet uploads.
    • Implemented Recipe Builder for meal creation and editing.
    • Added real-time notifications and dashboard updates across all staff views.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cc9e532e-7735-496d-b29e-2378866f990c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/chef-overflow

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 win

Fix 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 win

Align supported-file hint with actual validation rules.

Line 157 says xlsx,csv, but Lines 52-53 and 153 also allow xls.

🤖 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 win

Use the correct colSpan for 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 win

Remove 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 win

Fix 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 win

Clear file input value so the same file can be selected again.

After a successful upload, selectedFile is reset (Line 83) but the input value remains unchanged (Line 153), so choosing the same file again may not fire onChange.

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 win

Pass change into CircleMetric to avoid always showing 0.00%.

Line 201 omits change, but Line 42 renders it; all cards currently display the default 0.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 win

Remove unused catch binding to satisfy linting.

Line 15 defines e but never uses it, matching the reported no-unused-vars error.

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 win

Avoid pointer cursor on disabled buttons.

Line 4 applies cursor: pointer to 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 win

Revoke createObjectURL previews 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 win

Remove unused maxValue to avoid lint failure.

maxValue is assigned but never read, which matches the no-unused-vars error 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 win

Preserve valid 0 nutrition values in the UI.

Using || "-" hides real zeroes (for example, 0 sugar becomes "-"). Use nullish coalescing so only null/undefined fallback.

💡 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 win

Fix 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 win

Fix 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 win

Make 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 win

Prefer 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 win

Make 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 win

Add 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 win

Make skeleton layout responsive for smaller viewports.

DashboardPageSkeleton currently 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 win

Add an accessible name to the notification button.

The bell button is icon-only; add aria-label so 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6a2db59 and ffd40bd.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • public/images/chef-avatar.png is excluded by !**/*.png
📒 Files selected for processing (62)
  • package.json
  • src/App.jsx
  • src/Layout/DashboardLayout.jsx
  • src/Layout/index.js
  • src/components/Dashboard/ChefMenuView.jsx
  • src/components/Dashboard/CustomerReviews.jsx
  • src/components/Dashboard/DashboardHeader.jsx
  • src/components/Dashboard/DashboardSidebar.jsx
  • src/components/Dashboard/DashboardView.jsx
  • src/components/Dashboard/IngredientsView.jsx
  • src/components/Dashboard/InventoryAlerts.jsx
  • src/components/Dashboard/LiveKitchenView.jsx
  • src/components/Dashboard/MenuManagementView.jsx
  • src/components/Dashboard/MetricCards.jsx
  • src/components/Dashboard/OrderTypes.jsx
  • src/components/Dashboard/OrdersView.jsx
  • src/components/Dashboard/RecentActivity.jsx
  • src/components/Dashboard/RecipeBuilderView.jsx
  • src/components/Dashboard/RevenueChart.jsx
  • src/components/Dashboard/TopCategories.jsx
  • src/components/Dashboard/TrendingMenus.jsx
  • src/components/Dashboard/shared/ConfirmModal.jsx
  • src/components/Dashboard/shared/DashboardSkeleton.jsx
  • src/components/Dashboard/shared/DishDetailsModal.jsx
  • src/components/Dashboard/shared/EmptyState.jsx
  • src/components/Dashboard/shared/ErrorState.jsx
  • src/components/Dashboard/shared/IngredientModal.jsx
  • src/components/Dashboard/shared/MenuModal.jsx
  • src/components/Dashboard/shared/OrderDetailsModal.jsx
  • src/components/Dashboard/shared/OrdersOverviewChart.jsx
  • src/components/Dashboard/shared/SortMenu.jsx
  • src/components/Dashboard/shared/StatusBadge.jsx
  • src/components/Dashboard/shared/TimeFilter.jsx
  • src/components/Dashboard/shared/useToast.jsx
  • src/components/ProtectedRoute.jsx
  • src/components/StaffRoute.jsx
  • src/components/ui/RegularFoodCard.jsx
  • src/hooks/dashboard/useDashboard.js
  • src/hooks/dashboard/useDashboardRealtime.js
  • src/hooks/dashboard/useIngredients.js
  • src/hooks/dashboard/useKitchenOrders.js
  • src/hooks/dashboard/useMenuItems.js
  • src/hooks/dashboard/useMenuUploads.js
  • src/hooks/dashboard/useOrders.js
  • src/index.css
  • src/lib/queryClient.js
  • src/main.jsx
  • src/mocks/dashboardMock.js
  • src/mocks/handlers.js
  • src/pages/Dashboard/ChefMenu.jsx
  • src/pages/Dashboard/Dashboard.jsx
  • src/pages/Dashboard/Ingredients.jsx
  • src/pages/Dashboard/LiveKitchen.jsx
  • src/pages/Dashboard/MenuManagement.jsx
  • src/pages/Dashboard/Orders.jsx
  • src/pages/Dashboard/RecipeBuilder.jsx
  • src/pages/Home/Home.jsx
  • src/pages/index.js
  • src/services/dashboardService.js
  • src/services/mappers/dashboardMappers.js
  • src/store/orderStore.js
  • src/store/toastStore.js
💤 Files with no reviewable changes (1)
  • src/pages/Home/Home.jsx

Comment on lines +146 to +149
const categoryCounts = categoryItems.map(cat => ({
...cat,
count: allItems.filter(i => i.category?.toLowerCase() === cat.name?.toLowerCase()).length || cat.count,
}));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
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.

Comment on lines +53 to +56
onError={(e) => {
e.currentTarget.style.display = "none";
e.currentTarget.nextSibling.style.display = "flex";
}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment on lines +91 to +97
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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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

Comment on lines +23 to +35
{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>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment on lines +47 to +50
if (initialIngredients && localIngredients.length === 0 && !editMeal) {
setLocalIngredients(initialIngredients);
}
}, [initialIngredients, localIngredients.length, editMeal]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment thread src/mocks/handlers.js
Comment on lines +432 to +447
{ 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 };
}},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment thread src/mocks/handlers.js
Comment on lines +454 to +534
{ 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 } }) },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment on lines +12 to +28
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",
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment thread src/store/orderStore.js
Comment on lines +347 to +356
// 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
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 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/lib

Repository: 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.js

Repository: 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.js

Repository: 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.

Comment thread src/store/orderStore.js
Comment on lines 358 to +360
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(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants