import { observer } from 'mobx-react-lite'; import React, { useCallback, useState } from 'react'; import { useVizStore } from '../../store'; import { HandThumbDownIcon, HandThumbUpIcon, PaperAirplaneIcon } from '@heroicons/react/24/outline'; import Spinner from '../spinner'; import { IAskVizFeedback, IChart, IViewField, IVisSpec } from '../../interfaces'; import { useTranslation } from 'react-i18next'; import { useReporter } from '../../utils/reportError'; import { parseErrorMessage } from '../../utils'; import { Button } from '../ui/button'; import { Input } from '../ui/input'; const api = import.meta.env.DEV ? 'http://localhost:2023/api/vis/text2gw' : 'https://api.kanaries.net/vis/text2gw'; async function vizQuery(api: string, metas: IViewField[], query: string, headers: Record) { const res = await fetch(api, { headers: { 'Content-Type': 'application/json', ...headers, }, credentials: 'include', method: 'POST', body: JSON.stringify({ metas, messages: [ { role: 'user', content: query, }, ], }), }); const result: { success: boolean; data: IVisSpec | IChart; message?: string; } = await res.json(); if (result.success) { return result.data; } else { throw new Error(result.message); } } async function reportVizQuery(api: string | ((data: IAskVizFeedback) => void), data: IAskVizFeedback, headers: Record) { if (typeof api === 'function') { return api(data); } const res = await fetch(api, { headers: { 'Content-Type': 'application/json', ...headers, }, credentials: 'include', method: 'POST', body: JSON.stringify(data), }); const result = await res.json(); if (result.success) { return; } else { throw new Error(result.message); } } const AskViz: React.FC<{ api?: string | ((metas: IViewField[], query: string) => PromiseLike | IVisSpec | IChart); feedbackApi?: string | ((data: IAskVizFeedback) => void); headers?: Record; }> = (props) => { const { feedbackApi } = props; const [query, setQuery] = useState(''); const [loading, setLoading] = useState(false); const vizStore = useVizStore(); const { t } = useTranslation(); const [askVizFeedback, setAskVizFeedback] = useState<'vote' | 'report' | 'none'>('none'); const allFields = vizStore.allFields; const { reportError } = useReporter(); const [lastData, setLastData] = useState<{ question: string; data: string } | null>(null); const startQuery = useCallback(() => { setLoading(true); const request = typeof props.api === 'function' ? Promise.resolve(props.api(allFields, query)) : vizQuery(props.api || api, allFields, query, props.headers ?? {}); request .then((data) => { vizStore.appendFromCode(data); vizStore.setAskvizFeedback(true); setLastData({ question: query, data: JSON.stringify(data) }); setAskVizFeedback('vote'); }) .catch((err) => { reportError(parseErrorMessage(err), 502); }) .finally(() => { setLoading(false); }); }, [props.api, props.headers, allFields, query, vizStore]); const showFeedback = feedbackApi && lastData && vizStore.showAskvizFeedbackIndex === vizStore.visIndex; return (
setQuery(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter' && !e.nativeEvent.isComposing && loading === false && query.length > 0) { startQuery(); } }} disabled={loading || allFields.length === 0} /> {showFeedback && askVizFeedback === 'vote' && (
{t('App.feedback.vote')}
)} {showFeedback && askVizFeedback === 'report' && (
{t('App.feedback.report')}
)}
); }; export default observer(AskViz);