From ff2dde1900342d575e6cd6cd01a499c35d735955 Mon Sep 17 00:00:00 2001 From: Tracewebstudio Dev Date: Sat, 15 Aug 2026 13:16:29 +0200 Subject: [PATCH] feat(ai-widget): Copy Cover Letter button + draft_content plumbing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ChatMessage now carries draftContent?: string (from API draft_content field) - ChatResponse interface extended with draft_content?: string - sendMessage populates draftContent on assistant messages - confirmSaveProfile uses draftContent directly (no more regex parsing) - 'Save to Profile' button guards on msg.draftContent being truthy - New blue 'Copy Cover Letter' button for cover_letter_draft messages: copies msg.draftContent to clipboard, marks message confirmed, appends a confirmation assistant message - 'Open Cover Letter Tool →' nav link still shows alongside the Copy button via the existing navUrl('open_cover_letter') path Co-Authored-By: Claude Sonnet 4.6 --- src/components/AiChatWidget.tsx | 53 ++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/src/components/AiChatWidget.tsx b/src/components/AiChatWidget.tsx index cc123fa..2842bef 100644 --- a/src/components/AiChatWidget.tsx +++ b/src/components/AiChatWidget.tsx @@ -9,10 +9,12 @@ interface ChatMessage { intent?: string; status?: string; suggestedAction?: string; - /** Semantic category: ticket_created | ticket_pending | kb_results | usage_info | navigation */ + /** Semantic category: ticket_created | ticket_pending | kb_results | usage_info | navigation | profile_draft | cover_letter_draft */ actionType?: string; /** Original user text — stored so ticket confirm has a description to send */ userQuery?: string; + /** Raw generated content (profile draft, cover letter body) for save/copy actions */ + draftContent?: string; /** True once the user has clicked the confirm/action button on this message */ confirmed?: boolean; } @@ -29,6 +31,8 @@ interface ChatResponse { status?: string; suggested_action?: string; action_type?: string; + /** Raw generated text (profile draft, cover letter body) for copy/save actions */ + draft_content?: string; } function statusLabel(status?: string): string | null { @@ -195,6 +199,7 @@ export function AiChatWidget() { status: data.status, suggestedAction: data.suggested_action, actionType: data.action_type, + draftContent: data.draft_content, userQuery: text, confirmed: false, }; @@ -280,10 +285,8 @@ export function AiChatWidget() { }; /** Called when user clicks "Save to Profile" on a profile_draft message. */ - const confirmSaveProfile = async (msgIdx: number, draftText: string) => { - // Extract just the quoted improved text if it's wrapped in our "Here's an improved..." message - const quotedMatch = draftText.match(/"([^"]+)"/); - const cleanDraft = quotedMatch ? quotedMatch[1] : draftText; + const confirmSaveProfile = async (msgIdx: number, draftContent: string) => { + const cleanDraft = draftContent; setMessages((prev) => prev.map((m, i) => (i === msgIdx ? { ...m, confirmed: true } : m))); setIsLoading(true); @@ -591,9 +594,9 @@ export function AiChatWidget() { {/* Save-profile button — for profile_draft messages */} - + + + {/* Copy button — for cover_letter_draft messages */} + + +