Skip to content

Commit b0869a1

Browse files
authored
Merge pull request #59 from saagpatel/codex/refactor/wave5-7-draft-workspace-artifacts
refactor(components): extract useWorkspaceArtifacts hook
2 parents 545aaa8 + c358dc5 commit b0869a1

3 files changed

Lines changed: 577 additions & 229 deletions

File tree

src/components/Draft/DraftTab.tsx

Lines changed: 42 additions & 229 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { useDraftGeneration } from "./useDraftGeneration";
1818
import { useDraftIntake } from "./useDraftIntake";
1919
import { useDraftPersistence } from "./useDraftPersistence";
2020
import { useGuidedRunbook } from "./useGuidedRunbook";
21+
import { useWorkspaceArtifacts } from "./useWorkspaceArtifacts";
2122
import { useWorkspaceClipboardPacks } from "./useWorkspaceClipboardPacks";
2223
import { ConversationInput } from "./ConversationInput";
2324
import { WorkspaceDialogs } from "./WorkspaceDialogs";
@@ -42,9 +43,6 @@ import { useWorkspaceDerivedArtifacts } from "../../features/workspace/useWorksp
4243
import { useWorkspaceCommandBridge } from "../../features/workspace/useWorkspaceCommandBridge";
4344
import { useWorkspaceDraftState } from "../../features/workspace/useWorkspaceDraftState";
4445
import {
45-
applyResolutionKit,
46-
buildResolutionKitFromWorkspace,
47-
buildSimilarCases,
4846
compactLines,
4947
parseCaseIntake,
5048
} from "../../features/workspace/workspaceAssistant";
@@ -62,11 +60,9 @@ import type { ContextSource } from "../../types/knowledge";
6260
import type {
6361
GuidedRunbookTemplate,
6462
NextActionRecommendation,
65-
ResolutionKit,
6663
ResponseLength,
6764
SavedDraft,
6865
SimilarCase,
69-
WorkspaceFavorite,
7066
WorkspacePersonalization,
7167
} from "../../types/workspace";
7268
import "./DraftTab.css";
@@ -330,9 +326,6 @@ export const DraftTab = forwardRef<DraftTabHandle, DraftTabProps>(
330326
ConversationEntry[]
331327
>([]);
332328
const [handoffTouched, setHandoffTouched] = useState(false);
333-
const [similarCases, setSimilarCases] = useState<SimilarCase[]>([]);
334-
const [similarCasesLoading, setSimilarCasesLoading] = useState(false);
335-
const [compareCase, setCompareCase] = useState<SimilarCase | null>(null);
336329
const [workspaceRunbookScopeKey, setWorkspaceRunbookScopeKey] =
337330
useState<string>(createWorkspaceRunbookScopeKey);
338331
const [autosaveDraftId, setAutosaveDraftId] = useState<string | null>(null);
@@ -538,69 +531,6 @@ export const DraftTab = forwardRef<DraftTabHandle, DraftTabProps>(
538531
[savedDraftId],
539532
);
540533

541-
const handleRefreshSimilarCases = useCallback(async () => {
542-
if (!similarCasesEnabled) {
543-
setSimilarCases([]);
544-
return;
545-
}
546-
547-
const query = [
548-
input,
549-
currentTicket?.summary,
550-
caseIntake.issue,
551-
caseIntake.symptoms,
552-
]
553-
.filter((value): value is string => Boolean(value?.trim()))
554-
.join(" ");
555-
556-
if (!query.trim()) {
557-
setSimilarCases([]);
558-
return;
559-
}
560-
561-
setSimilarCasesLoading(true);
562-
try {
563-
const results = await searchDrafts(query, 20);
564-
const next = buildSimilarCases({
565-
currentDraftId: savedDraftId,
566-
queryText: query,
567-
drafts: results,
568-
});
569-
setSimilarCases(next);
570-
} finally {
571-
setSimilarCasesLoading(false);
572-
}
573-
}, [
574-
similarCasesEnabled,
575-
input,
576-
currentTicket?.summary,
577-
caseIntake.issue,
578-
caseIntake.symptoms,
579-
searchDrafts,
580-
savedDraftId,
581-
]);
582-
583-
const handleCompareLastResolution = useCallback(() => {
584-
if (!response.trim()) {
585-
showError(
586-
"Generate or paste a response before comparing it to a prior resolution",
587-
);
588-
return;
589-
}
590-
591-
const bestMatch = similarCases[0];
592-
if (!bestMatch || !bestMatch.response_text.trim()) {
593-
showError("No similar solved case is ready to compare yet");
594-
return;
595-
}
596-
597-
setCompareCase(bestMatch);
598-
void logEvent("workspace_compare_last_resolution_opened", {
599-
ticket_id: currentTicketId,
600-
similar_case_id: bestMatch.draft_id,
601-
});
602-
}, [response, similarCases, showError, logEvent, currentTicketId]);
603-
604534
const handleApplyTemplate = useCallback((content: string) => {
605535
setResponse(content);
606536
}, []);
@@ -697,9 +627,7 @@ export const DraftTab = forwardRef<DraftTabHandle, DraftTabProps>(
697627
...parseCaseIntake(null),
698628
note_audience: workspacePersonalization.preferred_note_audience,
699629
});
700-
setSimilarCases([]);
701-
setSimilarCasesLoading(false);
702-
setCompareCase(null);
630+
resetWorkspaceArtifacts();
703631
setGuidedRunbookSession(null);
704632
setGuidedRunbookNote("");
705633
setRunbookSessionSourceScopeKey(null);
@@ -835,38 +763,6 @@ export const DraftTab = forwardRef<DraftTabHandle, DraftTabProps>(
835763
return () => window.removeEventListener("keydown", handleKeydown);
836764
}, [viewMode, handlePanelDensityModeChange]);
837765

838-
useEffect(() => {
839-
if (!similarCasesEnabled) {
840-
return;
841-
}
842-
843-
const query = [
844-
input,
845-
caseIntake.issue,
846-
caseIntake.symptoms,
847-
currentTicket?.summary,
848-
]
849-
.filter((value): value is string => Boolean(value?.trim()))
850-
.join(" ");
851-
if (!query.trim()) {
852-
setSimilarCases([]);
853-
return;
854-
}
855-
856-
const timer = window.setTimeout(() => {
857-
void handleRefreshSimilarCases();
858-
}, 350);
859-
860-
return () => window.clearTimeout(timer);
861-
}, [
862-
similarCasesEnabled,
863-
input,
864-
caseIntake.issue,
865-
caseIntake.symptoms,
866-
currentTicket?.summary,
867-
handleRefreshSimilarCases,
868-
]);
869-
870766
const buildDiagnosisJson = useCallback(() => {
871767
const completedIds = Object.keys(checklistCompleted).filter(
872768
(id) => checklistCompleted[id],
@@ -979,6 +875,45 @@ export const DraftTab = forwardRef<DraftTabHandle, DraftTabProps>(
979875
originalResponse,
980876
});
981877

878+
const {
879+
similarCases,
880+
similarCasesLoading,
881+
compareCase,
882+
setCompareCase,
883+
handleRefreshSimilarCases,
884+
handleCompareLastResolution,
885+
handleCompareSimilarCase,
886+
handleSaveCurrentResolutionKit,
887+
handleApplyResolutionKit,
888+
handleToggleWorkspaceFavorite,
889+
resetWorkspaceArtifacts,
890+
} = useWorkspaceArtifacts({
891+
similarCasesEnabled,
892+
input,
893+
response,
894+
currentTicket,
895+
currentTicketId,
896+
caseIntake,
897+
kbDraft,
898+
sources,
899+
savedDraftId,
900+
workspaceFavorites,
901+
searchDrafts,
902+
saveResolutionKit,
903+
saveWorkspaceFavorite,
904+
deleteWorkspaceFavorite,
905+
refreshWorkspaceCatalog,
906+
logEvent,
907+
setResponse,
908+
setOriginalResponse,
909+
setIsResponseEdited,
910+
setCaseIntake,
911+
setDiagnosticNotes,
912+
setPanelDensityMode,
913+
onShowSuccess: showSuccess,
914+
onShowError: showError,
915+
});
916+
982917
const {
983918
pendingSimilarCaseOpen,
984919
setPendingSimilarCaseOpen,
@@ -1061,116 +996,6 @@ export const DraftTab = forwardRef<DraftTabHandle, DraftTabProps>(
1061996
onShowError: showError,
1062997
});
1063998

1064-
const handleSaveCurrentResolutionKit = useCallback(async () => {
1065-
try {
1066-
const nextKit = buildResolutionKitFromWorkspace({
1067-
intake: caseIntake,
1068-
kbDraft,
1069-
responseText: response,
1070-
sources,
1071-
});
1072-
await saveResolutionKit({
1073-
...nextKit,
1074-
response_template: nextKit.response_template,
1075-
checklist_items: nextKit.checklist_items,
1076-
kb_document_ids: nextKit.kb_document_ids,
1077-
});
1078-
await refreshWorkspaceCatalog();
1079-
void logEvent("workspace_resolution_kit_saved", {
1080-
ticket_id: currentTicketId,
1081-
category: nextKit.category,
1082-
});
1083-
showSuccess("Saved the current workspace as a resolution kit");
1084-
} catch {
1085-
showError("Failed to save resolution kit");
1086-
}
1087-
}, [
1088-
caseIntake,
1089-
kbDraft,
1090-
response,
1091-
sources,
1092-
saveResolutionKit,
1093-
refreshWorkspaceCatalog,
1094-
logEvent,
1095-
currentTicketId,
1096-
showSuccess,
1097-
showError,
1098-
]);
1099-
1100-
const handleApplyResolutionKit = useCallback(
1101-
(kit: ResolutionKit) => {
1102-
const applied = applyResolutionKit({
1103-
currentInput: input,
1104-
currentResponse: response,
1105-
currentIntake: caseIntake,
1106-
kit,
1107-
});
1108-
setResponse(applied.responseText);
1109-
if (!response.trim() && applied.responseText) {
1110-
setOriginalResponse(applied.responseText);
1111-
setIsResponseEdited(false);
1112-
}
1113-
setCaseIntake(applied.intake);
1114-
setDiagnosticNotes((prev) =>
1115-
compactLines([prev, applied.checklistText]),
1116-
);
1117-
setPanelDensityMode("focus-intake");
1118-
void logEvent("workspace_resolution_kit_applied", {
1119-
ticket_id: currentTicketId,
1120-
kit_id: kit.id,
1121-
category: kit.category,
1122-
});
1123-
showSuccess(`Applied ${kit.name}`);
1124-
},
1125-
[input, response, caseIntake, logEvent, currentTicketId, showSuccess],
1126-
);
1127-
1128-
const handleToggleWorkspaceFavorite = useCallback(
1129-
async (
1130-
kind: WorkspaceFavorite["kind"],
1131-
resourceId: string,
1132-
label: string,
1133-
metadata?: Record<string, string> | null,
1134-
) => {
1135-
try {
1136-
const existing = workspaceFavorites.find(
1137-
(favorite) =>
1138-
favorite.kind === kind && favorite.resource_id === resourceId,
1139-
);
1140-
if (existing) {
1141-
await deleteWorkspaceFavorite(existing.id);
1142-
showSuccess(`Removed ${label} from favorites`);
1143-
} else {
1144-
await saveWorkspaceFavorite({
1145-
kind,
1146-
label,
1147-
resource_id: resourceId,
1148-
metadata: metadata ?? null,
1149-
});
1150-
showSuccess(`Added ${label} to favorites`);
1151-
}
1152-
await refreshWorkspaceCatalog();
1153-
void logEvent("workspace_favorite_toggled", {
1154-
ticket_id: currentTicketId,
1155-
kind,
1156-
resource_id: resourceId,
1157-
});
1158-
} catch {
1159-
showError("Failed to update favorites");
1160-
}
1161-
},
1162-
[
1163-
workspaceFavorites,
1164-
deleteWorkspaceFavorite,
1165-
saveWorkspaceFavorite,
1166-
refreshWorkspaceCatalog,
1167-
showSuccess,
1168-
logEvent,
1169-
currentTicketId,
1170-
showError,
1171-
],
1172-
);
1173-
1174999
const loadSimilarCaseIntoWorkspace = useCallback(
11751000
async (similarCase: SimilarCase) => {
11761001
const fullDraft = await getDraft(similarCase.draft_id);
@@ -1206,24 +1031,12 @@ export const DraftTab = forwardRef<DraftTabHandle, DraftTabProps>(
12061031
logEvent,
12071032
currentTicketId,
12081033
requestOpenSimilarCase,
1034+
setPendingSimilarCaseOpen,
12091035
showError,
12101036
showSuccess,
12111037
],
12121038
);
12131039

1214-
const handleCompareSimilarCase = useCallback(
1215-
(similarCase: SimilarCase) => {
1216-
if (!response.trim()) {
1217-
showError(
1218-
"Generate or paste a response before comparing it to a prior resolution",
1219-
);
1220-
return;
1221-
}
1222-
setCompareCase(similarCase);
1223-
},
1224-
[response, showError],
1225-
);
1226-
12271040
const handleAcceptNextAction = useCallback(
12281041
(action: NextActionRecommendation) => {
12291042
void logEvent("workspace_next_action_accepted", {

0 commit comments

Comments
 (0)