import React, { useState, useRef, useEffect } from 'react'; import { IPublicClientApplication } from '@azure/msal-browser'; import type { DropdownOptions } from '../App'; import { analyzeWIPProof, getWIPChatResponse } from '../services/geminiService'; import type { AgentName } from '../types'; import { UserIcon } from './icons/UserIcon'; import { LeadAgentIcon } from './icons/LeadAgentIcon'; import { PaperClipIcon } from './icons/PaperClipIcon'; import { SendIcon } from './icons/SendIcon'; import { ChevronDownIcon } from './icons/ChevronDownIcon'; import { SpinnerIcon } from './icons/SpinnerIcon'; import { DocumentIcon } from './icons/DocumentIcon'; // --- TYPE DEFINITIONS --- interface TextContent { type: 'text'; text: string; } interface FilePreviewContent { type: 'file_preview'; fileName: string; previewUrl: string; mimeType: string; } interface ConfirmationContent { type: 'confirmation'; file: File; } interface LoadingContent { type: 'loading'; } interface ErrorContent { type: 'error'; text: string; } type MessageContent = TextContent | FilePreviewContent | ConfirmationContent | LoadingContent | ErrorContent; interface Message { id: string; sender: 'user' | 'agent'; content: MessageContent; } const fileToDataUrl = (file: File): Promise => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result as string); reader.onerror = reject; reader.readAsDataURL(file); }); }; // --- SUB-COMPONENTS --- const ConfirmationComponent: React.FC<{ dropdownOptions: DropdownOptions; onConfirm: (details: { channel: string; subChannel: string; proofType: string }) => void; }> = ({ dropdownOptions, onConfirm }) => { const [channel, setChannel] = useState(''); const [subChannel, setSubChannel] = useState(''); const [proofType, setProofType] = useState(''); const availableChannels = Object.keys(dropdownOptions.channels); const availableSubChannels = channel ? Object.keys(dropdownOptions.channels[channel] || {}) : []; const availableProofTypes = (channel && subChannel) ? (dropdownOptions.channels[channel][subChannel] || []) : []; const showProofType = availableProofTypes.length > 0; useEffect(() => { setSubChannel(''); setProofType(''); }, [channel]); useEffect(() => { if (!showProofType) setProofType(''); else setProofType(''); }, [subChannel, showProofType]); const handleSubmit = () => { if (channel && subChannel && (!showProofType || proofType)) { onConfirm({ channel, subChannel, proofType }); } }; const isSubmitDisabled = !channel || !subChannel || (showProofType && !proofType); return (

Please confirm the details for this proof:

{showProofType && (
)}
); }; const MessageBubble: React.FC<{ sender: 'user' | 'agent'; children: React.ReactNode }> = ({ sender, children }) => { const isUser = sender === 'user'; const bubbleClasses = isUser ? 'bg-oliver-azure text-white' : 'bg-white text-gray-800 border border-gray-200'; const alignmentClasses = isUser ? 'items-end' : 'items-start'; const avatar = isUser ?
:
; const isConfirmation = React.isValidElement(children) && children.type === ConfirmationComponent; return (
{avatar}
{children}
); }; // --- MAIN COMPONENT --- interface WIPReviewerProps { dropdownOptions: DropdownOptions; msalInstance: IPublicClientApplication; } export const WIPReviewer: React.FC = ({ dropdownOptions, msalInstance }) => { const [messages, setMessages] = useState([ { id: `msg_${Date.now()}`, sender: 'agent', content: { type: 'text', text: "Hello! I'm the Lead Agent. Upload a work-in-progress file for review, or ask me a question about marketing best practices." }, } ]); const [userInput, setUserInput] = useState(''); const [isLoading, setIsLoading] = useState(false); const fileInputRef = useRef(null); const messagesEndRef = useRef(null); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); const addMessage = (sender: 'user' | 'agent', content: MessageContent) => { setMessages(prev => [...prev, { id: `msg_${Date.now()}`, sender, content }]); }; const updateLastMessage = (newContent: MessageContent) => { setMessages(prev => { const newMessages = [...prev]; newMessages[newMessages.length - 1].content = newContent; return newMessages; }); }; const handleFileSelect = async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; try { const previewUrl = await fileToDataUrl(file); addMessage('user', { type: 'file_preview', fileName: file.name, previewUrl, mimeType: file.type }); addMessage('agent', { type: 'confirmation', file }); } catch (error) { console.error("Error creating file preview:", error); addMessage('agent', { type: 'error', text: "Sorry, I couldn't load a preview for that file. Please try another file." }); } // Reset file input if (event.target) event.target.value = ''; }; const handleSendMessage = async (e: React.FormEvent) => { e.preventDefault(); const trimmedInput = userInput.trim(); if (!trimmedInput || isLoading) return; addMessage('user', { type: 'text', text: trimmedInput }); setUserInput(''); setIsLoading(true); addMessage('agent', { type: 'loading' }); try { const responseText = await getWIPChatResponse(trimmedInput); updateLastMessage({ type: 'text', text: responseText }); } catch (err) { console.error("Chat response failed:", err); updateLastMessage({ type: 'error', text: "Sorry, I couldn't get a response. Please try again." }); } finally { setIsLoading(false); } }; const handleConfirmAnalysis = async ( messageId: string, details: { channel: string; subChannel: string; proofType: string }, file: File ) => { setIsLoading(true); // Replace confirmation message with loading indicator setMessages(prev => prev.map(msg => msg.id === messageId ? { ...msg, content: { type: 'loading' } } : msg)); try { const summary = await analyzeWIPProof(file, () => {}, msalInstance); setMessages(prev => prev.map(msg => msg.id === messageId ? { ...msg, content: { type: 'text', text: summary } } : msg)); } catch (err) { console.error("Analysis failed:", err); setMessages(prev => prev.map(msg => msg.id === messageId ? { ...msg, content: { type: 'error', text: "Sorry, an error occurred during the analysis. Please try again." } } : msg)); } finally { setIsLoading(false); } }; return (

WIP Reviewer

{/* Message Display Area */}
{messages.map((msg) => ( {(() => { switch (msg.content.type) { case 'text': return

{msg.content.text}

; case 'file_preview': const { mimeType, previewUrl, fileName } = msg.content; if (mimeType.startsWith('image/')) { return {fileName}; } return (
{fileName}
); case 'confirmation': return handleConfirmAnalysis(msg.id, details, msg.content.file)} />; case 'loading': return (
Thinking...
); case 'error': return

{msg.content.text}

default: return null; } })()}
))}
{/* Message Input Area */}