[VW-259] Work Order Ticket View#146
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
|
Warning Review limit reached
Next review available in: 58 seconds Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
✨ Finishing Touches🧪 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.
A Page under Settings to adjust the color of the tags mainly shown on the Work Orders Ticket Tracking Page, Seemed better than having hard coded values stored somewhere.
There was a problem hiding this comment.
Added the concept of child rows that accordion out from under their parent, mainly in the Work Orders Ticket Table.
There was a problem hiding this comment.
Is Tracking a good name for this page? Would Work Tickets be better? Ticket Tracking? Tracking just seems slightly off to me
There was a problem hiding this comment.
I like "Work Tickets". Siemens uses "Service Tickets"
There was a problem hiding this comment.
/tickets , /work-tickets , /work-orders
There was a problem hiding this comment.
A page under settings to add, edit, and remove departments. Also lists out Users and tickets attached to those departments
There was a problem hiding this comment.
The bottom most component of the Ticket Detail Page, shows an audit log of all changes pertaining to the Ticket
There was a problem hiding this comment.
The detail page of a Work Order Ticket
Shows all info pertaining to the Ticket, and allows editing of fields, attaching sub-tickets, attaching assets & files, and a comment / Audit log
There was a problem hiding this comment.
Linked Files wasn't sketched out, will need to make a pass through to add features later
There was a problem hiding this comment.
Linked Files wasn't sketched out, will need to make a pass through to add features later
I'm considering moving and linked assets this to their own tab after reviewing the notifications UI with Matt, just to give you a heads up about how that'll be displayed
| NETWORK_REMEDIATION: "Network Remediation", | ||
| NEW_ASSET_PROCUREMENT: "New Asset Procurement", | ||
| OTHER: "Other", | ||
| }; |
There was a problem hiding this comment.
Should we consider making statuses and categories user configurable like departments?
There was a problem hiding this comment.
I think the answer to that depends on what kind of data we see from other platforms -- if we need to make configurable statuses because hospitals are already using complex patterns, then yes, but for now no.
There was a problem hiding this comment.
Main Ticket Tracking Page.
Not 100% sure what we want the suggested tab to show, but current it shows any ticket / subticket that was created from an email / integration
Also the watch ticket button does track in the db, but doesn't do much outside of controlling the UI element for now.
There was a problem hiding this comment.
Also, do we want to expand the width of the table on this page? A few of the columns are hidden behind horizontal scrolling. (At least on my monitor)
There was a problem hiding this comment.
Not 100% sure what we want the suggested tab to show, but current it shows any ticket / subticket that was created from an email / integration
This will be a future ticket -- the AI tool will determine tickets that are important, and suggest them to the user. Currently figuring out how to do that for notifications.
Also, do we want to expand the width of the table on this page?
I couldn't see any way to eliminate horizontal scrolling besides eliminating columns
There was a problem hiding this comment.
All the plumbing for the audit log
There was a problem hiding this comment.
Actionable comments posted: 11
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (12)
src/features/tracking/components/tracking.test.tsx-223-232 (1)
223-232: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAssert the full search value, not the last keystroke.
TrackingSearchjust forwardsonSearchChangeintoEntitySearch, so after typing"icu"the final call should reflect the current input value, not just"u". This assertion encodes the wrong contract and can fail against a correct input implementation.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/tracking.test.tsx` around lines 223 - 232, The test in TrackingSearch is asserting the wrong contract by checking only the last keystroke instead of the full input value. Update the expectation in the tracking.test.tsx case for TrackingSearch so it verifies the final call to mockOnSearchChange with the complete typed search string after user.type on EntitySearch, rather than just "u"; keep the call-count assertion only if it still matches the intended behavior.prisma/seed.ts-2014-2014 (1)
2014-2014: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winAlign seeded comment timestamps with
lastCommentAt.
lastCommentAtandseenAtare fixed for deterministic unread state, but comments currently use default creation timestamps. That can make the unread indicator diverge from the visible comment timeline.Proposed fix
comments: ticket.comments?.length ? { create: ticket.comments.map((body) => ({ body, authorId: userId, + createdAt: COMMENTED_AT, + updatedAt: COMMENTED_AT, })), } : undefined,Also applies to: 2033-2039
🤖 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 `@prisma/seed.ts` at line 2014, Seeded comments are still getting default creation timestamps, which can drift from the fixed `lastCommentAt`/`seenAt` values used for deterministic unread state. Update the comment-seeding logic in `prisma/seed.ts` so the same timestamp source used for `lastCommentAt` is also applied to the generated comment records, keeping the visible comment timeline aligned with unread state. Make this change in the ticket/comment creation paths around the existing `lastCommentAt` assignment so both timestamps stay consistent.src/features/tracking/types.ts-316-320 (1)
316-320: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winKeep
locationopaque and nullable in the response schema.
Asset.locationis a nullable Prisma JSON field;z.any()widens the response type and does not document thenullcontract. Usez.unknown().nullable()here.Proposed fix
const detailLinkedAssetSchema = linkedAssetSchema.extend({ macAddress: z.string().nullable(), - // Prisma's Json column. Using z.any() so the inferred TS type is `any`, - // which stays assignable to Prisma.JsonValue on the UI side. - location: z.any(), + // Prisma's nullable Json column; keep it opaque at the response boundary. + location: z.unknown().nullable(), deviceGroupId: z.string(),Based on learnings, Zod response schemas for Prisma
Json?/JsonValuefields should usez.unknown().nullable()at the response boundary.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/types.ts` around lines 316 - 320, The response schema for `detailLinkedAssetSchema` currently uses `z.any()` for `location`, which hides the nullable JSON contract and over-widens the type. Update `detailLinkedAssetSchema` in `types.ts` to model `Asset.location` as opaque and nullable by using `z.unknown().nullable()` instead of `z.any()`, while keeping the rest of the schema unchanged.Source: Learnings
src/features/tracking/server/routers.ts-498-525 (1)
498-525: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winReject same-parent reattaches before recording activity.
If
input.childIdis already attached toinput.parentId, this update is a no-op onparentIdbut still writes a freshCHILD_ATTACHEDrow. FetchparentIdwith the child and short-circuit before the update/activity write.Suggested change
const child = await prisma.workOrderTicket.findUnique({ where: { id: input.childId }, - select: { _count: { select: { children: true } } }, + select: { parentId: true, _count: { select: { children: true } } }, }); if (!child) { throw new TRPCError({ code: "NOT_FOUND", message: "Ticket not found", }); } + if (child.parentId === input.parentId) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Ticket is already attached to this parent", + }); + } if (child._count.children > 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 `@src/features/tracking/server/routers.ts` around lines 498 - 525, The attachment flow in the work order ticket router is allowing same-parent reattaches to write duplicate CHILD_ATTACHED activity even when nothing changes. Update the child lookup in the attach handler to also fetch parentId, and in the attach path of the router logic short-circuit when input.childId already belongs to input.parentId before calling tx.workOrderTicket.update or recordChildActivity. Keep the existing checks for missing tickets and existing sub-tickets, but skip the transaction work when the relationship is already in place.src/features/departments/server/routers.ts-7-8 (1)
7-8: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winTrim
namebefore validating it.
min(1)still accepts whitespace-only names, and the duplicate checks on Lines 27-29 and 44-46 will treat"IT"and"IT "as different departments.Suggested fix
const departmentInputSchema = z.object({ - name: z.string().min(1).max(80), + name: z.string().trim().min(1).max(80), description: z.string().max(2000).nullish(), color: z.string().nullish(), });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/departments/server/routers.ts` around lines 7 - 8, The department name validation in departmentInputSchema should trim leading/trailing whitespace before applying the non-empty and length checks, since min(1) still accepts whitespace-only input and causes duplicate handling issues in the department router flow. Update the zod schema used by the server router so name is normalized before validation, and ensure the create/update logic in the department router continues to use the trimmed value when performing the duplicate checks and saving the department.src/app/(dashboard)/(rest)/tracking/[ticketId]/page.tsx-19-27 (1)
19-27: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winThe local fallback does not cover the initial fetch path.
Line 19 can reject before Line 23 ever renders, so a failed first load bypasses
<TicketDetailError />and goes straight to the route-level error handling instead. If you want this screen-level fallback on first render, handle the server-side failure before returning JSX.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/`(dashboard)/(rest)/tracking/[ticketId]/page.tsx around lines 19 - 27, The initial prefetch in the tracking page can throw before the JSX tree mounts, so the local ErrorBoundary around TicketDetailPage never gets a chance to render its fallback. Update the page component that calls prefetchTrackingTicket(ticketId) so the server-side fetch failure is handled before returning the HydrateClient/ErrorBoundary/Suspense tree, and use the screen-level fallback path there if the first load fails.src/features/tracking/components/ticket-detail/add-comment-form.tsx-39-51 (1)
39-51: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winGive the textarea an accessible name.
Right now this field only has a placeholder, which is not a reliable label for assistive tech. Add a
<label>or at least anaria-labelso the comment composer is identifiable.Suggested fix
<Textarea + aria-label="Comment" value={body} onChange={(e) => setBody(e.target.value)} placeholder="Write a comment..."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/ticket-detail/add-comment-form.tsx` around lines 39 - 51, The comment composer textarea in add-comment-form.tsx currently relies only on a placeholder, so give the Textarea in AddCommentForm an accessible name by adding a proper label or an aria-label tied to that field. Update the existing Textarea element used for the comment body so assistive tech can identify it reliably, while keeping the current submit and disabled behavior unchanged.src/features/tracking/components/ticket-detail/sub-tickets.tsx-43-59 (1)
43-59: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDon't render the empty state while child candidates are still loading.
Right after opening the popover,
candidatesis still unresolved, so the UI can briefly say "No eligible tickets found." before the query finishes. Please separate loading from empty so users don't get a false negative on slower requests.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/ticket-detail/sub-tickets.tsx` around lines 43 - 59, The empty state in sub-tickets is being shown before useAttachableChildren(parentId) finishes loading, causing a false “No eligible tickets found” flash. Update the SubTickets popover rendering to distinguish loading from empty by checking the query’s loading/fetching state alongside candidates. Keep CommandEmpty in the empty-only branch, and show nothing or a loading placeholder until the candidates list is resolved.src/features/tracking/components/ticket-detail/edit-form.tsx-265-301 (1)
265-301: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winMake the description tabs controlled.
defaultValueis only applied on the first mount. If the user removes the currently selected department, the tabs can keep a stale value and render no textarea until they click a different tab. Keep the active department id in state and resync it whenselectedDepartmentschanges.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/ticket-detail/edit-form.tsx` around lines 265 - 301, The ticket description tabs are uncontrolled because Tabs uses defaultValue, so the active tab can become stale when selectedDepartments changes and the current department is removed. Update the Tabs usage in edit-form.tsx to use a controlled value tied to state, and resync that state whenever selectedDepartments changes so the active tab always points to a valid department id. Refer to the Tabs, TabsTrigger, and TabsContent setup around the selectedDepartments map when making the change.src/features/tracking/components/ticket-detail/linked-assets.tsx-29-45 (1)
29-45: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDon't show the empty state before the candidate query resolves.
When this popover opens on a slow response,
candidatesis stillundefined, soCommandEmptyimmediately renders "No eligible assets found." even though results may arrive a moment later. Please gate the empty message behind the query's loading state and render a loading row first.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/ticket-detail/linked-assets.tsx` around lines 29 - 45, The linked-assets popover in the ticket detail view shows the empty state too early because `candidates` can still be undefined while `useAttachableAssets(ticketId)` is loading. Update `linked-assets.tsx` to gate `CommandEmpty` behind the query’s loading/settling state and render a loading row first, using the existing `useAttachableAssets` and `CommandEmpty`/`CommandList` flow so “No eligible assets found.” only appears after the candidate query has resolved.src/features/departments/components/departments.tsx-68-72 (1)
68-72: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winReset dialog state when
openorinitialchanges.Because the create dialog stays mounted at Line 237, these
useStateinitializers only run once. After canceling or creating a department, reopening the dialog will still show the previous values.Suggested fix
-import { useState } from "react"; +import { useEffect, useState } from "react"; @@ const [color, setColor] = useState<TagHue>( (initial?.color as TagHue | null) ?? DEFAULT_HUE, ); + + useEffect(() => { + if (!open) return; + setName(initial?.name ?? ""); + setDescription(initial?.description ?? ""); + setColor((initial?.color as TagHue | null) ?? DEFAULT_HUE); + }, [open, initial]);Also applies to: 237-243
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/departments/components/departments.tsx` around lines 68 - 72, Reset the department dialog form state when the dialog is reopened or when the `initial` department changes, because the `useState` initializers in `Departments` only run once while the mounted dialog stays hidden. Update the state setup around `name`, `description`, and `color` to re-sync from `initial` whenever `open` or `initial` changes, using an effect or by remounting the form from the dialog render path in the `Departments` component. Ensure the create/edit dialog shows fresh values after canceling, saving, or switching departments.src/features/tag-colors/components/tag-colors.tsx-66-71 (1)
66-71: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winBlock overlapping color writes from the picker.
Both handlers fire mutations immediately and keep the picker enabled, so rapid clicks can send concurrent updates and let request ordering persist the wrong final hue.
Suggested fix
<ColorPicker value={dept.color} + disabled={updateMutation.isPending} onChange={(hue: TagHue) => updateMutation.mutate({ id: dept.id, color: hue }) } /> @@ <ColorPicker value={color} + disabled={setColor.isPending} onChange={(hue: TagHue) => setColor.mutate({ category, color: hue }) } />Also applies to: 110-115
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tag-colors/components/tag-colors.tsx` around lines 66 - 71, The TagColors picker is allowing overlapping writes because ColorPicker’s onChange in the tag-colors component fires mutations immediately without any in-flight guard. Update the mutation flow around updateMutation and the ColorPicker usage so only one hue update can be sent at a time, and disable or ignore further picker changes while a request is pending. Apply the same protection in both affected ColorPicker handlers in TagColors so rapid clicks cannot enqueue concurrent updates and overwrite the final color out of order.
🧹 Nitpick comments (24)
src/features/tracking/components/tracking.tsx (1)
16-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the
@/alias for these feature imports.This file mixes aliased imports with relative
../and./paths. As per coding guidelines, "Use@/* import alias consistently throughout the codebase to reference src/* files".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/tracking.tsx` around lines 16 - 20, The imports in tracking.tsx are mixing relative paths with the project alias, so update the feature-level imports to use the `@/` alias consistently. Adjust the imports for useSuspenseTrackingTickets, useTrackingParams, TRACKING_TABS, TrackingTab, TrackingTicketChildRow, TrackingTicketRow, and trackingColumns so they all reference their src locations through `@/` instead of ../ or ./ paths.Source: Coding guidelines
src/features/tracking/components/columns.tsx (1)
24-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the
@/alias for the local tracking imports here too.These relative imports diverge from the repo’s
src/*import convention. As per coding guidelines, "Use@/* import alias consistently throughout the codebase to reference src/* files".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/columns.tsx` around lines 24 - 26, The local tracking imports in columns.tsx use relative paths instead of the repo’s `@/` alias, so update the imports for useSetWatching, TrackingTicketChildRow, and CategoryChip/statusHue/statusLabels to use the same `@/src-style` convention as the rest of the codebase. Keep the existing symbols and module targets the same, just switch these imports to the alias-based form so the file is consistent with the tracking feature’s import pattern.Source: Coding guidelines
src/features/tracking/server/routers.ts (2)
14-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
@/*imports for the local tracking modules here.This file mixes alias imports with
../params,../types, and./activities, which breaks the repo’s import convention forsrc/*modules. As per coding guidelines, use@/*import alias consistently throughout the codebase to referencesrc/*files.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/server/routers.ts` around lines 14 - 29, The imports in routers.ts are using relative paths for local tracking modules instead of the repo’s `@/`* alias convention. Update the TRACKING_TABS, type/schema imports, and the activity helpers imported from the nearby modules to use `@/src-style` alias paths consistently, keeping the same symbols such as TRACKING_TABS, paginatedWorkOrderListResponseSchema, workOrderListInclude, and recordUpdateActivities.Source: Coding guidelines
339-355: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPrefer
z.enum(...)overz.nativeEnum(...)in this repo.The shared tracking schemas already use
z.enum(TicketStatus)/z.enum(TicketCategory), so keepingz.nativeEnum()here reintroduces the deprecated Zod v4 pattern and drifts from the rest of the feature contract.Based on learnings, this repository treats
z.enum(PrismaEnumObject)as the Zod v4 pattern and avoidsz.nativeEnum().Suggested change
- status: z.nativeEnum(TicketStatus).optional(), - category: z.nativeEnum(TicketCategory).optional(), + status: z.enum(TicketStatus).optional(), + category: z.enum(TicketCategory).optional(),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/server/routers.ts` around lines 339 - 355, The schema in the tracking router still uses the deprecated Zod v4 pattern via z.nativeEnum for TicketStatus and TicketCategory. Update the validation object in the routers schema to use z.enum with the same enum sources, matching the shared tracking schemas and the repo’s preferred pattern. Keep the rest of the shape unchanged and ensure the identifiers TicketStatus, TicketCategory, and the router schema stay aligned with the shared contract.Source: Learnings
src/features/tag-colors/server/prefetch.ts (1)
1-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd
server-onlyto this prefetch helper.This lives under
server/and is only meant for server-render prefetching, so it should explicitly guard that boundary.As per coding guidelines, mark server utility files with
server-only.Suggested change
+import "server-only"; import { prefetch, trpc } from "`@/trpc/server`";🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tag-colors/server/prefetch.ts` around lines 1 - 5, The prefetch helper `prefetchCategoryColors` is a server-only utility but it does not explicitly guard the server boundary. Add the `server-only` import at the top of this module so `src/features/tag-colors/server/prefetch.ts` is treated as server-only, while keeping the existing `prefetch` and `trpc.tagColors.getCategoryColors.queryOptions()` logic unchanged.Source: Coding guidelines
src/features/tracking/server/params-loader.ts (1)
1-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMark this loader as server-only and switch to the src alias.
This module lives under
server/, so it should enforce that boundary withimport "server-only";. The../paramsimport also breaks the repo’s@/*alias convention.As per coding guidelines, mark server utility files with
server-onlyand use the@/*import alias consistently throughout the codebase.Suggested change
+import "server-only"; import { createLoader } from "nuqs/server"; -import { trackingParams } from "../params"; +import { trackingParams } from "`@/features/tracking/params`";🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/server/params-loader.ts` around lines 1 - 4, Mark this server utility as server-only by adding the server-only import at the top of trackingParamsLoader, and update the trackingParams import to use the repo’s `@/` alias instead of the relative ../ path. Keep the existing createLoader(trackingParams) export intact while enforcing the server boundary and alias convention.Source: Coding guidelines
src/hooks/use-before-unload.ts (1)
1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMark this hook as client-only.
This module uses
useEffectandwindow, so adding"use client"makes the boundary explicit and prevents accidental server imports. As per coding guidelines "Mark server utility files with 'server-only' and client components with 'use client' directive to enforce clear server/client boundaries".Suggested fix
+"use client"; + import { useEffect } 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 `@src/hooks/use-before-unload.ts` at line 1, This hook module should be explicitly client-only because it uses useEffect and window; add the "use client" directive at the top of use-before-unload so the boundary is clear and accidental server imports are prevented. Keep the fix localized to the module containing the hook implementation and ensure any related client-side logic remains under the same client boundary.Source: Coding guidelines
src/features/tracking/hooks/use-tracking-params.ts (1)
4-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the
@/alias for this src import.
../paramsbreaks the repo-wide import convention forsrc/*modules. Switch it to the aliased path so refactors do not depend on directory depth. As per coding guidelines "Use@/* import alias consistently throughout the codebase to reference src/* files".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/hooks/use-tracking-params.ts` at line 4, The import in use-tracking-params.ts is using a relative path for a src module, which breaks the repo-wide alias convention. Update the trackingParams import in use-tracking-params to use the `@/` alias instead of "../params", following the existing `@/`* import style used throughout the codebase.Source: Coding guidelines
src/features/tracking/hooks/use-tracking.ts (1)
11-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the
@/alias for the local src import.
./use-tracking-paramsbreaks the repo-wide import convention forsrc/*modules. As per coding guidelines "Use@/* import alias consistently throughout the codebase to reference src/* files".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/hooks/use-tracking.ts` at line 11, The import in useTracking should follow the repo’s src/* alias convention instead of a relative path. Update the use-tracking.ts module to reference use-tracking-params through the `@/` alias so it matches the rest of the codebase and keeps local src imports consistent.Source: Coding guidelines
src/features/tag-colors/context.tsx (1)
5-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the
@/alias for this src import.
./hooks/use-tag-colorsbreaks the repo-wide import convention forsrc/*modules. As per coding guidelines "Use@/* import alias consistently throughout the codebase to reference src/* files".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tag-colors/context.tsx` at line 5, The import in the tag colors context file is using a relative path for a src module instead of the repo-standard `@/` alias. Update the import in the context module that references useSuspenseCategoryColors to use the `@/` alias for the hooks path so it matches the project-wide convention for src/* imports.Source: Coding guidelines
src/features/tracking/components/ticket-detail/index.tsx (1)
37-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the
@/alias for these detail-module imports.These relative src imports are brittle and diverge from the repo convention. As per coding guidelines,
Use@/* import alias consistently throughout the codebase to reference src/* files.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/ticket-detail/index.tsx` around lines 37 - 50, The detail component imports are using brittle relative paths instead of the repo’s `@/` alias. Update the imports in `ticket-detail/index.tsx` for symbols like `ActivityTimeline`, `TicketEditForm`, `LinkedAssetsTabContent`, `CategoryChip`, `formatDate`, `formatScheduled`, `MetadataRow`, `Section`, `statusHue`, `statusLabels`, and `SubTicketsSection` to use the `@/` alias consistently across these src-module references.Source: Coding guidelines
src/features/tracking/components/ticket-detail/add-comment-form.tsx (1)
9-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the repo import alias here.
This relative src import breaks the convention used elsewhere in the PR. As per coding guidelines,
Use@/* import alias consistently throughout the codebase to reference src/* files.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/ticket-detail/add-comment-form.tsx` at line 9, The import in add-comment-form.tsx is using a relative src path instead of the repo alias, so update the useAddTicketComment import to use the consistent `@/`* alias convention used elsewhere in the codebase. Keep the same symbol reference and adjust only the module specifier so this file aligns with the existing import style.Source: Coding guidelines
src/features/tracking/components/ticket-detail/activity-timeline.tsx (1)
20-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winNormalize these imports to the
@/alias.This new component is mixing in relative src paths where the repo standard expects aliases. As per coding guidelines,
Use@/* import alias consistently throughout the codebase to reference src/* files.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/ticket-detail/activity-timeline.tsx` around lines 20 - 28, The imports in the activity timeline component are using relative paths instead of the repository’s standard `@/` alias. Update the import statements in the ticket detail activity timeline module to reference the same symbols (TicketDetail, AddCommentForm, and the shared helpers like categoryLabels, formatDate, Section, statusHue, and statusLabels) through the `@/` alias consistently so all src/* references follow the project convention.Source: Coding guidelines
src/features/tracking/components/ticket-detail/shared.tsx (1)
9-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the
@/alias for the shared type import.
../../typesstill points intosrc/*, so this one should follow the same alias convention as the rest of the module. As per coding guidelines, "Use@/* import alias consistently throughout the codebase to reference src/* files".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/ticket-detail/shared.tsx` at line 9, The shared type import in the `shared.tsx` module is still using a relative path into `src/*` instead of the project alias. Update the `TicketDetail` import in `shared.tsx` to use the `@/` alias consistently with the rest of the codebase, keeping the same symbol reference and moving the import source from the relative `../../types` path to the alias-based equivalent.Source: Coding guidelines
src/features/tracking/components/ticket-detail/sub-tickets.tsx (1)
1-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAlign this new component module with the repo's TSX conventions.
This file exports a React component from a kebab-case filename and still uses relative imports for
src/*modules. Renaming it to PascalCase and moving these imports to@/...will keep the new ticket-detail components consistent with the rest of the App Router code. As per coding guidelines, "Use PascalCase for component filenames (e.g., WorkflowNode.tsx) and kebab-case for utility filenames" and "Use@/* import alias consistently throughout the codebase to reference src/* files".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/ticket-detail/sub-tickets.tsx` around lines 1 - 39, The new React component module is using a kebab-case filename and relative src imports, which is inconsistent with the repo’s TSX conventions. Rename the component file to PascalCase to match other App Router components, and update the imports in this module to use the `@/` alias instead of relative paths. Keep the component symbol itself aligned with the PascalCase filename and preserve the existing exports/behavior.Source: Coding guidelines
src/features/tracking/components/ticket-detail/linked-assets.tsx (1)
1-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAlign this new component module with the repo's TSX conventions.
This file exports a React component from a kebab-case filename and still uses relative imports for
src/*modules. Renaming it to PascalCase and swapping these to@/...will keep the new ticket-detail surface aligned with the repo standard. As per coding guidelines, "Use PascalCase for component filenames (e.g., WorkflowNode.tsx) and kebab-case for utility filenames" and "Use@/* import alias consistently throughout the codebase to reference src/* files".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/ticket-detail/linked-assets.tsx` around lines 1 - 25, The new component module is not aligned with the repo’s TSX naming/import conventions. Rename the component file from kebab-case to PascalCase to match the existing React component pattern, and update the imports in the module to use the `@/`... alias instead of relative paths for src/* references. Make the change in the linked-assets component and ensure any related references to LinkedAssetsTable, use-tracking hooks, and shared types continue to resolve after the rename.Source: Coding guidelines
src/features/tracking/components/ticket-detail/edit-form.tsx (1)
1-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAlign this new component module with the repo's TSX conventions.
This file exports a React component from a kebab-case filename and still uses relative
src/*imports. Renaming the file to PascalCase and switching these imports to@/...will keep this new surface consistent before more callers accumulate. As per coding guidelines, "Use PascalCase for component filenames (e.g., WorkflowNode.tsx) and kebab-case for utility filenames" and "Use@/* import alias consistently throughout the codebase to reference src/* files".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/ticket-detail/edit-form.tsx` around lines 1 - 27, The new ticket detail edit form component should follow the repo’s TSX conventions by using a PascalCase filename for the React component module and replacing the remaining relative src-style imports with consistent `@/`... aliases. Update the component entry that contains the EditForm implementation and its imports (for example useBeforeUnload, use-tracking hooks, types, and local shared/component imports) so the module matches existing component naming and import patterns before it spreads to more callers.Source: Coding guidelines
src/features/tracking/components/ticket-detail/linked-assets-table.tsx (1)
1-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAlign this new component module with the repo's TSX conventions.
This file is a component module but is still named in kebab-case and imports local
src/*code through a relative path. Renaming it to PascalCase and using@/...here will keep the new ticket-detail components consistent with the project standard. As per coding guidelines, "Use PascalCase for component filenames (e.g., WorkflowNode.tsx) and kebab-case for utility filenames" and "Use@/* import alias consistently throughout the codebase to reference src/* files".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tracking/components/ticket-detail/linked-assets-table.tsx` around lines 1 - 20, Rename this component module to PascalCase to match the repo’s TSX component filename convention, since linked-assets-table.tsx is a React component file. Also update the local import from "./shared" to use the `@/` alias so this ticket-detail component follows the project’s standard for referencing src/* modules. Keep the component symbols in this module, like the linked assets table imports and helpers, unchanged aside from the filename and import path alignment.Source: Coding guidelines
src/features/tag-colors/components/tag-colors.tsx (2)
23-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the
@/*alias for localsrc/*imports.These relative imports break the repo’s alias convention and make this module more fragile to folder moves. As per coding guidelines, "Use
@/* import alias consistently throughout the codebase to reference src/* files".Suggested fix
import { useSetCategoryColor, useSuspenseCategoryColors, -} from "../hooks/use-tag-colors"; -import { getChipClass, type TagHue } from "../palette"; -import { ColorPicker } from "./color-picker"; +} from "`@/features/tag-colors/hooks/use-tag-colors`"; +import { getChipClass, type TagHue } from "`@/features/tag-colors/palette`"; +import { ColorPicker } from "`@/features/tag-colors/components/color-picker`";🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tag-colors/components/tag-colors.tsx` around lines 23 - 28, The local imports in tag-colors.tsx violate the repo’s `@/`* alias convention. Update the imports used by the tag-colors component to reference the same modules through the `@/`* alias instead of relative paths, including the hooks/use-tag-colors, palette, and color-picker symbols. Keep the change limited to the import statements in the tag-colors component so the rest of the implementation remains unchanged.Source: Coding guidelines
1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffRename this component file to PascalCase.
tag-colors.tsxis a component module, so it should follow the component-file naming rule (TagColors.tsx). As per coding guidelines, "Use PascalCase for component filenames (e.g., WorkflowNode.tsx)".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tag-colors/components/tag-colors.tsx` at line 1, The component module is using a kebab-case filename instead of the required PascalCase naming convention. Rename the TagColors component file from tag-colors.tsx to TagColors.tsx, and update any imports or references that point to the old filename so the module continues to resolve correctly.Source: Coding guidelines
src/features/tag-colors/components/color-picker.tsx (2)
11-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the
@/*alias for the palette import.This is still a
src/*import, so it should follow the shared alias convention instead of a relative path. As per coding guidelines, "Use@/* import alias consistently throughout the codebase to reference src/* files".Suggested fix
import { formatHueLabel, getSwatchClass, TAG_PALETTE, type TagHue, -} from "../palette"; +} from "`@/features/tag-colors/palette`";🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tag-colors/components/color-picker.tsx` around lines 11 - 16, The palette import in color-picker.tsx is using a relative src/* path instead of the shared `@/`* alias. Update the import that brings in formatHueLabel, getSwatchClass, TAG_PALETTE, and TagHue to use the alias convention consistently with the rest of the codebase, keeping the rest of the component unchanged.Source: Coding guidelines
1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffRename this component file to PascalCase.
color-picker.tsxis a component module, so it should be named likeColorPicker.tsxto match the repo convention. As per coding guidelines, "Use PascalCase for component filenames (e.g., WorkflowNode.tsx)".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/tag-colors/components/color-picker.tsx` at line 1, Rename the component module from color-picker.tsx to PascalCase to match the repository’s component naming convention. Update the file name for the component referenced by the ColorPicker module so it follows the same pattern as other components like WorkflowNode.tsx, and make sure any imports or references that point to the old filename are updated accordingly.Source: Coding guidelines
src/features/departments/components/departments.tsx (2)
1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffRename this component file to PascalCase.
departments.tsxis a component module, so it should follow the component-file convention (Departments.tsx) instead of a utility-style filename. As per coding guidelines, "Use PascalCase for component filenames (e.g., WorkflowNode.tsx)".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/departments/components/departments.tsx` at line 1, The component module uses a lowercase filename, but it should follow the component-file convention and be renamed to PascalCase. Update the Departments component file name from the current lowercase form to Departments.tsx, and make sure any imports or references to the existing departments component path are adjusted to the new filename so the component still resolves correctly.Source: Coding guidelines
47-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the
@/*alias for this hook import.The repo guideline asks for alias imports for
src/*; this relative path will drift as the folder moves. As per coding guidelines, "Use@/* import alias consistently throughout the codebase to reference src/* files".Suggested fix
import { useCreateDepartment, useRemoveDepartment, useSuspenseDepartments, useUpdateDepartment, -} from "../hooks/use-departments"; +} from "`@/features/departments/hooks/use-departments`";🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/departments/components/departments.tsx` around lines 47 - 52, The hook import in the departments component uses a relative path instead of the required `@/`* alias. Update the import in departments.tsx for useCreateDepartment, useRemoveDepartment, useSuspenseDepartments, and useUpdateDepartment to reference the same module through the alias so it stays consistent with the repo convention for src/* imports.Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a10fae60-2d29-43e5-b10e-72eba9e4b4e9
📒 Files selected for processing (47)
prisma/migrations/20260629120000_work_order_ticket_tracking/migration.sqlprisma/schema.prismaprisma/seed.tssrc/app/(dashboard)/(rest)/settings/departments/page.tsxsrc/app/(dashboard)/(rest)/settings/tag-colors/page.tsxsrc/app/(dashboard)/(rest)/tracking/[ticketId]/page.tsxsrc/app/(dashboard)/(rest)/tracking/page.tsxsrc/components/app-sidebar.tsxsrc/components/ui/data-table.tsxsrc/features/departments/components/departments.tsxsrc/features/departments/hooks/use-departments.tssrc/features/departments/server/prefetch.tssrc/features/departments/server/routers.tssrc/features/settings/components/settings-layout.tsxsrc/features/tag-colors/components/color-picker.tsxsrc/features/tag-colors/components/tag-colors.tsxsrc/features/tag-colors/context.tsxsrc/features/tag-colors/hooks/use-tag-colors.tssrc/features/tag-colors/palette.tssrc/features/tag-colors/server/prefetch.tssrc/features/tag-colors/server/routers.tssrc/features/tracking/components/columns.tsxsrc/features/tracking/components/ticket-detail.test.tsxsrc/features/tracking/components/ticket-detail/activity-timeline.tsxsrc/features/tracking/components/ticket-detail/add-comment-form.tsxsrc/features/tracking/components/ticket-detail/department-multi-select.tsxsrc/features/tracking/components/ticket-detail/edit-form.tsxsrc/features/tracking/components/ticket-detail/index.tsxsrc/features/tracking/components/ticket-detail/linked-assets-table.tsxsrc/features/tracking/components/ticket-detail/linked-assets.tsxsrc/features/tracking/components/ticket-detail/shared.tsxsrc/features/tracking/components/ticket-detail/sub-tickets.tsxsrc/features/tracking/components/tracking.test.tsxsrc/features/tracking/components/tracking.tsxsrc/features/tracking/hooks/use-tracking-params.tssrc/features/tracking/hooks/use-tracking.tssrc/features/tracking/params.tssrc/features/tracking/server/activities.tssrc/features/tracking/server/params-loader.tssrc/features/tracking/server/prefetch.tssrc/features/tracking/server/routers.test.tssrc/features/tracking/server/routers.tssrc/features/tracking/types.tssrc/features/user/server/routers.tssrc/hooks/use-before-unload.tssrc/lib/router-utils.tssrc/trpc/routers/_app.ts
| import type { inferInput } from "@trpc/tanstack-react-query"; | ||
| import { prefetch, trpc } from "@/trpc/server"; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Mark this server helper as server-only.
This file is a server utility, but it lacks the boundary guard used in the new routers. Add import "server-only"; so accidental client imports fail fast. As per coding guidelines, "Mark server utility files with 'server-only' and client components with 'use client' directive to enforce clear server/client boundaries."
Suggested fix
+import "server-only";
import type { inferInput } from "`@trpc/tanstack-react-query`";
import { prefetch, trpc } from "`@/trpc/server`";📝 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.
| import type { inferInput } from "@trpc/tanstack-react-query"; | |
| import { prefetch, trpc } from "@/trpc/server"; | |
| import "server-only"; | |
| import type { inferInput } from "`@trpc/tanstack-react-query`"; | |
| import { prefetch, trpc } from "`@/trpc/server`"; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/features/tracking/server/prefetch.ts` around lines 1 - 2, The server
helper in prefetch.ts is missing the server boundary guard, so add the
server-only import at the top of the module. Update the file that imports
inferInput, prefetch, and trpc to include import "server-only"; so accidental
client-side imports fail fast and the server/client boundary stays explicit.
Source: Coding guidelines
| issues Issue[] @relation("WorkOrderTicketIssues") | ||
| vulnerabilities Vulnerability[] @relation("WorkOrderTicketVulns") | ||
| remediations Remediation[] @relation("WorkOrderTicketRemediations") | ||
| advisories Advisory[] @relation("WorkOrderTicketAdvisories") | ||
| assets Asset[] @relation("WorkOrderTicketAssets") | ||
| deviceGroups NotificationDeviceGroupMapping[] |
There was a problem hiding this comment.
You link work orders directly to assets|issues|remediations|vulnerabilities
What was your opinion of using something like NotificationAssetMapping as a wrapper for this relation, to capture things like LLM-confidence / LLM reasons why something was linked?
There was a problem hiding this comment.
I like "Work Tickets". Siemens uses "Service Tickets"
There was a problem hiding this comment.
Linked Files wasn't sketched out, will need to make a pass through to add features later
I'm considering moving and linked assets this to their own tab after reviewing the notifications UI with Matt, just to give you a heads up about how that'll be displayed
| {data.advisories.length > 0 && ( | ||
| <Section title="Advisories" count={data.advisories.length}> | ||
| <ul className="flex flex-col gap-2"> | ||
| {data.advisories.map((a) => ( | ||
| <li key={a.id}> | ||
| <Link | ||
| href={`/advisories/${a.id}`} | ||
| className="flex items-center justify-between gap-3 rounded-md border bg-background hover:bg-muted/50 transition px-3 py-2" | ||
| > | ||
| <span className="text-sm">{a.title ?? a.id}</span> | ||
| <SeverityBadge severity={a.severity} /> | ||
| </Link> | ||
| </li> | ||
| ))} | ||
| </ul> | ||
| </Section> | ||
| )} |
There was a problem hiding this comment.
Advisories will never be linked to a ticket, and I'll probably remove that code soon. Would also modify schema.prisma. Not necessary for this PR though
| NETWORK_REMEDIATION: "Network Remediation", | ||
| NEW_ASSET_PROCUREMENT: "New Asset Procurement", | ||
| OTHER: "Other", | ||
| }; |
There was a problem hiding this comment.
I think the answer to that depends on what kind of data we see from other platforms -- if we need to make configurable statuses because hospitals are already using complex patterns, then yes, but for now no.
There was a problem hiding this comment.
Not 100% sure what we want the suggested tab to show, but current it shows any ticket / subticket that was created from an email / integration
This will be a future ticket -- the AI tool will determine tickets that are important, and suggest them to the user. Currently figuring out how to do that for notifications.
Also, do we want to expand the width of the table on this page?
I couldn't see any way to eliminate horizontal scrolling besides eliminating columns
| } else if (tab === "suggested") { | ||
| // "Suggested" surfaces auto-ingested tickets — those with a source | ||
| // artifact (email/integration) rather than a user creating it by hand. | ||
| const ingested = { sources: { some: {} } }; |
There was a problem hiding this comment.
This will change but fine for now
| const model = [ | ||
| a.deviceGroup?.vendor?.canonicalDisplayName, | ||
| a.deviceGroup?.product?.canonicalDisplayName, | ||
| ] | ||
| .filter(Boolean) | ||
| .join(" "); |
There was a problem hiding this comment.
You already made a helper for this, deviceGroupLabel, which is is in string-utils.
Adding Work Ticket Tracking Page & Ticket Detail Page
Summary by CodeRabbit
New Features
Bug Fixes