Skip to content

[VW-259] Work Order Ticket View#146

Merged
markglose-bc merged 12 commits into
mainfrom
VW-259
Jun 30, 2026
Merged

[VW-259] Work Order Ticket View#146
markglose-bc merged 12 commits into
mainfrom
VW-259

Conversation

@markglose-bc

@markglose-bc markglose-bc commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Adding Work Ticket Tracking Page & Ticket Detail Page

image image

Summary by CodeRabbit

  • New Features

    • Added a tracking area for work-order tickets, including ticket lists, detail pages, comments, watch status, sub-tickets, linked assets, and activity history.
    • Added department management and tag color settings in the app.
    • Added ticket categorization, department assignment, and color selection across the UI.
  • Bug Fixes

    • Improved table support for nested rows and expanded ticket hierarchies.
    • Added safer unsaved-changes prompts when editing tickets or comments.

@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
viper Ready Ready Preview, Comment Jun 29, 2026 7:53pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
viper-demo Ignored Ignored Jun 29, 2026 7:53pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Warning

Review limit reached

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

Next review available in: 58 seconds

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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 535e0519-8404-4f4c-9248-4945ae3ee20a

📥 Commits

Reviewing files that changed from the base of the PR and between aee3790 and 6e567d5.

📒 Files selected for processing (9)
  • prisma/migrations/20260629120000_work_order_ticket_tracking/migration.sql
  • src/app/(dashboard)/(rest)/settings/departments/page.tsx
  • src/app/(dashboard)/(rest)/settings/tag-colors/page.tsx
  • src/components/ui/data-table.tsx
  • src/features/departments/hooks/use-departments.ts
  • src/features/departments/server/routers.ts
  • src/features/tracking/components/tracking.tsx
  • src/features/tracking/hooks/use-tracking.ts
  • src/features/tracking/server/routers.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch VW-259

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Added the concept of child rows that accordion out from under their parent, mainly in the Work Orders Ticket Table.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Is Tracking a good name for this page? Would Work Tickets be better? Ticket Tracking? Tracking just seems slightly off to me

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I like "Work Tickets". Siemens uses "Service Tickets"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

/tickets , /work-tickets , /work-orders

@markglose-bc markglose-bc Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

A page under settings to add, edit, and remove departments. Also lists out Users and tickets attached to those departments

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The bottom most component of the Ticket Detail Page, shows an audit log of all changes pertaining to the Ticket

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Linked Files wasn't sketched out, will need to make a pass through to add features later

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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",
};

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Should we consider making statuses and categories user configurable like departments?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

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)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

All the plumbing for the audit log

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

Assert the full search value, not the last keystroke.

TrackingSearch just forwards onSearchChange into EntitySearch, 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 win

Align seeded comment timestamps with lastCommentAt.

lastCommentAt and seenAt are 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 win

Keep location opaque and nullable in the response schema.

Asset.location is a nullable Prisma JSON field; z.any() widens the response type and does not document the null contract. Use z.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? / JsonValue fields should use z.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 win

Reject same-parent reattaches before recording activity.

If input.childId is already attached to input.parentId, this update is a no-op on parentId but still writes a fresh CHILD_ATTACHED row. Fetch parentId with 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 win

Trim name before 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 win

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

Give 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 an aria-label so 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 win

Don't render the empty state while child candidates are still loading.

Right after opening the popover, candidates is 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 win

Make the description tabs controlled.

defaultValue is 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 when selectedDepartments changes.

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

Don't show the empty state before the candidate query resolves.

When this popover opens on a slow response, candidates is still undefined, so CommandEmpty immediately 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 win

Reset dialog state when open or initial changes.

Because the create dialog stays mounted at Line 237, these useState initializers 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 win

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

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

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

Use @/* imports for the local tracking modules here.

This file mixes alias imports with ../params, ../types, and ./activities, which breaks the repo’s import convention for src/* 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/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 win

Prefer z.enum(...) over z.nativeEnum(...) in this repo.

The shared tracking schemas already use z.enum(TicketStatus) / z.enum(TicketCategory), so keeping z.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 avoids z.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 win

Add server-only to 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 win

Mark this loader as server-only and switch to the src alias.

This module lives under server/, so it should enforce that boundary with import "server-only";. The ../params import also breaks the repo’s @/* alias convention.

As per coding guidelines, mark server utility files with server-only and 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 win

Mark this hook as client-only.

This module uses useEffect and window, 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 win

Use the @/ alias for this src import.

../params breaks the repo-wide import convention for src/* 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 win

Use the @/ alias for the local src import.

./use-tracking-params breaks the repo-wide import convention for src/* 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 win

Use the @/ alias for this src import.

./hooks/use-tag-colors breaks the repo-wide import convention for src/* 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 win

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

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

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

Use the @/ alias for the shared type import.

../../types still points into src/*, 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 win

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

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

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

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

Use the @/* alias for local src/* 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 tradeoff

Rename this component file to PascalCase.

tag-colors.tsx is 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 win

Use 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 tradeoff

Rename this component file to PascalCase.

color-picker.tsx is a component module, so it should be named like ColorPicker.tsx to 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 tradeoff

Rename this component file to PascalCase.

departments.tsx is 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 win

Use 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

📥 Commits

Reviewing files that changed from the base of the PR and between 23f4b46 and aee3790.

📒 Files selected for processing (47)
  • prisma/migrations/20260629120000_work_order_ticket_tracking/migration.sql
  • prisma/schema.prisma
  • prisma/seed.ts
  • src/app/(dashboard)/(rest)/settings/departments/page.tsx
  • src/app/(dashboard)/(rest)/settings/tag-colors/page.tsx
  • src/app/(dashboard)/(rest)/tracking/[ticketId]/page.tsx
  • src/app/(dashboard)/(rest)/tracking/page.tsx
  • src/components/app-sidebar.tsx
  • src/components/ui/data-table.tsx
  • src/features/departments/components/departments.tsx
  • src/features/departments/hooks/use-departments.ts
  • src/features/departments/server/prefetch.ts
  • src/features/departments/server/routers.ts
  • src/features/settings/components/settings-layout.tsx
  • src/features/tag-colors/components/color-picker.tsx
  • src/features/tag-colors/components/tag-colors.tsx
  • src/features/tag-colors/context.tsx
  • src/features/tag-colors/hooks/use-tag-colors.ts
  • src/features/tag-colors/palette.ts
  • src/features/tag-colors/server/prefetch.ts
  • src/features/tag-colors/server/routers.ts
  • src/features/tracking/components/columns.tsx
  • src/features/tracking/components/ticket-detail.test.tsx
  • src/features/tracking/components/ticket-detail/activity-timeline.tsx
  • src/features/tracking/components/ticket-detail/add-comment-form.tsx
  • src/features/tracking/components/ticket-detail/department-multi-select.tsx
  • src/features/tracking/components/ticket-detail/edit-form.tsx
  • src/features/tracking/components/ticket-detail/index.tsx
  • src/features/tracking/components/ticket-detail/linked-assets-table.tsx
  • src/features/tracking/components/ticket-detail/linked-assets.tsx
  • src/features/tracking/components/ticket-detail/shared.tsx
  • src/features/tracking/components/ticket-detail/sub-tickets.tsx
  • src/features/tracking/components/tracking.test.tsx
  • src/features/tracking/components/tracking.tsx
  • src/features/tracking/hooks/use-tracking-params.ts
  • src/features/tracking/hooks/use-tracking.ts
  • src/features/tracking/params.ts
  • src/features/tracking/server/activities.ts
  • src/features/tracking/server/params-loader.ts
  • src/features/tracking/server/prefetch.ts
  • src/features/tracking/server/routers.test.ts
  • src/features/tracking/server/routers.ts
  • src/features/tracking/types.ts
  • src/features/user/server/routers.ts
  • src/hooks/use-before-unload.ts
  • src/lib/router-utils.ts
  • src/trpc/routers/_app.ts

Comment thread src/app/(dashboard)/(rest)/settings/departments/page.tsx
Comment thread src/app/(dashboard)/(rest)/settings/tag-colors/page.tsx
Comment thread src/components/ui/data-table.tsx Outdated
Comment thread src/features/departments/hooks/use-departments.ts
Comment thread src/features/departments/server/routers.ts
Comment thread src/features/tracking/hooks/use-tracking.ts
Comment on lines +1 to +2
import type { inferInput } from "@trpc/tanstack-react-query";
import { prefetch, trpc } from "@/trpc/server";

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 | 🟠 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.

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

Comment thread src/features/tracking/server/routers.ts
Comment thread src/features/tracking/server/routers.ts Outdated
@markglose-bc markglose-bc marked this pull request as ready for review June 29, 2026 20:07
Comment thread prisma/schema.prisma
Comment on lines +1013 to +1018
issues Issue[] @relation("WorkOrderTicketIssues")
vulnerabilities Vulnerability[] @relation("WorkOrderTicketVulns")
remediations Remediation[] @relation("WorkOrderTicketRemediations")
advisories Advisory[] @relation("WorkOrderTicketAdvisories")
assets Asset[] @relation("WorkOrderTicketAssets")
deviceGroups NotificationDeviceGroupMapping[]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I like "Work Tickets". Siemens uses "Service Tickets"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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

Comment on lines +255 to +271
{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>
)}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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",
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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: {} } };

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This will change but fine for now

Comment on lines +48 to +53
const model = [
a.deviceGroup?.vendor?.canonicalDisplayName,
a.deviceGroup?.product?.canonicalDisplayName,
]
.filter(Boolean)
.join(" ");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You already made a helper for this, deviceGroupLabel, which is is in string-utils.

@markglose-bc markglose-bc merged commit 5293841 into main Jun 30, 2026
12 checks passed
@markglose-bc markglose-bc deleted the VW-259 branch June 30, 2026 17:59
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.

3 participants