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} />