diff --git a/src/app/(protected)/workorders/[id].tsx b/src/app/(protected)/workorders/[id].tsx
index f7350669..e9fb5b56 100644
--- a/src/app/(protected)/workorders/[id].tsx
+++ b/src/app/(protected)/workorders/[id].tsx
@@ -31,9 +31,12 @@ import {
Typography,
} from "@mui/material";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
+import { CommentMentionComposer } from "@/components/common/comment-mention-composer";
+import { CommentTimeline } from "@/components/common/comment-timeline";
+import { mapWorkOrderCommentsToTimeline } from "@/components/common/comment-timeline-mappers";
import { DispatchCreateModal } from "./_components/dispatch-create-modal";
import { DispatchDetailModal } from "./_components/dispatch-detail-modal";
-import type { WorkOrderAuditEntry, WorkOrderComment } from "@/domain/work-orders/types/work-order";
+import type { WorkOrderAuditEntry } from "@/domain/work-orders/types/work-order";
import { WORK_ORDER_STATUS_OPTIONS } from "@/domain/work-orders/types/work-order";
import { useWorkOrderDetail } from "@/domain/work-orders/use-cases/use-work-order-detail";
import {
@@ -45,6 +48,7 @@ import {
import { useWorkOrderUsers } from "@/domain/work-orders/use-cases/use-work-orders-list";
import { vendorsApi } from "@/domain/vendors/api/vendors-api";
import type { VendorPreferredContact } from "@/domain/vendors/types/vendor";
+import { extractMentionIds } from "@/domain/work-orders/utils/comment-mentions";
import { formatDateTime, timeSince, waitTimeColor } from "@/lib/time-utils";
const STATUS_COLORS: Record<
@@ -85,57 +89,6 @@ function isVendorEditorValid(editor: VendorEditorState): boolean {
);
}
-function CommentTimeline({ comments }: { comments: WorkOrderComment[] }) {
- const sorted = useMemo(
- () =>
- [...comments].sort(
- (a, b) => new Date(a.createdDate).getTime() - new Date(b.createdDate).getTime(),
- ),
- [comments],
- );
-
- if (!sorted.length) {
- return (
-
- No comments yet.
-
- );
- }
-
- return (
-
- {sorted.map((comment) => (
-
-
-
- {comment.userName || comment.commenter || "System"}
-
-
- {comment.createdDate ? formatDateTime(comment.createdDate) : ""}
-
-
- {comment.commenttext}
-
- ))}
-
- );
-}
-
function AuditTimeline({ entries }: { entries: WorkOrderAuditEntry[] }) {
const sorted = useMemo(
() =>
@@ -220,6 +173,18 @@ export default function WorkOrderViewPage() {
[wo?.comments],
);
const auditLog = wo?.auditLog ?? [];
+ const mentionUsers = useMemo(
+ () => users.map((user) => ({ id: user.id, name: user.name })),
+ [users],
+ );
+ const customerTimelineItems = useMemo(
+ () => mapWorkOrderCommentsToTimeline(customerComments),
+ [customerComments],
+ );
+ const internalTimelineItems = useMemo(
+ () => mapWorkOrderCommentsToTimeline(internalComments),
+ [internalComments],
+ );
const handleCommentSubmit = (event: React.FormEvent) => {
event.preventDefault();
@@ -230,8 +195,15 @@ export default function WorkOrderViewPage() {
return;
}
+ const trimmedText = commentText.trim();
+ const mentions = extractMentionIds(trimmedText);
+
commentMutation.mutate(
- { text: commentText.trim(), commentType: activeTab },
+ {
+ text: trimmedText,
+ mentions: mentions.length > 0 ? mentions : undefined,
+ commentType: activeTab,
+ },
{ onSuccess: () => setCommentText("") },
);
};
@@ -626,18 +598,20 @@ export default function WorkOrderViewPage() {
- {activeTab === "customer" && }
- {activeTab === "internal" && }
+ {activeTab === "customer" && (
+
+ )}
+ {activeTab === "internal" && (
+
+ )}
{activeTab === "audit" && }
{activeTab === "internal" && (
- setCommentText(event.target.value)}
- placeholder="Type a comment..."
+ onChange={setCommentText}
+ candidates={mentionUsers}
disabled={commentMutation.isPending}
/>