// chat.jsx — AI 어시스턴트(채팅). 자연어로 문서 생성·심사·점검을 실제 실행. const { Icon, Pill, Button } = window.RAlly; const SUGGEST = [ '이 트랙 심사해줘', '요건 충족됐어?', '위험관리계획서 다시 써줘', '뭘 만들 수 있어?', 'GMP 갱신심사가 뭐야?', ]; const Chat = ({ go }) => { const trackId = (typeof window !== 'undefined' && window.__RALLY_TRACK) || 1; const [msgs, setMsgs] = React.useState([ { role: 'bot', text: '안녕하세요! RAlly 어시스턴트예요. 문서 생성·가상 심사·요건 점검을 자연어로 시켜보세요.\n예: "이 트랙 심사해줘", "위험관리계획서 다시 써줘", "별표1이 뭐야?"', actions: [] }, ]); const [input, setInput] = React.useState(''); const [busy, setBusy] = React.useState(false); const endRef = React.useRef(null); React.useEffect(() => { if (endRef.current) endRef.current.scrollIntoView({ behavior: 'smooth' }); }, [msgs, busy]); const send = (text) => { const q = (text != null ? text : input).trim(); if (!q || busy) return; setMsgs((m) => [...m, { role: 'me', text: q }]); setInput(''); setBusy(true); fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: q, track_id: trackId }), }).then((r) => r.json()).then((d) => { setMsgs((m) => [...m, { role: 'bot', text: d.reply || '...', actions: d.actions || [] }]); setBusy(false); }).catch(() => { setMsgs((m) => [...m, { role: 'bot', text: '연결에 문제가 있어요. 다시 시도해 주세요.', actions: [] }]); setBusy(false); }); }; const doAction = (a) => { if (a.track_id) window.__RALLY_TRACK = a.track_id; if (a.doc_id) window.__RALLY_DOC = a.doc_id; if (a.screen) go(a.screen); }; return (
AI 어시스턴트 자연어로 실행
무엇을 도와드릴까요?
{/* 대화 스레드 */}
{msgs.map((m, i) => (
{m.role === 'bot' && (
RAlly
)}
{m.text}
{m.actions && m.actions.length > 0 && (
{m.actions.map((a, j) => ( ))}
)}
))} {busy && (
RAlly
처리 중…
)}
{/* 추천 + 입력 */} {msgs.length <= 1 && (
{SUGGEST.map((s) => ( send(s)} style={{ cursor: 'pointer', fontSize: 12.5, padding: '6px 11px', borderRadius: 999, border: '1px solid var(--line)', color: 'var(--ink-2)' }} onMouseEnter={(e) => (e.currentTarget.style.borderColor = 'var(--coral)')} onMouseLeave={(e) => (e.currentTarget.style.borderColor = 'var(--line)')}>{s} ))}
)}
setInput(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') send(); }} placeholder="문서 생성·심사·질문을 입력하세요…" disabled={busy} style={{ flex: 1, fontFamily: 'inherit', fontSize: 14, padding: '12px 14px', border: '1px solid var(--line)', borderRadius: 10, outline: 'none' }} />
실제 백엔드를 호출해 동작합니다. 생성·재작성 문서는 제출 전 RA 검토가 필요합니다.
); }; window.RAlly.Chat = Chat;