addStatisticsAndChart,fixbugAdminpage - #12
Conversation
📝 WalkthroughWalkthroughThis PR adds a statistics dashboard (page, sidebar, chart, hook, backend controllers/routes), introduces skeleton loading placeholders for cards/filters, expands equipment management with bulk editing and import confirmation flows, adds confirmation-dialog save/update flows to admin category/location/user pages, and reworks the ticket detail image gallery, plus various backend and styling adjustments. ChangesStatistics Dashboard Feature
Skeleton Loading UI
Equipment Management and Import
Admin Confirmation Flows for Categories, Locations, and Users
Ticket Detail Image Gallery and Status Handling
Miscellaneous Dependency, Styling, and Cleanup Changes
Estimated code review effort: 4 (Complex) | ~75 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant ImportEquipments
participant ConfirmButton
participant useImportEquipments
participant Server
User->>ImportEquipments: select file, click upload
ImportEquipments->>ConfirmButton: open confirmation modal
User->>ConfirmButton: confirm
ConfirmButton->>useImportEquipments: uploadFile()
useImportEquipments->>Server: POST file
Server-->>useImportEquipments: response.data (errors or success)
useImportEquipments-->>ImportEquipments: errorList or refetch()
ImportEquipments-->>User: show error modal or cleared file state
sequenceDiagram
participant Statistics
participant useStatistics
participant statisticController
participant Prisma
Statistics->>useStatistics: call with selectedYear, selectedMonth
useStatistics->>statisticController: GET getMostCategoriesOfProblems
useStatistics->>statisticController: GET getMostUpvotedTickets
useStatistics->>statisticController: GET getTicket-stats
statisticController->>Prisma: query ticket aggregates
Prisma-->>statisticController: aggregated data
statisticController-->>useStatistics: JSON response
useStatistics-->>Statistics: mostCategoriesOfProblems, mostUpvotedTickets, ticketStats
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 18
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
client/src/App.jsx (1)
44-51: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winRemoved wildcard redirect leaves unmatched authenticated paths dangling.
Per the change details, the prior logged-in catch-all route (
<Route path="*" element={<Navigate to="/" />} />) was removed while adding the new/statistics/*route. Now, any unmatched path for a logged-in user (typos, stale bookmarks, removed routes) will render nothing inside<Routes>instead of redirecting home, unlike the logged-out branch which still has<Route path="*" element={<Login />} />.🩹 Suggested fix
<Route path="/statistics/*" element={<Statistics />} /> + <Route path="*" element={<Navigate to="/" />} />🤖 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 `@client/src/App.jsx` around lines 44 - 51, The authenticated route set in App’s <Routes> is missing the previous catch-all redirect, so unmatched logged-in paths now fall through with no render. Restore a wildcard route in the authenticated branch using the existing routing pattern in App.jsx, and make it redirect to the home/dashboard route the same way the logged-out branch handles unknown paths. Keep the new /statistics/* route intact while ensuring all other unmatched authenticated paths are covered.client/src/pages/DetailTicket.jsx (1)
21-41: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAdd labels/styles for all ticket statuses
DetailTicket.jsxonly mapspending,in_progress, andresolved, but the backend also usescanceled,rejected, andduplicate. Those statuses will render an empty badge here and have no matching.ticketStatus.*styles inDetailTicket.css. Add the missing entries or a fallback label.🤖 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 `@client/src/pages/DetailTicket.jsx` around lines 21 - 41, DetailTicket.jsx only handles a subset of ticket statuses, so canceled, rejected, and duplicate can render with empty labels and no matching styling. Update the statusLabels mapping in DetailTicket.jsx to include those missing statuses or add a safe fallback label, and make sure DetailTicket.css includes matching .ticketStatus classes for each status used by ticket.ticketStatus.client/src/hooks/useUsers.js (1)
7-16: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
fetchUsersnow rethrows but its mount-time call is unguarded.
fetchUserspreviously only logged errors; it now rethrows (Line 14). TheuseEffectat Lines 27-29 callsfetchUsers()without.catch/await-try, so a failed initial fetch becomes an unhandled promise rejection.🐛 Proposed fix
useEffect(() => { - fetchUsers(); + fetchUsers().catch(() => {}); }, [fetchUsers]);Also applies to: 27-29
🤖 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 `@client/src/hooks/useUsers.js` around lines 7 - 16, The initial mount-time call to fetchUsers in useUsers is now unsafe because fetchUsers rethrows errors, so the useEffect-triggered call can produce an unhandled promise rejection. Update the useEffect that invokes fetchUsers to handle the returned promise explicitly, either by awaiting it inside an async helper with try/catch or by attaching a .catch handler, while keeping fetchUsers’ current error propagation behavior intact.
🟡 Minor comments (6)
client/src/hooks/useStatistics.js-20-27 (1)
20-27: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winNo fallback if
response.data.datais missing.
setMostUpvotedTickets(response.data.data)has no guard; if the backend response shape changes ordatais absent, this sets state toundefined, andStatistics.jsximmediately calls.map()on it, crashing the render.🩹 Suggested fix
- setMostUpvotedTickets(response.data.data); + setMostUpvotedTickets(response.data.data || []);🤖 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 `@client/src/hooks/useStatistics.js` around lines 20 - 27, The fetchMostUpvotedTickets logic in useStatistics should guard against missing response.data.data before updating state, since setting it to undefined causes Statistics.jsx to fail when it calls .map(). Update the axios response handling in fetchMostUpvotedTickets so it always stores a safe array fallback when the backend payload shape is absent or changed, and keep the error handling in the same function consistent with that fallback.server/controllers/statisticController.js-64-120 (1)
64-120: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winMissing/invalid handling when
monthquery param is absent.If
req.query.monthis undefined (not'all'and not a numeric string), execution falls into theelsebranch whereparseInt(undefined)yieldsNaN, producing anInvalid Daterange. The Prismawherefilter then silently matches nothing, so the endpoint returns all-zero stats instead of surfacing an error or falling back to a sensible default (e.g., current month).🐛 Proposed fix
const { year, month } = req.query; const targetYear = year ? parseInt(year) : new Date().getFullYear(); + const targetMonthRaw = month === undefined ? new Date().getMonth().toString() : month; - if (month === 'all') { + if (targetMonthRaw === 'all') {🤖 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 `@server/controllers/statisticController.js` around lines 64 - 120, The getTicketStats controller currently falls into the month-specific branch even when req.query.month is missing, causing parseInt(month) to become NaN and the Prisma date range to use Invalid Date. Update getTicketStats to validate the month input before building the date window: if month is absent or not a valid numeric month, either default to the current month or return a clear 400 response, and keep the existing month === 'all' path unchanged.client/src/pages/adminPage/UserManagement.jsx-41-70 (1)
41-70: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winMissing check that a user row is selected before confirming role update.
handleConfirmUpdateRoleonly validatesformData.userRole. The role<select>(Lines 135-144) isn't disabled based onselectedId, so an admin can pick a role without clicking a table row first, sendingupdateRoleUser({ userId: null, ... })and getting a generic backend error instead of a clear client-side message.🛡️ Proposed fix
const handleConfirmUpdateRole = () => { - + if (!selectedId) { + setError('กรุณาเลือกผู้ใช้งานที่ต้องการแก้ไข'); + return; + } if (!formData.userRole) { setError('กรุณาเลือกข้อมูลที่ต้องการจะอัพเดท') return; } setConfirmSubmit({ isOpen: true, }) }🤖 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 `@client/src/pages/adminPage/UserManagement.jsx` around lines 41 - 70, The role update flow in UserManagement.jsx only checks formData.userRole, so handleConfirmUpdateRole can proceed even when no table row is selected and selectedId is null. Update handleConfirmUpdateRole to also validate that a user row has been selected before opening confirmSubmit, and show a clear client-side error if selectedId is missing. Use the existing selectedId, handleConfirmUpdateRole, and handleUpdateRole flow to prevent calling updateRoleUser with an invalid userId.client/src/pages/adminPage/Categories.jsx-146-160 (1)
146-160: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winConfirm dialog always says "add" even when updating a category.
title/messageare hardcoded to "ยืนยันการเพิ่มประเภทปัญหา" (confirm adding), buthandleSaveCategoryalso handles updates (whenformData.ticketCtgIdis set). Users updating an existing category see a misleading confirmation.✏️ Proposed fix
<ConfirmButton isOpen={confirmSubmit.isOpen} - title="ยืนยันการเพิ่มประเภทปัญหา" - message="คุณแน่ใจหรือไม่ว่าต้องการเพิ่มประเภทปัญหาใหม่นี้? โปรดตรวจสอบข้อมูลให้ถูกต้องก่อนยืนยัน" + title={formData.ticketCtgId ? "ยืนยันการแก้ไขประเภทปัญหา" : "ยืนยันการเพิ่มประเภทปัญหา"} + message={formData.ticketCtgId ? "คุณแน่ใจหรือไม่ว่าต้องการแก้ไขประเภทปัญหานี้?" : "คุณแน่ใจหรือไม่ว่าต้องการเพิ่มประเภทปัญหาใหม่นี้?"}🤖 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 `@client/src/pages/adminPage/Categories.jsx` around lines 146 - 160, The ConfirmButton in Categories.jsx always shows add-category text even though handleSaveCategory also updates existing categories. Make the dialog title/message dynamic based on whether formData.ticketCtgId is present, so it shows add wording for new categories and update wording for edits. Use the existing symbols handleSaveCategory, formData.ticketCtgId, and ConfirmButton to keep the confirm copy aligned with the actual action.client/src/pages/adminPage/LocationManagement.jsx-39-63 (1)
39-63: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winEnable-guards never trigger — status values use
'active', not'enable'.Lines 50 and 55 check
value === 'enable', but the corresponding<select>options (Lines 494-495, 511-512, 528-529) only ever emit'active'/'inactive'. These cascade-prevention branches are unreachable dead code. In practice thedisabledprops on the selects already block invalid transitions, but this guard is a broken second line of defense.🐛 Proposed fix
- if (field === 'roomStatus' && value === 'enable') { + if (field === 'roomStatus' && value === 'active') { if (newData.locationStatus === 'inactive' || newData.floorStatus === 'inactive') { return prev; } } - if (field === 'floorStatus' && value === 'enable') { + if (field === 'floorStatus' && value === 'active') { if (newData.locationStatus === 'inactive') { return prev; } }🤖 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 `@client/src/pages/adminPage/LocationManagement.jsx` around lines 39 - 63, The cascade-prevention checks in handleStatusChange are unreachable because the status selects only produce 'active' and 'inactive', not 'enable'. Update the guards in handleStatusChange to match the actual option values emitted by the locationStatus, floorStatus, and roomStatus selects, or remove the dead branches if the disabled props already enforce the valid transitions. Use the existing handleStatusChange logic and the related select fields as the location points for the fix.client/src/pages/adminPage/AssetManagement.jsx-328-336 (1)
328-336: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winRow highlight never activates from checkbox selection.
selectedEquipmentsonly ever holds strings (seehandleSelectOne/handleSelectAll), but this comparison usesitem.equipmentId(unconverted) instead ofString(item.equipmentId)like the checkbox'scheckedprop does at line 342. Theselectedclass will never apply when a row is selected via checkbox.🔧 Suggested fix
- className={`data-layout-row ${selectedEquipments.includes(item.equipmentId) || selectedId === item.equipmentId ? 'selected' : ''}`} + className={`data-layout-row ${selectedEquipments.includes(String(item.equipmentId)) || selectedId === item.equipmentId ? 'selected' : ''}`}🤖 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 `@client/src/pages/adminPage/AssetManagement.jsx` around lines 328 - 336, The row highlight logic in the table row’s className is comparing selectedEquipments against item.equipmentId without converting it, while handleSelectOne/handleSelectAll store strings and the checkbox checked prop already uses String(item.equipmentId). Update the selected class condition in AssetManagement.jsx to use the same string form as the checkbox so checkbox-driven selections can activate the row highlight consistently.
🧹 Nitpick comments (13)
client/src/components/CardFinishProblem.jsx (1)
44-70: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated
SkeletonThemeconfig across files.The same
baseColor="#ebebeb" highlightColor="#ccc7c7" duration={2}triple is repeated verbatim inCardPendingProblem.jsxandFilterProblem.jsx. Consider extracting a sharedAppSkeletonThemewrapper or a constants file so theme tweaks don't require touching three files.♻️ Example extraction
+// client/src/components/AppSkeletonTheme.jsx +import { SkeletonTheme } from 'react-loading-skeleton'; +export const AppSkeletonTheme = ({ children }) => ( + <SkeletonTheme baseColor="`#ebebeb`" highlightColor="`#ccc7c7`" duration={2}> + {children} + </SkeletonTheme> +);🤖 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 `@client/src/components/CardFinishProblem.jsx` around lines 44 - 70, The SkeletonTheme configuration is duplicated in SkeletonCardFinishProblem and the other skeleton components, so extract the shared baseColor/highlightColor/duration values into a reusable AppSkeletonTheme wrapper or constants module. Update the skeleton components to consume that shared theme setup instead of hardcoding the same props in each file, so changes only need to be made in one place.client/src/components/CardPendingProblem.css (1)
88-142: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueNewly added skeleton CSS classes appear unused.
.header-skleton-card,.skleton-img,.skleton-text,.skleton-title-card h3.skleton-text, and.skleton-statusare defined here, butCardPendingSkeletoninCardPendingProblem.jsxonly applies.skleton-card-pendingand reuses the real card's classes (header-card,title-card,operator,location-text,description-text) withreact-loading-skeleton's<Skeleton>for the placeholder boxes. These.skleton-*rules are dead code.🤖 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 `@client/src/components/CardPendingProblem.css` around lines 88 - 142, The newly added skeleton-specific CSS rules are unused because CardPendingSkeleton in CardPendingProblem.jsx only uses skleton-card-pending plus the existing real card class names and react-loading-skeleton placeholders. Remove the dead .header-skleton-card, .skleton-img, .skleton-text, .skleton-title-card h3.skleton-text, and .skleton-status selectors from CardPendingProblem.css, or if they are meant to be used, update CardPendingSkeleton to apply those class names consistently.client/src/components/StatisticsSidebar.jsx (1)
5-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
activeTab/onTabChangeprops are unused; dead code left in place.
activeTabandonTabChangeare accepted but never referenced in the component body — active-link styling is delegated entirely toNavLink. Combined with the commented-outmenuItemsarray, this looks like leftover state from a prior tab-based (non-routed) implementation. Keeping unused props on the public component API is misleading for future maintainers (e.g.,Statistics.jsxstill wiresactiveTab/handleTabChangeinto this component believing it does something).♻️ Suggested cleanup
-export const StatisticsSidebar = ({ activeTab, onTabChange }) => { - // const menuItems = [ - // { id: 'statistic-all-prblem', label: 'สถิติปัญหา' }, - // { id: 'all', label: 'ประเภทปัญหาที่รับแจ้งมากสุด' }, - // { id: 'mine', label: 'สถานที่รับแจ้งมากสุด' }, - // { id: 'upvoted', label: 'ปัญหาที่ได้รับ Upvote มากสุด' }, - // ]; - +export const StatisticsSidebar = () => {🤖 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 `@client/src/components/StatisticsSidebar.jsx` around lines 5 - 11, Remove the unused activeTab and onTabChange props from StatisticsSidebar and update the component signature and any callers such as Statistics so they no longer pass tab state that the component does not use. Also clean up the leftover commented-out menuItems block in StatisticsSidebar to reflect the current NavLink-based routing implementation and avoid exposing a misleading tab API.server/controllers/statisticController.js (1)
1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove unused/unrelated import.
createfromnode:domainis never used, andnode:domainis an unrelated legacy Node core module.🤖 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 `@server/controllers/statisticController.js` at line 1, Remove the unused and unrelated node:domain import from statisticController since create is never referenced anywhere in the controller. Update the top-level imports in statisticController to keep only symbols that are actually used, and verify no remaining references depend on create or domain.server/routes/managementRoutes.js (1)
55-55: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueInconsistent route naming convention.
/getTicket-statsmixes camelCase and kebab-case, unlike the sibling/getMostCategoriesOfProblemsand/getMostUpvotedTicketsroutes.🤖 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 `@server/routes/managementRoutes.js` at line 55, The route path in the managementRoutes router uses mixed naming conventions, so update the getTicketStats endpoint to follow the same pattern as the sibling routes and use one consistent style across these route definitions. Adjust the router.get registration for getTicketStats so its URL naming matches the existing getMostCategoriesOfProblems and getMostUpvotedTickets endpoints.client/src/services/equipmentService.js (1)
5-14: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUnused destructured variable
data.
equipmentIdanddataare destructured butdatais never used — the fullpayload(includingequipmentId) is sent instead. Either drop the destructure or usedataintentionally in the request body.🔧 Suggested cleanup
updateEquipment: async (payload) => { try { - const { equipmentId, ...data } = payload; const response = await axios.put('/api/manage/updateEquipment', payload); return response.data;🤖 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 `@client/src/services/equipmentService.js` around lines 5 - 14, The `updateEquipment` method destructures `equipmentId` and `data`, but `data` is never used because the full `payload` is sent in the axios request. Fix this by either removing the unused destructuring in `updateEquipment` or changing the request body to use `data` intentionally, and keep the behavior consistent with the `axios.put('/api/manage/updateEquipment', ...)` call.client/src/pages/adminPage/AssetManagement.jsx (1)
36-36: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove leftover debug
console.logstatements.Multiple debug logs (
'ssss',"เลือก","Payload แบบ Array...",'onemode') were left in; clean these up before merging.Also applies to: 163-163, 201-201, 215-215
🤖 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 `@client/src/pages/adminPage/AssetManagement.jsx` at line 36, Remove the leftover debug console.log statements from AssetManagement.jsx before merging. Clean up all the identified logs in the AssetManagement component and related handlers, including the ones printing EquipmentCtgs, the Thai debug messages, the payload array message, and the onemode value, so no temporary debugging output remains in the final code.client/src/hooks/useEquipment.js (1)
33-40: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the unused
updateEquipmentexport fromuseEquipment
No current caller uses it, andAssetManagement.jsxupdates throughequipmentService.updateEquipmentinstead, so this extra PATCH path is redundant and can drift from the active PUT contract.🤖 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 `@client/src/hooks/useEquipment.js` around lines 33 - 40, The useEquipment hook still defines an unused updateEquipment callback with its own PATCH request, but the active update flow is handled elsewhere through equipmentService.updateEquipment. Remove the updateEquipment function/export from useEquipment and clean up any related references so the hook only exposes the APIs that are actually consumed, keeping the hook aligned with the current update contract.client/src/pages/adminPage/AssetManagement.css (2)
152-156: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDuplicate
.layout-tableselector.
.layout-tableis declared twice with disjoint rule sets; consider merging into a single block for clarity.Also applies to: 175-177
🤖 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 `@client/src/pages/adminPage/AssetManagement.css` around lines 152 - 156, The `.layout-table` selector in AssetManagement.css is duplicated with separate rule blocks, so merge the duplicated styles into one consolidated `.layout-table` definition. Update the CSS near the existing `.layout-table` rules to combine all properties currently split across the two blocks, and remove the redundant declaration so the table styles live in a single place.
135-149: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winScrollbar hidden only for Firefox/legacy Edge/IE, not Chromium/WebKit.
-ms-overflow-styleandscrollbar-widthcover Firefox/old Edge/IE, but Chrome/Safari/new Edge still render the native scrollbar since::-webkit-scrollbarisn't targeted.🎨 Suggested addition
.table-responsive-wrapper { -ms-overflow-style: none; /* ซ่อนใน IE และ Edge */ scrollbar-width: none; /* ซ่อนใน Firefox */ +} +.table-responsive-wrapper::-webkit-scrollbar { + display: none; + /* ซ่อนใน Chrome, Safari, Edge (Chromium) */🤖 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 `@client/src/pages/adminPage/AssetManagement.css` around lines 135 - 149, The scrollbar-hiding styles in the .table-responsive-wrapper rule only cover Firefox and legacy IE/Edge, so Chromium/WebKit browsers still show the native scrollbar. Update the AssetManagement.css styles for .table-responsive-wrapper to also target the WebKit scrollbar pseudo-element so Chrome, Safari, and new Edge hide it consistently alongside the existing -ms-overflow-style and scrollbar-width rules.client/src/components/componentsAdmin/ImportEquipments.jsx (1)
103-131: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winNew error modal lacks dialog accessibility semantics.
The overlay/content div has no
role="dialog",aria-modal="true", initial focus management, or Escape-to-close handling, so keyboard and screen-reader users have no clear entry/exit from this newly introduced full-screen modal.♻️ Suggested addition
{errorList.length > 0 && ( - <div className="custom-modal-overlay"> - <div className="custom-modal-content"> + <div className="custom-modal-overlay" role="presentation"> + <div className="custom-modal-content" role="dialog" aria-modal="true" aria-labelledby="import-error-title">🤖 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 `@client/src/components/componentsAdmin/ImportEquipments.jsx` around lines 103 - 131, The new error modal in ImportEquipments lacks basic dialog accessibility, so update the modal overlay/content to behave like a true dialog. Add dialog semantics and focus handling in the same component that renders errorList, including a dialog role, aria-modal, a label tied to the header, initial focus when it opens, and Escape-to-close support that reuses the existing setErrorList([]) close behavior.server/controllers/EquipmentControllers.js (1)
220-242: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winPartial-success response drops insertion count.
When
errors.length > 0, the response only returns{ errors }even thoughvalidDataToInsertmay have already been persisted viacreateManyabove. The client has no way to know how many rows actually succeeded before the errors occurred, only that "something went wrong."♻️ Suggested addition
if (errors.length > 0) { return res.status(200).json({ - errors: errors + errors: errors, + insertedCount: validDataToInsert.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 `@server/controllers/EquipmentControllers.js` around lines 220 - 242, The partial-success path in the equipment upload handler loses the count of rows that were actually persisted because the `errors.length > 0` response only returns `errors`. Update the upload flow in `EquipmentControllers` so the response in that branch also includes the number of successful inserts from `validDataToInsert` (and, if helpful, a success message), matching the information already returned in the full-success path. Make sure the `createMany` result handling and the final `res.status(200).json(...)` response clearly communicate both successes and failures.client/src/components/componentsAdmin/ImportEquipments.css (1)
161-161: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRename
popInkeyframes to kebab-case.Stylelint flags this as violating
keyframes-name-pattern.🎨 Suggested fix
- animation: popIn 0.3s ease-out forwards; /* เพิ่มลูกเล่นแอนิเมชันเด้งเข้ามา */ + animation: pop-in 0.3s ease-out forwards; /* เพิ่มลูกเล่นแอนิเมชันเด้งเข้ามา */-@keyframes popIn { +@keyframes pop-in {Also applies to: 208-210
🤖 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 `@client/src/components/componentsAdmin/ImportEquipments.css` at line 161, The `popIn` animation name in `ImportEquipments.css` violates the `keyframes-name-pattern` rule, so rename the `@keyframes popIn` definition to a kebab-case name and update every `animation: popIn ...` reference in the same stylesheet to match, including the occurrences near the later keyframes block. Use the existing `popIn` animation usage and keyframes declaration as the symbols to find and replace consistently.Source: Linters/SAST tools
🤖 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 `@client/src/components/CardPendingProblem.css`:
- Around line 20-25: The image styling in the CardPendingProblem CSS is too
broad because the `.img-card, img` selector also targets every bare img on the
page. Update the rule in `CardPendingProblem.css` to scope the
object-fit/object-position/size styles only to `.img-card`, so other images like
the Dashboard empty-state image are not affected.
In `@client/src/hooks/useImportEquipments.js`:
- Around line 45-59: The error handling in useImportEquipments is missing
backend responses that return a singular error field, so users get no feedback
on failures. Update the catch block to check error.response.data.error in
addition to errors and message, and surface it with alert or equivalent so
uploadEquipments failures from EquipmentControllers.js are shown to the user;
keep the existing setErrorList behavior for validation errors and use the same
response parsing flow in useImportEquipments.
- Around line 3-9: `useImportEquipments` is creating its own `useEquipment()`
instance, so the refresh it triggers won’t update the table in
`AssetManagement.jsx`; change `useImportEquipments`/`ImportEquipments` to
receive the parent `refetch` callback as an argument and call that after a
successful upload instead of the local hook instance. Also update the upload
error handling in `useImportEquipments` so the catch block reads and surfaces
`error.response.data.error` (when present) from `uploadEquipments`, ensuring
400/500 responses show a user-facing message instead of being swallowed.
In `@client/src/hooks/useStatistics.js`:
- Around line 7-10: The initial ticketStats state in useStatistics.js is sized
for the weekly view, but Statistics.jsx defaults to the all-months view, causing
the chart data length to mismatch the categories before fetch completes. Update
the initial state in useStatistics so created and resolved default to 12-item
arrays (or otherwise match the all-months display used by Statistics and its
ticketStats?.created/ticketStats?.resolved fallback logic) so the first render
aligns with chartCategories.
In `@client/src/pages/adminPage/AssetManagement.jsx`:
- Around line 24-25: The delete confirmation flow in AssetManagement is broken
because the confirm dialog is commented out and the delete state shape is
inconsistent. Restore the ConfirmButton block so the “ลบ” action can open a
visible confirmation dialog and wire its onConfirm to handleDelete, and change
isDeleteConfirmOpen to use a boolean consistently with its setter/getter usage
so ConfirmButton’s isOpen guard works correctly.
- Around line 178-204: The bulk-update branch in AssetManagement.jsx currently
builds a payload and shows success without actually persisting anything because
the update call is commented out. Add the real bulk update flow by implementing
equipmentService.updateMultipleEquipments and wiring it to the matching backend
endpoint, then call it from the isBulkMode path before setSuccess runs. If the
backend/API is not ready yet, disable or hide the bulk-edit path so the UI does
not report a false success.
- Around line 59-72: The single-select prefill in AssetManagement.jsx is reading
locationId, floorId, and roomId from the wrong object shape, so those fields can
stay empty and later submit invalid numeric values. Update the
selectedItem-to-formData mapping in the single-selection branch to derive these
values from the nested room/floor/location structure used elsewhere in this page
(the same shape coming from equipment), and make sure setFormData always
receives the correctly resolved ids before submit.
In `@client/src/pages/adminPage/Categories.jsx`:
- Around line 54-81: The handleSaveCategory flow in Categories.jsx includes an
unnecessary artificial 1.5s delay before calling IssueCategoryService, which
slows every save without benefit. Remove the setTimeout-based await from
handleSaveCategory so the save goes directly into the
addIssueCategoryApi/updateIssueCategoryApi call path, then keep the existing
success, fetchCategories, and form reset logic unchanged.
In `@client/src/pages/adminPage/LocationManagement.jsx`:
- Around line 247-253: `handleConfirmSaveStatus` is validating the wrong
`formData` fields and opening the confirm dialog without any message. Update the
guard in `LocationManagement` to use the actual state keys for
location/floor/room selection (the same keys used throughout the form state),
and when calling `setConfirmSaveStatus`, include the dialog text so
`ConfirmButton` can render a non-empty `confirmSaveStatus.message`.
- Around line 179-203: Remove the hardcoded 1.5s artificial delay from the
add/save flows so the UI does not pause unnecessarily. In
LocationManagement.jsx, update handleAddNewfloor, handleAddNewLocation, and
handleSaveStatus to proceed directly with their existing payload/service logic
instead of awaiting setTimeout, and keep their loading/error/success handling
unchanged. This will align them with handleAddNewRoom and make all add/save
actions consistent.
- Around line 243-246: The cancel flow in LocationManagement is broken because
handleCancelSubmit references a non-existent setConfirmSubmit state setter, and
the adjacent “ยกเลิก” button is not wired to any handler. Update
handleCancelSubmit to use the correct existing confirm state or remove it if
unnecessary, and attach the cancel button to the proper cancel handler so it
closes the dialog and resets the form. Use the existing confirmSubmitLocation,
confirmSubmitFloor, confirmSubmitRoom, and confirmSaveStatus state patterns to
locate the right confirmation state to update.
In `@client/src/pages/DetailTicket.jsx`:
- Around line 95-102: The “see more” trigger in DetailTicket is missing visible
label text, so the clickable paragraph is undiscoverable; update the
`see-more-text` elements in the before/after image sections to render clear text
inside the `<p>` while keeping the existing `onClick` behavior and styling, and
make sure both the before-images and after-images variants are fixed
consistently.
In `@client/src/pages/Statistics.jsx`:
- Around line 78-169: The nested route in Statistics is using an absolute path
for top-categories, so it will not resolve under the parent /statistics route
like the sidebar expects. Update the Route for top-categories in Statistics.jsx
to use a relative path, matching the existing routing setup used by Routes/Route
and the /statistics/top-locations entry, so the tab renders correctly.
In `@server/controllers/managementControllers.js`:
- Around line 449-471: The updateRoleUsers handler is logging the full req.user
token payload, which can leak sensitive user data into server logs. Remove the
console.log that prints the entire req.user object and keep only minimal,
non-sensitive debugging output if needed, using updateRoleUsers and myUserId as
the key places to adjust. Ensure the role update flow still validates self-edits
and continues to log only safe identifiers such as userId when necessary.
- Around line 459-465: The userRole value is being written directly through
prisma.user.update without any server-side validation, so add an allowlist check
before updating the user. In the management controller that handles this update,
validate req.body.userRole against the permitted role values used by the app,
reject invalid input with an error response, and only pass the sanitized role
into the update call. Keep the fix localized around the userRole handling in
this controller so role-based checks elsewhere remain consistent.
- Around line 59-77: Add a real uniqueness guarantee for locationName instead of
relying on the findFirst check in managementControllers.js. Update the Prisma
model in schema.prisma to mark locationName as unique, generate/apply the
migration so the database enforces it, and then in the create flow around
prisma.location.create handle Prisma error P2002 by returning the same 400-style
duplicate-location response.
In `@server/controllers/statisticController.js`:
- Around line 6-19: The Prisma model accessors in the statistics controller are
using the wrong casing, which will break at runtime because the generated client
exposes camelCase names. Update the queries in statisticController to use
prisma.ticketCategory (and prisma.ticket where applicable) instead of
prisma.TicketCategory / prisma.Ticket, keeping the same include and orderBy
logic.
- Around line 36-50: The Prisma delegate names in statisticController are using
the wrong casing and will fail at runtime. Update the queries in the relevant
controller method to use the generated lowercase delegates on prisma, replacing
prisma.Ticket with prisma.ticket and any prisma.TicketCategory usage with
prisma.ticketCategory, while keeping the existing findMany and related
include/orderBy logic unchanged.
---
Outside diff comments:
In `@client/src/App.jsx`:
- Around line 44-51: The authenticated route set in App’s <Routes> is missing
the previous catch-all redirect, so unmatched logged-in paths now fall through
with no render. Restore a wildcard route in the authenticated branch using the
existing routing pattern in App.jsx, and make it redirect to the home/dashboard
route the same way the logged-out branch handles unknown paths. Keep the new
/statistics/* route intact while ensuring all other unmatched authenticated
paths are covered.
In `@client/src/hooks/useUsers.js`:
- Around line 7-16: The initial mount-time call to fetchUsers in useUsers is now
unsafe because fetchUsers rethrows errors, so the useEffect-triggered call can
produce an unhandled promise rejection. Update the useEffect that invokes
fetchUsers to handle the returned promise explicitly, either by awaiting it
inside an async helper with try/catch or by attaching a .catch handler, while
keeping fetchUsers’ current error propagation behavior intact.
In `@client/src/pages/DetailTicket.jsx`:
- Around line 21-41: DetailTicket.jsx only handles a subset of ticket statuses,
so canceled, rejected, and duplicate can render with empty labels and no
matching styling. Update the statusLabels mapping in DetailTicket.jsx to include
those missing statuses or add a safe fallback label, and make sure
DetailTicket.css includes matching .ticketStatus classes for each status used by
ticket.ticketStatus.
---
Minor comments:
In `@client/src/hooks/useStatistics.js`:
- Around line 20-27: The fetchMostUpvotedTickets logic in useStatistics should
guard against missing response.data.data before updating state, since setting it
to undefined causes Statistics.jsx to fail when it calls .map(). Update the
axios response handling in fetchMostUpvotedTickets so it always stores a safe
array fallback when the backend payload shape is absent or changed, and keep the
error handling in the same function consistent with that fallback.
In `@client/src/pages/adminPage/AssetManagement.jsx`:
- Around line 328-336: The row highlight logic in the table row’s className is
comparing selectedEquipments against item.equipmentId without converting it,
while handleSelectOne/handleSelectAll store strings and the checkbox checked
prop already uses String(item.equipmentId). Update the selected class condition
in AssetManagement.jsx to use the same string form as the checkbox so
checkbox-driven selections can activate the row highlight consistently.
In `@client/src/pages/adminPage/Categories.jsx`:
- Around line 146-160: The ConfirmButton in Categories.jsx always shows
add-category text even though handleSaveCategory also updates existing
categories. Make the dialog title/message dynamic based on whether
formData.ticketCtgId is present, so it shows add wording for new categories and
update wording for edits. Use the existing symbols handleSaveCategory,
formData.ticketCtgId, and ConfirmButton to keep the confirm copy aligned with
the actual action.
In `@client/src/pages/adminPage/LocationManagement.jsx`:
- Around line 39-63: The cascade-prevention checks in handleStatusChange are
unreachable because the status selects only produce 'active' and 'inactive', not
'enable'. Update the guards in handleStatusChange to match the actual option
values emitted by the locationStatus, floorStatus, and roomStatus selects, or
remove the dead branches if the disabled props already enforce the valid
transitions. Use the existing handleStatusChange logic and the related select
fields as the location points for the fix.
In `@client/src/pages/adminPage/UserManagement.jsx`:
- Around line 41-70: The role update flow in UserManagement.jsx only checks
formData.userRole, so handleConfirmUpdateRole can proceed even when no table row
is selected and selectedId is null. Update handleConfirmUpdateRole to also
validate that a user row has been selected before opening confirmSubmit, and
show a clear client-side error if selectedId is missing. Use the existing
selectedId, handleConfirmUpdateRole, and handleUpdateRole flow to prevent
calling updateRoleUser with an invalid userId.
In `@server/controllers/statisticController.js`:
- Around line 64-120: The getTicketStats controller currently falls into the
month-specific branch even when req.query.month is missing, causing
parseInt(month) to become NaN and the Prisma date range to use Invalid Date.
Update getTicketStats to validate the month input before building the date
window: if month is absent or not a valid numeric month, either default to the
current month or return a clear 400 response, and keep the existing month ===
'all' path unchanged.
---
Nitpick comments:
In `@client/src/components/CardFinishProblem.jsx`:
- Around line 44-70: The SkeletonTheme configuration is duplicated in
SkeletonCardFinishProblem and the other skeleton components, so extract the
shared baseColor/highlightColor/duration values into a reusable AppSkeletonTheme
wrapper or constants module. Update the skeleton components to consume that
shared theme setup instead of hardcoding the same props in each file, so changes
only need to be made in one place.
In `@client/src/components/CardPendingProblem.css`:
- Around line 88-142: The newly added skeleton-specific CSS rules are unused
because CardPendingSkeleton in CardPendingProblem.jsx only uses
skleton-card-pending plus the existing real card class names and
react-loading-skeleton placeholders. Remove the dead .header-skleton-card,
.skleton-img, .skleton-text, .skleton-title-card h3.skleton-text, and
.skleton-status selectors from CardPendingProblem.css, or if they are meant to
be used, update CardPendingSkeleton to apply those class names consistently.
In `@client/src/components/componentsAdmin/ImportEquipments.css`:
- Line 161: The `popIn` animation name in `ImportEquipments.css` violates the
`keyframes-name-pattern` rule, so rename the `@keyframes popIn` definition to a
kebab-case name and update every `animation: popIn ...` reference in the same
stylesheet to match, including the occurrences near the later keyframes block.
Use the existing `popIn` animation usage and keyframes declaration as the
symbols to find and replace consistently.
In `@client/src/components/componentsAdmin/ImportEquipments.jsx`:
- Around line 103-131: The new error modal in ImportEquipments lacks basic
dialog accessibility, so update the modal overlay/content to behave like a true
dialog. Add dialog semantics and focus handling in the same component that
renders errorList, including a dialog role, aria-modal, a label tied to the
header, initial focus when it opens, and Escape-to-close support that reuses the
existing setErrorList([]) close behavior.
In `@client/src/components/StatisticsSidebar.jsx`:
- Around line 5-11: Remove the unused activeTab and onTabChange props from
StatisticsSidebar and update the component signature and any callers such as
Statistics so they no longer pass tab state that the component does not use.
Also clean up the leftover commented-out menuItems block in StatisticsSidebar to
reflect the current NavLink-based routing implementation and avoid exposing a
misleading tab API.
In `@client/src/hooks/useEquipment.js`:
- Around line 33-40: The useEquipment hook still defines an unused
updateEquipment callback with its own PATCH request, but the active update flow
is handled elsewhere through equipmentService.updateEquipment. Remove the
updateEquipment function/export from useEquipment and clean up any related
references so the hook only exposes the APIs that are actually consumed, keeping
the hook aligned with the current update contract.
In `@client/src/pages/adminPage/AssetManagement.css`:
- Around line 152-156: The `.layout-table` selector in AssetManagement.css is
duplicated with separate rule blocks, so merge the duplicated styles into one
consolidated `.layout-table` definition. Update the CSS near the existing
`.layout-table` rules to combine all properties currently split across the two
blocks, and remove the redundant declaration so the table styles live in a
single place.
- Around line 135-149: The scrollbar-hiding styles in the
.table-responsive-wrapper rule only cover Firefox and legacy IE/Edge, so
Chromium/WebKit browsers still show the native scrollbar. Update the
AssetManagement.css styles for .table-responsive-wrapper to also target the
WebKit scrollbar pseudo-element so Chrome, Safari, and new Edge hide it
consistently alongside the existing -ms-overflow-style and scrollbar-width
rules.
In `@client/src/pages/adminPage/AssetManagement.jsx`:
- Line 36: Remove the leftover debug console.log statements from
AssetManagement.jsx before merging. Clean up all the identified logs in the
AssetManagement component and related handlers, including the ones printing
EquipmentCtgs, the Thai debug messages, the payload array message, and the
onemode value, so no temporary debugging output remains in the final code.
In `@client/src/services/equipmentService.js`:
- Around line 5-14: The `updateEquipment` method destructures `equipmentId` and
`data`, but `data` is never used because the full `payload` is sent in the axios
request. Fix this by either removing the unused destructuring in
`updateEquipment` or changing the request body to use `data` intentionally, and
keep the behavior consistent with the `axios.put('/api/manage/updateEquipment',
...)` call.
In `@server/controllers/EquipmentControllers.js`:
- Around line 220-242: The partial-success path in the equipment upload handler
loses the count of rows that were actually persisted because the `errors.length
> 0` response only returns `errors`. Update the upload flow in
`EquipmentControllers` so the response in that branch also includes the number
of successful inserts from `validDataToInsert` (and, if helpful, a success
message), matching the information already returned in the full-success path.
Make sure the `createMany` result handling and the final
`res.status(200).json(...)` response clearly communicate both successes and
failures.
In `@server/controllers/statisticController.js`:
- Line 1: Remove the unused and unrelated node:domain import from
statisticController since create is never referenced anywhere in the controller.
Update the top-level imports in statisticController to keep only symbols that
are actually used, and verify no remaining references depend on create or
domain.
In `@server/routes/managementRoutes.js`:
- Line 55: The route path in the managementRoutes router uses mixed naming
conventions, so update the getTicketStats endpoint to follow the same pattern as
the sibling routes and use one consistent style across these route definitions.
Adjust the router.get registration for getTicketStats so its URL naming matches
the existing getMostCategoriesOfProblems and getMostUpvotedTickets endpoints.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 00ce7fb4-ff4d-4caf-85eb-1c5c0f5275c7
⛔ Files ignored due to path filters (1)
client/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (40)
client/package.jsonclient/src/App.jsxclient/src/components/CardFinishProblem.jsxclient/src/components/CardPendingProblem.cssclient/src/components/CardPendingProblem.jsxclient/src/components/ChartForStatistic.jsxclient/src/components/FilterProblem.jsxclient/src/components/StatisticsSidebar.cssclient/src/components/StatisticsSidebar.jsxclient/src/components/componentsAdmin/ImportEquipments.cssclient/src/components/componentsAdmin/ImportEquipments.jsxclient/src/components/componentsStyles/CardFinishProblem.cssclient/src/hooks/useEquipment.jsclient/src/hooks/useEquiptmentCtg.jsclient/src/hooks/useImportEquipments.jsclient/src/hooks/useStatistics.jsclient/src/hooks/useTickets.jsclient/src/hooks/useUsers.jsclient/src/index.cssclient/src/pages/AddIssue.jsxclient/src/pages/Dashboard.jsxclient/src/pages/DetailTicket.cssclient/src/pages/DetailTicket.jsxclient/src/pages/Statistics.cssclient/src/pages/Statistics.jsxclient/src/pages/Tracking.cssclient/src/pages/adminPage/AssetManagement.cssclient/src/pages/adminPage/AssetManagement.jsxclient/src/pages/adminPage/Categories.cssclient/src/pages/adminPage/Categories.jsxclient/src/pages/adminPage/LocationManagement.jsxclient/src/pages/adminPage/UserManagement.jsxclient/src/services/equipmentService.jsclient/src/utils/timeline.jsserver/controllers/EquipmentControllers.jsserver/controllers/managementControllers.jsserver/controllers/statisticController.jsserver/controllers/ticketControllers.jsserver/routes/managementRoutes.jsserver/routes/ticketRoutes.js
💤 Files with no reviewable changes (1)
- client/src/pages/AddIssue.jsx
| .img-card, | ||
| img { | ||
| object-fit: cover; | ||
| object-position: center; | ||
| width: 150px; | ||
| height: 150px; | ||
| height: 160px; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '== File list ==\n'
git ls-files 'client/src/components/CardPendingProblem.css' 'client/src/**/Dashboard.jsx' 'client/src/**/Dashboard.tsx' 'client/src/**/Dashboard.js' 'client/src/**/Dashboard.ts' || true
printf '\n== CSS file ==\n'
cat -n client/src/components/CardPendingProblem.css
printf '\n== Search for stylesheet imports and bare img usage ==\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' 'CardPendingProblem\.css|<img\b|className=.*img-card|img-card' client/src
printf '\n== Relevant Dashboard file(s) ==\n'
for f in $(git ls-files 'client/src/**/Dashboard.jsx' 'client/src/**/Dashboard.tsx' 'client/src/**/Dashboard.js' 'client/src/**/Dashboard.ts' | head -n 5); do
echo "--- $f ---"
cat -n "$f"
doneRepository: CNPJB/trouble-tracking-system-project
Length of output: 13481
Scope the image rule to .img-card
.img-card, img { ... } is a selector list, so the img part applies to every bare <img> on pages that load this stylesheet. That also affects the empty-state image in Dashboard.jsx.
🐛 Proposed fix
-.img-card,
-img {
+.img-card img {
object-fit: cover;
object-position: center;
width: 150px;
height: 160px;
}📝 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.
| .img-card, | |
| img { | |
| object-fit: cover; | |
| object-position: center; | |
| width: 150px; | |
| height: 150px; | |
| height: 160px; | |
| .img-card img { | |
| object-fit: cover; | |
| object-position: center; | |
| width: 150px; | |
| height: 160px; | |
| } |
🤖 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 `@client/src/components/CardPendingProblem.css` around lines 20 - 25, The image
styling in the CardPendingProblem CSS is too broad because the `.img-card, img`
selector also targets every bare img on the page. Update the rule in
`CardPendingProblem.css` to scope the object-fit/object-position/size styles
only to `.img-card`, so other images like the Dashboard empty-state image are
not affected.
| // hooks | ||
| import { useEquipment } from '../hooks/useEquipment' | ||
| import { useLoadingState } from '../hooks/useLoadingState' | ||
|
|
||
| export const useImportEquipments = (url) => { | ||
| const { loading, reset } = useLoadingState(); | ||
| const { refetch } = useEquipment(); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm AssetManagement.jsx independently calls useEquipment() and uses `equipment`/`refetch` from it
rg -n "useEquipment\(" client/src/pages/adminPage/AssetManagement.jsx client/src/hooks/useImportEquipments.js
rg -n "<ImportEquipments" client/src/pages/adminPage/AssetManagement.jsxRepository: CNPJB/trouble-tracking-system-project
Length of output: 479
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map the relevant files first
ast-grep outline client/src/hooks/useEquipment.js --view expanded || true
ast-grep outline client/src/hooks/useImportEquipments.js --view expanded || true
ast-grep outline client/src/pages/adminPage/AssetManagement.jsx --view expanded || true
# Read the relevant sections
printf '\n--- useEquipment.js ---\n'
wc -l client/src/hooks/useEquipment.js
sed -n '1,220p' client/src/hooks/useEquipment.js
printf '\n--- useImportEquipments.js ---\n'
wc -l client/src/hooks/useImportEquipments.js
sed -n '1,240p' client/src/hooks/useImportEquipments.js
printf '\n--- AssetManagement.jsx (relevant slices) ---\n'
wc -l client/src/pages/adminPage/AssetManagement.jsx
sed -n '1,120p' client/src/pages/adminPage/AssetManagement.jsx
sed -n '460,540p' client/src/pages/adminPage/AssetManagement.jsxRepository: CNPJB/trouble-tracking-system-project
Length of output: 10797
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n--- ImportEquipments component ---\n'
fd -a "ImportEquipments.*" client/src
if [ -f client/src/components/componentsAdmin/ImportEquipments.jsx ]; then
wc -l client/src/components/componentsAdmin/ImportEquipments.jsx
sed -n '1,220p' client/src/components/componentsAdmin/ImportEquipments.jsx
fi
printf '\n--- Search for useImportEquipments usage ---\n'
rg -n "useImportEquipments\(" client/src
printf '\n--- Search for uploadEquipments endpoint / error shapes ---\n'
rg -n "uploadEquipments|No file uploaded|errors:|error:" client/src server . || trueRepository: CNPJB/trouble-tracking-system-project
Length of output: 21935
Pass refetch from AssetManagement.jsx into ImportEquipments.
useImportEquipmentscreates its ownuseEquipment()instance, sorefetch()here only updates that hook’s local state and won’t refresh the table the admin sees.- The upload catch block also ignores
error.response.data.error, so 400/500 responses fromuploadEquipmentscan fail without a user-facing message.
🤖 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 `@client/src/hooks/useImportEquipments.js` around lines 3 - 9,
`useImportEquipments` is creating its own `useEquipment()` instance, so the
refresh it triggers won’t update the table in `AssetManagement.jsx`; change
`useImportEquipments`/`ImportEquipments` to receive the parent `refetch`
callback as an argument and call that after a successful upload instead of the
local hook instance. Also update the upload error handling in
`useImportEquipments` so the catch block reads and surfaces
`error.response.data.error` (when present) from `uploadEquipments`, ensuring
400/500 responses show a user-facing message instead of being swallowed.
| } catch (error) { | ||
| console.error(error); | ||
| if (error.response && error.response.data.errors) { | ||
| setErrorList(error.response.data.errors); | ||
| console.error("Upload Error:", error); | ||
|
|
||
| if (error.response && error.response.data) { | ||
| if (error.response.data.errors) { | ||
| setErrorList(error.response.data.errors); | ||
| } else if (error.response.data.message) { | ||
| alert(error.response.data.message); | ||
| } | ||
| } else { | ||
| alert("เกิดข้อผิดพลาดในการอัปโหลด"); | ||
| alert("เกิดข้อผิดพลาดในการรับส่งข้อมูลกับเซิร์ฟเวอร์"); | ||
| } | ||
|
|
||
| return false; | ||
|
|
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Some backend failure responses use error (singular), which this handler never checks — silent failure for the user.
EquipmentControllers.js's uploadEquipments returns { error: 'No file uploaded' } (400) and { error: 'Failed to upload equipment data' } (500) on failure paths, but this catch block only inspects error.response.data.errors and error.response.data.message. For those two response shapes, neither branch matches, so no alert() fires and errorList stays empty — the user sees the submit button simply re-enable with zero feedback.
🐛 Suggested fix
if (error.response && error.response.data) {
if (error.response.data.errors) {
setErrorList(error.response.data.errors);
} else if (error.response.data.message) {
alert(error.response.data.message);
+ } else if (error.response.data.error) {
+ alert(error.response.data.error);
}
} else {
alert("เกิดข้อผิดพลาดในการรับส่งข้อมูลกับเซิร์ฟเวอร์");
}📝 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.
| } catch (error) { | |
| console.error(error); | |
| if (error.response && error.response.data.errors) { | |
| setErrorList(error.response.data.errors); | |
| console.error("Upload Error:", error); | |
| if (error.response && error.response.data) { | |
| if (error.response.data.errors) { | |
| setErrorList(error.response.data.errors); | |
| } else if (error.response.data.message) { | |
| alert(error.response.data.message); | |
| } | |
| } else { | |
| alert("เกิดข้อผิดพลาดในการอัปโหลด"); | |
| alert("เกิดข้อผิดพลาดในการรับส่งข้อมูลกับเซิร์ฟเวอร์"); | |
| } | |
| return false; | |
| } catch (error) { | |
| console.error("Upload Error:", error); | |
| if (error.response && error.response.data) { | |
| if (error.response.data.errors) { | |
| setErrorList(error.response.data.errors); | |
| } else if (error.response.data.message) { | |
| alert(error.response.data.message); | |
| } else if (error.response.data.error) { | |
| alert(error.response.data.error); | |
| } | |
| } else { | |
| alert("เกิดข้อผิดพลาดในการรับส่งข้อมูลกับเซิร์ฟเวอร์"); | |
| } | |
| return false; |
🤖 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 `@client/src/hooks/useImportEquipments.js` around lines 45 - 59, The error
handling in useImportEquipments is missing backend responses that return a
singular error field, so users get no feedback on failures. Update the catch
block to check error.response.data.error in addition to errors and message, and
surface it with alert or equivalent so uploadEquipments failures from
EquipmentControllers.js are shown to the user; keep the existing setErrorList
behavior for validation errors and use the same response parsing flow in
useImportEquipments.
| const [ticketStats, setTicketStats] = useState({ | ||
| created: [0, 0, 0, 0, 0], | ||
| resolved: [0, 0, 0, 0, 0] | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Initial ticketStats shape mismatches the default "all months" view.
The default UI state (selectedMonth = 'all' in Statistics.jsx) expects up to 12 monthly data points, but the initial state here only provides 5-element arrays (sized for the weekly view). Since Statistics.jsx does (ticketStats?.created || Array(12).fill(0)).slice(0, displayCount), the truthy 5-item default is used instead of a 12-item fallback, so on first render (before the fetch resolves) finalCreated/finalResolved will have length 5 while chartCategories has up to 12 entries — a data/category length mismatch fed into ApexCharts.
🩹 Suggested fix
const [ticketStats, setTicketStats] = useState({
- created: [0, 0, 0, 0, 0],
- resolved: [0, 0, 0, 0, 0]
+ created: Array(12).fill(0),
+ resolved: Array(12).fill(0)
});📝 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 [ticketStats, setTicketStats] = useState({ | |
| created: [0, 0, 0, 0, 0], | |
| resolved: [0, 0, 0, 0, 0] | |
| }); | |
| const [ticketStats, setTicketStats] = useState({ | |
| created: Array(12).fill(0), | |
| resolved: Array(12).fill(0) | |
| }); |
🤖 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 `@client/src/hooks/useStatistics.js` around lines 7 - 10, The initial
ticketStats state in useStatistics.js is sized for the weekly view, but
Statistics.jsx defaults to the all-months view, causing the chart data length to
mismatch the categories before fetch completes. Update the initial state in
useStatistics so created and resolved default to 12-item arrays (or otherwise
match the all-months display used by Statistics and its
ticketStats?.created/ticketStats?.resolved fallback logic) so the first render
aligns with chartCategories.
| const [isUpdateConfirmOpen, setIsUpdateConfirmOpen] = useState({ isOpen: false }); | ||
| const [isDeleteConfirmOpen, setIsDeleteConfirmOpen] = useState({ isOpen: false }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
Delete confirmation flow is non-functional; state shape also mismatched.
Two issues:
- The
ConfirmButtonfor delete is fully commented out, so clicking "ลบ" only callssetIsDeleteConfirmOpen(true)with no visible dialog rendered anywhere —handleDelete(and thus deleting equipment) is now unreachable from the UI. isDeleteConfirmOpenis initialized as{ isOpen: false }(an object, always truthy) but everywhere it's actually set/read as a boolean (setIsDeleteConfirmOpen(true),setIsDeleteConfirmOpen(false),isOpen={isDeleteConfirmOpen}). If the commented block is restored as-is,ConfirmButton'sif (!isOpen) return null;guard would never block rendering because the initial object is truthy.
🔧 Suggested fix
- const [isDeleteConfirmOpen, setIsDeleteConfirmOpen] = useState({ isOpen: false });
+ const [isDeleteConfirmOpen, setIsDeleteConfirmOpen] = useState(false);And restore the ConfirmButton block wiring onConfirm={handleDelete}.
Also applies to: 488-503
🤖 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 `@client/src/pages/adminPage/AssetManagement.jsx` around lines 24 - 25, The
delete confirmation flow in AssetManagement is broken because the confirm dialog
is commented out and the delete state shape is inconsistent. Restore the
ConfirmButton block so the “ลบ” action can open a visible confirmation dialog
and wire its onConfirm to handleDelete, and change isDeleteConfirmOpen to use a
boolean consistently with its setter/getter usage so ConfirmButton’s isOpen
guard works correctly.
| if (!locationName || locationName.trim() === '') { | ||
| return res.status(400).json({ error: 'กรุณาระบุชื่อสถานที่' }); | ||
| } | ||
|
|
||
| const cleanLocationName = locationName.trim(); | ||
|
|
||
| const findLocation = await prisma.location.findFirst({ | ||
| where: { | ||
| locationName: cleanLocationName | ||
| } | ||
| }); | ||
|
|
||
| if (findLocation) { | ||
| return res.status(400).json({ error: 'สถานที่นี้มีอยู่แล้วในระบบ' }); | ||
| } | ||
|
|
||
| const location = await prisma.location.create({ | ||
| data: { | ||
| locationName, | ||
| locationName: cleanLocationName, |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
fd -e prisma | xargs -I{} rg -n -B2 -A2 'locationName' {}Repository: CNPJB/trouble-tracking-system-project
Length of output: 517
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate schema and migrations mentioning location_name / Location / P2002
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
-e 'model Location' \
-e 'location_name' \
-e '`@unique`' \
-e 'P2002' \
prisma server . 2>/dev/null || true
printf '\n--- schema slice ---\n'
schema_file="$(fd -a -e prisma . | head -n 1 || true)"
if [ -n "${schema_file:-}" ]; then
sed -n '20,45p' "$schema_file"
fi
printf '\n--- migrations mentioning location_name ---\n'
fd -a -t f prisma migrations . 2>/dev/null | while read -r f; do
rg -n 'location_name|locationName|UNIQUE|unique' "$f" && echo "--- $f ---"
doneRepository: CNPJB/trouble-tracking-system-project
Length of output: 3570
Add a unique constraint for locationName. findFirst + create still allows duplicate locations under concurrency because server/prisma/schema.prisma doesn’t declare locationName as @unique, and the migration doesn’t enforce it either. Add the DB constraint and handle P2002 here as a 400.
🤖 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 `@server/controllers/managementControllers.js` around lines 59 - 77, Add a real
uniqueness guarantee for locationName instead of relying on the findFirst check
in managementControllers.js. Update the Prisma model in schema.prisma to mark
locationName as unique, generate/apply the migration so the database enforces
it, and then in the create flow around prisma.location.create handle Prisma
error P2002 by returning the same 400-style duplicate-location response.
| export const updateRoleUsers = async (req, res) => { | ||
| try { | ||
| const { userId, userRole } = req.body; | ||
|
|
||
| const myUserId = req.user.userId; | ||
| console.log("ID จากหน้าบ้าน:", userId); | ||
| console.log("ข้อมูลใน Token ของฉัน:", req.user); | ||
| if (String(userId) === String(myUserId)) { | ||
| return res.status(400).json({ error: "ไม่อนุญาตให้แก้ไขสิทธิ์ของตัวเอง" }); | ||
| } | ||
| const users = await prisma.user.update({ | ||
| where: { | ||
| userId: Number(userId) | ||
| }, data: { | ||
| role: userRole | ||
| } | ||
| }); | ||
| res.status(200).json(users); | ||
| } catch (error) { | ||
| console.error('Error update users:', error); | ||
| res.status(500).json({ error: 'เกิดข้อผิดพลาดในการอัปเดตสิทธิ์ผู้ใช้งาน' }); | ||
| } | ||
| }; |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Logging full req.user leaks token payload to server logs.
console.log("ข้อมูลใน Token ของฉัน:", req.user) writes the entire decoded user object (potentially including email/PII) to logs on every role-update call.
🔒 Proposed fix
- console.log("ID จากหน้าบ้าน:", userId);
- console.log("ข้อมูลใน Token ของฉัน:", req.user);
+ // debug logs removed📝 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.
| export const updateRoleUsers = async (req, res) => { | |
| try { | |
| const { userId, userRole } = req.body; | |
| const myUserId = req.user.userId; | |
| console.log("ID จากหน้าบ้าน:", userId); | |
| console.log("ข้อมูลใน Token ของฉัน:", req.user); | |
| if (String(userId) === String(myUserId)) { | |
| return res.status(400).json({ error: "ไม่อนุญาตให้แก้ไขสิทธิ์ของตัวเอง" }); | |
| } | |
| const users = await prisma.user.update({ | |
| where: { | |
| userId: Number(userId) | |
| }, data: { | |
| role: userRole | |
| } | |
| }); | |
| res.status(200).json(users); | |
| } catch (error) { | |
| console.error('Error update users:', error); | |
| res.status(500).json({ error: 'เกิดข้อผิดพลาดในการอัปเดตสิทธิ์ผู้ใช้งาน' }); | |
| } | |
| }; | |
| export const updateRoleUsers = async (req, res) => { | |
| try { | |
| const { userId, userRole } = req.body; | |
| const myUserId = req.user.userId; | |
| // debug logs removed | |
| if (String(userId) === String(myUserId)) { | |
| return res.status(400).json({ error: "ไม่อนุญาตให้แก้ไขสิทธิ์ของตัวเอง" }); | |
| } | |
| const users = await prisma.user.update({ | |
| where: { | |
| userId: Number(userId) | |
| }, data: { | |
| role: userRole | |
| } | |
| }); | |
| res.status(200).json(users); | |
| } catch (error) { | |
| console.error('Error update users:', error); | |
| res.status(500).json({ error: 'เกิดข้อผิดพลาดในการอัปเดตสิทธิ์ผู้ใช้งาน' }); | |
| } | |
| }; |
🤖 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 `@server/controllers/managementControllers.js` around lines 449 - 471, The
updateRoleUsers handler is logging the full req.user token payload, which can
leak sensitive user data into server logs. Remove the console.log that prints
the entire req.user object and keep only minimal, non-sensitive debugging output
if needed, using updateRoleUsers and myUserId as the key places to adjust.
Ensure the role update flow still validates self-edits and continues to log only
safe identifiers such as userId when necessary.
| const users = await prisma.user.update({ | ||
| where: { | ||
| userId: Number(userId) | ||
| }, data: { | ||
| role: userRole | ||
| } | ||
| }); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
userRole is written to the DB without validating it against allowed values.
Any string sent in req.body.userRole is persisted directly to role. An unexpected/typo'd value would silently break every role === 'admin'/role-based check across the app (e.g., routing/authorization logic in App.jsx), with no server-side guardrail.
🛡️ Proposed fix
+ const ALLOWED_ROLES = ['admin', 'user'];
+ if (!ALLOWED_ROLES.includes(userRole)) {
+ return res.status(400).json({ error: 'บทบาทไม่ถูกต้อง' });
+ }
const users = await prisma.user.update({📝 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 users = await prisma.user.update({ | |
| where: { | |
| userId: Number(userId) | |
| }, data: { | |
| role: userRole | |
| } | |
| }); | |
| const ALLOWED_ROLES = ['admin', 'user']; | |
| if (!ALLOWED_ROLES.includes(userRole)) { | |
| return res.status(400).json({ error: 'บทบาทไม่ถูกต้อง' }); | |
| } | |
| const users = await prisma.user.update({ | |
| where: { | |
| userId: Number(userId) | |
| }, data: { | |
| role: userRole | |
| } | |
| }); |
🤖 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 `@server/controllers/managementControllers.js` around lines 459 - 465, The
userRole value is being written directly through prisma.user.update without any
server-side validation, so add an allowlist check before updating the user. In
the management controller that handles this update, validate req.body.userRole
against the permitted role values used by the app, reject invalid input with an
error response, and only pass the sanitized role into the update call. Keep the
fix localized around the userRole handling in this controller so role-based
checks elsewhere remain consistent.
| const MostCategories = await prisma.TicketCategory.findMany({ | ||
| include: { | ||
| _count: { | ||
| select: { | ||
| tickets: true | ||
| } | ||
| } | ||
| }, | ||
| orderBy: { | ||
| tickets: { | ||
| _count: "desc" | ||
| } | ||
| }, | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- Files of interest ---\n'
git ls-files | rg '(^|/)(schema\.prisma|.*prisma.*|server/controllers/statisticController\.js|prisma)' || true
printf '\n--- Search for TicketCategory usage ---\n'
rg -n "TicketCategory|ticketCategory|prisma\.[A-Za-z0-9_]+\.findMany" server prisma . || true
printf '\n--- If schema exists, show model names ---\n'
if git ls-files | rg -q 'schema\.prisma$'; then
schema_path="$(git ls-files | rg 'schema\.prisma$' | head -n1)"
echo "Schema: $schema_path"
rg -n '^\s*model\s+[A-Za-z0-9_]+' "$schema_path" || true
fiRepository: CNPJB/trouble-tracking-system-project
Length of output: 266
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- Files of interest ---'
git ls-files | rg '(^|/)(schema\.prisma|.*prisma.*|server/controllers/statisticController\.js|prisma)' || true
printf '%s\n' ''
printf '%s\n' '--- Search for TicketCategory usage ---'
rg -n "TicketCategory|ticketCategory|prisma\.[A-Za-z0-9_]+\.findMany" server prisma . || true
printf '%s\n' ''
printf '%s\n' '--- Schema model names (if present) ---'
schema_path="$(git ls-files | rg 'schema\.prisma$' | head -n1 || true)"
if [ -n "${schema_path}" ]; then
echo "Schema: ${schema_path}"
rg -n '^\s*model\s+[A-Za-z0-9_]+' "${schema_path}" || true
fi
printf '%s\n' ''
printf '%s\n' '--- statisticController.js excerpt ---'
sed -n '1,120p' server/controllers/statisticController.jsRepository: CNPJB/trouble-tracking-system-project
Length of output: 10859
Use camelCase Prisma accessors here. prisma.TicketCategory and prisma.Ticket don’t exist on the generated client; this should be prisma.ticketCategory and prisma.ticket, or these endpoints will fail at runtime.
🤖 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 `@server/controllers/statisticController.js` around lines 6 - 19, The Prisma
model accessors in the statistics controller are using the wrong casing, which
will break at runtime because the generated client exposes camelCase names.
Update the queries in statisticController to use prisma.ticketCategory (and
prisma.ticket where applicable) instead of prisma.TicketCategory /
prisma.Ticket, keeping the same include and orderBy logic.
| const mostUpvotedTickets = await prisma.Ticket.findMany({ | ||
| orderBy: { | ||
| upvotes: { | ||
| _count: "desc" | ||
| } | ||
| }, | ||
| include: { | ||
| _count: { | ||
| select: { upvotes: true } | ||
| }, | ||
| location: true, | ||
| }, | ||
|
|
||
| take: 5 | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
git ls-files | rg '(^|/)(schema\.prisma|.*prisma.*|server/controllers/statisticController\.js)$' && printf '\n-- controller --\n' && sed -n '1,120p' server/controllers/statisticController.js && printf '\n-- prisma casing usage --\n' && rg -n 'prisma\.(Ticket|ticket)\b' . && printf '\n-- schema models --\n' && rg -n '^\s*model\s+\w+' prisma server -g 'schema.prisma'Repository: CNPJB/trouble-tracking-system-project
Length of output: 6897
🏁 Script executed:
sed -n '100,140p' server/prisma/schema.prisma && printf '\n--\n' && sed -n '1,40p' server/prisma/schema.prismaRepository: CNPJB/trouble-tracking-system-project
Length of output: 3773
Use the generated Prisma delegate names here prisma.Ticket should be prisma.ticket here, and prisma.TicketCategory should be prisma.ticketCategory; the current casing will fail at runtime.
🤖 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 `@server/controllers/statisticController.js` around lines 36 - 50, The Prisma
delegate names in statisticController are using the wrong casing and will fail
at runtime. Update the queries in the relevant controller method to use the
generated lowercase delegates on prisma, replacing prisma.Ticket with
prisma.ticket and any prisma.TicketCategory usage with prisma.ticketCategory,
while keeping the existing findMany and related include/orderBy logic unchanged.
addStatistic addChart
Summary by CodeRabbit
New Features
Bug Fixes
Style