Skip to content

Commit 03eb090

Browse files
committed
refactor(features): regroup TicketWorkspaceRail props into 8 bundles
Collapse the 38-prop flat interface (deferred from Wave 5.5) into 8 named bundles plus 2 primitives. Each bundle maps to one feature area: intake, nextActions, similarCases, packs, kits, favorites, runbooks, personalization. DraftTab shell, rail internals, and the test suite all updated in lockstep; consumer behavior is unchanged.
1 parent a3b5010 commit 03eb090

3 files changed

Lines changed: 308 additions & 263 deletions

File tree

src/components/Draft/DraftTab.tsx

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,42 +1293,58 @@ export const DraftTab = forwardRef<DraftTabHandle, DraftTabProps>(
12931293

12941294
const workspacePanel = (
12951295
<TicketWorkspaceRail
1296-
intake={caseIntake}
1297-
onIntakeChange={handleIntakeFieldChange}
1298-
onAnalyzeIntake={handleAnalyzeIntake}
1299-
onApplyIntakePreset={handleApplyIntakePreset}
1300-
onNoteAudienceChange={handleNoteAudienceChange}
1301-
nextActions={nextActions}
1302-
missingQuestions={missingQuestions}
1303-
onAcceptNextAction={handleAcceptNextAction}
1304-
similarCases={similarCases}
1305-
similarCasesLoading={similarCasesLoading}
1306-
onRefreshSimilarCases={handleRefreshSimilarCases}
1307-
onOpenSimilarCase={handleOpenSimilarCase}
1308-
onCompareSimilarCase={handleCompareSimilarCase}
1309-
onCompareLastResolution={handleCompareLastResolution}
1310-
compareCase={compareCase}
1311-
onCloseCompareCase={() => setCompareCase(null)}
1312-
handoffPack={handoffPack}
1313-
evidencePack={evidencePack}
1314-
kbDraft={kbDraft}
1315-
onCopyHandoffPack={handleCopyHandoffPack}
1316-
onCopyEvidencePack={handleCopyEvidencePack}
1317-
onCopyKbDraft={handleCopyKbDraft}
1318-
resolutionKits={resolutionKits}
1319-
onSaveResolutionKit={handleSaveCurrentResolutionKit}
1320-
onApplyResolutionKit={handleApplyResolutionKit}
1321-
favorites={workspaceFavorites}
1322-
onToggleFavorite={handleToggleWorkspaceFavorite}
1323-
runbookTemplates={runbookTemplates}
1324-
guidedRunbookSession={guidedRunbookSession}
1325-
runbookNote={guidedRunbookNote}
1326-
onRunbookNoteChange={handleGuidedRunbookNoteChange}
1327-
onStartGuidedRunbook={handleStartGuidedRunbook}
1328-
onAdvanceGuidedRunbook={handleAdvanceGuidedRunbook}
1329-
onCopyRunbookProgressToNotes={handleCopyRunbookProgressToNotes}
1330-
workspacePersonalization={workspacePersonalization}
1331-
onPersonalizationChange={handleWorkspacePersonalizationChange}
1296+
intake={{
1297+
data: caseIntake,
1298+
onChange: handleIntakeFieldChange,
1299+
onAnalyze: handleAnalyzeIntake,
1300+
onApplyPreset: handleApplyIntakePreset,
1301+
onNoteAudienceChange: handleNoteAudienceChange,
1302+
missingQuestions,
1303+
}}
1304+
nextActions={{
1305+
items: nextActions,
1306+
onAccept: handleAcceptNextAction,
1307+
}}
1308+
similarCases={{
1309+
items: similarCases,
1310+
loading: similarCasesLoading,
1311+
onRefresh: handleRefreshSimilarCases,
1312+
onOpen: handleOpenSimilarCase,
1313+
onCompare: handleCompareSimilarCase,
1314+
onCompareLast: handleCompareLastResolution,
1315+
compareCase,
1316+
onCloseCompare: () => setCompareCase(null),
1317+
}}
1318+
packs={{
1319+
handoffPack,
1320+
evidencePack,
1321+
kbDraft,
1322+
onCopyHandoff: handleCopyHandoffPack,
1323+
onCopyEvidence: handleCopyEvidencePack,
1324+
onCopyKb: handleCopyKbDraft,
1325+
}}
1326+
kits={{
1327+
items: resolutionKits,
1328+
onSaveCurrent: handleSaveCurrentResolutionKit,
1329+
onApply: handleApplyResolutionKit,
1330+
}}
1331+
favorites={{
1332+
items: workspaceFavorites,
1333+
onToggle: handleToggleWorkspaceFavorite,
1334+
}}
1335+
runbooks={{
1336+
templates: runbookTemplates,
1337+
session: guidedRunbookSession,
1338+
note: guidedRunbookNote,
1339+
onNoteChange: handleGuidedRunbookNoteChange,
1340+
onStart: handleStartGuidedRunbook,
1341+
onAdvance: handleAdvanceGuidedRunbook,
1342+
onCopyProgress: handleCopyRunbookProgressToNotes,
1343+
}}
1344+
personalization={{
1345+
value: workspacePersonalization,
1346+
onChange: handleWorkspacePersonalizationChange,
1347+
}}
13321348
workspaceCatalogLoading={workspaceCatalogLoading}
13331349
currentResponse={response}
13341350
/>

src/features/workspace/TicketWorkspaceRail.test.tsx

Lines changed: 87 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -97,73 +97,91 @@ const basePersonalization: WorkspacePersonalization = {
9797
default_evidence_format: "clipboard",
9898
};
9999

100-
function renderRail(
101-
overrides: Partial<ComponentProps<typeof TicketWorkspaceRail>> = {},
102-
) {
103-
const props: ComponentProps<typeof TicketWorkspaceRail> = {
104-
intake: baseIntake,
105-
onIntakeChange: vi.fn(),
106-
onAnalyzeIntake: vi.fn(),
107-
onApplyIntakePreset: vi.fn(),
108-
onNoteAudienceChange: vi.fn(),
109-
nextActions: [],
110-
missingQuestions: [],
111-
onAcceptNextAction: vi.fn(),
112-
similarCases: [baseSimilarCase],
113-
similarCasesLoading: false,
114-
onRefreshSimilarCases: vi.fn(),
115-
onOpenSimilarCase: vi.fn(),
116-
onCompareSimilarCase: vi.fn(),
117-
onCompareLastResolution: vi.fn(),
118-
compareCase: null,
119-
onCloseCompareCase: vi.fn(),
120-
handoffPack: {
121-
summary: "VPN issue under review",
122-
actions_taken: ["Reset VPN profile"],
123-
current_blocker: "West region still affected",
124-
next_step: "Escalate to network engineering",
125-
customer_safe_update: "We are actively working the VPN issue.",
126-
escalation_note: "Escalate the remaining west region failures.",
100+
type RailProps = ComponentProps<typeof TicketWorkspaceRail>;
101+
102+
function makeBundles(): RailProps {
103+
return {
104+
intake: {
105+
data: baseIntake,
106+
onChange: vi.fn(),
107+
onAnalyze: vi.fn(),
108+
onApplyPreset: vi.fn(),
109+
onNoteAudienceChange: vi.fn(),
110+
missingQuestions: [],
111+
},
112+
nextActions: {
113+
items: [],
114+
onAccept: vi.fn(),
115+
},
116+
similarCases: {
117+
items: [baseSimilarCase],
118+
loading: false,
119+
onRefresh: vi.fn(),
120+
onOpen: vi.fn(),
121+
onCompare: vi.fn(),
122+
onCompareLast: vi.fn(),
123+
compareCase: null,
124+
onCloseCompare: vi.fn(),
127125
},
128-
evidencePack: {
129-
title: "Evidence Pack · INC-1001",
130-
summary: "VPN issue under review",
131-
sections: [],
126+
packs: {
127+
handoffPack: {
128+
summary: "VPN issue under review",
129+
actions_taken: ["Reset VPN profile"],
130+
current_blocker: "West region still affected",
131+
next_step: "Escalate to network engineering",
132+
customer_safe_update: "We are actively working the VPN issue.",
133+
escalation_note: "Escalate the remaining west region failures.",
134+
},
135+
evidencePack: {
136+
title: "Evidence Pack · INC-1001",
137+
summary: "VPN issue under review",
138+
sections: [],
139+
},
140+
kbDraft: {
141+
title: "VPN disconnects every morning",
142+
summary: "Repeated VPN disconnects for remote users.",
143+
symptoms: "Users disconnect every morning.",
144+
environment: "Managed Windows laptops",
145+
cause: "Likely regional gateway issue",
146+
resolution: "Reset profile and escalate to network engineering.",
147+
warnings: [],
148+
prerequisites: [],
149+
policy_links: [],
150+
tags: ["incident"],
151+
},
152+
onCopyHandoff: vi.fn(),
153+
onCopyEvidence: vi.fn(),
154+
onCopyKb: vi.fn(),
132155
},
133-
kbDraft: {
134-
title: "VPN disconnects every morning",
135-
summary: "Repeated VPN disconnects for remote users.",
136-
symptoms: "Users disconnect every morning.",
137-
environment: "Managed Windows laptops",
138-
cause: "Likely regional gateway issue",
139-
resolution: "Reset profile and escalate to network engineering.",
140-
warnings: [],
141-
prerequisites: [],
142-
policy_links: [],
143-
tags: ["incident"],
156+
kits: {
157+
items: [baseResolutionKit],
158+
onSaveCurrent: vi.fn(),
159+
onApply: vi.fn(),
160+
},
161+
favorites: {
162+
items: baseFavorites,
163+
onToggle: vi.fn(),
164+
},
165+
runbooks: {
166+
templates: [baseRunbookTemplate],
167+
session: baseRunbookSession,
168+
note: "",
169+
onNoteChange: vi.fn(),
170+
onStart: vi.fn(),
171+
onAdvance: vi.fn(),
172+
onCopyProgress: vi.fn(),
173+
},
174+
personalization: {
175+
value: basePersonalization,
176+
onChange: vi.fn(),
144177
},
145-
onCopyHandoffPack: vi.fn(),
146-
onCopyEvidencePack: vi.fn(),
147-
onCopyKbDraft: vi.fn(),
148-
resolutionKits: [baseResolutionKit],
149-
onSaveResolutionKit: vi.fn(),
150-
onApplyResolutionKit: vi.fn(),
151-
favorites: baseFavorites,
152-
onToggleFavorite: vi.fn(),
153-
runbookTemplates: [baseRunbookTemplate],
154-
guidedRunbookSession: baseRunbookSession,
155-
runbookNote: "",
156-
onRunbookNoteChange: vi.fn(),
157-
onStartGuidedRunbook: vi.fn(),
158-
onAdvanceGuidedRunbook: vi.fn(),
159-
onCopyRunbookProgressToNotes: vi.fn(),
160-
workspacePersonalization: basePersonalization,
161-
onPersonalizationChange: vi.fn(),
162178
workspaceCatalogLoading: false,
163179
currentResponse: "Reset the VPN profile and verify MFA enrollment.",
164-
...overrides,
165180
};
181+
}
166182

183+
function renderRail(overrides: Partial<RailProps> = {}) {
184+
const props: RailProps = { ...makeBundles(), ...overrides };
167185
return {
168186
props,
169187
...render(<TicketWorkspaceRail {...props} />),
@@ -211,9 +229,9 @@ describe("TicketWorkspaceRail", () => {
211229
}),
212230
);
213231

214-
expect(props.onCompareLastResolution).toHaveBeenCalledTimes(1);
215-
expect(props.onApplyResolutionKit).toHaveBeenCalledTimes(1);
216-
expect(props.onCopyRunbookProgressToNotes).toHaveBeenCalledTimes(1);
232+
expect(props.similarCases.onCompareLast).toHaveBeenCalledTimes(1);
233+
expect(props.kits.onApply).toHaveBeenCalledTimes(1);
234+
expect(props.runbooks.onCopyProgress).toHaveBeenCalledTimes(1);
217235
expect(screen.getByText("Favorites")).toBeTruthy();
218236
expect(screen.getByText("Guided runbooks")).toBeTruthy();
219237
});
@@ -243,19 +261,19 @@ describe("TicketWorkspaceRail", () => {
243261
{ target: { value: "Long" } },
244262
);
245263

246-
expect(props.onPersonalizationChange).toHaveBeenCalledWith({
264+
expect(props.personalization.onChange).toHaveBeenCalledWith({
247265
preferred_output_length: "Long",
248266
});
249267
});
250268

251269
it("shows empty states when the catalog is unavailable and compare is not ready", () => {
270+
const base = makeBundles();
252271
const { container } = renderRail({
253272
currentResponse: "",
254-
similarCases: [],
255-
resolutionKits: [],
256-
favorites: [],
257-
guidedRunbookSession: null,
258-
runbookTemplates: [],
273+
similarCases: { ...base.similarCases, items: [] },
274+
kits: { ...base.kits, items: [] },
275+
favorites: { ...base.favorites, items: [] },
276+
runbooks: { ...base.runbooks, session: null, templates: [] },
259277
});
260278
const rail = getRailRoot(container);
261279

0 commit comments

Comments
 (0)