feat(ai-widget): Copy Cover Letter button + draft_content plumbing
All checks were successful
build-and-release / build (push) Successful in 1m46s
All checks were successful
build-and-release / build (push) Successful in 1m46s
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
106561189f
commit
ff2dde1900
1 changed files with 46 additions and 7 deletions
|
|
@ -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() {
|
|||
</Show>
|
||||
|
||||
{/* Save-profile button — for profile_draft messages */}
|
||||
<Show when={msg.actionType === "profile_draft"}>
|
||||
<Show when={msg.actionType === "profile_draft" && msg.draftContent}>
|
||||
<button
|
||||
onClick={() => confirmSaveProfile(idx(), msg.content)}
|
||||
onClick={() => confirmSaveProfile(idx(), msg.draftContent!)}
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
"align-items": "center",
|
||||
|
|
@ -610,6 +613,42 @@ export function AiChatWidget() {
|
|||
Save to Profile
|
||||
</button>
|
||||
</Show>
|
||||
|
||||
{/* Copy button — for cover_letter_draft messages */}
|
||||
<Show when={msg.actionType === "cover_letter_draft" && msg.draftContent}>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(msg.draftContent!).then(() => {
|
||||
// Mark confirmed so the buttons disappear after copy
|
||||
setMessages((prev) =>
|
||||
prev.map((m, i) => (i === idx() ? { ...m, confirmed: true } : m))
|
||||
);
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
role: "assistant" as const,
|
||||
content: "Cover letter copied to clipboard! Paste it into your application and tailor it to the specific role.",
|
||||
confirmed: true,
|
||||
},
|
||||
]);
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
"align-items": "center",
|
||||
padding: "5px 12px",
|
||||
background: "#2563EB",
|
||||
border: "none",
|
||||
"border-radius": "20px",
|
||||
color: "#fff",
|
||||
"font-size": "11px",
|
||||
"font-weight": "600",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
Copy Cover Letter
|
||||
</button>
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
</>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue