Skip to content

Feature/advanced filter - #18

Merged
CNPJB merged 3 commits into
developfrom
feature/AdvancedFilter
Jul 31, 2026
Merged

Feature/advanced filter#18
CNPJB merged 3 commits into
developfrom
feature/AdvancedFilter

Conversation

@CNPJB

@CNPJB CNPJB commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Add feature and fix migration file.

Summary by CodeRabbit

  • New Features

    • Added advanced, responsive filtering for tracking and administrative ticket views.
    • Added equipment-category filtering and expanded ticket status options.
    • Added similar-ticket review and upvoting during issue submission.
    • Added before/after image galleries with modal previews.
    • Added rated-only resolved-ticket views and active-filter counts.
  • Bug Fixes

    • Tickets can no longer be resolved or updated without required images.
    • Improved ticket cancellation handling and duplicate-ticket restoration.
    • Improved mobile layouts, text wrapping, image previews, and filter controls.
    • Added clearer required-field indicators and validation messages.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@CNPJB, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 12164a8c-6e64-4977-920c-0519a456608f

📥 Commits

Reviewing files that changed from the base of the PR and between 9659365 and 29a125d.

📒 Files selected for processing (5)
  • client/src/components/componentsAdmin/Adminsidebar.css
  • client/src/components/componentsStyles/FilterProblem.css
  • client/src/components/componentsStyles/StatisticsSidebar.css
  • client/src/pages/adminPage/IssueManagement.jsx
  • client/src/pages/pageStyles/Tracking.css
📝 Walkthrough

Walkthrough

This PR adds shared advanced filtering, ticket image and status validation, rated resolved-ticket retrieval, responsive ticket galleries, mobile similar-ticket review, and responsive administration layouts. It also updates equipment relations and development-server origins.

Changes

Ticket entry, status, and image workflows

Layer / File(s) Summary
Image requirements and mobile submission review
client/src/components/ImageUploader.jsx, client/src/pages/AddIssue.jsx, client/src/pages/EditIssue.jsx, client/src/components/componentsAdmin/TicketActionPanel.jsx, server/controllers/ticketControllers.js, server/controllers/ticketManagementControllers.js
Image requirements now apply to issue creation, editing, resolution, and ticket updates. Mobile issue submission can display similar tickets before confirmation.
Status and resolved-ticket retrieval
client/src/hooks/useMasterData.js, client/src/hooks/useResolvedTickets.js, client/src/pages/Dashboard.jsx, server/controllers/getTicketControllers.js
Additional ticket statuses are available. Resolved-ticket requests can require ratings.
Cancellation transaction
server/controllers/ticketControllers.js
Cancellation now requires pending status, updates the ticket transactionally, and restores duplicate child tickets to pending.

Shared and administrative filters

Layer / File(s) Summary
Shared filter controls
client/src/components/AdvancedFilterPanel.jsx, client/src/components/EquipmentCategoryFilter.jsx, client/src/components/TIcketStatusFilter.jsx, client/src/components/TicketCategoryFilter.jsx, client/src/components/TicketLocationFilter.jsx, client/src/components/componentsStyles/*Filter*.css
Shared controls support category, location, equipment-category, and configurable status filtering with active counts and clear-all actions.
Tracking filters
client/src/pages/Tracking.jsx, client/src/pages/pageStyles/Tracking.css
Tracking adds category, location, and status filters, clear-all behavior, responsive layout, and an empty state.
Administrative filter panels
client/src/pages/adminPage/AssetManagement.jsx, client/src/pages/adminPage/AuditIssues.jsx, client/src/pages/adminPage/IssueManagement.jsx, client/src/pages/adminPage/*.css
Administrative pages use advanced filters and responsive panels. Mobile audit controls use an off-canvas management panel.

Ticket presentation and responsive styling

Layer / File(s) Summary
Before/after image galleries
client/src/pages/DetailTicket.jsx, client/src/pages/adminPage/IssueManagementDetail.jsx, client/src/pages/adminPage/IssueManagementDetail.css
Ticket images are grouped by repair stage. Preview clicks open modal galleries. Status labels and badges include new ticket states.
Responsive supporting styles
client/src/components/componentsStyles/CardPendingProblem.css, client/src/components/componentsStyles/ImageUploader.css, client/src/components/componentsStyles/SimilarTickets.css, client/src/pages/pageStyles/AddIssue.css, client/src/pages/pageStyles/EditIssue.css, client/src/pages/pageStyles/DetailTicket.css
Responsive rules adjust wrapping, card sizing, upload previews, modal layouts, image cards, and detail-page spacing.
Project wiring and unchanged-behavior cleanup
client/src/components/componentsAdmin/AdminLayout.jsx, client/src/components/componentsAdmin/Adminsidebar.css, client/src/components/TicketActionMenu.jsx, client/src/components/componentsAdmin/ToggleSwitch.jsx, client/vite.config.js, server/src/index.js, server/prisma/*
Formatting and layout updates are applied. The development host and CORS origin lists include an additional ngrok address. The equipment migration adds nullable floor and location relations.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested reviewers: poner2547

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the pull request's primary change, which is the advanced filter feature.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/AdvancedFilter

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.

❤️ Share

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

@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: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
client/src/pages/DetailTicket.jsx (1)

87-95: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make image gallery controls keyboard-operable.

Clickable <img> elements and “+ ดูกรูปเพิ่มเติม” text use pointer-only activation. Replace these triggers with focusable labeled buttons, and add dialog support for the popup: role="dialog", aria-modal="true", initial focus, focus return, and Escape-key close.

🤖 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 87 - 95, The image gallery
triggers in client/src/pages/DetailTicket.jsx lines 87-95 and 124-132, plus
client/src/pages/adminPage/IssueManagementDetail.jsx lines 147-164, 179-196, and
205-232, must be keyboard-operable: replace clickable images and “+
ดูกรูปเพิ่มเติม” text with focusable, visibly labeled buttons. Add dialog
behavior to each popup with role="dialog", aria-modal="true", initial focus,
focus return to the triggering button, and Escape-key closing.
client/src/hooks/useResolvedTickets.js (1)

4-30: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add ratedOnly to the useEffect dependency array.

The useEffect at Line 30 depends only on [limit]. ratedOnly is a parameter of the hook but is not tracked as a dependency. With the current caller (Dashboard.jsx passes fixed literals), this does not manifest as a bug. If a future caller passes a ratedOnly value that changes across renders (e.g., from a toggle), the effect will not re-run when only ratedOnly changes, and the fetch will use a stale value.

🖊️ Proposed fix
         fetchResolved();
-    }, [limit]);
+    }, [limit, ratedOnly]);
🤖 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/useResolvedTickets.js` around lines 4 - 30, Update the
dependency array of the useEffect inside useResolvedTickets to include ratedOnly
alongside limit, so the resolved-ticket request re-runs whenever either query
parameter changes.
🧹 Nitpick comments (3)
client/src/pages/pageStyles/AddIssue.css (2)

193-213: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate duplicate media-query rules at 1024px and 820px.

The @media (max-width: 1024px) block and the @media (max-width: 820px) block define the same rules for .add-issue-container and .form-row. Because 820px is narrower than 1024px, the 820px block is redundant; the 1024px rules already apply at that width. Merge them into a single breakpoint, or add the distinct rules the 820px block was meant to introduce.

♻️ Proposed fix
 `@media` (max-width: 1024px) {
   .add-issue-container {
     grid-template-columns: 1fr;
   }

   .form-row {
     flex-direction: column;
     gap: 15px;
   }
 }

-@media (max-width: 820px) {
-  .add-issue-container {
-    grid-template-columns: 1fr;
-  }
-
-  .form-row {
-    flex-direction: column;
-    gap: 15px;
-  }
-}
-
 `@media` (max-width: 430px) {
🤖 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/pageStyles/AddIssue.css` around lines 193 - 213, Remove the
redundant `@media` (max-width: 820px) block in the responsive styles, keeping the
shared .add-issue-container and .form-row rules under the existing `@media`
(max-width: 1024px) breakpoint; only retain a separate 820px block if distinct
narrower-screen rules are required.

106-109: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove unused .urgent-label styling.

.urgent-label is only defined in client/src/pages/pageStyles/AddIssue.css; no rendered element in client/src/pages/AddIssue.jsx applies it. Remove the selector if it is not needed.

🤖 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/pageStyles/AddIssue.css` around lines 106 - 109, Remove the
unused .urgent-label selector and its color declaration from AddIssue.css, since
AddIssue.jsx does not apply this class.
server/prisma/migrations/20260731145100_add_floor_location_to_equipments/migration.sql (1)

10-13: 🩺 Stability & Availability | 🔵 Trivial | 🏗️ Heavy lift

Verify the foreign-key lock window.

Each ADD CONSTRAINT can scan existing rows and block writes on equipments and the referenced table. If low downtime is required, add the foreign keys as NOT VALID and validate them in a separate migration. Otherwise, document the maintenance window.

🤖 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/prisma/migrations/20260731145100_add_floor_location_to_equipments/migration.sql`
around lines 10 - 13, Update the migration’s foreign-key creation for
equipments_floor_id_fkey and equipments_location_id_fkey to use a low-downtime
approach: add each constraint as NOT VALID, then validate them separately in a
follow-up migration. If the migration system cannot support this sequence,
document the required maintenance window instead.

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/componentsAdmin/AdminLayout.jsx`:
- Line 3: Update the React import in AdminLayout to remove the invalid lowercase
children named import, retaining only the imports actually used by the component
and avoiding the nonexistent React export.

In `@client/src/components/componentsAdmin/Adminsidebar.css`:
- Around line 96-105: Update the `@media` (max-width: 430px) rules in
Adminsidebar.css so .admin-sidebar remains hidden while .mobile-sidebar is
displayed. Remove or override the !important hide declaration for
.mobile-sidebar to keep mobile navigation visible at this breakpoint.

In `@client/src/components/componentsAdmin/TicketActionPanel.jsx`:
- Around line 41-47: Update the ImageUploader rendered by TicketActionPanel to
pass minImages={1}, aligning its required-image indicator with the existing
handleResolve validation and the AddIssue/EditIssue usage.

In `@client/src/components/componentsStyles/AdvancedFilterPanel.css`:
- Around line 18-33: Update the .btn-toggle-filter CSS block and the related
declarations through the additionally affected section to remove blank lines
between declaration groups. Rename the fadeInDown keyframe to fade-in-down and
update every animation reference to use the new kebab-case name.

In `@client/src/components/componentsStyles/SimilarTickets.css`:
- Line 149: Replace the deprecated word-break declaration with overflow-wrap:
anywhere in the .ticket-info p rule in
client/src/components/componentsStyles/SimilarTickets.css:149-149 and all five
occurrences in client/src/components/componentsStyles/CardPendingProblem.css at
30-30, 103-103, 123-123, and 134-134.

In `@client/src/components/TIcketStatusFilter.jsx`:
- Around line 6-22: Update the default all-status handling in TicketStatusFilter
so omitted allOptionValue represents every status currently shown by
displayStatuses, including filtered allowedStatuses. Prefer deriving the value
from displayStatuses, while preserving an explicitly provided allOptionValue.

In `@client/src/pages/AddIssue.jsx`:
- Around line 230-235: Reset hasBypassedSimilar whenever the form fields used to
determine similarTickets change, and also clear it in handleCancelSubmit so
bypassing one submission does not disable future duplicate checks. Keep the
existing mobile modal condition in the similar-ticket guard and preserve the
bypass behavior for the current submission.
- Around line 452-461: Remove the empty div with className "form-group"
immediately before the ImageUploader component, leaving ImageUploader and its
existing props unchanged.

In `@client/src/pages/adminPage/AuditIssues.css`:
- Line 216: Remove the empty .panel-content-wrapper rule from
client/src/pages/adminPage/AuditIssues.css at lines 216-216 and the empty
.admin-sidebar rule from client/src/components/componentsAdmin/Adminsidebar.css
at lines 86-86; no replacement styles are needed.
- Around line 127-162: Update the mobile drawer styles in .audit-right-panel and
.panel-overlay to stay within the viewport: replace the 120vh/120vw dimensions
with viewport-bounded sizing such as 100dvh, and use inset: 0 for the overlay’s
fixed positioning while preserving the existing drawer behavior and layout.

In `@client/src/pages/adminPage/AuditIssues.jsx`:
- Around line 255-259: Add an aria-label describing the close action to the
button rendering FaTimes in the panel-close-header, while preserving its
existing setIsMobilePanelOpen(false) click behavior.

In `@client/src/pages/adminPage/IssueManagement.css`:
- Around line 267-271: Update the .issue-management-container rule in the
max-width: 820px media query to use a fluid width constrained by a maximum
instead of the fixed 525px width, preventing horizontal overflow on intermediate
viewports while preserving the intended layout limit.

In `@client/src/pages/adminPage/IssueManagement.jsx`:
- Around line 86-91: Update handleClearAllFilters to also disable the My Tasks
state by resetting isMyTasksOnly and remove adminId from the filters passed to
updateFilters, while preserving the existing category, location, and status
resets.
- Around line 51-53: Update the hasActiveFilter calculation in IssueManagement
to include selectedStatus when it represents a non-default status, while
preserving the existing checks for searchKeyword, selectedCategory,
selectedLocation, and isMyTasksOnly.

In `@client/src/pages/adminPage/IssueManagementDetail.css`:
- Around line 438-463: Remove the fixed width and minimum-width constraints from
.info-card-detail and .action-card, and update .detail-layout so it switches to
a single-column grid at a breakpoint where both panels cannot fit. Ensure the
`@media` (max-width: 820px) rules preserve the single-column layout and prevent
horizontal overflow.
- Around line 61-89: The status classes in IssueManagementDetail.css use white
text with insufficient contrast on several default backgrounds. Update the
affected .status-pending, .status-in_progress, .status-resolved,
.status-rejected, and .status-duplicate rules by using accessible darker
backgrounds or dark text, and ensure the resolved --status-* custom-property
values also meet a 4.5:1 contrast ratio.

In `@client/src/pages/EditIssue.jsx`:
- Around line 328-342: Update the title field in the EditIssue form,
identifiable by name="title" and formData.title, to enforce maxLength={20} and
display the same character counter behavior used by AddIssue.jsx. Preserve the
existing required, value, and change-handler behavior.

In `@client/src/pages/Tracking.jsx`:
- Around line 65-68: Update handleTabChange so that when it applies baseFilters
and resets the query status to allowedStatuses.join(','), it also sets
selectedStatus to that same all-status value. Keep the select value and
activeDropdownFiltersCount synchronized with the reset filters.

In `@server/controllers/ticketControllers.js`:
- Around line 448-475: Update the auto-unmerge logic in the transaction to
capture the result of the `tx.ticket.updateMany` call and use its `count` field
for `releasedSubTicketsCount`. Replace the current
`ticketToCancel._count.subTickets` value while preserving the existing filter
and response messaging.

---

Outside diff comments:
In `@client/src/hooks/useResolvedTickets.js`:
- Around line 4-30: Update the dependency array of the useEffect inside
useResolvedTickets to include ratedOnly alongside limit, so the resolved-ticket
request re-runs whenever either query parameter changes.

In `@client/src/pages/DetailTicket.jsx`:
- Around line 87-95: The image gallery triggers in
client/src/pages/DetailTicket.jsx lines 87-95 and 124-132, plus
client/src/pages/adminPage/IssueManagementDetail.jsx lines 147-164, 179-196, and
205-232, must be keyboard-operable: replace clickable images and “+
ดูกรูปเพิ่มเติม” text with focusable, visibly labeled buttons. Add dialog
behavior to each popup with role="dialog", aria-modal="true", initial focus,
focus return to the triggering button, and Escape-key closing.

---

Nitpick comments:
In `@client/src/pages/pageStyles/AddIssue.css`:
- Around line 193-213: Remove the redundant `@media` (max-width: 820px) block in
the responsive styles, keeping the shared .add-issue-container and .form-row
rules under the existing `@media` (max-width: 1024px) breakpoint; only retain a
separate 820px block if distinct narrower-screen rules are required.
- Around line 106-109: Remove the unused .urgent-label selector and its color
declaration from AddIssue.css, since AddIssue.jsx does not apply this class.

In
`@server/prisma/migrations/20260731145100_add_floor_location_to_equipments/migration.sql`:
- Around line 10-13: Update the migration’s foreign-key creation for
equipments_floor_id_fkey and equipments_location_id_fkey to use a low-downtime
approach: add each constraint as NOT VALID, then validate them separately in a
follow-up migration. If the migration system cannot support this sequence,
document the required maintenance window instead.
🪄 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: c4eca366-92b7-4232-b58e-0945be4d247b

📥 Commits

Reviewing files that changed from the base of the PR and between b6e8b1f and 9659365.

📒 Files selected for processing (49)
  • client/src/components/AdvancedFilterPanel.jsx
  • client/src/components/EquipmentCategoryFilter.jsx
  • client/src/components/ImageUploader.jsx
  • client/src/components/SimilarTickets.jsx
  • client/src/components/TIcketStatusFilter.jsx
  • client/src/components/TicketActionMenu.jsx
  • client/src/components/TicketCategoryFilter.jsx
  • client/src/components/TicketLocationFilter.jsx
  • client/src/components/componentsAdmin/AdminLayout.jsx
  • client/src/components/componentsAdmin/Adminsidebar.css
  • client/src/components/componentsAdmin/MergeManagementPanel.css
  • client/src/components/componentsAdmin/MergeManagementPanel.jsx
  • client/src/components/componentsAdmin/TicketActionPanel.jsx
  • client/src/components/componentsAdmin/ToggleSwitch.jsx
  • client/src/components/componentsStyles/AdvancedFilterPanel.css
  • client/src/components/componentsStyles/CardPendingProblem.css
  • client/src/components/componentsStyles/FilterProblem.css
  • client/src/components/componentsStyles/ImageUploader.css
  • client/src/components/componentsStyles/SimilarTickets.css
  • client/src/components/componentsStyles/StatisticsSidebar.css
  • client/src/components/componentsStyles/TicketCategoryFilter.css
  • client/src/components/componentsStyles/TicketLocationFilter.css
  • client/src/components/componentsStyles/TicketStatusFilter.css
  • client/src/hooks/useMasterData.js
  • client/src/hooks/useResolvedTickets.js
  • client/src/pages/AddIssue.jsx
  • client/src/pages/Dashboard.jsx
  • client/src/pages/DetailTicket.jsx
  • client/src/pages/EditIssue.jsx
  • client/src/pages/Tracking.jsx
  • client/src/pages/adminPage/AssetManagement.css
  • client/src/pages/adminPage/AssetManagement.jsx
  • client/src/pages/adminPage/AuditIssues.css
  • client/src/pages/adminPage/AuditIssues.jsx
  • client/src/pages/adminPage/IssueManagement.css
  • client/src/pages/adminPage/IssueManagement.jsx
  • client/src/pages/adminPage/IssueManagementDetail.css
  • client/src/pages/adminPage/IssueManagementDetail.jsx
  • client/src/pages/pageStyles/AddIssue.css
  • client/src/pages/pageStyles/DetailTicket.css
  • client/src/pages/pageStyles/EditIssue.css
  • client/src/pages/pageStyles/Tracking.css
  • client/vite.config.js
  • server/controllers/getTicketControllers.js
  • server/controllers/ticketControllers.js
  • server/controllers/ticketManagementControllers.js
  • server/prisma/migrations/20260731145100_add_floor_location_to_equipments/migration.sql
  • server/prisma/schema.prisma
  • server/src/index.js

import { AdminSidebar } from "./AdminSidebar.jsx";
import { Outlet } from 'react-router-dom';
import React , {children} from "react";
import React, { children } from "react";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

curl -fsSL https://unpkg.com/react@19.2.4/cjs/react.development.js |
  rg -n 'exports\.(Children|children)\s*='

# Expect `exports.Children`; do not expect `exports.children`.

Repository: CNPJB/trouble-tracking-system-project

Length of output: 210


Remove the invalid children named import.

React exports Children, not lowercase children; this named import fails during module loading.

Proposed fix
-import React, { children } from "react";
+import React from "react";
📝 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
import React, { children } from "react";
import React from "react";
🤖 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/AdminLayout.jsx` at line 3, Update the
React import in AdminLayout to remove the invalid lowercase children named
import, retaining only the imports actually used by the component and avoiding
the nonexistent React export.

Comment on lines 96 to +105
@media (max-width: 430px) {
.admin-sidebar {
display: none !important; /* สั่งโชว์เมนูคอมพิวเตอร์ */
}
.mobile-sidebar {
display: none !important; /* สั่งโชว์เมนูมือถือ */
}
.admin-sidebar {
display: none !important;
/* สั่งโชว์เมนูคอมพิวเตอร์ */
}

.mobile-sidebar {
display: none !important;
/* สั่งโชว์เมนูมือถือ */
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep the mobile navigation visible below 430px.

Lines 97-104 hide both .admin-sidebar and .mobile-sidebar. Users cannot navigate the administration area at this breakpoint.

Hide the desktop sidebar, but display the mobile sidebar.

 `@media` (max-width: 430px) {
     .admin-sidebar {
         display: none !important;
     }

     .mobile-sidebar {
-        display: none !important;
+        display: block !important;
     }
 }
📝 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
@media (max-width: 430px) {
.admin-sidebar {
display: none !important; /* สั่งโชว์เมนูคอมพิวเตอร์ */
}
.mobile-sidebar {
display: none !important; /* สั่งโชว์เมนูมือถือ */
}
.admin-sidebar {
display: none !important;
/* สั่งโชว์เมนูคอมพิวเตอร์ */
}
.mobile-sidebar {
display: none !important;
/* สั่งโชว์เมนูมือถือ */
}
`@media` (max-width: 430px) {
.admin-sidebar {
display: none !important;
/* สั่งโชว์เมนูคอมพิวเตอร์ */
}
.mobile-sidebar {
display: block !important;
/* สั่งโชว์เมนูมือถือ */
}
🤖 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/Adminsidebar.css` around lines 96 -
105, Update the `@media` (max-width: 430px) rules in Adminsidebar.css so
.admin-sidebar remains hidden while .mobile-sidebar is displayed. Remove or
override the !important hide declaration for .mobile-sidebar to keep mobile
navigation visible at this breakpoint.

Comment on lines 41 to +47
const handleResolve = () => {
if (selectedImages.length === 0) {
setErrorMsg('กรุณาอัปโหลดรูปภาพอย่างน้อย 1 รูปเพื่อเป็นหลักฐานการซ่อมแซม');
return;
}
setErrorMsg('');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Pass minImages={1} to ImageUploader to match the new validation.

handleResolve requires at least one image before allowing resolution. The ImageUploader rendered at Line 164 does not receive minImages, so it defaults to 0 and shows only the "ไม่เกิน 3 รูป" label. The user sees no required-image indicator until the error message appears after clicking "บันทึกและปิดงาน". AddIssue.jsx and EditIssue.jsx both pass minImages={1} for the same requirement.

🖊️ Proposed fix
                     <ImageUploader 
                         selectedImages={selectedImages}
                         fileInputRef={fileInputRef}
                         onImageChange={handleImageChange}
                         onRemoveImage={removeImage}
                         maxImages={3}
+                        minImages={1}
                     />
🤖 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/TicketActionPanel.jsx` around lines 41
- 47, Update the ImageUploader rendered by TicketActionPanel to pass
minImages={1}, aligning its required-image indicator with the existing
handleResolve validation and the AddIssue/EditIssue usage.

Comment on lines +18 to +33
.btn-toggle-filter {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 20px;

border-radius: 30px;
border: 2px solid #e0e0e0;
background-color: #ffffff;

font-size: 15px;
color: #334155;

cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Resolve the Stylelint errors.

Stylelint rejects the blank declaration spacing in .btn-toggle-filter. It also rejects the camel-case fadeInDown keyframe name. Remove the blank lines, rename the keyframe to fade-in-down, and update the animation reference.

Also applies to: 82-119

🧰 Tools
🪛 Stylelint (17.14.1)

[error] 24-24: Expected no empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)


[error] 28-28: Expected no empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)


[error] 31-31: Expected no empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🤖 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/componentsStyles/AdvancedFilterPanel.css` around lines
18 - 33, Update the .btn-toggle-filter CSS block and the related declarations
through the additionally affected section to remove blank lines between
declaration groups. Rename the fadeInDown keyframe to fade-in-down and update
every animation reference to use the new kebab-case name.

Source: Linters/SAST tools

-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
word-break: break-word;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- CSS files ---\n'
cat -n client/src/components/componentsStyles/SimilarTickets.css | sed -n '140,156p'
printf '\n--- CardPendingProblem occurrences ---\n'
cat -n client/src/components/componentsStyles/CardPendingProblem.css | sed -n '24,140p'

printf '\n--- Stylelint config references ---\n'
for f in package.json .stylelintrc .stylelintrc.json .stylelintrc.js .stylelintrc.yml .stylelintrc.yaml .stylelint.config.js; do
  if [ -f "$f" ]; then
    echo "### $f"
    sed -n '1,240p' "$f"
  fi
done

printf '\n--- package stylelint deps ---\n'
if [ -f package.json ]; then
  node - <<'JS'
const fs = require('fs');
const p = JSON.parse(fs.readFileSync('package.json','utf8'));
for (const sec of ['dependencies','devDependencies']) {
  if (p[sec]) {
    for (const [k,v] of Object.entries(p[sec])) {
      if (k.includes('stylelint') || k.includes('css')) console.log(sec, k, v);
    }
  }
}
JS
fi

Repository: CNPJB/trouble-tracking-system-project

Length of output: 266


🌐 Web query:

MDN CSS word-break break-word deprecated overflow-wrap anywhere

💡 Result:

The word-break: break-word value is officially deprecated and should be avoided in modern CSS [1][2]. It is recommended to use overflow-wrap: anywhere instead [2]. Key technical details: 1. Deprecation Status: The break-word value for the word-break property is deprecated by the W3C CSS Text Module Level 3 specification [3][4]. Authors are advised against using it in new style sheets [4]. 2. Functional Equivalence: When word-break: break-word is used, it behaves as if overflow-wrap: anywhere and word-break: normal were both applied [1][3]. It essentially forces a line break at an arbitrary point within an unbreakable string (such as a long URL or word) if that string would otherwise overflow its container [5][6]. 3. Why overflow-wrap: anywhere is preferred: - overflow-wrap: anywhere is the modern, standardized way to achieve this wrapping behavior [6]. - Unlike overflow-wrap: break-word, the anywhere value considers the length of the string when calculating min-content intrinsic sizes, which helps prevent flex containers from expanding beyond their intended limits [5][6]. - It is broadly supported across all modern browsers [7]. If you need to support the deprecated behavior using standard properties, you can achieve the same effect by combining overflow-wrap: anywhere and word-break: normal [1][8].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- SimilarTickets occurrences ---'
sed -n '140,156p' client/src/components/componentsStyles/SimilarTickets.css | cat -n

printf '%s\n' ''
printf '%s\n' '--- CardPendingProblem occurrences ---'
sed -n '24,140p' client/src/components/componentsStyles/CardPendingProblem.css | cat -n

printf '%s\n' ''
printf '%s\n' '--- all word-break: break-word occurrences ---'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' 'word-break\s*:\s*break-word' .

printf '%s\n' ''
printf '%s\n' '--- stylelint config references ---'
for f in package.json .stylelintrc .stylelintrc.json .stylelintrc.js .stylelintrc.yml .stylelintrc.yaml .stylelint.config.js; do
  if [ -f "$f" ]; then
    echo "### $f"
    sed -n '1,240p' "$f"
  fi
done

printf '%s\n' ''
printf '%s\n' '--- package stylelint deps ---'
if [ -f package.json ]; then
  node - <<'JS'
const fs = require('fs');
const p = JSON.parse(fs.readFileSync('package.json','utf8'));
for (const sec of ['dependencies','devDependencies']) {
  if (p[sec]) {
    for (const [k,v] of Object.entries(p[sec])) {
      if (k.toLowerCase().includes('stylelint') || k.toLowerCase().includes('css')) console.log(sec, k, v);
    }
  }
}
JS
fi

Repository: CNPJB/trouble-tracking-system-project

Length of output: 4362


Replace word-break: break-word with overflow-wrap: anywhere.

word-break: break-word is deprecated, while overflow-wrap: anywhere provides the same modern behavior. Replace it in one .ticket-info p rule and all five CardPendingProblem.css occurrences.

🧰 Tools
🪛 Stylelint (17.14.1)

[error] 149-149: Deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)

(declaration-property-value-keyword-no-deprecated)

📍 Affects 2 files
  • client/src/components/componentsStyles/SimilarTickets.css#L149-L149 (this comment)
  • client/src/components/componentsStyles/CardPendingProblem.css#L30-L30
  • client/src/components/componentsStyles/CardPendingProblem.css#L103-L103
  • client/src/components/componentsStyles/CardPendingProblem.css#L123-L123
  • client/src/components/componentsStyles/CardPendingProblem.css#L134-L134
🤖 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/componentsStyles/SimilarTickets.css` at line 149,
Replace the deprecated word-break declaration with overflow-wrap: anywhere in
the .ticket-info p rule in
client/src/components/componentsStyles/SimilarTickets.css:149-149 and all five
occurrences in client/src/components/componentsStyles/CardPendingProblem.css at
30-30, 103-103, 123-123, and 134-134.

Source: Linters/SAST tools

Comment on lines +61 to +89
.status-pending {
background-color: var(--status-pending-bg, #f59e0b);
color: #ffffff;
}

.status-in_progress {
background-color: var(--status-in-progress-bg, #3b82f6);
color: #ffffff;
}

.status-resolved {
background-color: var(--status-resolved-bg, #10b981);
color: #ffffff;
}

.status-rejected {
background-color: var(--status-reject-bg, #ef4444);
color: #ffffff;
}

.status-canceled {
background-color: var(--status-canceled-bg, #6b7280);
color: #ffffff;
}

.status-duplicate {
background-color: var(--status-duplicate-bg, #8b5cf6);
color: #ffffff;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use status colors with sufficient text contrast.

The default white text does not have sufficient contrast on #f59e0b, #3b82f6, #10b981, #ef4444, and #8b5cf6. Use darker backgrounds or dark text that meets a 4.5:1 contrast ratio. Validate the resolved --status-* values 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 `@client/src/pages/adminPage/IssueManagementDetail.css` around lines 61 - 89,
The status classes in IssueManagementDetail.css use white text with insufficient
contrast on several default backgrounds. Update the affected .status-pending,
.status-in_progress, .status-resolved, .status-rejected, and .status-duplicate
rules by using accessible darker backgrounds or dark text, and ensure the
resolved --status-* custom-property values also meet a 4.5:1 contrast ratio.

Comment on lines +438 to +463
width: 750px;
}

.detail-layout {
gap: 15px;
max-width: 750px;
}
}

@media (max-width: 820px) {
.info-card-detail {
max-width: 520px;
}

.issue-detail-container {
padding: 20px 0;
}

.ticket-img-admin-zone {
grid-template-columns: 1fr;
gap: 20px;
}

.gallery-admin {
grid-template-columns: repeat(2, 1fr);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove the incompatible fixed layout widths.

At this breakpoint, .detail-layout has max-width: 750px, .info-card-detail has width: 750px, and .action-card has min-width: 400px. The two grid columns and gap cannot fit in 750px. The page will overflow horizontally on common desktop and tablet widths. The 820px breakpoint also keeps the two-column layout.

Remove the fixed card width. Set the layout to a single column before its available width cannot contain both panels.

🤖 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/IssueManagementDetail.css` around lines 438 - 463,
Remove the fixed width and minimum-width constraints from .info-card-detail and
.action-card, and update .detail-layout so it switches to a single-column grid
at a breakpoint where both panels cannot fit. Ensure the `@media` (max-width:
820px) rules preserve the single-column layout and prevent horizontal overflow.

Comment on lines +328 to +342
<label>ประเภทปัญหา <span style={{ color: 'red' }}>*</span></label>
<select name="categoryId" onChange={handleChange} value={formData.categoryId} required>
<option value="">เลือกประเภทปัญหา</option>
{activeCategories.map(c => <option key={c.ticketCtgId} value={c.ticketCtgId}>{c.ticketCtgName}</option>)}
</select>
</div>
<div className="form-group">
<label>หัวข้อปัญหา <span>*</span></label>
<label>หัวข้อปัญหา <span style={{ color: 'red' }}>*</span></label>
<input type="text" name="title" onChange={handleChange} value={formData.title} required />
</div>
</div>

<div className="form-row">
<div className="form-group">
<label>สถานที่ <span>*</span></label>
<label>สถานที่ <span style={{ color: 'red' }}>*</span></label>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add the same 20-character title limit used in AddIssue.jsx.

AddIssue.jsx now enforces maxLength={20} on the title field and shows a character counter. The title field here (Line 336) has neither. A user can edit an existing ticket's title past 20 characters through this form, producing data that is inconsistent with tickets created through AddIssue.jsx and that may not fit UI elements sized for the 20-character limit.

🖊️ Proposed fix
                         <div className="form-group">
-                            <label>หัวข้อปัญหา <span style={{ color: 'red' }}>*</span></label>
-                            <input type="text" name="title" onChange={handleChange} value={formData.title} required />
+                            <div className="label-with-counter">
+                                <label>หัวข้อปัญหา <span style={{ color: 'red' }}>*</span></label>
+                                <span className="char-counter">{formData.title.length}/20</span>
+                            </div>
+                            <input type="text" name="title" onChange={handleChange} value={formData.title} maxLength={20} required />
                         </div>
📝 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
<label>ประเภทปัญหา <span style={{ color: 'red' }}>*</span></label>
<select name="categoryId" onChange={handleChange} value={formData.categoryId} required>
<option value="">เลือกประเภทปัญหา</option>
{activeCategories.map(c => <option key={c.ticketCtgId} value={c.ticketCtgId}>{c.ticketCtgName}</option>)}
</select>
</div>
<div className="form-group">
<label>หัวข้อปัญหา <span>*</span></label>
<label>หัวข้อปัญหา <span style={{ color: 'red' }}>*</span></label>
<input type="text" name="title" onChange={handleChange} value={formData.title} required />
</div>
</div>
<div className="form-row">
<div className="form-group">
<label>สถานที่ <span>*</span></label>
<label>สถานที่ <span style={{ color: 'red' }}>*</span></label>
<label>ประเภทปัญหา <span style={{ color: 'red' }}>*</span></label>
<select name="categoryId" onChange={handleChange} value={formData.categoryId} required>
<option value="">เลือกประเภทปัญหา</option>
{activeCategories.map(c => <option key={c.ticketCtgId} value={c.ticketCtgId}>{c.ticketCtgName}</option>)}
</select>
</div>
<div className="form-group">
<div className="label-with-counter">
<label>หัวข้อปัญหา <span style={{ color: 'red' }}>*</span></label>
<span className="char-counter">{formData.title.length}/20</span>
</div>
<input type="text" name="title" onChange={handleChange} value={formData.title} maxLength={20} required />
</div>
</div>
<div className="form-row">
<div className="form-group">
<label>สถานที่ <span style={{ color: 'red' }}>*</span></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/EditIssue.jsx` around lines 328 - 342, Update the title
field in the EditIssue form, identifiable by name="title" and formData.title, to
enforce maxLength={20} and display the same character counter behavior used by
AddIssue.jsx. Preserve the existing required, value, and change-handler
behavior.

Comment on lines +65 to +68
const activeDropdownFiltersCount =
(selectedCategory ? 1 : 0) +
(selectedLocation ? 1 : 0) +
(selectedStatus !== 'pending,in_progress,duplicate,resolved,canceled,rejected' ? 1 : 0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Synchronize the status select when a tab resets filters.

handleTabChange resets the query status to allowedStatuses.join(','), but it does not reset selectedStatus. After a user selects rejected and changes tabs, the list contains all statuses while the select and active-filter badge still show rejected.

Set selectedStatus to the same all-status value when handleTabChange applies baseFilters.

Proposed fix
 const handleTabChange = (tabId) => {
     setActiveTab(tabId);
+    setSelectedStatus(allowedStatuses.join(','));

     const baseFilters = {

Also applies to: 261-275

🤖 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/Tracking.jsx` around lines 65 - 68, Update handleTabChange
so that when it applies baseFilters and resets the query status to
allowedStatuses.join(','), it also sets selectedStatus to that same all-status
value. Keep the select value and activeDropdownFiltersCount synchronized with
the reset filters.

Comment on lines +448 to 475
// Auto-Unmerge: หากตั๋วใบนี้มีลูก ให้ทำการปลดแอกตั๋วลูกทั้งหมด
if (ticketToCancel._count.subTickets > 0) {
await tx.ticket.updateMany({
where: {
parentTicketId: id,
ticketStatus: 'duplicate'
},
data: {
ticketStatus: 'pending',
parentTicketId: null,
updatedAt: new Date()
}
});
}

return {
canceledTicket,
releasedSubTicketsCount: ticketToCancel._count.subTickets
};
});

res.status(200).json({
success: true,
message: "Cancel ticket Successfully!",
data: canceledTicket
message: result.releasedSubTicketsCount > 0
? `ยกเลิกรายการสำเร็จ และระบบได้แยกตั๋วที่ถูกรวม ${result.releasedSubTicketsCount} รายการกลับสู่กระดาน`
: "ยกเลิกรายการแจ้งปัญหาสำเร็จ",
data: result.canceledTicket
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Derive releasedSubTicketsCount from the actual updateMany result, not from the total child count.

Line 465 reports ticketToCancel._count.subTickets, which counts all children with parentTicketId: id regardless of status. The updateMany at Lines 450-461 only releases children with ticketStatus: 'duplicate'. If a subticket's status ever diverges from 'duplicate' while the parent is still pending, the reported count would not match the number of tickets actually released. Use the updateMany result's count field so the message always matches the actual mutation.

🖊️ Proposed fix
-            // Auto-Unmerge: หากตั๋วใบนี้มีลูก ให้ทำการปลดแอกตั๋วลูกทั้งหมด
-            if (ticketToCancel._count.subTickets > 0) {
-                await tx.ticket.updateMany({
-                    where: { 
-                        parentTicketId: id,
-                        ticketStatus: 'duplicate'
-                    },
-                    data: {
-                        ticketStatus: 'pending', 
-                        parentTicketId: null,    
-                        updatedAt: new Date()
-                    }
-                });
-            }
-
-            return {
-                canceledTicket,
-                releasedSubTicketsCount: ticketToCancel._count.subTickets
-            };
+            // Auto-Unmerge: หากตั๋วใบนี้มีลูก ให้ทำการปลดแอกตั๋วลูกทั้งหมด
+            let releasedSubTicketsCount = 0;
+            if (ticketToCancel._count.subTickets > 0) {
+                const releaseResult = await tx.ticket.updateMany({
+                    where: { 
+                        parentTicketId: id,
+                        ticketStatus: 'duplicate'
+                    },
+                    data: {
+                        ticketStatus: 'pending', 
+                        parentTicketId: null,    
+                        updatedAt: new Date()
+                    }
+                });
+                releasedSubTicketsCount = releaseResult.count;
+            }
+
+            return {
+                canceledTicket,
+                releasedSubTicketsCount
+            };
📝 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
// Auto-Unmerge: หากตั๋วใบนี้มีลูก ให้ทำการปลดแอกตั๋วลูกทั้งหมด
if (ticketToCancel._count.subTickets > 0) {
await tx.ticket.updateMany({
where: {
parentTicketId: id,
ticketStatus: 'duplicate'
},
data: {
ticketStatus: 'pending',
parentTicketId: null,
updatedAt: new Date()
}
});
}
return {
canceledTicket,
releasedSubTicketsCount: ticketToCancel._count.subTickets
};
});
res.status(200).json({
success: true,
message: "Cancel ticket Successfully!",
data: canceledTicket
message: result.releasedSubTicketsCount > 0
? `ยกเลิกรายการสำเร็จ และระบบได้แยกตั๋วที่ถูกรวม ${result.releasedSubTicketsCount} รายการกลับสู่กระดาน`
: "ยกเลิกรายการแจ้งปัญหาสำเร็จ",
data: result.canceledTicket
});
// Auto-Unmerge: หากตั๋วใบนี้มีลูก ให้ทำการปลดแอกตั๋วลูกทั้งหมด
let releasedSubTicketsCount = 0;
if (ticketToCancel._count.subTickets > 0) {
const releaseResult = await tx.ticket.updateMany({
where: {
parentTicketId: id,
ticketStatus: 'duplicate'
},
data: {
ticketStatus: 'pending',
parentTicketId: null,
updatedAt: new Date()
}
});
releasedSubTicketsCount = releaseResult.count;
}
return {
canceledTicket,
releasedSubTicketsCount
};
});
res.status(200).json({
success: true,
message: result.releasedSubTicketsCount > 0
? `ยกเลิกรายการสำเร็จ และระบบได้แยกตั๋วที่ถูกรวม ${result.releasedSubTicketsCount} รายการกลับสู่กระดาน`
: "ยกเลิกรายการแจ้งปัญหาสำเร็จ",
data: result.canceledTicket
});
🤖 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/ticketControllers.js` around lines 448 - 475, Update the
auto-unmerge logic in the transaction to capture the result of the
`tx.ticket.updateMany` call and use its `count` field for
`releasedSubTicketsCount`. Replace the current
`ticketToCancel._count.subTickets` value while preserving the existing filter
and response messaging.

@CNPJB
CNPJB merged commit bbe2178 into develop Jul 31, 2026
1 check passed
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.

1 participant