/** * The "Extract it from your Claude / ChatGPT" modal: shows the prompt the * merchant copies into their own assistant, then imports the pasted export * back into the Company ID record as reviewable proposals. */ import React, { useEffect, useState } from 'react'; import { CheckIcon, CopyIcon, UploadIcon } from 'lucide-react'; import { Button } from '../ui/button'; import { Textarea } from '../ui/textarea'; import { ChatGptLogo } from '../icons/chatgpt-logo'; import { ClaudeLogo } from '../icons/claude-logo'; import { CompanyIdExtractImportResult } from '../../service/company-id/company-id.interface'; import { getCompanyIdExtractPrompt, importCompanyIdExtract, } from '../../service/company-id/company-id.service'; import { MODAL_GHOST_BUTTON_CLASS, MODAL_PRIMARY_BUTTON_CLASS, } from './modal-buttons'; /** The assistants the extract flow supports. */ export const EXTRACT_ASSISTANT = { CLAUDE: 'claude', CHATGPT: 'chatgpt', } as const; export type ExtractAssistant = (typeof EXTRACT_ASSISTANT)[keyof typeof EXTRACT_ASSISTANT]; const ASSISTANT_LABELS: Record = { [EXTRACT_ASSISTANT.CLAUDE]: 'Claude', [EXTRACT_ASSISTANT.CHATGPT]: 'ChatGPT', }; const COPY_RESET_MS = 2000; interface ExtractModalProps { assistant: ExtractAssistant; clientId: string; token: string; onClose: () => void; /** Fired after a successful import so the page can refresh the record. */ onImported: (result: CompanyIdExtractImportResult) => void; onToast: (message: string) => void; } export function ExtractModal({ assistant, clientId, token, onClose, onImported, onToast, }: ExtractModalProps) { const [prompt, setPrompt] = useState(''); const [promptLoading, setPromptLoading] = useState(true); const [copied, setCopied] = useState(false); const [pasteText, setPasteText] = useState(''); const [importBusy, setImportBusy] = useState(false); const assistantLabel = ASSISTANT_LABELS[assistant]; useEffect(() => { let cancelled = false; setPromptLoading(true); getCompanyIdExtractPrompt(clientId, token) .then(text => { if (!cancelled) setPrompt(text); }) .catch((promptError: unknown) => { if (!cancelled) { onToast( promptError instanceof Error ? promptError.message : 'Could not load the prompt' ); } }) .finally(() => { if (!cancelled) setPromptLoading(false); }); return () => { cancelled = true; }; // One fetch per open; the prompt does not depend on modal-local state. }, [clientId, token]); const copyPrompt = async () => { try { await navigator.clipboard.writeText(prompt); setCopied(true); setTimeout(() => setCopied(false), COPY_RESET_MS); } catch { onToast('Copy failed. Select the prompt text and copy it manually.'); } }; const runImport = async () => { if (importBusy || !pasteText.trim()) return; setImportBusy(true); try { const result = await importCompanyIdExtract( clientId, token, pasteText.trim() ); onImported(result); } catch (importError) { onToast( importError instanceof Error ? importError.message : 'Import failed. Paste the full export and try again.' ); } finally { setImportBusy(false); } }; return (
!importBusy && onClose()} className="fixed inset-0 z-60 flex items-center justify-center bg-black/40 p-4" >
event.stopPropagation()} className="flex max-h-[88vh] w-full max-w-[680px] flex-col gap-4 overflow-y-auto rounded-2xl border border-border bg-card p-7 shadow-[0_24px_60px_rgba(0,0,0,0.18)]" >
{assistant === EXTRACT_ASSISTANT.CLAUDE ? ( ) : ( )}
Extract it from your {assistantLabel}
Step 1 {' '} Copy this prompt and run it in your {assistantLabel}. It answers from everything it already knows about your business and asks you for what's missing.