feat(Builder): Limit Output preview size (#7554)

* feat(Builder): Limit Output preview size

* fix indent
pull/7564/head^2
Bently 2024-07-24 08:23:43 +01:00 committed by GitHub
parent 77034f2df0
commit 03ea51b266
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 78 additions and 9 deletions

View File

@ -1,8 +1,9 @@
import React, { useState, useEffect, FC, memo } from 'react'; import React, { useState, useEffect, FC, memo, useRef } from 'react';
import { NodeProps } from 'reactflow'; import { NodeProps } from 'reactflow';
import 'reactflow/dist/style.css'; import 'reactflow/dist/style.css';
import './customnode.css'; import './customnode.css';
import ModalComponent from './ModalComponent'; import InputModalComponent from './InputModalComponent';
import OutputModalComponent from './OutputModalComponent';
import { Button } from './ui/button'; import { Button } from './ui/button';
import { Input } from './ui/input'; import { Input } from './ui/input';
import { BlockSchema } from '@/lib/types'; import { BlockSchema } from '@/lib/types';
@ -33,6 +34,8 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
const [activeKey, setActiveKey] = useState<string | null>(null); const [activeKey, setActiveKey] = useState<string | null>(null);
const [modalValue, setModalValue] = useState<string>(''); const [modalValue, setModalValue] = useState<string>('');
const [errors, setErrors] = useState<{ [key: string]: string | null }>({}); const [errors, setErrors] = useState<{ [key: string]: string | null }>({});
const [isOutputModalOpen, setIsOutputModalOpen] = useState(false);
const outputDataRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
if (data.output_data || data.status) { if (data.output_data || data.status) {
@ -400,6 +403,16 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
return Object.values(newErrors).every((error) => error === null); return Object.values(newErrors).every((error) => error === null);
}; };
const handleOutputClick = () => {
setIsOutputModalOpen(true);
setModalValue(typeof data.output_data === 'object' ? JSON.stringify(data.output_data, null, 2) : data.output_data);
};
const isTextTruncated = (element: HTMLElement | null): boolean => {
if (!element) return false;
return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
};
return ( return (
<div className={`custom-node dark-theme ${data.status === 'RUNNING' ? 'running' : data.status === 'COMPLETED' ? 'completed' : data.status === 'FAILED' ? 'failed' : ''}`}> <div className={`custom-node dark-theme ${data.status === 'RUNNING' ? 'running' : data.status === 'COMPLETED' ? 'completed' : data.status === 'FAILED' ? 'failed' : ''}`}>
<div className="mb-2"> <div className="mb-2">
@ -423,16 +436,24 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
</div> </div>
</div> </div>
{isOutputOpen && ( {isOutputOpen && (
<div className="node-output"> <div className="node-output" onClick={handleOutputClick}>
<p> <p>
<strong>Status:</strong>{' '} <strong>Status:</strong>{' '}
{typeof data.status === 'object' ? JSON.stringify(data.status) : data.status || 'N/A'} {typeof data.status === 'object' ? JSON.stringify(data.status) : data.status || 'N/A'}
</p> </p>
<p> <p>
<strong>Output Data:</strong>{' '} <strong>Output Data:</strong>{' '}
{typeof data.output_data === 'object' {(() => {
const outputText = typeof data.output_data === 'object'
? JSON.stringify(data.output_data) ? JSON.stringify(data.output_data)
: data.output_data || 'N/A'} : data.output_data;
if (!outputText) return 'No output data';
return outputText.length > 100
? `${outputText.slice(0, 100)}... Press To Read More`
: outputText;
})()}
</p> </p>
</div> </div>
)} )}
@ -446,13 +467,18 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
</> </>
)} )}
</div> </div>
<ModalComponent <InputModalComponent
isOpen={isModalOpen} isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)} onClose={() => setIsModalOpen(false)}
onSave={handleModalSave} onSave={handleModalSave}
value={modalValue} value={modalValue}
key={activeKey} key={activeKey}
/> />
<OutputModalComponent
isOpen={isOutputModalOpen}
onClose={() => setIsOutputModalOpen(false)}
value={modalValue}
/>
</div> </div>
); );
}; };

View File

@ -9,7 +9,7 @@ interface ModalProps {
value: string; value: string;
} }
const ModalComponent: FC<ModalProps> = ({ isOpen, onClose, onSave, value }) => { const InputModalComponent: FC<ModalProps> = ({ isOpen, onClose, onSave, value }) => {
const [tempValue, setTempValue] = React.useState(value); const [tempValue, setTempValue] = React.useState(value);
const textAreaRef = useRef<HTMLTextAreaElement>(null); const textAreaRef = useRef<HTMLTextAreaElement>(null);
@ -50,4 +50,4 @@ const ModalComponent: FC<ModalProps> = ({ isOpen, onClose, onSave, value }) => {
); );
}; };
export default ModalComponent; export default InputModalComponent;

View File

@ -0,0 +1,43 @@
import React, { FC, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { Button } from './ui/button';
import { Textarea } from './ui/textarea';
interface OutputModalProps {
isOpen: boolean;
onClose: () => void;
value: string;
}
const OutputModalComponent: FC<OutputModalProps> = ({ isOpen, onClose, value }) => {
const [tempValue, setTempValue] = React.useState(value);
useEffect(() => {
if (isOpen) {
setTempValue(value);
}
}, [isOpen, value]);
if (!isOpen) {
return null;
}
return createPortal(
<div className="fixed inset-0 bg-white bg-opacity-60 flex justify-center items-center z-50">
<div className="bg-white p-5 rounded-lg w-[1000px] max-w-[100%]">
<center><h1 style={{ color: 'black' }}>Full Output</h1></center>
<Textarea
className="w-full h-[400px] p-2.5 rounded border border-[#dfdfdf] text-black bg-[#dfdfdf]"
value={tempValue}
readOnly
/>
<div className="flex justify-end gap-2.5 mt-2.5">
<Button onClick={onClose}>Close</Button>
</div>
</div>
</div>,
document.body
);
};
export default OutputModalComponent;