|
|
|
@@ -1,11 +1,15 @@ |
|
|
|
import { useFetchFlow } from '@/hooks/flow-hooks'; |
|
|
|
import get from 'lodash/get'; |
|
|
|
import React, { MouseEventHandler, useCallback, useMemo } from 'react'; |
|
|
|
import React, { |
|
|
|
MouseEventHandler, |
|
|
|
useCallback, |
|
|
|
useMemo, |
|
|
|
useState, |
|
|
|
} from 'react'; |
|
|
|
import JsonView from 'react18-json-view'; |
|
|
|
import 'react18-json-view/src/style.css'; |
|
|
|
import { useReplaceIdWithText } from '../../hooks'; |
|
|
|
|
|
|
|
import { useTheme } from '@/components/theme-provider'; |
|
|
|
import { |
|
|
|
Popover, |
|
|
|
PopoverContent, |
|
|
|
@@ -20,6 +24,17 @@ import { |
|
|
|
TableRow, |
|
|
|
} from '@/components/ui/table'; |
|
|
|
import { useTranslate } from '@/hooks/common-hooks'; |
|
|
|
import { |
|
|
|
Button, |
|
|
|
Card, |
|
|
|
Col, |
|
|
|
Input, |
|
|
|
Row, |
|
|
|
Space, |
|
|
|
Tabs, |
|
|
|
Typography, |
|
|
|
message, |
|
|
|
} from 'antd'; |
|
|
|
import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query'; |
|
|
|
|
|
|
|
interface IProps extends React.PropsWithChildren { |
|
|
|
@@ -31,7 +46,8 @@ export function NextNodePopover({ children, nodeId, name }: IProps) { |
|
|
|
const { t } = useTranslate('flow'); |
|
|
|
|
|
|
|
const { data } = useFetchFlow(); |
|
|
|
const { theme } = useTheme(); |
|
|
|
console.log(data); |
|
|
|
|
|
|
|
const component = useMemo(() => { |
|
|
|
return get(data, ['dsl', 'components', nodeId], {}); |
|
|
|
}, [nodeId, data]); |
|
|
|
@@ -42,6 +58,11 @@ export function NextNodePopover({ children, nodeId, name }: IProps) { |
|
|
|
[], |
|
|
|
); |
|
|
|
const output = get(component, ['obj', 'output'], {}); |
|
|
|
const { conf, messages, prompt } = get( |
|
|
|
component, |
|
|
|
['obj', 'params', 'infor'], |
|
|
|
{}, |
|
|
|
); |
|
|
|
const { replacedOutput } = useReplaceIdWithText(output); |
|
|
|
const stopPropagation: MouseEventHandler = useCallback((e) => { |
|
|
|
e.stopPropagation(); |
|
|
|
@@ -49,6 +70,13 @@ export function NextNodePopover({ children, nodeId, name }: IProps) { |
|
|
|
|
|
|
|
const getLabel = useGetComponentLabelByValue(nodeId); |
|
|
|
|
|
|
|
const [inputPage, setInputPage] = useState(1); |
|
|
|
const pageSize = 3; |
|
|
|
const pagedInputs = inputs.slice( |
|
|
|
(inputPage - 1) * pageSize, |
|
|
|
inputPage * pageSize, |
|
|
|
); |
|
|
|
|
|
|
|
return ( |
|
|
|
<Popover> |
|
|
|
<PopoverTrigger onClick={stopPropagation} asChild> |
|
|
|
@@ -59,62 +87,226 @@ export function NextNodePopover({ children, nodeId, name }: IProps) { |
|
|
|
side={'right'} |
|
|
|
sideOffset={20} |
|
|
|
onClick={stopPropagation} |
|
|
|
className="w-[400px]" |
|
|
|
className="w-[800px] p-4" |
|
|
|
style={{ maxHeight: 600, overflow: 'auto' }} |
|
|
|
> |
|
|
|
<div className="mb-3 font-semibold text-[16px]"> |
|
|
|
{name} {t('operationResults')} |
|
|
|
</div> |
|
|
|
<div className="flex w-full gap-4 flex-col"> |
|
|
|
<div className="flex flex-col space-y-1.5"> |
|
|
|
<span className="font-semibold text-[14px]">{t('input')}</span> |
|
|
|
<div |
|
|
|
style={ |
|
|
|
theme === 'dark' |
|
|
|
? { |
|
|
|
backgroundColor: 'rgba(150, 150, 150, 0.2)', |
|
|
|
} |
|
|
|
: {} |
|
|
|
} |
|
|
|
className={`bg-gray-100 p-1 rounded`} |
|
|
|
> |
|
|
|
<Table> |
|
|
|
<TableHeader> |
|
|
|
<TableRow> |
|
|
|
<TableHead>{t('componentId')}</TableHead> |
|
|
|
<TableHead className="w-[60px]">{t('content')}</TableHead> |
|
|
|
</TableRow> |
|
|
|
</TableHeader> |
|
|
|
<TableBody> |
|
|
|
{inputs.map((x, idx) => ( |
|
|
|
<TableRow key={idx}> |
|
|
|
<TableCell>{getLabel(x.component_id)}</TableCell> |
|
|
|
<TableCell className="truncate">{x.content}</TableCell> |
|
|
|
</TableRow> |
|
|
|
))} |
|
|
|
</TableBody> |
|
|
|
</Table> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div className="flex flex-col space-y-1.5"> |
|
|
|
<span className="font-semibold text-[14px]">{t('output')}</span> |
|
|
|
<div |
|
|
|
style={ |
|
|
|
theme === 'dark' |
|
|
|
? { |
|
|
|
backgroundColor: 'rgba(150, 150, 150, 0.2)', |
|
|
|
} |
|
|
|
: {} |
|
|
|
} |
|
|
|
className="bg-gray-100 p-1 rounded" |
|
|
|
> |
|
|
|
<JsonView |
|
|
|
src={replacedOutput} |
|
|
|
displaySize={30} |
|
|
|
className="w-full max-h-[300px] break-words overflow-auto" |
|
|
|
/> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<Card |
|
|
|
bordered={false} |
|
|
|
style={{ marginBottom: 16, padding: 0 }} |
|
|
|
bodyStyle={{ padding: 0 }} |
|
|
|
> |
|
|
|
<Typography.Title |
|
|
|
level={5} |
|
|
|
style={{ |
|
|
|
marginBottom: 16, |
|
|
|
fontWeight: 600, |
|
|
|
fontSize: 18, |
|
|
|
borderBottom: '1px solid #f0f0f0', |
|
|
|
paddingBottom: 8, |
|
|
|
}} |
|
|
|
> |
|
|
|
{name} {t('operationResults')} |
|
|
|
</Typography.Title> |
|
|
|
</Card> |
|
|
|
<Tabs |
|
|
|
defaultActiveKey="input" |
|
|
|
items={[ |
|
|
|
{ |
|
|
|
key: 'input', |
|
|
|
label: t('input'), |
|
|
|
children: ( |
|
|
|
<Card |
|
|
|
size="small" |
|
|
|
className="bg-gray-50 dark:bg-gray-800" |
|
|
|
style={{ borderRadius: 8, border: '1px solid #e5e7eb' }} |
|
|
|
bodyStyle={{ padding: 16 }} |
|
|
|
> |
|
|
|
<Table> |
|
|
|
<TableHeader> |
|
|
|
<TableRow> |
|
|
|
<TableHead>{t('componentId')}</TableHead> |
|
|
|
<TableHead className="w-[60px]"> |
|
|
|
{t('content')} |
|
|
|
</TableHead> |
|
|
|
</TableRow> |
|
|
|
</TableHeader> |
|
|
|
<TableBody> |
|
|
|
{pagedInputs.map((x, idx) => ( |
|
|
|
<TableRow key={idx + (inputPage - 1) * pageSize}> |
|
|
|
<TableCell>{getLabel(x.component_id)}</TableCell> |
|
|
|
<TableCell className="truncate"> |
|
|
|
{x.content} |
|
|
|
</TableCell> |
|
|
|
</TableRow> |
|
|
|
))} |
|
|
|
</TableBody> |
|
|
|
</Table> |
|
|
|
{/* Pagination */} |
|
|
|
{inputs.length > pageSize && ( |
|
|
|
<Row justify="end" style={{ marginTop: 8 }}> |
|
|
|
<Space> |
|
|
|
<Button |
|
|
|
size="small" |
|
|
|
disabled={inputPage === 1} |
|
|
|
onClick={() => setInputPage(inputPage - 1)} |
|
|
|
> |
|
|
|
Prev |
|
|
|
</Button> |
|
|
|
<span className="mx-2 text-sm"> |
|
|
|
{inputPage} / {Math.ceil(inputs.length / pageSize)} |
|
|
|
</span> |
|
|
|
<Button |
|
|
|
size="small" |
|
|
|
disabled={ |
|
|
|
inputPage === Math.ceil(inputs.length / pageSize) |
|
|
|
} |
|
|
|
onClick={() => setInputPage(inputPage + 1)} |
|
|
|
> |
|
|
|
Next |
|
|
|
</Button> |
|
|
|
</Space> |
|
|
|
</Row> |
|
|
|
)} |
|
|
|
</Card> |
|
|
|
), |
|
|
|
}, |
|
|
|
{ |
|
|
|
key: 'output', |
|
|
|
label: t('output'), |
|
|
|
children: ( |
|
|
|
<Card |
|
|
|
size="small" |
|
|
|
className="bg-gray-50 dark:bg-gray-800" |
|
|
|
style={{ borderRadius: 8, border: '1px solid #e5e7eb' }} |
|
|
|
bodyStyle={{ padding: 16 }} |
|
|
|
> |
|
|
|
<JsonView |
|
|
|
src={replacedOutput} |
|
|
|
displaySize={30} |
|
|
|
className="w-full max-h-[300px] break-words overflow-auto" |
|
|
|
/> |
|
|
|
</Card> |
|
|
|
), |
|
|
|
}, |
|
|
|
{ |
|
|
|
key: 'infor', |
|
|
|
label: t('infor'), |
|
|
|
children: ( |
|
|
|
<Card |
|
|
|
size="small" |
|
|
|
className="bg-gray-50 dark:bg-gray-800" |
|
|
|
style={{ borderRadius: 8, border: '1px solid #e5e7eb' }} |
|
|
|
bodyStyle={{ padding: 16 }} |
|
|
|
> |
|
|
|
<Row gutter={16}> |
|
|
|
<Col span={12}> |
|
|
|
{conf && ( |
|
|
|
<Card |
|
|
|
size="small" |
|
|
|
bordered={false} |
|
|
|
style={{ |
|
|
|
marginBottom: 16, |
|
|
|
background: 'transparent', |
|
|
|
}} |
|
|
|
bodyStyle={{ padding: 0 }} |
|
|
|
> |
|
|
|
<Typography.Text |
|
|
|
strong |
|
|
|
style={{ |
|
|
|
color: '#888', |
|
|
|
marginBottom: 8, |
|
|
|
display: 'block', |
|
|
|
}} |
|
|
|
> |
|
|
|
Configuration: |
|
|
|
</Typography.Text> |
|
|
|
<JsonView |
|
|
|
src={conf} |
|
|
|
displaySize={30} |
|
|
|
className="w-full max-h-[120px] break-words overflow-auto" |
|
|
|
/> |
|
|
|
</Card> |
|
|
|
)} |
|
|
|
{prompt && ( |
|
|
|
<Card |
|
|
|
size="small" |
|
|
|
bordered={false} |
|
|
|
style={{ background: 'transparent' }} |
|
|
|
bodyStyle={{ padding: 0 }} |
|
|
|
> |
|
|
|
<Row |
|
|
|
align="middle" |
|
|
|
justify="space-between" |
|
|
|
style={{ marginBottom: 8 }} |
|
|
|
> |
|
|
|
<Col> |
|
|
|
<Typography.Text strong style={{ color: '#888' }}> |
|
|
|
Prompt: |
|
|
|
</Typography.Text> |
|
|
|
</Col> |
|
|
|
<Col> |
|
|
|
<Button |
|
|
|
size="small" |
|
|
|
onClick={() => { |
|
|
|
const inlineString = prompt |
|
|
|
.replace(/\s+/g, ' ') |
|
|
|
.trim(); |
|
|
|
navigator.clipboard.writeText(inlineString); |
|
|
|
message.success( |
|
|
|
'Prompt copied as single line!', |
|
|
|
); |
|
|
|
}} |
|
|
|
> |
|
|
|
Copy as single line |
|
|
|
</Button> |
|
|
|
</Col> |
|
|
|
</Row> |
|
|
|
<Input.TextArea |
|
|
|
value={prompt} |
|
|
|
readOnly |
|
|
|
autoSize={{ minRows: 2, maxRows: 6 }} |
|
|
|
className="bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700" |
|
|
|
/> |
|
|
|
</Card> |
|
|
|
)} |
|
|
|
</Col> |
|
|
|
<Col span={12}> |
|
|
|
{messages && ( |
|
|
|
<Card |
|
|
|
size="small" |
|
|
|
bordered={false} |
|
|
|
style={{ |
|
|
|
marginBottom: 16, |
|
|
|
background: 'transparent', |
|
|
|
}} |
|
|
|
bodyStyle={{ padding: 0 }} |
|
|
|
> |
|
|
|
<Typography.Text |
|
|
|
strong |
|
|
|
style={{ |
|
|
|
color: '#888', |
|
|
|
marginBottom: 8, |
|
|
|
display: 'block', |
|
|
|
}} |
|
|
|
> |
|
|
|
Messages: |
|
|
|
</Typography.Text> |
|
|
|
<div className="max-h-[300px] overflow-auto"> |
|
|
|
<JsonView |
|
|
|
src={messages} |
|
|
|
displaySize={30} |
|
|
|
className="w-full break-words" |
|
|
|
/> |
|
|
|
</div> |
|
|
|
</Card> |
|
|
|
)} |
|
|
|
</Col> |
|
|
|
</Row> |
|
|
|
</Card> |
|
|
|
), |
|
|
|
}, |
|
|
|
]} |
|
|
|
/> |
|
|
|
</PopoverContent> |
|
|
|
</Popover> |
|
|
|
); |