Merge branch 'two_edges_merge' into two_edges

This commit is contained in:
Lucas Oliveira 2024-06-11 18:19:59 -03:00
commit fde9a77035
385 changed files with 7921 additions and 2253 deletions

View file

@ -48,7 +48,7 @@
"file-saver": "^2.0.5",
"framer-motion": "^11.0.6",
"lodash": "^4.17.21",
"lucide-react": "^0.331.0",
"lucide-react": "^0.394.0",
"million": "^3.0.6",
"moment": "^2.29.4",
"openseadragon": "^4.1.1",
@ -481,7 +481,6 @@
},
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
"version": "1.3.0",
"extraneous": true,
"inBundle": true,
"license": "MIT",
"engines": {
@ -8614,9 +8613,9 @@
}
},
"node_modules/lucide-react": {
"version": "0.331.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.331.0.tgz",
"integrity": "sha512-CHFJ0ve9vaZ7bB2VRAl27SlX1ELh6pfNC0jS96qGpPEEzLkLDGq4pDBFU8RhOoRMqsjXqTzLm9U6bZ1OcIHq7Q==",
"version": "0.394.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.394.0.tgz",
"integrity": "sha512-PzTbJ0bsyXRhH59k5qe7MpTd5MxlpYZUcM9kGSwvPGAfnn0J6FElDwu2EX6Vuh//F7y60rcVJiFQ7EK9DCMgfw==",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
}

View file

@ -6,7 +6,6 @@
"@headlessui/react": "^1.7.17",
"@hookform/resolvers": "^3.3.4",
"@million/lint": "^0.0.73",
"react-hotkeys-hook": "^4.5.0",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4",
@ -44,7 +43,7 @@
"file-saver": "^2.0.5",
"framer-motion": "^11.0.6",
"lodash": "^4.17.21",
"lucide-react": "^0.331.0",
"lucide-react": "^0.394.0",
"million": "^3.0.6",
"moment": "^2.29.4",
"openseadragon": "^4.1.1",
@ -56,6 +55,7 @@
"react-dom": "^18.2.21",
"react-error-boundary": "^4.0.11",
"react-hook-form": "^7.51.4",
"react-hotkeys-hook": "^4.5.0",
"react-icons": "^5.0.1",
"react-laag": "^2.0.5",
"react-markdown": "^8.0.7",
@ -138,4 +138,4 @@
"ua-parser-js": "^1.0.37",
"vite": "^4.5.2"
}
}
}

View file

@ -90,31 +90,31 @@ body {
}
::-webkit-scrollbar-track {
background-color: #f1f1f1 !important;
background-color: hsl(var(--muted)) !important;
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background-color: #ccc !important;
background-color: hsl(var(--border)) !important;
border-radius: 999px !important;
}
::-webkit-scrollbar-thumb:hover {
background-color: #bbb !important;
background-color: hsl(var(--ring)) !important;
}
.jv-indent::-webkit-scrollbar-track {
background-color: #f1f1f1 !important;
background-color: hsl(var(--muted)) !important;
border-radius: 10px;
}
.jv-indent::-webkit-scrollbar-thumb {
background-color: #ccc !important;
background-color: hsl(var(--border)) !important;
border-radius: 999px !important;
}
.jv-indent::-webkit-scrollbar-thumb:hover {
background-color: #bbb !important;
background-color: hsl(var(--ring)) !important;
}
.custom-hover {
@ -138,33 +138,6 @@ body {
background-color: #bbb !important;
}
.ag-body-horizontal-scroll-viewport,
.ag-body-vertical-scroll-viewport {
cursor: auto;
}
.ag-body-horizontal-scroll-viewport::-webkit-scrollbar,
.ag-body-vertical-scroll-viewport::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.ag-body-horizontal-scroll-viewport::-webkit-scrollbar-track,
.ag-body-vertical-scroll-viewport::-webkit-scrollbar-track {
background-color: #f1f1f1;
}
.ag-body-horizontal-scroll-viewport::-webkit-scrollbar-thumb,
.ag-body-vertical-scroll-viewport::-webkit-scrollbar-thumb {
background-color: #ccc;
border-radius: 999px;
}
.ag-body-horizontal-scroll-viewport::-webkit-scrollbar-thumb:hover,
.ag-body-vertical-scroll-viewport::-webkit-scrollbar-thumb:hover {
background-color: #bbb;
}
/* This CSS is to not apply the border for the column having 'no-border' class */
.no-border.ag-cell:focus {
border: none !important;
@ -183,3 +156,8 @@ body {
.react-flow__edge .react-flow__edge-path {
stroke: var(--connection) !important;
}
.ag-react-container {
width: 100%;
height: 100%;
}

View file

@ -29,10 +29,10 @@ export default function App() {
useTrackLastVisitedPath();
const removeFromTempNotificationList = useAlertStore(
(state) => state.removeFromTempNotificationList
(state) => state.removeFromTempNotificationList,
);
const tempNotificationList = useAlertStore(
(state) => state.tempNotificationList
(state) => state.tempNotificationList,
);
const [fetchError, setFetchError] = useState(false);
const isLoading = useFlowsManagerStore((state) => state.isLoading);
@ -50,7 +50,7 @@ export default function App() {
const refreshVersion = useDarkStore((state) => state.refreshVersion);
const refreshStars = useDarkStore((state) => state.refreshStars);
const setGlobalVariables = useGlobalVariablesStore(
(state) => state.setGlobalVariables
(state) => state.setGlobalVariables,
);
const checkHasStore = useStoreStore((state) => state.checkHasStore);
const navigate = useNavigate();

View file

@ -36,15 +36,21 @@ import {
scapedJSONStringfy,
} from "../../../../utils/reactflowUtils";
import { nodeColors } from "../../../../utils/styleUtils";
import { classNames, groupByFamily } from "../../../../utils/utils";
import {
classNames,
groupByFamily,
isThereModal,
} from "../../../../utils/utils";
import useFetchDataOnMount from "../../../hooks/use-fetch-data-on-mount";
import useHandleOnNewValue from "../../../hooks/use-handle-new-value";
import useHandleNodeClass from "../../../hooks/use-handle-node-class";
import useHandleRefreshButtonPress from "../../../hooks/use-handle-refresh-buttons";
import HandleTooltips from "../HandleTooltipComponent";
import OutputComponent from "../OutputComponent";
import OutputModal from "../outputModal";
import { TEXT_FIELD_TYPES } from "./constants";
import OutputModal from "../outputModal";
import { useShortcutsStore } from "../../../../stores/shortcuts";
import { useHotkeys } from "react-hotkeys-hook";
export default function ParameterComponent({
left,
@ -62,6 +68,7 @@ export default function ParameterComponent({
showNode,
index,
outputName,
selected,
outputProxy,
}: ParameterComponentType): JSX.Element {
const infoHtml = useRef<HTMLDivElement & ReactNode>(null);
@ -78,7 +85,9 @@ export default function ParameterComponent({
const [openOutputModal, setOpenOutputModal] = useState(false);
const flowPool = useFlowStore((state) => state.flowPool);
const displayOutputPreview = !!flowPool[data.id];
const displayOutputPreview =
!!flowPool[data.id] &&
flowPool[data.id][flowPool[data.id].length - 1]?.valid;
const unknownOutput = !!(
flowPool[data.id] &&
@ -86,6 +95,19 @@ export default function ParameterComponent({
"unknown"
);
const preventDefault = true;
function handleOutputWShortcut() {
if (!displayOutputPreview || unknownOutput) return;
if (isThereModal() && !openOutputModal) return;
if (selected && !left) {
setOpenOutputModal((state) => !state);
}
}
const output = useShortcutsStore((state) => state.output);
useHotkeys(output, handleOutputWShortcut, { preventDefault });
const { handleOnNewValue: handleOnNewValueHook } = useHandleOnNewValue(
data,
name,
@ -93,7 +115,7 @@ export default function ParameterComponent({
handleUpdateValues,
debouncedHandleUpdateValues,
setNode,
setIsLoading
setIsLoading,
);
const { handleNodeClass: handleNodeClassHook } = useHandleNodeClass(
@ -101,7 +123,7 @@ export default function ParameterComponent({
name,
takeSnapshot,
setNode,
updateNodeInternals
updateNodeInternals,
);
const { handleRefreshButtonPress: handleRefreshButtonPressHook } =
@ -110,7 +132,7 @@ export default function ParameterComponent({
let disabled =
edges.some(
(edge) =>
edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id)
edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id),
) ?? false;
const handleRefreshButtonPress = async (name, data) => {
@ -121,7 +143,7 @@ export default function ParameterComponent({
const handleOnNewValue = async (
newValue: string | string[] | boolean | Object[],
skipSnapshot: boolean | undefined = false
skipSnapshot: boolean | undefined = false,
): Promise<void> => {
handleOnNewValueHook(newValue, skipSnapshot);
};
@ -208,14 +230,14 @@ export default function ParameterComponent({
className={classNames(
left ? "my-12 -ml-0.5 " : " my-12 -mr-0.5 ",
"h-3 w-3 rounded-full border-2 bg-background",
!showNode ? "mt-0" : ""
!showNode ? "mt-0" : "",
)}
style={{
borderColor: color ?? nodeColors.unknown,
}}
onClick={() => {
setFilterEdge(
groupByFamily(myData, tooltipTitle!, left, nodes!)
groupByFamily(myData, tooltipTitle!, left, nodes!),
);
}}
></Handle>
@ -277,7 +299,7 @@ export default function ParameterComponent({
"h-5 w-5 rounded-md",
displayOutputPreview && !unknownOutput
? " hover:bg-secondary-foreground/5 hover:text-medium-indigo"
: " cursor-not-allowed text-muted-foreground"
: " cursor-not-allowed text-muted-foreground",
)}
name={"ScanEye"}
/>
@ -333,12 +355,12 @@ export default function ParameterComponent({
}
className={classNames(
left ? "-ml-0.5" : "-mr-0.5",
"h-3 w-3 rounded-full border-2 bg-background"
"h-3 w-3 rounded-full border-2 bg-background",
)}
style={{ borderColor: color ?? nodeColors.unknown }}
onClick={() => {
setFilterEdge(
groupByFamily(myData, tooltipTitle!, left, nodes!)
groupByFamily(myData, tooltipTitle!, left, nodes!),
);
}}
/>

View file

@ -24,7 +24,7 @@ const TooltipRenderComponent = ({ item, index, left }) => {
<span
key={index}
className={classNames(
index > 0 ? "mt-2 flex items-center" : "mt-3 flex items-center"
index > 0 ? "mt-2 flex items-center" : "mt-3 flex items-center",
)}
>
<div

View file

@ -1,5 +1,6 @@
import emojiRegex from "emoji-regex";
import { useEffect, useMemo, useState } from "react";
import Markdown from "react-markdown";
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
import IconComponent from "../../components/genericIconComponent";
import InputComponent from "../../components/inputComponent";
@ -37,6 +38,7 @@ import useValidationStatusString from "../hooks/use-validation-status-string";
import getFieldTitle from "../utils/get-field-title";
import sortFields from "../utils/sort-fields";
import ParameterComponent from "./components/parameterComponent";
import { useHotkeys } from "react-hotkeys-hook";
export default function GenericNode({
data,
@ -48,6 +50,7 @@ export default function GenericNode({
xPos?: number;
yPos?: number;
}): JSX.Element {
const preventDefault = true;
const types = useTypesStore((state) => state.types);
const templates = useTypesStore((state) => state.templates);
const deleteNode = useFlowStore((state) => state.deleteNode);
@ -64,14 +67,14 @@ export default function GenericNode({
const [nodeName, setNodeName] = useState(data.node!.display_name);
const [inputDescription, setInputDescription] = useState(false);
const [nodeDescription, setNodeDescription] = useState(
data.node?.description!
data.node?.description!,
);
const [isOutdated, setIsOutdated] = useState(false);
const buildStatus = useFlowStore(
(state) => state.flowBuildStatus[data.id]?.status
(state) => state.flowBuildStatus[data.id]?.status,
);
const lastRunTime = useFlowStore(
(state) => state.flowBuildStatus[data.id]?.timestamp
(state) => state.flowBuildStatus[data.id]?.timestamp,
);
const [validationStatus, setValidationStatus] =
useState<VertexBuildTypeAPI | null>(null);
@ -88,7 +91,7 @@ export default function GenericNode({
data.node!,
setNode,
setIsOutdated,
updateNodeInternals
updateNodeInternals,
);
const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
@ -108,23 +111,19 @@ export default function GenericNode({
};
const renderIconStatus = () => {
return (
<div className="generic-node-status-position flex items-center justify-center">
{iconStatus}
</div>
);
return <div className="cursor-help">{iconStatus}</div>;
};
const getNodeBorderClassName = (
selected: boolean,
showNode: boolean,
buildStatus: BuildStatus | undefined,
validationStatus: VertexBuildTypeAPI | null
validationStatus: VertexBuildTypeAPI | null,
) => {
const specificClassFromBuildStatus = getSpecificClassFromBuildStatus(
buildStatus,
validationStatus,
isDark
isDark,
);
const baseBorderClass = getBaseBorderClass(selected);
@ -133,7 +132,7 @@ export default function GenericNode({
baseBorderClass,
nodeSizeClass,
"generic-node-div group/node",
specificClassFromBuildStatus
specificClassFromBuildStatus,
);
return names;
};
@ -141,7 +140,9 @@ export default function GenericNode({
// const [openWDoubleCLick, setOpenWDoubleCLick] = useState(false);
const getBaseBorderClass = (selected) => {
let className = selected ? "border border-ring" : "border";
let className = selected
? "border border-ring hover:shadow-node"
: "border hover:shadow-node";
let frozenClass = selected ? "border-ring-frozen" : "border-frozen";
return data.node?.frozen ? frozenClass : className;
};
@ -176,7 +177,7 @@ export default function GenericNode({
showNode,
isEmoji,
nodeIconFragment,
checkNodeIconFragment
checkNodeIconFragment,
);
function countHandles(): void {
@ -234,6 +235,25 @@ export default function GenericNode({
}
};
function handleUpdateCodeWShortcut() {
if (isOutdated && selected) {
handleUpdateCode();
}
}
function handlePlayWShortcut() {
if (buildStatus === BuildStatus.BUILDING || isBuilding || !selected) return;
setValidationStatus(null);
console.log(data.node?.display_name);
buildFlow({ stopNodeId: data.id });
}
const update = useShortcutsStore((state) => state.update);
const play = useShortcutsStore((state) => state.play);
useHotkeys(update, handleUpdateCodeWShortcut, { preventDefault });
useHotkeys(play, handlePlayWShortcut, { preventDefault });
const shortcuts = useShortcutsStore((state) => state.shortcuts);
const memoizedNodeToolbarComponent = useMemo(() => {
@ -259,6 +279,7 @@ export default function GenericNode({
openAdvancedModal={false}
onCloseAdvancedModal={() => {}}
selected={selected}
updateNode={handleUpdateCode}
/>
</NodeToolbar>
);
@ -289,7 +310,7 @@ export default function GenericNode({
selected,
showNode,
buildStatus,
validationStatus
validationStatus,
)}
>
{data.node?.beta && showNode && (
@ -346,10 +367,10 @@ export default function GenericNode({
/>
</div>
) : (
<div className="group flex items-start gap-1.5">
<div className="group flex items-center gap-1">
<ShadTooltip content={data.node?.display_name}>
<div
onClick={(event) => {
onDoubleClick={(event) => {
if (nameEditable) {
setInputName(true);
}
@ -363,6 +384,22 @@ export default function GenericNode({
{data.node?.display_name}
</div>
</ShadTooltip>
{isOutdated && (
<ShadTooltip content={TOOLTIP_OUTDATED_NODE}>
<Button
onClick={handleUpdateCode}
variant="none"
size="none"
className={"group p-1"}
loading={loadingUpdate}
>
<IconComponent
name="AlertTriangle"
className="h-5 w-5 fill-status-yellow text-muted"
/>
</Button>
</ShadTooltip>
)}
</div>
)}
</div>
@ -420,7 +457,7 @@ export default function GenericNode({
}
title={getFieldTitle(
data.node?.template!,
templateField
templateField,
)}
info={data.node?.template[templateField].info}
name={templateField}
@ -448,7 +485,7 @@ export default function GenericNode({
proxy={data.node?.template[templateField].proxy}
showNode={showNode}
/>
)
),
)}
{/* <ParameterComponent
index={0}
@ -481,22 +518,7 @@ export default function GenericNode({
</div>
{showNode && (
<>
<div className="flex flex-shrink-0 items-center gap-1">
{isOutdated && (
<ShadTooltip content={TOOLTIP_OUTDATED_NODE}>
<Button
onClick={handleUpdateCode}
variant="secondary"
className={"h-9 px-1.5"}
loading={loadingUpdate}
>
<IconComponent
name="AlertTriangle"
className="h-5 w-5 text-status-yellow"
/>
</Button>
</ShadTooltip>
)}
<div className="flex flex-shrink-0 items-center gap-2">
<ShadTooltip
content={
buildStatus === BuildStatus.BUILDING ? (
@ -526,25 +548,32 @@ export default function GenericNode({
}
side="bottom"
>
<Button
onClick={() => {
if (buildStatus === BuildStatus.BUILDING || isBuilding)
return;
setValidationStatus(null);
buildFlow({ stopNodeId: data.id });
}}
variant="secondary"
className={"group h-9 px-1.5"}
>
<div
data-testid={
`button_run_` + data?.node?.display_name.toLowerCase()
}
>
{renderIconStatus()}
</div>
</Button>
{renderIconStatus()}
</ShadTooltip>
<Button
onClick={() => {
if (buildStatus === BuildStatus.BUILDING || isBuilding)
return;
setValidationStatus(null);
buildFlow({ stopNodeId: data.id });
}}
variant="none"
size="none"
className="group p-1"
>
<div
data-testid={
`button_run_` + data?.node?.display_name.toLowerCase()
}
>
<IconComponent
name="Play"
className={
"h-5 w-5 fill-current stroke-2 text-muted-foreground transition-all group-hover:text-medium-indigo group-hover/node:opacity-100"
}
/>
</div>
</Button>
</div>
</>
)}
@ -561,9 +590,11 @@ export default function GenericNode({
: ""
}
>
{/* increase height!! */}
<div className="generic-node-desc">
{showNode && nameEditable && inputDescription ? (
<Textarea
className="nowheel min-h-40"
autoFocus
onBlur={() => {
setInputDescription(false);
@ -608,22 +639,29 @@ export default function GenericNode({
) : (
<div
className={cn(
"nodoubleclick generic-node-desc-text cursor-text truncate-multiline word-break-break-word",
"nodoubleclick generic-node-desc-text cursor-text word-break-break-word",
(data.node?.description === "" ||
!data.node?.description) &&
nameEditable
? "font-light italic"
: ""
: "",
)}
onClick={(e) => {
onDoubleClick={(e) => {
setInputDescription(true);
takeSnapshot();
}}
>
{(data.node?.description === "" || !data.node?.description) &&
nameEditable
? "Double Click to Edit Description"
: data.node?.description}
nameEditable ? (
"Double Click to Edit Description"
) : (
<Markdown
className="markdown prose flex flex-col text-primary word-break-break-word
dark:prose-invert"
>
{String(data.node?.description)}
</Markdown>
)}
</div>
)}
</div>
@ -675,13 +713,13 @@ export default function GenericNode({
}
title={getFieldTitle(
data.node?.template!,
templateField
templateField,
)}
info={data.node?.template[templateField].info}
name={templateField}
tooltipTitle={
data.node?.template[templateField].input_types?.join(
"\n"
"\n",
) ?? data.node?.template[templateField].type
}
required={data.node!.template[templateField].required}
@ -708,7 +746,7 @@ export default function GenericNode({
<div
className={classNames(
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
"flex-max-width justify-center"
"flex-max-width justify-center",
)}
>
{" "}

View file

@ -4,7 +4,7 @@ import { VertexBuildTypeAPI } from "../../types/api";
export const getSpecificClassFromBuildStatus = (
buildStatus: BuildStatus | undefined,
validationStatus: VertexBuildTypeAPI | null,
isDark: boolean
isDark: boolean,
) => {
let isInvalid = validationStatus && !validationStatus.valid;

View file

@ -6,7 +6,7 @@ const useCheckCodeValidity = (
data: NodeDataType,
templates: { [key: string]: any },
setIsOutdated: (value: boolean) => void,
types
types,
) => {
useEffect(() => {
// This one should run only once

View file

@ -12,8 +12,8 @@ const useIconNodeRender = (
checkNodeIconFragment: (
iconColor: string,
iconName: string,
iconClassName: string
) => JSX.Element
iconClassName: string,
) => JSX.Element,
) => {
const iconNodeRender = useCallback(() => {
const iconElement = data?.node?.icon;

View file

@ -1,46 +1,49 @@
import IconComponent from "../../components/genericIconComponent";
import ForwardedIconComponent from "../../components/genericIconComponent";
import Checkmark from "../../components/ui/checkmark";
import Loading from "../../components/ui/loading";
import Xmark from "../../components/ui/xmark";
import { BuildStatus } from "../../constants/enums";
import { VertexBuildTypeAPI } from "../../types/api";
import { cn } from "../../utils/utils";
const useIconStatus = (
buildStatus: BuildStatus | undefined,
validationStatus: VertexBuildTypeAPI | null
validationStatus: VertexBuildTypeAPI | null,
) => {
const conditionSuccess = validationStatus && validationStatus.valid;
const conditionInactive =
validationStatus &&
!validationStatus.valid &&
buildStatus === BuildStatus.INACTIVE;
const conditionError =
buildStatus === BuildStatus.ERROR ||
(validationStatus && !validationStatus.valid);
const renderIconStatus = () => {
if (buildStatus === BuildStatus.BUILDING) {
return <Loading className="text-medium-indigo" />;
} else {
return (
<>
<IconComponent
name="Play"
className="absolute ml-0.5 h-5 fill-current stroke-2 text-medium-indigo opacity-0 transition-all group-hover:opacity-100"
/>
{validationStatus && validationStatus.valid ? (
{conditionSuccess ? (
<Checkmark
className="absolute ml-0.5 h-5 stroke-2 text-status-green opacity-100 transition-all group-hover:opacity-0"
className="h-6 w-6 stroke-2 text-status-green transition-all"
isVisible={true}
/>
) : validationStatus &&
!validationStatus.valid &&
buildStatus === BuildStatus.INACTIVE ? (
<IconComponent
name="Play"
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-green opacity-30 transition-all group-hover:opacity-0"
) : conditionInactive ? (
<ForwardedIconComponent
name="Ellipsis"
className="h-6 w-6 fill-current stroke-2 text-status-gray opacity-30"
/>
) : buildStatus === BuildStatus.ERROR ||
(validationStatus && !validationStatus.valid) ? (
) : conditionError ? (
<Xmark
isVisible={true}
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-red opacity-100 transition-all group-hover:opacity-0"
className="h-6 w-6 fill-current stroke-2 text-status-red"
/>
) : (
<IconComponent
name="Play"
className="absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground opacity-100 transition-all group-hover:opacity-0"
<ForwardedIconComponent
name="Ellipsis"
className="h-6 w-6 fill-current stroke-2 text-status-gray opacity-70"
/>
)}
</>

View file

@ -7,7 +7,7 @@ const useUpdateNodeCode = (
dataNode: APIClassType, // Define YourNodeType according to your data structure
setNode: (id: string, callback: (oldNode) => any) => void,
setIsOutdated: (value: boolean) => void,
updateNodeInternals: (id: string) => void
updateNodeInternals: (id: string) => void,
) => {
const updateNodeCode = useCallback(
(newNodeClass: APIClassType, code: string, name: string) => {
@ -30,7 +30,7 @@ const useUpdateNodeCode = (
updateNodeInternals(dataId);
},
[dataId, dataNode, setNode, setIsOutdated, updateNodeInternals]
[dataId, dataNode, setNode, setIsOutdated, updateNodeInternals],
);
return updateNodeCode;

View file

@ -2,7 +2,7 @@ import { APITemplateType } from "../../types/api";
export default function getFieldTitle(
template: APITemplateType,
templateField: string
templateField: string,
): string {
return template[templateField].display_name
? template[templateField].display_name!

View file

@ -16,13 +16,13 @@ export default function AlertDropdown({
}: AlertDropdownType): JSX.Element {
const notificationList = useAlertStore((state) => state.notificationList);
const clearNotificationList = useAlertStore(
(state) => state.clearNotificationList
(state) => state.clearNotificationList,
);
const removeFromNotificationList = useAlertStore(
(state) => state.removeFromNotificationList
(state) => state.removeFromNotificationList,
);
const setNotificationCenter = useAlertStore(
(state) => state.setNotificationCenter
(state) => state.setNotificationCenter,
);
const [open, setOpen] = useState(false);

View file

@ -31,14 +31,14 @@ export default function ImageViewer({ image }) {
const fullPageButton = document.getElementById("full-page-button");
zoomInButton!.addEventListener("click", () =>
viewer.viewport.zoomBy(1.2)
viewer.viewport.zoomBy(1.2),
);
zoomOutButton!.addEventListener("click", () =>
viewer.viewport.zoomBy(0.8)
viewer.viewport.zoomBy(0.8),
);
homeButton!.addEventListener("click", () => viewer.viewport.goHome());
fullPageButton!.addEventListener("click", () =>
viewer.setFullScreen(true)
viewer.setFullScreen(true),
);
// Optionally, you can set additional viewer options here
@ -47,16 +47,16 @@ export default function ImageViewer({ image }) {
return () => {
viewer.destroy();
zoomInButton!.removeEventListener("click", () =>
viewer.viewport.zoomBy(1.2)
viewer.viewport.zoomBy(1.2),
);
zoomOutButton!.removeEventListener("click", () =>
viewer.viewport.zoomBy(0.8)
viewer.viewport.zoomBy(0.8),
);
homeButton!.removeEventListener("click", () =>
viewer.viewport.goHome()
viewer.viewport.goHome(),
);
fullPageButton!.removeEventListener("click", () =>
viewer.setFullScreen(true)
viewer.setFullScreen(true),
);
};
}

View file

@ -15,7 +15,7 @@ export default function FolderAccordionComponent({
options,
}: AccordionComponentType): JSX.Element {
const [value, setValue] = useState(
open.length === 0 ? "" : getOpenAccordion()
open.length === 0 ? "" : getOpenAccordion(),
);
function getOpenAccordion(): string {

View file

@ -17,7 +17,7 @@ export default function AccordionComponent({
sideBar,
}: AccordionComponentType): JSX.Element {
const [value, setValue] = useState(
open.length === 0 ? "" : getOpenAccordion()
open.length === 0 ? "" : getOpenAccordion(),
);
function getOpenAccordion(): string {
@ -52,7 +52,7 @@ export default function AccordionComponent({
disabled={disabled}
className={cn(
sideBar ? "w-full bg-muted px-[0.75rem] py-[0.5rem]" : "ml-3",
disabled ? "cursor-not-allowed" : "cursor-pointer"
disabled ? "cursor-not-allowed" : "cursor-pointer",
)}
>
{trigger}

View file

@ -14,7 +14,13 @@ import sortByName from "./utils/sort-by-name";
//TODO IMPLEMENT FORM LOGIC
export default function AddNewVariableButton({ children }): JSX.Element {
export default function AddNewVariableButton({
children,
asChild,
}: {
children: JSX.Element;
asChild?: boolean;
}): JSX.Element {
const [key, setKey] = useState("");
const [value, setValue] = useState("");
const [type, setType] = useState("Generic");
@ -23,19 +29,19 @@ export default function AddNewVariableButton({ children }): JSX.Element {
const setErrorData = useAlertStore((state) => state.setErrorData);
const componentFields = useTypesStore((state) => state.ComponentFields);
const unavaliableFields = new Set(
Object.keys(useGlobalVariablesStore((state) => state.unavaliableFields))
Object.keys(useGlobalVariablesStore((state) => state.unavaliableFields)),
);
const availableFields = () => {
const fields = Array.from(componentFields).filter(
(field) => !unavaliableFields.has(field)
(field) => !unavaliableFields.has(field),
);
return sortByName(fields);
};
const addGlobalVariable = useGlobalVariablesStore(
(state) => state.addGlobalVariable
(state) => state.addGlobalVariable,
);
function handleSaveVariable() {
@ -87,7 +93,7 @@ export default function AddNewVariableButton({ children }): JSX.Element {
aria-hidden="true"
/>
</BaseModal.Header>
<BaseModal.Trigger>{children}</BaseModal.Trigger>
<BaseModal.Trigger asChild={asChild}>{children}</BaseModal.Trigger>
<BaseModal.Content>
<div className="flex h-full w-full flex-col gap-4 align-middle">
<Label>Variable Name</Label>

View file

@ -10,7 +10,7 @@ export default function DragCardComponent({ data }: { data: storeComponent }) {
draggable
//TODO check color schema
className={cn(
"group relative flex flex-col justify-between overflow-hidden transition-all hover:bg-muted/50 hover:shadow-md hover:dark:bg-[#ffffff10]"
"group relative flex flex-col justify-between overflow-hidden transition-all hover:bg-muted/50 hover:shadow-md hover:dark:bg-[#ffffff10]",
)}
>
<div>
@ -22,7 +22,7 @@ export default function DragCardComponent({ data }: { data: storeComponent }) {
"visible flex-shrink-0",
data.is_component
? "mx-0.5 h-6 w-6 text-component-icon"
: "h-7 w-7 flex-shrink-0 text-flow-icon"
: "h-7 w-7 flex-shrink-0 text-flow-icon",
)}
name={data.is_component ? "ToyBrick" : "Group"}
/>

View file

@ -60,11 +60,11 @@ export default function CollectionCardComponent({
const [loading, setLoading] = useState(false);
const [loadingLike, setLoadingLike] = useState(false);
const [liked_by_user, setLiked_by_user] = useState(
data?.liked_by_user ?? false
data?.liked_by_user ?? false,
);
const [likes_count, setLikes_count] = useState(data?.liked_by_count ?? 0);
const [downloads_count, setDownloads_count] = useState(
data?.downloads_count ?? 0
data?.downloads_count ?? 0,
);
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
@ -75,12 +75,12 @@ export default function CollectionCardComponent({
const [openPlayground, setOpenPlayground] = useState(false);
const [openDelete, setOpenDelete] = useState(false);
const setCurrentFlowId = useFlowsManagerStore(
(state) => state.setCurrentFlowId
(state) => state.setCurrentFlowId,
);
const [loadingPlayground, setLoadingPlayground] = useState(false);
const selectedFlowsComponentsCards = useFlowsManagerStore(
(state) => state.selectedFlowsComponentsCards
(state) => state.selectedFlowsComponentsCards,
);
const name = data.is_component ? "Component" : "Flow";
@ -220,7 +220,7 @@ export default function CollectionCardComponent({
"group relative flex h-[11rem] flex-col justify-between overflow-hidden hover:bg-muted/50 hover:shadow-md hover:dark:bg-[#5f5f5f0e]",
disabled ? "pointer-events-none opacity-50" : "",
onClick ? "cursor-pointer" : "",
isSelectedCard ? "border border-selected" : ""
isSelectedCard ? "border border-selected" : "",
)}
onClick={onClick}
>
@ -233,7 +233,7 @@ export default function CollectionCardComponent({
"visible flex-shrink-0",
data.is_component
? "mx-0.5 h-6 w-6 text-component-icon"
: "h-7 w-7 flex-shrink-0 text-flow-icon"
: "h-7 w-7 flex-shrink-0 text-flow-icon",
)}
name={data.is_component ? "ToyBrick" : "Group"}
/>
@ -428,7 +428,7 @@ export default function CollectionCardComponent({
name="Trash2"
className={cn(
"h-5 w-5",
!authorized ? " text-ring" : ""
!authorized ? " text-ring" : "",
)}
/>
</Button>
@ -463,7 +463,7 @@ export default function CollectionCardComponent({
liked_by_user
? "fill-destructive stroke-destructive"
: "",
!authorized ? " text-ring" : ""
!authorized ? " text-ring" : "",
)}
/>
</Button>
@ -501,7 +501,7 @@ export default function CollectionCardComponent({
}
className={cn(
loading ? "h-5 w-5 animate-spin" : "h-5 w-5",
!authorized ? " text-ring" : ""
!authorized ? " text-ring" : "",
)}
/>
</Button>

View file

@ -65,7 +65,7 @@ export default function CardsWrapComponent({
"h-full w-full",
isDragging
? "mb-36 flex flex-col items-center justify-center gap-4 text-2xl font-light"
: ""
: "",
)}
>
{isDragging ? (

View file

@ -2,7 +2,7 @@ import { Transition } from "@headlessui/react";
import { useMemo, useRef, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import IOModal from "../../modals/IOModal";
import ApiModal from "../../modals/apiModal/views";
import ApiModal from "../../modals/apiModal";
import ShareModal from "../../modals/shareModal";
import useFlowStore from "../../stores/flowStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
@ -65,7 +65,7 @@ export default function FlowToolbar(): JSX.Element {
"relative inline-flex h-full w-full items-center justify-center gap-[4px] bg-muted px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-background hover:bg-hover ",
!hasApiKey || !validApiKey || !hasStore
? " button-disable text-muted-foreground "
: ""
: "",
)}
>
<ForwardedIconComponent
@ -74,7 +74,7 @@ export default function FlowToolbar(): JSX.Element {
"-m-0.5 -ml-1 h-6 w-6",
!hasApiKey || !validApiKey || !hasStore
? "extra-side-bar-save-disable"
: ""
: "",
)}
/>
Share
@ -88,7 +88,7 @@ export default function FlowToolbar(): JSX.Element {
hasStore,
openShareModal,
setOpenShareModal,
]
],
);
return (
@ -144,7 +144,7 @@ export default function FlowToolbar(): JSX.Element {
>
<div
className={classNames(
"relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-hover"
"relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-hover",
)}
>
<ForwardedIconComponent

View file

@ -18,7 +18,7 @@ export default function CodeAreaComponent({
setOpen,
}: CodeAreaComponentType) {
const [myValue, setMyValue] = useState(
typeof value == "string" ? value : JSON.stringify(value)
typeof value == "string" ? value : JSON.stringify(value),
);
useEffect(() => {
if (disabled && myValue !== "") {

View file

@ -67,7 +67,7 @@ function CsvOutputComponent({
if (file) {
const { rowData: data, colDefs: columns } = convertCSVToData(
file,
separator
separator,
);
setRowData(data);
setColDefs(columns);

View file

@ -1,4 +1,4 @@
import { useEffect, useRef } from "react";
import { useEffect } from "react";
import { DictComponentType } from "../../types/components";
import DictAreaModal from "../../modals/dictAreaModal";
@ -13,7 +13,6 @@ export default function DictComponent({
id = "",
}: DictComponentType): JSX.Element {
// Create a reference to the value
const ref = useRef(value);
useEffect(() => {
if (disabled) {
@ -21,21 +20,17 @@ export default function DictComponent({
}
}, [disabled]);
useEffect(() => {
// Update the reference value
ref.current = value;
}, [value]);
return (
<div
className={classNames(
value.length > 1 && editNode ? "my-1" : "",
"flex flex-col gap-3"
"flex w-full flex-col gap-3",
)}
>
{
<div className="flex w-full gap-3" data-testid={id}>
<DictAreaModal
value={ref.current}
value={value}
onChange={(obj) => {
onChange(obj);
}}

View file

@ -58,7 +58,7 @@ export default function Dropdown({
? "dropdown-component-outline"
: "dropdown-component-false-outline",
"w-full justify-between font-normal",
editNode ? "input-edit-node" : "py-2"
editNode ? "input-edit-node" : "py-2",
)}
>
<span data-testid={`value-dropdown-` + id}>
@ -106,7 +106,7 @@ export default function Dropdown({
name="Check"
className={cn(
"ml-auto h-4 w-4 text-primary",
value === option ? "opacity-100" : "opacity-0"
value === option ? "opacity-100" : "opacity-0",
)}
/>
</CommandItem>

View file

@ -99,7 +99,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
<span
className={cn(
"font-normal text-muted-foreground word-break-break-word",
description === "" ? "font-light italic" : ""
description === "" ? "font-light italic" : "",
)}
>
{description === "" ? "No description" : description}

View file

@ -26,10 +26,8 @@ export default function FetchErrorComponent({
name="Unplug"
></IconComponent>
<br></br>
<span className="text-lg text-almost-medium-blue">{message}</span>
<span className="text-lg text-almost-medium-blue">
{description}
</span>
<span className="text-lg text-primary">{message}</span>
<span className="text-lg text-primary">{description}</span>
</div>
</BaseModal.Content>

View file

@ -18,7 +18,7 @@ export const ForwardedIconComponent = memo(
strokeWidth,
id = "",
}: IconComponentProps,
ref
ref,
) => {
const [showFallback, setShowFallback] = useState(false);
@ -65,8 +65,8 @@ export const ForwardedIconComponent = memo(
/>
</Suspense>
);
}
)
},
),
);
export default ForwardedIconComponent;

View file

@ -123,7 +123,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
title: UPLOAD_ERROR_ALERT,
list: [error],
});
}
},
);
}}
>
@ -221,7 +221,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
name={isBuilding || saveLoading ? "Loader2" : "CheckCircle2"}
className={cn(
"h-4 w-4",
isBuilding || saveLoading ? "animate-spin" : "animate-wiggle"
isBuilding || saveLoading ? "animate-spin" : "animate-wiggle",
)}
/>
{printByBuildStatus()}

View file

@ -51,7 +51,7 @@ export default function Header(): JSX.Element {
const profileImageUrl =
`${BACKEND_URL.slice(
0,
BACKEND_URL.length - 1
BACKEND_URL.length - 1,
)}${BASE_URL_API}files/profile_pictures/${userData?.profile_image}` ??
profileCircle;
async function checkForChanges(): Promise<void> {
@ -64,7 +64,7 @@ export default function Header(): JSX.Element {
const lastFlowVisitedIndex = routeHistory
.reverse()
.findIndex(
(path) => path.includes("/flow/") && path !== location.pathname
(path) => path.includes("/flow/") && path !== location.pathname,
);
const lastFlowVisited = routeHistory[lastFlowVisitedIndex];
@ -74,8 +74,8 @@ export default function Header(): JSX.Element {
};
const visitedFlowPathBefore = () => {
const lastThreeVisitedPaths = routeHistory.slice(-3);
return lastThreeVisitedPaths.some((path) => path.includes("/flow/"));
const last100VisitedPaths = routeHistory.slice(-99);
return last100VisitedPaths.some((path) => path.includes("/flow/"));
};
const showArrowReturnIcon =
@ -84,7 +84,7 @@ export default function Header(): JSX.Element {
return (
<div className="header-arrangement">
<div className="header-start-display lg:w-[30%]">
<div className="header-start-display lg:w-[407px]">
<Link to="/all" className="cursor-pointer" onClick={checkForChanges}>
<span className="ml-4 text-2xl">⛓️</span>
</Link>
@ -137,7 +137,7 @@ export default function Header(): JSX.Element {
</Link>
)}
</div>
<div className="header-end-division lg:w-[30%]">
<div className="header-end-division lg:w-[407px]">
<div className="header-end-display">
<a
href="https://github.com/langflow-ai/langflow"
@ -200,17 +200,18 @@ export default function Header(): JSX.Element {
variant="none"
size="none"
data-testid="user-profile-settings"
className="shrink-0"
>
<img
src={
`${BACKEND_URL.slice(
0,
BACKEND_URL.length - 1
BACKEND_URL.length - 1,
)}${BASE_URL_API}files/profile_pictures/${
userData?.profile_image ?? "Space/046-rocket.png"
}` ?? profileCircle
}
className="h-7 w-7 focus-visible:outline-0 "
className="h-7 w-7 shrink-0 focus-visible:outline-0"
/>
</Button>
</DropdownMenuTrigger>
@ -220,7 +221,14 @@ export default function Header(): JSX.Element {
<DropdownMenuLabel>
<div className="flex items-center gap-3">
<img
src={profileImageUrl}
src={
`${BACKEND_URL.slice(
0,
BACKEND_URL.length - 1,
)}${BASE_URL_API}files/profile_pictures/${
userData?.profile_image
}` ?? profileCircle
}
className="h-5 w-5 focus-visible:outline-0 "
/>

View file

@ -32,11 +32,11 @@ export default function HorizontalScrollFadeComponent({
fadeContainerRef.current.classList.toggle(
"fade-left",
isScrollable && !atStart
isScrollable && !atStart,
);
fadeContainerRef.current.classList.toggle(
"fade-right",
isScrollable && !atEnd
isScrollable && !atEnd,
);
};

View file

@ -75,9 +75,9 @@ const CustomInputPopover = ({
(selectedOption !== "" || !onChange) && setSelectedOption
? selectedOption
: (selectedOptions?.length !== 0 || !onChange) &&
setSelectedOptions
? selectedOptions?.join(", ")
: value
setSelectedOptions
? selectedOptions?.join(", ")
: value
}
autoFocus={autoFocus}
disabled={disabled}
@ -103,7 +103,7 @@ const CustomInputPopover = ({
(password && !(setSelectedOption || setSelectedOptions))
? "pr-8"
: "",
className!
className!,
)}
placeholder={password && editNode ? "Key" : placeholder}
onChange={handleInputChange}
@ -141,15 +141,15 @@ const CustomInputPopover = ({
onSelect={(currentValue) => {
setSelectedOption &&
setSelectedOption(
currentValue === selectedOption ? "" : currentValue
currentValue === selectedOption ? "" : currentValue,
);
setSelectedOptions &&
setSelectedOptions(
selectedOptions?.includes(currentValue)
? selectedOptions.filter(
(item) => item !== currentValue
(item) => item !== currentValue,
)
: [...selectedOptions, currentValue]
: [...selectedOptions, currentValue],
);
!setSelectedOptions && setShowOptions(false);
}}
@ -162,7 +162,7 @@ const CustomInputPopover = ({
selectedOption === option ||
selectedOptions?.includes(option)
? "opacity-100"
: "opacity-0"
: "opacity-0",
)}
>
<div className="absolute opacity-100 transition-all group-hover:opacity-0">

View file

@ -60,14 +60,14 @@ const CustomInputPopoverObject = ({
? options.find((option) => option.id === selectedOption)?.name ||
""
: (selectedOptions?.length !== 0 || !onChange) &&
setSelectedOptions
? selectedOptions
.map(
(optionId) =>
options.find((option) => option.id === optionId)?.name
)
.join(", ")
: value
setSelectedOptions
? selectedOptions
.map(
(optionId) =>
options.find((option) => option.id === optionId)?.name,
)
.join(", ")
: value
}
autoFocus={autoFocus}
disabled={disabled}
@ -115,15 +115,15 @@ const CustomInputPopoverObject = ({
onSelect={(currentValue) => {
setSelectedOption &&
setSelectedOption(
currentValue === selectedOption ? "" : currentValue
currentValue === selectedOption ? "" : currentValue,
);
setSelectedOptions &&
setSelectedOptions(
selectedOptions?.includes(currentValue)
? selectedOptions.filter(
(item) => item !== currentValue
(item) => item !== currentValue,
)
: [...selectedOptions, currentValue]
: [...selectedOptions, currentValue],
);
!setSelectedOptions && setShowOptions(false);
}}
@ -136,7 +136,7 @@ const CustomInputPopoverObject = ({
selectedOption === option.id ||
selectedOptions?.includes(option.id)
? "opacity-100"
: "opacity-0"
: "opacity-0",
)}
>
<div className="absolute opacity-100 transition-all group-hover:opacity-0">

View file

@ -24,6 +24,7 @@ export default function InputComponent({
blurOnEnter = false,
optionsIcon = "ChevronsUpDown",
selectedOption,
setSelectedOption,
selectedOptions = [],
setSelectedOptions,
@ -71,7 +72,7 @@ export default function InputComponent({
editNode ? " input-edit-node " : "",
password && editNode ? "pr-8" : "",
password && !editNode ? "pr-10" : "",
className!
className!,
)}
placeholder={password && editNode ? "Key" : placeholder}
onChange={(e) => {
@ -154,7 +155,7 @@ export default function InputComponent({
<span
className={cn(
password && selectedOption === "" ? "right-8" : "right-0",
"absolute inset-y-0 flex items-center pr-2.5"
"absolute inset-y-0 flex items-center pr-2.5",
)}
>
<button
@ -164,10 +165,10 @@ export default function InputComponent({
e.stopPropagation();
}}
className={cn(
selectedOption !== ""
onChange && setSelectedOption && selectedOption !== ""
? "text-medium-indigo"
: "text-muted-foreground",
"hover:text-accent-foreground"
"hover:text-accent-foreground",
)}
>
<ForwardedIconComponent
@ -187,7 +188,7 @@ export default function InputComponent({
"mb-px",
editNode
? "input-component-true-button"
: "input-component-false-button"
: "input-component-false-button",
)}
onClick={(event) => {
event.preventDefault();
@ -204,7 +205,7 @@ export default function InputComponent({
className={classNames(
editNode
? "input-component-true-svg"
: "input-component-false-svg"
: "input-component-false-svg",
)}
>
<path
@ -223,7 +224,7 @@ export default function InputComponent({
className={classNames(
editNode
? "input-component-true-svg"
: "input-component-false-svg"
: "input-component-false-svg",
)}
>
<path

View file

@ -48,12 +48,12 @@ export default function InputFileComponent({
const handleButtonClick = (): void => {
// Create a file input element
const input = document.createElement("input");
document.body.appendChild(input);
input.type = "file";
input.accept = fileTypes?.join(",");
input.style.display = "none"; // Hidden from view
input.multiple = false; // Allow only one file selection
input.onchange = (event: Event): void => {
const onChangeFile = (event: Event): void => {
setLoading(true);
// Get the selected file
@ -90,6 +90,8 @@ export default function InputFileComponent({
}
};
input.addEventListener("change", onChangeFile);
// Trigger the file selection dialog
input.click();
};
@ -103,8 +105,8 @@ export default function InputFileComponent({
editNode
? "input-edit-node input-dialog text-muted-foreground"
: disabled
? "input-disable input-dialog primary-input"
: "input-dialog primary-input text-muted-foreground"
? "input-disable input-dialog primary-input"
: "input-dialog primary-input text-muted-foreground"
}
>
{myValue !== "" ? myValue : "No file"}

View file

@ -19,15 +19,15 @@ export default function InputGlobalComponent({
editNode = false,
}: InputGlobalComponentType): JSX.Element {
const globalVariablesEntries = useGlobalVariablesStore(
(state) => state.globalVariablesEntries
(state) => state.globalVariablesEntries,
);
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
const unavaliableFields = useGlobalVariablesStore(
(state) => state.unavaliableFields
(state) => state.unavaliableFields,
);
const removeGlobalVariable = useGlobalVariablesStore(
(state) => state.removeGlobalVariable
(state) => state.removeGlobalVariable,
);
const setErrorData = useAlertStore((state) => state.setErrorData);
@ -121,7 +121,7 @@ export default function InputGlobalComponent({
<ForwardedIconComponent
name="Trash2"
className={cn(
"h-4 w-4 text-primary opacity-0 hover:text-status-red group-hover:opacity-100"
"h-4 w-4 text-primary opacity-0 hover:text-status-red group-hover:opacity-100",
)}
aria-hidden="true"
/>

View file

@ -31,7 +31,7 @@ export default function InputListComponent({
<div
className={classNames(
value.length > 1 && editNode ? "my-1" : "",
"flex flex-col gap-3"
"flex flex-col gap-3",
)}
>
{value.map((singleValue, idx) => {

View file

@ -7,7 +7,7 @@ export default function LoadingComponent({
<div role="status" className="flex flex-col items-center justify-center">
<svg
aria-hidden="true"
className={`w-${remSize} h-${remSize} animate-spin fill-almost-medium-blue text-muted`}
className={`w-${remSize} h-${remSize} animate-spin fill-primary text-muted`}
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
@ -22,9 +22,7 @@ export default function LoadingComponent({
/>
</svg>
<br></br>
<span className="animate-pulse text-lg text-almost-medium-blue">
Loading...
</span>
<span className="animate-pulse text-lg text-primary">Loading...</span>
</div>
);
}

View file

@ -5,7 +5,7 @@ export default function ObjectRender({ object }: { object: any }): JSX.Element {
return (
<DictAreaModal value={object}>
<div className="flex h-full w-full items-center align-middle transition-all hover:scale-105">
<div className="flex h-full w-full items-center align-middle transition-all">
<div className="truncate">{JSON.stringify(object)}</div>
</div>
</DictAreaModal>

View file

@ -12,35 +12,35 @@ export default function RenderIcons({
shortcutWPlus: string[];
}): JSX.Element {
return hasShift ? (
<>
<span className="flex gap-0.5 text-xs">
{isMac ? (
<ForwardedIconComponent name="Command" className="h-4 w-4" />
<ForwardedIconComponent name="Command" className="h-3 w-3" />
) : (
filteredShortcut[0]
)}
<ForwardedIconComponent name="ArrowBigUp" className="ml-1 h-5 w-5" />
<ForwardedIconComponent name="ArrowBigUp" className=" h-4 w-4" />
{filteredShortcut.map((key, idx) => {
if (idx > 0) {
return <span className="ml-1"> {key.toUpperCase()} </span>;
return <span className=""> {key.toUpperCase()} </span>;
}
})}
</>
</span>
) : (
<>
<span className="flex gap-1 text-xs">
{shortcutWPlus[0].toLowerCase() === "space" ? (
"Space"
) : shortcutWPlus[0].length <= 1 ? (
shortcutWPlus[0]
) : isMac ? (
<ForwardedIconComponent name="Command" className="h-4 w-4" />
<ForwardedIconComponent name="Command" className="h-3 w-3" />
) : (
shortcutWPlus[0]
)}
{shortcutWPlus.map((key, idx) => {
if (idx > 0) {
return <span className="ml-0.5"> {key.toUpperCase()} </span>;
return <span className=""> {key.toUpperCase()} </span>;
}
})}
</>
</span>
);
}

View file

@ -27,7 +27,7 @@ const SideBarButtonsComponent = ({
pathname === item.href
? "border border-border bg-muted hover:bg-muted"
: "border border-transparent hover:border-border hover:bg-transparent",
"flex w-full shrink-0 justify-start gap-4"
"flex w-full shrink-0 justify-start gap-4",
)}
>
{item.icon}

View file

@ -31,7 +31,7 @@ const SideBarFoldersButtonsComponent = ({
const [foldersNames, setFoldersNames] = useState({});
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
const [editFolders, setEditFolderName] = useState(
folders.map((obj) => ({ name: obj.name, edit: false }))
folders.map((obj) => ({ name: obj.name, edit: false })),
);
const uploadFolder = useFolderStore((state) => state.uploadFolder);
const currentFolder = pathname.split("/");
@ -58,7 +58,7 @@ const SideBarFoldersButtonsComponent = ({
const { dragOver, dragEnter, dragLeave, onDrop } = useFileDrop(
folderId,
handleFolderChange
handleFolderChange,
);
const handleUploadFlowsToFolder = () => {
@ -86,7 +86,7 @@ const SideBarFoldersButtonsComponent = ({
addFolder({ name: "New Folder", parent_id: null, description: "" }).then(
(res) => {
getFoldersApi(true);
}
},
);
}
@ -132,7 +132,7 @@ const SideBarFoldersButtonsComponent = ({
<>
{folders.map((item, index) => {
const editFolderName = editFolders?.filter(
(folder) => folder.name === item.name
(folder) => folder.name === item.name,
)[0];
return (
<div
@ -148,7 +148,7 @@ const SideBarFoldersButtonsComponent = ({
? "border border-border bg-muted hover:bg-muted"
: "border hover:bg-transparent lg:border-transparent lg:hover:border-border",
"group flex w-full shrink-0 cursor-pointer gap-2 opacity-100 lg:min-w-full",
folderIdDragging === item.id! ? "bg-border" : ""
folderIdDragging === item.id! ? "bg-border" : "",
)}
onClick={() => handleChangeFolder!(item.id!)}
>
@ -218,7 +218,7 @@ const SideBarFoldersButtonsComponent = ({
folders.map((obj) => ({
name: obj.name,
edit: false,
}))
})),
);
}
if (e.key === "Enter") {
@ -251,10 +251,10 @@ const SideBarFoldersButtonsComponent = ({
};
const updatedFolder = await updateFolder(
body,
item.id!
item.id!,
);
const updateFolders = folders.filter(
(f) => f.name !== item.name
(f) => f.name !== item.name,
);
setFolders([...updateFolders, updatedFolder]);
setFoldersNames({});
@ -262,7 +262,7 @@ const SideBarFoldersButtonsComponent = ({
folders.map((obj) => ({
name: obj.name,
edit: false,
}))
})),
);
} else {
setFoldersNames((old) => ({

View file

@ -12,7 +12,7 @@ import { addVersionToDuplicates } from "../../../utils/reactflowUtils";
const useFileDrop = (folderId, folderChangeCallback) => {
const setFolderDragging = useFolderStore((state) => state.setFolderDragging);
const setFolderIdDragging = useFolderStore(
(state) => state.setFolderIdDragging
(state) => state.setFolderIdDragging,
);
const setErrorData = useAlertStore((state) => state.setErrorData);
@ -45,7 +45,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
| React.DragEvent<HTMLDivElement>
| React.DragEvent<HTMLButtonElement>
| React.DragEvent<HTMLAnchorElement>,
folderId: string
folderId: string,
) => {
e.preventDefault();
@ -60,7 +60,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
| React.DragEvent<HTMLDivElement>
| React.DragEvent<HTMLButtonElement>
| React.DragEvent<HTMLAnchorElement>,
folderId: string
folderId: string,
) => {
if (e.dataTransfer.types.some((types) => types === "Files")) {
setFolderDragging(true);
@ -73,7 +73,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
e:
| React.DragEvent<HTMLDivElement>
| React.DragEvent<HTMLButtonElement>
| React.DragEvent<HTMLAnchorElement>
| React.DragEvent<HTMLAnchorElement>,
) => {
e.preventDefault();
if (e.target === e.currentTarget) {
@ -87,7 +87,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
| React.DragEvent<HTMLDivElement>
| React.DragEvent<HTMLButtonElement>
| React.DragEvent<HTMLAnchorElement>,
folderId: string
folderId: string,
) => {
if (e?.dataTransfer?.getData("flow")) {
const data = JSON.parse(e?.dataTransfer?.getData("flow"));

View file

@ -33,7 +33,7 @@ export default function TableOptions({
name="RotateCcw"
strokeWidth={2}
className={cn(
"h-5 w-5 text-primary transition-all hover:text-accent-foreground"
"h-5 w-5 text-primary transition-all hover:text-accent-foreground",
)}
/>
</Button>
@ -85,7 +85,7 @@ export default function TableOptions({
name="Trash2"
className={cn(
"h-5 w-5 text-primary transition-all",
!hasSelection ? "" : "hover:text-status-red "
!hasSelection ? "" : "hover:text-status-red ",
)}
/>
</Button>

View file

@ -0,0 +1,9 @@
import Loading from "../../../ui/loading";
export default function LoadingOverlay() {
return (
<div className=" flex h-full w-full items-center justify-center bg-background align-middle">
<Loading />
</div>
);
}

View file

@ -30,7 +30,7 @@ export default function TableAutoCellRender({
variant="outline"
size="sq"
className={cn(
"min-w-min bg-success-background text-success-foreground hover:bg-success-background"
"min-w-min bg-success-background text-success-foreground hover:bg-success-background",
)}
>
{value}

View file

@ -28,7 +28,7 @@ export default function TableNodeCellRender({
value,
nodeClass,
handleOnNewValue: handleOnNewValueNode,
handleOnChangeDb,
handleOnChangeDb: handleOnChangeDbNode,
},
}: CustomCellRendererProps) {
const handleOnNewValue = (newValue: any, name: string) => {
@ -41,6 +41,15 @@ export default function TableNodeCellRender({
setTemplateValue(newValue);
};
const handleOnChangeDb = (newValue: boolean, name: string) => {
handleOnChangeDbNode(newValue, name);
setTemplateData((old) => {
let newData = cloneDeep(old);
newData.load_from_db = newValue;
return newData;
});
};
const [templateValue, setTemplateValue] = useState(value);
const [templateData, setTemplateData] = useState(data);
@ -63,8 +72,8 @@ export default function TableNodeCellRender({
...id,
proxy: templateData.proxy,
}
: id
)
: id,
),
) ?? false;
function getCellType() {
switch (templateData.type) {
@ -135,7 +144,7 @@ export default function TableNodeCellRender({
<div
className={classNames(
"max-h-48 w-full overflow-auto custom-scroll",
templateValue?.length > 1 ? "my-3" : ""
templateValue?.length > 1 ? "my-3" : "",
)}
>
<KeypairListComponent
@ -259,7 +268,7 @@ export default function TableNodeCellRender({
}
return (
<div className="group flex h-full w-[300px] items-center justify-center py-2.5">
<div className="group mx-auto flex h-full w-[300px] items-center justify-center py-2.5">
{getCellType()}
</div>
);

View file

@ -14,6 +14,7 @@ import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
import ResetColumns from "./components/ResetColumns";
import TableOptions from "./components/TableOptions";
import resetGrid from "./utils/reset-grid-columns";
import Loading from "../ui/loading";
interface TableComponentProps extends AgGridReactProps {
columnDefs: NonNullable<AgGridReactProps["columnDefs"]>;
@ -21,6 +22,7 @@ interface TableComponentProps extends AgGridReactProps {
alertTitle?: string;
alertDescription?: string;
editable?: boolean | string[];
pagination?: boolean;
onDelete?: () => void;
onDuplicate?: () => void;
}
@ -35,7 +37,7 @@ const TableComponent = forwardRef<
alertDescription = DEFAULT_TABLE_ALERT_MSG,
...props
},
ref
ref,
) => {
let colDef = props.columnDefs.map((col, index) => {
let newCol = {
@ -76,7 +78,7 @@ const TableComponent = forwardRef<
const dark = useDarkStore((state) => state.dark);
const initialColumnDefs = useRef(colDef);
const [columnStateChange, setColumnStateChange] = useState(false);
const storeReference = props.columnDefs.map((e) => e.headerName).join("_");
const makeLastColumnNonResizable = (columnDefs) => {
columnDefs.forEach((colDef, index) => {
colDef.resizable = index !== columnDefs.length - 1;
@ -89,21 +91,33 @@ const TableComponent = forwardRef<
realRef.current = params;
const updatedColumnDefs = makeLastColumnNonResizable([...colDef]);
params.api.setGridOption("columnDefs", updatedColumnDefs);
const customInit = localStorage.getItem(storeReference);
initialColumnDefs.current = params.api.getColumnDefs();
if (props.onGridReady) props.onGridReady(params);
if (customInit && realRef.current) {
realRef.current.api.applyColumnState({
state: JSON.parse(customInit),
applyOrder: true,
});
}
setTimeout(() => {
setColumnStateChange(false);
if (customInit && realRef.current) {
setColumnStateChange(true);
} else {
setColumnStateChange(false);
}
}, 50);
setTimeout(() => {
realRef.current.api.hideOverlay();
}, 1000);
if (props.onGridReady) props.onGridReady(params);
};
const onColumnMoved = (params) => {
const updatedColumnDefs = makeLastColumnNonResizable(
params.columnApi.getAllGridColumns().map((col) => col.getColDef())
params.columnApi.getAllGridColumns().map((col) => col.getColDef()),
);
params.api.setGridOption("columnDefs", updatedColumnDefs);
if (props.onColumnMoved) props.onColumnMoved(params);
};
if (props.rowData.length === 0) {
return (
<div className="flex h-full w-full items-center justify-center rounded-md border">
@ -123,59 +137,47 @@ const TableComponent = forwardRef<
className={cn(
dark ? "ag-theme-quartz-dark" : "ag-theme-quartz",
"ag-theme-shadcn flex h-full flex-col",
"relative"
"relative",
)} // applying the grid theme
>
<AgGridReact
{...props}
animateRows={false}
className={cn(props.className, "cusm-scroll")}
defaultColDef={{
minWidth: 100,
autoHeight: true,
}}
columnDefs={colDef}
ref={realRef}
pagination={true}
onGridReady={onGridReady}
onColumnMoved={onColumnMoved}
onStateUpdated={(e) => {
console.log(e);
if (e.sources.some((source) => source.includes("column"))) {
localStorage.setItem(
storeReference,
JSON.stringify(realRef.current?.api?.getColumnState()),
);
setColumnStateChange(true);
}
}}
/>
<TableOptions
stateChange={columnStateChange}
hasSelection={realRef.current?.api?.getSelectedRows().length > 0}
duplicateRow={props.onDuplicate ? props.onDuplicate : undefined}
deleteRow={props.onDelete ? props.onDelete : undefined}
resetGrid={() => {
console.log("teste");
resetGrid(realRef, initialColumnDefs);
setTimeout(() => {
setColumnStateChange(false);
}, 100);
}}
/>
<TableOptions
stateChange={columnStateChange}
hasSelection={realRef.current?.api?.getSelectedRows().length > 0}
duplicateRow={props.onDuplicate ? props.onDuplicate : undefined}
deleteRow={props.onDelete ? props.onDelete : undefined}
resetGrid={() => {
console.log("teste");
resetGrid(realRef, initialColumnDefs);
setTimeout(() => {
setColumnStateChange(false);
}, 100);
}}
/>
<ResetColumns resetGrid={() => resetGrid(realRef, initialColumnDefs)} />
{props.pagination && (
<TableOptions
stateChange={columnStateChange}
hasSelection={realRef.current?.api?.getSelectedRows().length > 0}
duplicateRow={props.onDuplicate ? props.onDuplicate : undefined}
deleteRow={props.onDelete ? props.onDelete : undefined}
resetGrid={() => {
resetGrid(realRef, initialColumnDefs);
setTimeout(() => {
setColumnStateChange(false);
localStorage.removeItem(storeReference);
}, 100);
}}
/>
)}
</div>
);
}
},
);
export default TableComponent;

View file

@ -48,7 +48,7 @@ export function TagsSelector({
className={cn(
selectedTags.some((category) => category === tag.name)
? "min-w-min bg-beta-foreground text-background hover:bg-beta-foreground"
: ""
: "",
)}
>
{tag.name}

View file

@ -34,7 +34,7 @@ const AccordionTrigger = React.forwardRef<
<div
className={cn(
"flex flex-1 cursor-pointer items-center justify-between py-4 text-sm font-medium transition-all [&[data-state=open]>svg]:rotate-180",
className
className,
)}
>
{children}
@ -46,7 +46,7 @@ const AccordionTrigger = React.forwardRef<
<ChevronDownIcon
className={cn(
"h-4 w-4 font-bold transition-transform duration-200",
disabled ? "text-muted-foreground" : "text-primary"
disabled ? "text-muted-foreground" : "text-primary",
)}
/>
</ShadTooltip>
@ -64,7 +64,7 @@ const AccordionContent = React.forwardRef<
ref={ref}
className={cn(
"data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm",
className
className,
)}
{...props}
>

View file

@ -15,7 +15,7 @@ const alertVariants = cva(
defaultVariants: {
variant: "default",
},
}
},
);
const Alert = React.forwardRef<

View file

@ -14,7 +14,7 @@ const badgeVariants = cva(
"bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground",
destructive:
"bg-destructive hover:bg-destructive/80 border-transparent text-destructive-foreground",
outline: "text-primary/80 hover:bg-muted border-ring/60",
outline: "text-primary/80 border-ring/60",
},
size: {
sm: "h-4 text-xs",
@ -27,7 +27,7 @@ const badgeVariants = cva(
defaultVariants: {
variant: "default",
},
}
},
);
export interface BadgeProps

View file

@ -5,7 +5,7 @@ import { cn } from "../../utils/utils";
import ForwardedIconComponent from "../genericIconComponent";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background",
"nocopy nowheel nopan nodelete nodrag noundo inline-flex items-center gap-2 justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background",
{
variants: {
variant: {
@ -35,7 +35,7 @@ const buttonVariants = cva(
variant: "default",
size: "default",
},
}
},
);
export interface ButtonProps
@ -65,7 +65,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
children,
...props
},
ref
ref,
) => {
const Comp = asChild ? Slot : "button";
let newChildren = children;
@ -97,7 +97,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
</Comp>
</>
);
}
},
);
Button.displayName = "Button";

View file

@ -9,7 +9,7 @@ const Card = React.forwardRef<
ref={ref}
className={cn(
"flex flex-col justify-between rounded-lg border bg-muted text-card-foreground shadow-sm transition-all",
className
className,
)}
{...props}
/>
@ -36,7 +36,7 @@ const CardTitle = React.forwardRef<
ref={ref}
className={cn(
"text-base font-semibold leading-tight tracking-tight",
className
className,
)}
{...props}
/>

View file

@ -13,7 +13,7 @@ const Checkbox = React.forwardRef<
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className
className,
)}
{...props}
>
@ -37,7 +37,7 @@ const CheckBoxDiv = ({
className={cn(
className,
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
checked ? "bg-primary text-primary-foreground" : ""
checked ? "bg-primary text-primary-foreground" : "",
)}
>
{checked && (

View file

@ -7,7 +7,7 @@ export default function Checkmark({ initial = true, isVisible, className }) {
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
strokeWidth={2}
stroke="currentColor"
className={"CheckIcon " + className}
>

View file

@ -24,7 +24,7 @@ const AccordionTrigger = React.forwardRef<
<div
className={cn(
" flex flex-1 cursor-pointer items-center justify-between border-[1px] py-2 text-sm font-medium data-[state=closed]:rounded-md data-[state=open]:rounded-t-md data-[state=open]:border-b-0 data-[state=open]:bg-muted [&[data-state=open]>svg]:rotate-180",
className
className,
)}
>
{children}
@ -43,7 +43,7 @@ const AccordionContent = React.forwardRef<
ref={ref}
className={cn(
"data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden border-[1px] text-sm data-[state=open]:rounded-b-md data-[state=open]:border-t-0 data-[state=open]:bg-muted",
className
className,
)}
{...props}
>

View file

@ -16,18 +16,18 @@ const Form = FormProvider;
type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName;
};
const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue
{} as FormFieldContextValue,
);
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
@ -66,7 +66,7 @@ type FormItemContextValue = {
};
const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue
{} as FormItemContextValue,
);
const FormItem = React.forwardRef<

View file

@ -36,7 +36,7 @@ const SelectContent = React.forwardRef<
"relative z-50 min-w-[14rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
className,
)}
position={position}
{...props}
@ -45,7 +45,7 @@ const SelectContent = React.forwardRef<
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
)}
>
{children}
@ -75,7 +75,7 @@ const SelectItem = React.forwardRef<
ref={ref}
className={cn(
"relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-3 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
className,
)}
{...props}
>

View file

@ -25,7 +25,7 @@ const toggleVariants = cva(
variant: "default",
size: "default",
},
}
},
);
const Toggle = React.forwardRef<

View file

@ -20,7 +20,7 @@ const TooltipContent = React.forwardRef<
sideOffset={sideOffset}
className={cn(
"z-45 overflow-y-auto rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1",
className
className,
)}
{...props}
/>
@ -37,7 +37,7 @@ const TooltipContentWithoutPortal = React.forwardRef<
sideOffset={sideOffset}
className={cn(
"z-45 overflow-y-auto rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1",
className
className,
)}
{...props}
/>

View file

@ -758,7 +758,7 @@ export const defaultShortcuts = [
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + D`,
},
{
name: "Share",
name: "Component Share",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Shift + S`,
},
{
@ -799,7 +799,7 @@ export const defaultShortcuts = [
},
{
name: "API",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + R`,
shortcut: `R`,
},
{
name: "Download",
@ -817,6 +817,14 @@ export const defaultShortcuts = [
name: "Flow Share",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + B`,
},
{
name: "Play",
shortcut: `P`,
},
{
name: "Output Inspection",
shortcut: `O`,
},
];
export const DEFAULT_TABLE_ALERT_MSG = `Oops! It seems there's no data to display right now. Please check back later.`;

View file

@ -48,7 +48,7 @@ function ApiInterceptor() {
}
await clearBuildVerticesState(error);
return Promise.reject(error);
}
},
);
const isAuthorizedURL = (url) => {
@ -65,10 +65,10 @@ function ApiInterceptor() {
const parsedURL = new URL(url);
const isDomainAllowed = authorizedDomains.some(
(domain) => parsedURL.origin === new URL(domain).origin
(domain) => parsedURL.origin === new URL(domain).origin,
);
const isEndpointAllowed = authorizedEndpoints.some((endpoint) =>
parsedURL.pathname.includes(endpoint)
parsedURL.pathname.includes(endpoint),
);
return isDomainAllowed || isEndpointAllowed;
@ -101,7 +101,7 @@ function ApiInterceptor() {
},
(error) => {
return Promise.reject(error);
}
},
);
return () => {
@ -133,7 +133,7 @@ function ApiInterceptor() {
if (error?.config?.headers) {
delete error.config.headers["Authorization"];
error.config.headers["Authorization"] = `Bearer ${cookies.get(
"access_token_lf"
"access_token_lf",
)}`;
const response = await axios.request(error.config);
return response;

View file

@ -8,7 +8,7 @@ export function checkDuplicateRequestAndStoreRequest(config) {
const currentTime = Date.now();
const isContained = AUTHORIZED_DUPLICATE_REQUESTS.some((request) =>
config?.url!.includes(request)
config?.url!.includes(request),
);
if (

View file

@ -38,7 +38,7 @@ import {
* @returns {Promise<AxiosResponse<APIObjectType>>} A promise that resolves to an AxiosResponse containing all the objects.
*/
export async function getAll(
force_refresh: boolean = true
force_refresh: boolean = true,
): Promise<AxiosResponse<APIObjectType>> {
return await api.get(`${BASE_URL_API}all?force_refresh=${force_refresh}`);
}
@ -66,7 +66,7 @@ export async function sendAll(data: sendAllProps) {
}
export async function postValidateCode(
code: string
code: string,
): Promise<AxiosResponse<errorsTypeAPI>> {
return await api.post(`${BASE_URL_API}validate/code`, { code });
}
@ -81,7 +81,7 @@ export async function postValidateCode(
export async function postValidatePrompt(
name: string,
template: string,
frontend_node: APIClassType
frontend_node: APIClassType,
): Promise<AxiosResponse<PromptTypeAPI>> {
return api.post(`${BASE_URL_API}validate/prompt`, {
name,
@ -154,7 +154,7 @@ export async function saveFlowToDatabase(newFlow: {
* @throws Will throw an error if the update fails.
*/
export async function updateFlowInDatabase(
updatedFlow: FlowType
updatedFlow: FlowType,
): Promise<FlowType> {
try {
const response = await api.patch(`${BASE_URL_API}flows/${updatedFlow.id}`, {
@ -332,7 +332,7 @@ export async function getHealth() {
*
*/
export async function getBuildStatus(
flowId: string
flowId: string,
): Promise<AxiosResponse<BuildStatusTypeAPI>> {
return await api.get(`${BASE_URL_API}build/${flowId}/status`);
}
@ -345,7 +345,7 @@ export async function getBuildStatus(
*
*/
export async function postBuildInit(
flow: FlowType
flow: FlowType,
): Promise<AxiosResponse<InitTypeAPI>> {
return await api.post(`${BASE_URL_API}build/init/${flow.id}`, flow);
}
@ -361,7 +361,7 @@ export async function postBuildInit(
*/
export async function uploadFile(
file: File,
id: string
id: string,
): Promise<AxiosResponse<UploadFileTypeAPI>> {
const formData = new FormData();
formData.append("file", file);
@ -383,7 +383,7 @@ export async function getProfilePictures(): Promise<ProfilePicturesTypeAPI | nul
export async function postCustomComponent(
code: string,
apiClass: APIClassType
apiClass: APIClassType,
): Promise<AxiosResponse<APIClassType>> {
// let template = apiClass.template;
return await api.post(`${BASE_URL_API}custom_component`, {
@ -396,7 +396,7 @@ export async function postCustomComponentUpdate(
code: string,
template: APITemplateType,
field: string,
field_value: any
field_value: any,
): Promise<AxiosResponse<APIClassType>> {
return await api.post(`${BASE_URL_API}custom_component/update`, {
code,
@ -418,7 +418,7 @@ export async function onLogin(user: LoginType) {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}
},
);
if (response.status === 200) {
@ -480,11 +480,11 @@ export async function addUser(user: UserInputType): Promise<Array<Users>> {
export async function getUsersPage(
skip: number,
limit: number
limit: number,
): Promise<Array<Users>> {
try {
const res = await api.get(
`${BASE_URL_API}users/?skip=${skip}&limit=${limit}`
`${BASE_URL_API}users/?skip=${skip}&limit=${limit}`,
);
if (res.status === 200) {
return res.data;
@ -521,7 +521,7 @@ export async function resetPassword(user_id: string, user: resetPasswordType) {
try {
const res = await api.patch(
`${BASE_URL_API}users/${user_id}/reset-password`,
user
user,
);
if (res.status === 200) {
return res.data;
@ -595,7 +595,7 @@ export async function saveFlowStore(
last_tested_version?: string;
},
tags: string[],
publicFlow = false
publicFlow = false,
): Promise<FlowType> {
try {
const response = await api.post(`${BASE_URL_API}store/components/`, {
@ -724,7 +724,7 @@ export async function postStoreComponents(component: Component) {
export async function getComponent(component_id: string) {
try {
const res = await api.get(
`${BASE_URL_API}store/components/${component_id}`
`${BASE_URL_API}store/components/${component_id}`,
);
if (res.status === 200) {
return res.data;
@ -739,7 +739,7 @@ export async function searchComponent(
page?: number | null,
limit?: number | null,
status?: string | null,
tags?: string[]
tags?: string[],
): Promise<StoreComponentResponse | undefined> {
try {
let url = `${BASE_URL_API}store/components/`;
@ -851,7 +851,7 @@ export async function updateFlowStore(
},
tags: string[],
publicFlow = false,
id: string
id: string,
): Promise<FlowType> {
try {
const response = await api.patch(`${BASE_URL_API}store/components/${id}`, {
@ -935,7 +935,7 @@ export async function deleteGlobalVariable(id: string) {
export async function updateGlobalVariable(
name: string,
value: string,
id: string
id: string,
) {
try {
const response = api.patch(`${BASE_URL_API}variables/${id}`, {
@ -954,7 +954,7 @@ export async function getVerticesOrder(
startNodeId?: string | null,
stopNodeId?: string | null,
nodes?: Node[],
Edges?: Edge[]
Edges?: Edge[],
): Promise<AxiosResponse<VerticesOrderTypeAPI>> {
// nodeId is optional and is a query parameter
// if nodeId is not provided, the API will return all vertices
@ -974,7 +974,7 @@ export async function getVerticesOrder(
return await api.post(
`${BASE_URL_API}build/${flowId}/vertices`,
data,
config
config,
);
}
@ -982,7 +982,7 @@ export async function postBuildVertex(
flowId: string,
vertexId: string,
input_value: string,
files?: string[]
files?: string[],
): Promise<AxiosResponse<VertexBuildTypeAPI>> {
// input_value is optional and is a query parameter
let data = {};
@ -994,7 +994,7 @@ export async function postBuildVertex(
}
return await api.post(
`${BASE_URL_API}build/${flowId}/vertices/${vertexId}`,
data
data,
);
}
@ -1018,7 +1018,7 @@ export async function getFlowPool({
}
export async function deleteFlowPool(
flowId: string
flowId: string,
): Promise<AxiosResponse<any>> {
const config = {};
config["params"] = { flow_id: flowId };
@ -1032,7 +1032,7 @@ export async function deleteFlowPool(
* @returns A promise that resolves to an array of AxiosResponse objects representing the delete responses.
*/
export async function multipleDeleteFlowsComponents(
flowIds: string[]
flowIds: string[],
): Promise<AxiosResponse<any>[]> {
const batches: string[][] = [];
@ -1055,7 +1055,7 @@ export async function multipleDeleteFlowsComponents(
// Execute all delete requests
const responses: Promise<AxiosResponse<any>>[] = batches.map((batch) =>
deleteBatch(batch)
deleteBatch(batch),
);
// Return the responses after all requests are completed
@ -1065,7 +1065,7 @@ export async function multipleDeleteFlowsComponents(
export async function getTransactionTable(
id: string,
mode: "intersection" | "union",
params = {}
params = {},
): Promise<{ rows: Array<object>; columns: Array<ColDef | ColGroupDef> }> {
const config = {};
config["params"] = { flow_id: id };
@ -1081,7 +1081,7 @@ export async function getMessagesTable(
mode: "intersection" | "union",
id?: string,
excludedFields?: string[],
params = {}
params = {},
): Promise<{ rows: Array<Message>; columns: Array<ColDef | ColGroupDef> }> {
const config = {};
if (id) {
@ -1091,12 +1091,17 @@ export async function getMessagesTable(
config["params"] = { ...config["params"], ...params };
}
const rows = await api.get(`${BASE_URL_API}monitor/messages`, config);
const columns = extractColumnsFromRows(rows.data, mode, excludedFields);
const rowsOrganized = rows.data;
console.log(rowsOrganized);
const columns = extractColumnsFromRows(rowsOrganized, mode, excludedFields);
const sessions = new Set<string>();
rows.data.forEach((row) => {
rowsOrganized.forEach((row) => {
sessions.add(row.session_id);
});
return { rows: rows.data, columns };
return { rows: rowsOrganized, columns };
}
export async function deleteMessagesFn(ids: number[]) {

View file

@ -4,5 +4,5 @@ import SvgGroqLogo from "./GroqLogo";
export const GroqIcon = forwardRef<SVGSVGElement, React.PropsWithChildren<{}>>(
(props, ref) => {
return <SvgGroqLogo ref={ref} {...props} />;
}
},
);

View file

@ -4,5 +4,5 @@ import SvgStreamlit from "./SvgStreamlit";
export const Streamlit = forwardRef<SVGSVGElement, React.PropsWithChildren<{}>>(
(props, ref) => {
return <SvgStreamlit className="icon" ref={ref} {...props} />;
}
},
);

View file

@ -11,11 +11,11 @@ import "./style/applies.css";
import "./style/classes.css";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
document.getElementById("root") as HTMLElement,
);
root.render(
<ContextWrapper>
<App />
</ContextWrapper>
</ContextWrapper>,
);
reportWebVitals();

View file

@ -27,7 +27,7 @@ const useFolderSubmit = (setOpen, folderToEdit) => {
getFoldersApi(true);
setOpen(false);
}
}
},
);
} else {
addFolder(data).then(
@ -42,7 +42,7 @@ const useFolderSubmit = (setOpen, folderToEdit) => {
setErrorData({
title: `Error creating folder.`,
});
}
},
);
}
};

View file

@ -29,7 +29,7 @@ export default function CsvSelect({ node, handleChangeSelect }): JSX.Element {
<SelectItem key={separator} value={separator}>
{separator}
</SelectItem>
)
),
)}
</SelectGroup>
</SelectContent>

View file

@ -251,9 +251,11 @@ export default function IOFieldView({
<RecordsOutputComponent
pagination={!left}
rows={
flowPoolNode?.data?.artifacts?.map(
(artifact) => artifact.data
) ?? []
Array.isArray(flowPoolNode?.data?.artifacts)
? flowPoolNode?.data?.artifacts?.map(
(artifact) => artifact.data,
) ?? []
: [flowPoolNode?.data?.artifacts]
}
columnMode="union"
/>

View file

@ -10,7 +10,7 @@ const useRemoveSession = (setSuccessData, setErrorData) => {
await deleteMessagesFn(
messages
.filter((msg) => msg.session_id === session_id)
.map((msg) => msg.index)
.map((msg) => msg.index),
);
deleteSession(session_id);
setSuccessData({

View file

@ -17,7 +17,7 @@ export default function SessionView({ rows }: { rows: Array<any> }) {
setSelectedRows,
setSuccessData,
setErrorData,
selectedRows
selectedRows,
);
const { handleUpdate } = useUpdateMessage(setSuccessData, setErrorData);

View file

@ -28,8 +28,8 @@ const ButtonSendWrapper = ({
noInput
? "bg-high-indigo text-background"
: chatValue === ""
? "text-primary"
: "bg-chat-send text-background"
? "text-primary"
: "bg-chat-send text-background",
)}
disabled={lockChat || saveLoading}
onClick={(): void => send()}

View file

@ -18,7 +18,7 @@ const TextAreaWrapper = ({
}) => {
const getPlaceholderText = (
isDragging: boolean,
noInput: boolean
noInput: boolean,
): string => {
if (isDragging) {
return "Drop here";
@ -33,8 +33,8 @@ const TextAreaWrapper = ({
lockChat || saveLoading
? "form-modal-lock-true bg-input"
: noInput
? "form-modal-no-input bg-input"
: "form-modal-lock-false bg-background";
? "form-modal-no-input bg-input"
: "form-modal-lock-false bg-background";
const fileClass =
files.length > 0

View file

@ -1,13 +1,16 @@
import ShortUniqueId from "short-unique-id";
import { ALLOWED_IMAGE_INPUT_EXTENSIONS, FS_ERROR_TEXT, SN_ERROR_TEXT } from "../../../../../../constants/constants";
import {
ALLOWED_IMAGE_INPUT_EXTENSIONS,
FS_ERROR_TEXT,
SN_ERROR_TEXT,
} from "../../../../../../constants/constants";
import useFileUpload from "./use-file-upload";
const useDragAndDrop = (
setIsDragging,
setFiles,
currentFlowId,
setErrorData
setErrorData,
) => {
const dragOver = (e) => {
e.preventDefault();

View file

@ -10,7 +10,7 @@ import useFileUpload from "./use-file-upload";
export const useHandleFileChange = (setFiles, currentFlowId) => {
const setErrorData = useAlertStore((state) => state.setErrorData);
const handleFileChange = async (
event: React.ChangeEvent<HTMLInputElement>
event: React.ChangeEvent<HTMLInputElement>,
) => {
const fileInput = event.target;
const file = fileInput.files?.[0];

View file

@ -116,7 +116,7 @@ export default function ChatInput({
key={file.id}
onDelete={() => {
setFiles((prev: FilePreviewType[]) =>
prev.filter((f) => f.id !== file.id)
prev.filter((f) => f.id !== file.id),
);
// TODO: delete file on backend
}}

View file

@ -115,19 +115,19 @@ export default function ChatMessage({
<div
className={classNames(
"form-modal-chat-position",
chat.isSend ? "" : " "
chat.isSend ? "" : " ",
)}
>
<div
className={classNames(
"mr-3 mt-1 flex w-24 flex-col items-center gap-1 overflow-hidden px-3 pb-3"
"mr-3 mt-1 flex w-24 flex-col items-center gap-1 overflow-hidden px-3 pb-3",
)}
>
<div className="flex flex-col items-center gap-1">
<div
className={cn(
"relative flex h-8 w-8 items-center justify-center overflow-hidden rounded-md p-5 text-2xl",
!chat.isSend ? "bg-chat-bot-icon" : "bg-chat-user-icon"
!chat.isSend ? "bg-chat-bot-icon" : "bg-chat-user-icon",
)}
>
<img
@ -211,12 +211,12 @@ dark:prose-invert"
children[0] = (children[0] as string).replace(
"`▍`",
"▍"
"▍",
);
}
const match = /language-(\w+)/.exec(
className || ""
className || "",
);
return !inline ? (
@ -231,7 +231,7 @@ dark:prose-invert"
language: (match && match[1]) || "",
code: String(children).replace(
/\n$/,
""
"",
),
},
]}
@ -249,7 +249,7 @@ dark:prose-invert"
{chatMessage}
</Markdown>
),
[chat.message, chatMessage]
[chat.message, chatMessage],
)}
</div>
</div>
@ -292,7 +292,7 @@ dark:prose-invert"
parts.push(
<span className="chat-message-highlight">
{chat.message[match[1]]}
</span>
</span>,
);
}

View file

@ -29,7 +29,7 @@ export default function FileCard({
const imgSrc = `${BACKEND_URL.slice(
0,
BACKEND_URL.length - 1
BACKEND_URL.length - 1,
)}${BASE_URL_API}files/images/${content}`;
if (showFile) {

View file

@ -20,8 +20,8 @@ export default async function handleDownload({
const response = await fetch(
`${BACKEND_URL.slice(
0,
BACKEND_URL.length - 1
)}${BASE_URL_API}files/download/${content}`
BACKEND_URL.length - 1,
)}${BASE_URL_API}files/download/${content}`,
);
if (!response.ok) {
throw new Error("Network response was not ok");

View file

@ -1,6 +1,6 @@
export default function formatFileName(
name: string,
numberToTruncate: number = 25
numberToTruncate: number = 25,
): string {
if (name[numberToTruncate] === undefined) {
return name;

View file

@ -10,7 +10,7 @@ import useAlertStore from "../../../../stores/alertStore";
import useFlowStore from "../../../../stores/flowStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
import { VertexBuildTypeAPI, sendAllProps } from "../../../../types/api";
import { ChatMessageType, ChatOutputType } from "../../../../types/chat";
import { ChatMessageType } from "../../../../types/chat";
import { FilePreviewType, chatViewProps } from "../../../../types/components";
import { classNames } from "../../../../utils/utils";
import ChatInput from "./chatInput";
@ -65,10 +65,13 @@ export default function ChatView({
.filter((output) => output.data.message)
.map((output, index) => {
try {
const { sender, message, sender_name, stream_url, files } = output
.data.message as ChatOutputType;
const is_ai = sender === "Machine" || sender === null;
console.log("output:", output);
const { sender, message, sender_name, stream_url, files } =
output.data.message;
console.log("output.data.message:", output.data.message);
console.log("output.data.message.files:", output.data.message.files);
const is_ai =
sender === "Machine" || sender === null || sender === undefined;
return {
isSend: !is_ai,
message: message,
@ -131,7 +134,7 @@ export default function ChatView({
function updateChat(
chat: ChatMessageType,
message: string,
stream_url?: string
stream_url?: string,
) {
// if (message === "") return;
chat.message = message;
@ -161,7 +164,7 @@ export default function ChatView({
setIsDragging,
setFiles,
currentFlowId,
setErrorData
setErrorData,
);
return (

View file

@ -36,25 +36,25 @@ export default function IOModal({
const allNodes = useFlowStore((state) => state.nodes);
const setMessages = useMessagesStore((state) => state.setMessages);
const inputs = useFlowStore((state) => state.inputs).filter(
(input) => input.type !== "ChatInput"
(input) => input.type !== "ChatInput",
);
const chatInput = useFlowStore((state) => state.inputs).find(
(input) => input.type === "ChatInput"
(input) => input.type === "ChatInput",
);
const outputs = useFlowStore((state) => state.outputs).filter(
(output) => output.type !== "ChatOutput"
(output) => output.type !== "ChatOutput",
);
const chatOutput = useFlowStore((state) => state.outputs).find(
(output) => output.type === "ChatOutput"
(output) => output.type === "ChatOutput",
);
const nodes = useFlowStore((state) => state.nodes).filter(
(node) =>
inputs.some((input) => input.id === node.id) ||
outputs.some((output) => output.id === node.id)
outputs.some((output) => output.id === node.id),
);
const haveChat = chatInput || chatOutput;
const [selectedTab, setSelectedTab] = useState(
inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0
inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0,
);
const setErrorData = useAlertStore((state) => state.setErrorData);
const setSuccessData = useAlertStore((state) => state.setSuccessData);
@ -85,6 +85,7 @@ export default function IOModal({
const [sessions, setSessions] = useState<string[]>([]);
const messages = useMessagesStore((state) => state.messages);
const setColumns = useMessagesStore((state) => state.setColumns);
const flowPool = useFlowStore((state) => state.flowPool);
async function updateVertices() {
return updateVerticesOrder(currentFlow!.id, null);
}
@ -111,7 +112,10 @@ export default function IOModal({
setLockChat(false);
});
}
const { rows, columns } = await getMessagesTable("union", currentFlow!.id);
const { rows, columns } = await getMessagesTable("union", currentFlow!.id, [
"index",
"flow_id",
]);
setMessages(rows);
setColumns(columns);
setLockChat(false);
@ -127,7 +131,7 @@ export default function IOModal({
const { handleRemoveSession } = useRemoveSession(
setSuccessData,
setErrorData
setErrorData,
);
useEffect(() => {
@ -147,10 +151,12 @@ export default function IOModal({
useEffect(() => {
setSelectedViewField(startView());
if (haveChat) {
getMessagesTable("union", currentFlow!.id).then(({ rows, columns }) => {
setMessages(rows);
setColumns(columns);
});
getMessagesTable("union", currentFlow!.id, ["index", "flow_id"]).then(
({ rows, columns }) => {
setMessages(rows);
setColumns(columns);
},
);
}
}, [open]);
@ -183,12 +189,12 @@ export default function IOModal({
/>
</div>
</BaseModal.Header>
<BaseModal.Content>
<div className="flex h-full flex-col ">
<BaseModal.Content overflowHidden>
<div className="flex h-full flex-col">
<div className="flex-max-width h-full">
<div
className={cn(
"mr-6 flex h-full w-2/6 flex-shrink-0 flex-col justify-start transition-all duration-300"
"mr-6 flex h-full w-2/6 flex-shrink-0 flex-col justify-start transition-all duration-300",
)}
>
<Tabs
@ -217,11 +223,11 @@ export default function IOModal({
<TabsContent value={"1"} className="api-modal-tabs-content">
{nodes
.filter((node) =>
inputs.some((input) => input.id === node.id)
inputs.some((input) => input.id === node.id),
)
.map((node, index) => {
const input = inputs.find(
(input) => input.id === node.id
(input) => input.id === node.id,
)!;
return (
<div
@ -229,10 +235,6 @@ export default function IOModal({
key={index}
>
<AccordionComponent
disabled={
node.data.node!.template["input_value"]?.value ===
""
}
trigger={
<div className="file-component-badge-div">
<ShadTooltip
@ -282,18 +284,26 @@ export default function IOModal({
<TabsContent value={"2"} className="api-modal-tabs-content">
{nodes
.filter((node) =>
outputs.some((output) => output.id === node.id)
outputs.some((output) => output.id === node.id),
)
.map((node, index) => {
const output = outputs.find(
(output) => output.id === node.id
(output) => output.id === node.id,
)!;
const textOutputValue =
(flowPool[node!.id] ?? [])[
(flowPool[node!.id]?.length ?? 1) - 1
]?.data?.artifacts ?? "";
const disabled =
textOutputValue === "" ||
JSON.stringify(textOutputValue) === "{}";
return (
<div
className="file-component-accordion-div"
key={index}
>
<AccordionComponent
disabled={disabled}
trigger={
<div className="file-component-badge-div">
<ShadTooltip
@ -353,13 +363,24 @@ export default function IOModal({
});
}}
>
<div className="flex w-full items-center justify-between border-b px-2 py-1 align-middle">
<Badge variant="gray" size="md">
{session}
</Badge>
<div className="flex items-center justify-center gap-2 align-middle">
<div className="flex w-full items-center justify-between gap-2 overflow-hidden border-b px-2 py-3.5 align-middle">
<ShadTooltip styleClasses="z-50" content={session}>
<div className="flex min-w-0">
<Badge
variant="gray"
size="md"
className="block truncate"
>
{session === currentFlow?.id
? "Default Session"
: session}
</Badge>
</div>
</ShadTooltip>
<div className="flex shrink-0 items-center justify-center gap-2 align-middle">
<Button
variant="ghost"
variant="none"
size="icon"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -370,7 +391,7 @@ export default function IOModal({
>
<ShadTooltip
styleClasses="z-50"
content={"delete"}
content={"Delete"}
>
<div>
<IconComponent
@ -418,7 +439,7 @@ export default function IOModal({
<div
className={cn(
"flex h-full w-full flex-col items-start gap-4 pt-4",
!selectedViewField ? "hidden" : ""
!selectedViewField ? "hidden" : "",
)}
>
<div className="font-xl flex items-center justify-center gap-3 font-semibold">
@ -437,7 +458,7 @@ export default function IOModal({
</div>
<div className="h-full w-full">
{inputs.some(
(input) => input.id === selectedViewField.id
(input) => input.id === selectedViewField.id,
) && (
<IOFieldView
type={InputOutput.INPUT}
@ -447,7 +468,7 @@ export default function IOModal({
/>
)}
{outputs.some(
(output) => output.id === selectedViewField.id
(output) => output.id === selectedViewField.id,
) && (
<IOFieldView
type={InputOutput.OUTPUT}
@ -457,12 +478,12 @@ export default function IOModal({
/>
)}
{sessions.some(
(session) => session === selectedViewField.id
(session) => session === selectedViewField.id,
) && (
<SessionView
rows={messages.filter(
(message) =>
message.session_id === selectedViewField.id
message.session_id === selectedViewField.id,
)}
/>
)}
@ -472,7 +493,7 @@ export default function IOModal({
<div
className={cn(
"flex h-full w-full",
selectedViewField ? "hidden" : ""
selectedViewField ? "hidden" : "",
)}
>
{haveChat ? (
@ -504,7 +525,7 @@ export default function IOModal({
"h-4 w-4",
isBuilding
? "animate-spin"
: "fill-current text-medium-indigo"
: "fill-current text-medium-indigo",
)}
/>
),

View file

@ -2,35 +2,29 @@ import "ace-builds/src-noconflict/ext-language_tools";
import "ace-builds/src-noconflict/mode-python";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/theme-twilight";
import {
ReactNode,
forwardRef,
useContext,
useEffect,
useRef,
useState,
} from "react";
import { ReactNode, forwardRef, useContext, useEffect, useState } from "react";
// import "ace-builds/webpack-resolver";
import { cloneDeep } from "lodash";
import CodeTabsComponent from "../../components/codeTabsComponent";
import IconComponent from "../../components/genericIconComponent";
import {
EXPORT_CODE_DIALOG,
LANGFLOW_SUPPORTED_TYPES,
} from "../../constants/constants";
import { EXPORT_CODE_DIALOG } from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext";
import useFlowStore from "../../stores/flowStore";
import { TemplateVariableType } from "../../types/api";
import { tweakType, uniqueTweakType } from "../../types/components";
import { FlowType, NodeType } from "../../types/flow/index";
import { buildTweaks, convertArrayToObj } from "../../utils/reactflowUtils";
import {
getCurlCode,
getPythonApiCode,
getPythonCode,
getWidgetCode,
tabsArray,
} from "../../utils/utils";
import { useTweaksStore } from "../../stores/tweaksStore";
import { InputFieldType } from "../../types/api";
import { uniqueTweakType } from "../../types/components";
import { FlowType } from "../../types/flow/index";
import BaseModal from "../baseModal";
import { buildContent } from "./utils/build-content";
import { buildTweaks } from "./utils/build-tweaks";
import { checkCanBuildTweakObject } from "./utils/check-can-build-tweak-object";
import { getChangesType } from "./utils/get-changes-types";
import { getCurlRunCode, getCurlWebhookCode } from "./utils/get-curl-code";
import { getNodesWithDefaultValue } from "./utils/get-nodes-with-default-value";
import getPythonApiCode from "./utils/get-python-api-code";
import getPythonCode from "./utils/get-python-code";
import { getValue } from "./utils/get-value";
import getWidgetCode from "./utils/get-widget-code";
import { createTabsArray } from "./utils/tabs-array";
const ApiModal = forwardRef(
(
@ -42,122 +36,136 @@ const ApiModal = forwardRef(
}: {
flow: FlowType;
children: ReactNode;
open: any;
setOpen: any;
open?: boolean;
setOpen?: (a: boolean | ((o?: boolean) => boolean)) => void;
},
ref
ref,
) => {
const tweak = useTweaksStore((state) => state.tweak);
const addTweaks = useTweaksStore((state) => state.setTweak);
const setTweaksList = useTweaksStore((state) => state.setTweaksList);
const tweaksList = useTweaksStore((state) => state.tweaksList);
const [activeTweaks, setActiveTweaks] = useState(false);
const { autoLogin } = useContext(AuthContext);
const [open, setOpen] =
mySetOpen !== undefined && myOpen !== undefined
? [myOpen, mySetOpen]
: useState(false);
const [activeTab, setActiveTab] = useState("0");
const tweak = useRef<tweakType>([]);
const tweaksList = useRef<string[]>([]);
const [getTweak, setTweak] = useState<tweakType>([]);
const flowState = useFlowStore((state) => state.flowState);
const pythonApiCode = getPythonApiCode(flow, autoLogin, tweak.current);
const curl_code = getCurlCode(flow, autoLogin, tweak.current);
const pythonCode = getPythonCode(flow, tweak.current);
const widgetCode = getWidgetCode(flow, autoLogin, flowState);
const pythonApiCode = getPythonApiCode(
flow?.id,
autoLogin,
tweak,
flow?.endpoint_name,
);
const curl_run_code = getCurlRunCode(
flow?.id,
autoLogin,
tweak,
flow?.endpoint_name,
);
const curl_webhook_code = getCurlWebhookCode(
flow?.id,
autoLogin,
flow?.endpoint_name,
);
const pythonCode = getPythonCode(flow?.name, tweak);
const widgetCode = getWidgetCode(flow?.id, flow?.name, autoLogin);
const includeWebhook = flow.webhook;
const tweaksCode = buildTweaks(flow);
const codesArray = [
curl_code,
curl_run_code,
curl_webhook_code,
pythonApiCode,
pythonCode,
widgetCode,
pythonCode,
];
const [tabs, setTabs] = useState(tabsArray(codesArray, 0));
const [tabs, setTabs] = useState(
createTabsArray(codesArray, includeWebhook),
);
function startState() {
tweak.current = [];
setTweak([]);
tweaksList.current = [];
}
const canShowTweaks =
flow &&
flow["data"] &&
flow["data"]!["nodes"] &&
tweak &&
tweak?.length > 0 &&
activeTweaks === true;
const buildTweaksInitialState = () => {
const newTweak: any = [];
const t = buildTweaks(flow);
newTweak.push(t);
addTweaks(newTweak);
addCodes(newTweak);
};
useEffect(() => {
if (flow["data"]!["nodes"].length == 0) {
startState();
addTweaks([]);
setTweaksList([]);
} else {
tweak.current = [];
const t = buildTweaks(flow);
tweak.current.push(t);
buildTweaksInitialState();
}
filterNodes();
if (Object.keys(tweaksCode).length > 0) {
setActiveTab("0");
setTabs(tabsArray(codesArray, 1));
setTabs(createTabsArray(codesArray, includeWebhook, true));
} else {
setTabs(tabsArray(codesArray, 1));
setTabs(createTabsArray(codesArray, includeWebhook, true));
}
}, [flow["data"]!["nodes"], open]);
function filterNodes() {
let arrNodesWithValues: string[] = [];
useEffect(() => {
if (canShowTweaks) {
const nodes = flow["data"]!["nodes"];
nodes.forEach((element) => {
const nodeId = element["id"];
const template = element["data"]["node"]["template"];
flow["data"]!["nodes"].forEach((node) => {
if (!node["data"]["node"]["template"]) {
return;
}
Object.keys(node["data"]["node"]["template"])
.filter(
(templateField) =>
templateField.charAt(0) !== "_" &&
node.data.node.template[templateField].show &&
LANGFLOW_SUPPORTED_TYPES.has(
node.data.node.template[templateField].type
)
)
.map((n, i) => {
arrNodesWithValues.push(node["id"]);
Object.keys(template).forEach((templateField) => {
if (checkCanBuildTweakObject(element, templateField)) {
buildTweakObject(
nodeId,
element.data.node.template[templateField].value,
element.data.node.template[templateField],
);
}
});
});
});
} else {
buildTweaksInitialState();
}
}, [activeTweaks]);
tweaksList.current = arrNodesWithValues.filter((value, index, self) => {
return self.indexOf(value) === index;
});
}
function buildTweakObject(
const filterNodes = () => {
setTweaksList(getNodesWithDefaultValue(flow));
};
async function buildTweakObject(
tw: string,
changes: string | string[] | boolean | number | Object[] | Object,
template: TemplateVariableType
template: InputFieldType,
) {
if (typeof changes === "string" && template.type === "float") {
changes = parseFloat(changes);
}
if (typeof changes === "string" && template.type === "int") {
changes = parseInt(changes);
}
if (template.list === true && Array.isArray(changes)) {
changes = changes?.filter((x) => x !== "");
}
changes = getChangesType(changes, template);
if (template.type === "dict" && Array.isArray(changes)) {
changes = convertArrayToObj(changes);
}
if (template.type === "NestedDict") {
changes = JSON.stringify(changes);
}
const existingTweak = tweak.current.find((element) =>
element.hasOwnProperty(tw)
);
const existingTweak = tweak.find((element) => element.hasOwnProperty(tw));
if (existingTweak) {
existingTweak[tw][template["name"]!] = changes as string;
if (existingTweak[tw][template["name"]!] == template.value) {
tweak.current.forEach((element) => {
tweak.forEach((element) => {
if (element[tw] && Object.keys(element[tw])?.length === 0) {
tweak.current = tweak.current.filter((obj) => {
const filteredTweaks = tweak.filter((obj) => {
const prop = obj[Object.keys(obj)[0]].prop;
return prop !== undefined && prop !== null && prop !== "";
});
addTweaks(filteredTweaks);
}
});
}
@ -167,56 +175,34 @@ const ApiModal = forwardRef(
[template["name"]!]: changes,
},
} as uniqueTweakType;
tweak.current.push(newTweak);
tweak.push(newTweak);
}
const pythonApiCode = getPythonApiCode(flow, autoLogin, tweak.current);
const curl_code = getCurlCode(flow, autoLogin, tweak.current);
const pythonCode = getPythonCode(flow, tweak.current);
const widgetCode = getWidgetCode(flow, autoLogin, flowState);
tabs![0].code = curl_code;
tabs![1].code = pythonApiCode;
tabs![2].code = pythonCode;
tabs![3].code = widgetCode;
setTweak(tweak.current);
if (tweak && tweak.length > 0) {
const cloneTweak = cloneDeep(tweak);
addCodes(cloneTweak);
addTweaks(cloneTweak);
}
}
function buildContent(value: string) {
const htmlContent = (
<div className="w-[200px]">
<span>{value != null && value != "" ? value : "None"}</span>
</div>
const addCodes = (cloneTweak) => {
const pythonApiCode = getPythonApiCode(flow?.id, autoLogin, cloneTweak);
const curl_code = getCurlRunCode(
flow?.id,
autoLogin,
cloneTweak,
flow?.endpoint_name,
);
return htmlContent;
}
const pythonCode = getPythonCode(flow?.name, cloneTweak);
const widgetCode = getWidgetCode(flow?.id, flow?.name, autoLogin);
function getValue(
value: string,
node: NodeType,
template: TemplateVariableType
) {
let returnValue = value ?? "";
if (getTweak.length > 0) {
for (const obj of getTweak) {
Object.keys(obj).forEach((key) => {
const value = obj[key];
if (key == node["id"]) {
Object.keys(value).forEach((key) => {
if (key == template["name"]) {
returnValue = value[key];
}
});
}
});
}
} else {
return value ?? "";
if (tabs && tabs?.length > 0) {
tabs![0].code = curl_code;
tabs![1].code = pythonApiCode;
tabs![2].code = pythonCode;
tabs![3].code = widgetCode;
}
return returnValue;
}
};
return (
<BaseModal open={open} setOpen={setOpen}>
@ -229,7 +215,7 @@ const ApiModal = forwardRef(
aria-hidden="true"
/>
</BaseModal.Header>
<BaseModal.Content>
<BaseModal.Content overflowHidden>
<CodeTabsComponent
flow={flow}
tabs={tabs!}
@ -242,11 +228,14 @@ const ApiModal = forwardRef(
buildTweakObject,
getValue,
}}
activeTweaks={activeTweaks}
setActiveTweaks={setActiveTweaks}
allowExport
/>
</BaseModal.Content>
</BaseModal>
);
}
},
);
export default ApiModal;

View file

@ -6,7 +6,7 @@ export const checkCanBuildTweakObject = (element, templateField) => {
templateField.charAt(0) !== "_" &&
element.data.node.template[templateField].show &&
LANGFLOW_SUPPORTED_TYPES.has(
element.data.node.template[templateField].type
element.data.node.template[templateField].type,
) &&
templateField !== "code"
);

View file

@ -8,14 +8,14 @@ export function getCurlRunCode(
flowId: string,
isAuth: boolean,
tweaksBuildedObject,
endpointName?: string
endpointName?: string,
): string {
const tweaksObject = tweaksBuildedObject[0];
// show the endpoint name in the curl command if it exists
return `curl -X POST \\
"${window.location.protocol}//${window.location.host}/api/v1/run/${
endpointName || flowId
}?stream=false" \\
endpointName || flowId
}?stream=false" \\
-H 'Content-Type: application/json'\\${
!isAuth ? `\n -H 'x-api-key: <your api key>'\\` : ""
}

View file

@ -13,8 +13,8 @@ export const getNodesWithDefaultValue = (flow) => {
templateField.charAt(0) !== "_" &&
node.data.node.template[templateField].show &&
LANGFLOW_SUPPORTED_TYPES.has(
node.data.node.template[templateField].type
)
node.data.node.template[templateField].type,
),
)
.map((n, i) => {
arrNodesWithValues.push(node["id"]);

View file

@ -6,7 +6,7 @@
export default function getWidgetCode(
flowId: string,
flowName: string,
isAuth: boolean
isAuth: boolean,
): string {
return `<script src="https://cdn.jsdelivr.net/gh/langflow-ai/langflow-embedded-chat@1.0_alpha/dist/build/static/js/bundle.min.js"></script>

View file

@ -1,7 +1,7 @@
export function createTabsArray(
codes,
includeWebhookCurl = false,
includeTweaks = false
includeTweaks = false,
) {
const tabs = [
{

View file

@ -1,241 +0,0 @@
import "ace-builds/src-noconflict/ext-language_tools";
import "ace-builds/src-noconflict/mode-python";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/theme-twilight";
import { ReactNode, forwardRef, useContext, useEffect, useState } from "react";
// import "ace-builds/webpack-resolver";
import { cloneDeep } from "lodash";
import CodeTabsComponent from "../../../components/codeTabsComponent";
import IconComponent from "../../../components/genericIconComponent";
import { EXPORT_CODE_DIALOG } from "../../../constants/constants";
import { AuthContext } from "../../../contexts/authContext";
import { useTweaksStore } from "../../../stores/tweaksStore";
import { InputFieldType } from "../../../types/api";
import { uniqueTweakType } from "../../../types/components";
import { FlowType } from "../../../types/flow/index";
import BaseModal from "../../baseModal";
import { buildContent } from "../utils/build-content";
import { buildTweaks } from "../utils/build-tweaks";
import { checkCanBuildTweakObject } from "../utils/check-can-build-tweak-object";
import { getChangesType } from "../utils/get-changes-types";
import { getCurlRunCode, getCurlWebhookCode } from "../utils/get-curl-code";
import { getNodesWithDefaultValue } from "../utils/get-nodes-with-default-value";
import getPythonApiCode from "../utils/get-python-api-code";
import getPythonCode from "../utils/get-python-code";
import { getValue } from "../utils/get-value";
import getWidgetCode from "../utils/get-widget-code";
import { createTabsArray } from "../utils/tabs-array";
const ApiModal = forwardRef(
(
{
flow,
children,
open: myOpen,
setOpen: mySetOpen,
}: {
flow: FlowType;
children: ReactNode;
open?: boolean;
setOpen?: (a: boolean | ((o?: boolean) => boolean)) => void;
},
ref
) => {
const tweak = useTweaksStore((state) => state.tweak);
const addTweaks = useTweaksStore((state) => state.setTweak);
const setTweaksList = useTweaksStore((state) => state.setTweaksList);
const tweaksList = useTweaksStore((state) => state.tweaksList);
const [activeTweaks, setActiveTweaks] = useState(false);
const { autoLogin } = useContext(AuthContext);
const [open, setOpen] =
mySetOpen !== undefined && myOpen !== undefined
? [myOpen, mySetOpen]
: useState(false);
const [activeTab, setActiveTab] = useState("0");
const pythonApiCode = getPythonApiCode(
flow?.id,
autoLogin,
tweak,
flow?.endpoint_name
);
const curl_run_code = getCurlRunCode(
flow?.id,
autoLogin,
tweak,
flow?.endpoint_name
);
const curl_webhook_code = getCurlWebhookCode(
flow?.id,
autoLogin,
flow?.endpoint_name
);
const pythonCode = getPythonCode(flow?.name, tweak);
const widgetCode = getWidgetCode(flow?.id, flow?.name, autoLogin);
const includeWebhook = flow.webhook;
const tweaksCode = buildTweaks(flow);
const codesArray = [
curl_run_code,
curl_webhook_code,
pythonApiCode,
pythonCode,
widgetCode,
pythonCode,
];
const [tabs, setTabs] = useState(
createTabsArray(codesArray, includeWebhook)
);
const canShowTweaks =
flow &&
flow["data"] &&
flow["data"]!["nodes"] &&
tweak &&
tweak?.length > 0 &&
activeTweaks === true;
const buildTweaksInitialState = () => {
const newTweak: any = [];
const t = buildTweaks(flow);
newTweak.push(t);
addTweaks(newTweak);
addCodes(newTweak);
};
useEffect(() => {
if (flow["data"]!["nodes"].length == 0) {
addTweaks([]);
setTweaksList([]);
} else {
buildTweaksInitialState();
}
filterNodes();
if (Object.keys(tweaksCode).length > 0) {
setActiveTab("0");
setTabs(createTabsArray(codesArray, includeWebhook, true));
} else {
setTabs(createTabsArray(codesArray, includeWebhook, true));
}
}, [flow["data"]!["nodes"], open]);
useEffect(() => {
if (canShowTweaks) {
const nodes = flow["data"]!["nodes"];
nodes.forEach((element) => {
const nodeId = element["id"];
const template = element["data"]["node"]["template"];
Object.keys(template).forEach((templateField) => {
if (checkCanBuildTweakObject(element, templateField)) {
buildTweakObject(
nodeId,
element.data.node.template[templateField].value,
element.data.node.template[templateField]
);
}
});
});
} else {
buildTweaksInitialState();
}
}, [activeTweaks]);
const filterNodes = () => {
setTweaksList(getNodesWithDefaultValue(flow));
};
async function buildTweakObject(
tw: string,
changes: string | string[] | boolean | number | Object[] | Object,
template: InputFieldType
) {
changes = getChangesType(changes, template);
const existingTweak = tweak.find((element) => element.hasOwnProperty(tw));
if (existingTweak) {
existingTweak[tw][template["name"]!] = changes as string;
if (existingTweak[tw][template["name"]!] == template.value) {
tweak.forEach((element) => {
if (element[tw] && Object.keys(element[tw])?.length === 0) {
const filteredTweaks = tweak.filter((obj) => {
const prop = obj[Object.keys(obj)[0]].prop;
return prop !== undefined && prop !== null && prop !== "";
});
addTweaks(filteredTweaks);
}
});
}
} else {
const newTweak = {
[tw]: {
[template["name"]!]: changes,
},
} as uniqueTweakType;
tweak.push(newTweak);
}
if (tweak && tweak.length > 0) {
const cloneTweak = cloneDeep(tweak);
addCodes(cloneTweak);
addTweaks(cloneTweak);
}
}
const addCodes = (cloneTweak) => {
const pythonApiCode = getPythonApiCode(flow?.id, autoLogin, cloneTweak);
const curl_code = getCurlRunCode(
flow?.id,
autoLogin,
cloneTweak,
flow?.endpoint_name
);
const pythonCode = getPythonCode(flow?.name, cloneTweak);
const widgetCode = getWidgetCode(flow?.id, flow?.name, autoLogin);
if (tabs && tabs?.length > 0) {
tabs![0].code = curl_code;
tabs![1].code = pythonApiCode;
tabs![2].code = pythonCode;
tabs![3].code = widgetCode;
}
};
return (
<BaseModal open={open} setOpen={setOpen}>
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
<BaseModal.Header description={EXPORT_CODE_DIALOG}>
<span className="pr-2">API</span>
<IconComponent
name="Code2"
className="h-6 w-6 pl-1 text-gray-800 dark:text-white"
aria-hidden="true"
/>
</BaseModal.Header>
<BaseModal.Content>
<CodeTabsComponent
flow={flow}
tabs={tabs!}
activeTab={activeTab}
setActiveTab={setActiveTab}
tweaks={{
tweak,
tweaksList,
buildContent,
buildTweakObject,
getValue,
}}
activeTweaks={activeTweaks}
setActiveTweaks={setActiveTweaks}
allowExport
/>
</BaseModal.Content>
</BaseModal>
);
}
);
export default ApiModal;

View file

@ -22,7 +22,7 @@ import { modalHeaderType } from "../../types/components";
import { cn } from "../../utils/utils";
import { switchCaseModalSize } from "./helpers/switch-case-size";
type ContentProps = { children: ReactNode };
type ContentProps = { children: ReactNode; overflowHidden?: boolean };
type HeaderProps = { children: ReactNode; description: string };
type FooterProps = { children: ReactNode };
type TriggerProps = {
@ -32,8 +32,17 @@ type TriggerProps = {
className?: string;
};
const Content: React.FC<ContentProps> = ({ children }) => {
return <div className="flex h-full w-full flex-col">{children}</div>;
const Content: React.FC<ContentProps> = ({ children, overflowHidden }) => {
return (
<div
className={cn(
`flex w-full flex-grow flex-col transition-all duration-300`,
overflowHidden ? "overflow-hidden" : "overflow-visible",
)}
>
{children}
</div>
);
};
const Trigger: React.FC<TriggerProps> = ({
children,
@ -59,8 +68,12 @@ const Header: React.FC<{
}> = ({ children, description }: modalHeaderType): JSX.Element => {
return (
<DialogHeader>
<DialogTitle className="flex items-center">{children}</DialogTitle>
<DialogDescription>{description}</DialogDescription>
<DialogTitle className="line-clamp-1 flex items-center">
{children}
</DialogTitle>
<DialogDescription className="line-clamp-2">
{description}
</DialogDescription>
</DialogHeader>
);
};
@ -73,29 +86,35 @@ const Footer: React.FC<{
loading?: boolean;
disabled?: boolean;
dataTestId?: string;
onClick?: () => void;
};
}> = ({ children, submit }) => {
return submit ? (
<div className="flex w-full items-center justify-between">
{children ?? <div />}
<div className="flex items-center gap-3">
<DialogClose asChild>
<Button variant="outline" type="button">
Cancel
</Button>
</DialogClose>
<Button
data-testid={submit.dataTestId}
type="submit"
loading={submit.loading}
>
{submit.icon && submit.icon}
{submit.label}
</Button>
</div>
return (
<div className="flex flex-shrink-0 flex-row-reverse">
{submit ? (
<div className="flex w-full items-center justify-between">
{children ?? <div />}
<div className="flex items-center gap-3">
<DialogClose asChild>
<Button variant="outline" type="button">
Cancel
</Button>
</DialogClose>
<Button
data-testid={submit.dataTestId}
type={submit.onClick ? "button" : "submit"}
onClick={submit.onClick}
loading={submit.loading}
>
{submit.icon && submit.icon}
{submit.label}
</Button>
</div>
</div>
) : (
<>{children && children}</>
)}
</div>
) : (
<>{children && children}</>
);
};
interface BaseModalProps {
@ -103,7 +122,7 @@ interface BaseModalProps {
React.ReactElement<ContentProps>,
React.ReactElement<HeaderProps>,
React.ReactElement<TriggerProps>?,
React.ReactElement<FooterProps>?
React.ReactElement<FooterProps>?,
];
open?: boolean;
setOpen?: (open: boolean) => void;
@ -139,16 +158,16 @@ function BaseModal({
onSubmit,
}: BaseModalProps) {
const headerChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Header
(child) => (child as React.ReactElement).type === Header,
);
const triggerChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Trigger
(child) => (child as React.ReactElement).type === Trigger,
);
const ContentChild = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Content
(child) => (child as React.ReactElement).type === Content,
);
const ContentFooter = React.Children.toArray(children).find(
(child) => (child as React.ReactElement).type === Footer
(child) => (child as React.ReactElement).type === Footer,
);
let { minWidth, height } = switchCaseModalSize(size);
@ -159,71 +178,44 @@ function BaseModal({
}
}, [open]);
const modalContent = (
<>
{headerChild}
{ContentChild}
{ContentFooter && ContentFooter}
</>
);
const contentClasses = cn(
minWidth,
height,
"flex flex-col duration-300 overflow-hidden",
);
//UPDATE COLORS AND STYLE CLASSSES
return (
<>
{type === "modal" ? (
<Modal open={open} onOpenChange={setOpen}>
{triggerChild}
<ModalContent
className={cn(minWidth, height, "flex flex-col duration-300")}
>
<div className="flex-shrink-0 truncate-doubleline word-break-break-word">
{headerChild}
</div>
<div
className={`flex w-full flex-1 flex-col transition-all duration-300`}
>
{ContentChild}
</div>
{ContentFooter && (
<div className="flex flex-shrink-0 flex-row-reverse">
{ContentFooter}
</div>
)}
</ModalContent>
<ModalContent className={contentClasses}>{modalContent}</ModalContent>
</Modal>
) : (
<Dialog open={open} onOpenChange={setOpen}>
{triggerChild}
<DialogContent
className={cn(minWidth, height, "flex flex-col duration-300")}
>
<div className="flex-shrink-0 truncate-doubleline word-break-break-word">
{headerChild}
</div>
<DialogContent className={contentClasses}>
{onSubmit ? (
<Form.Root
onSubmit={(event) => {
event.preventDefault();
onSubmit();
}}
className="flex min-h-0 flex-1 flex-col gap-6"
className="flex h-full flex-col gap-6"
>
<div
className={`flex w-full flex-1 flex-col overflow-hidden transition-all duration-300`}
>
{ContentChild}
</div>
{ContentFooter && (
<div className="flex flex-shrink-0 flex-row-reverse">
{ContentFooter}
</div>
)}
{modalContent}
</Form.Root>
) : (
<>
<div
className={`flex min-h-0 w-full flex-1 flex-col transition-all duration-300`}
>
{ContentChild}
</div>
{ContentFooter && (
<div className="flex flex-shrink-0 flex-row-reverse">
{ContentFooter}
</div>
)}
</>
modalContent
)}
</DialogContent>
</Dialog>

View file

@ -146,83 +146,75 @@ export default function CodeAreaModal({
setCode(value);
}, [value, open]);
const handlePreventEsc = (e: React.KeyboardEvent) => {
if (e.key === "Escape") {
e.preventDefault();
}
};
return (
<div onKeyDown={(e) => handlePreventEsc(e)}>
<BaseModal open={open} setOpen={setOpen}>
<BaseModal.Trigger>{children}</BaseModal.Trigger>
<BaseModal.Header description={CODE_PROMPT_DIALOG_SUBTITLE}>
<span className="pr-2"> {EDIT_CODE_TITLE} </span>
<IconComponent
name="prompts"
className="h-6 w-6 pl-1 text-primary "
aria-hidden="true"
/>
</BaseModal.Header>
<BaseModal.Content>
<Input
value={code}
readOnly
className="absolute left-[500%] top-[500%]"
id="codeValue"
/>
<div className="flex h-full w-full flex-col transition-all">
<div className="h-full w-full">
<AceEditor
readOnly={readonly}
value={code}
mode="python"
setOptions={{ fontFamily: "monospace" }}
height={height ?? "100%"}
highlightActiveLine={true}
showPrintMargin={false}
fontSize={14}
showGutter
enableLiveAutocompletion
theme={dark ? "twilight" : "github"}
name="CodeEditor"
onChange={(value) => {
setCode(value);
}}
className="h-full w-full rounded-lg border-[1px] border-gray-300 custom-scroll dark:border-gray-600"
/>
</div>
<div
className={
"whitespace-break-spaces transition-all delay-500" +
(error?.detail?.error !== undefined ? "h-2/6" : "h-0")
}
>
<div className="mt-1 h-full max-h-[10rem] w-full overflow-y-auto overflow-x-clip text-left custom-scroll">
<h1 className="text-lg text-destructive">
{error?.detail?.error}
</h1>
<div className="ml-2 w-full text-sm text-status-red word-break-break-word">
<span className="w-full word-break-break-word">
{error?.detail?.traceback}
</span>
</div>
<BaseModal open={open} setOpen={setOpen}>
<BaseModal.Trigger>{children}</BaseModal.Trigger>
<BaseModal.Header description={CODE_PROMPT_DIALOG_SUBTITLE}>
<span className="pr-2"> {EDIT_CODE_TITLE} </span>
<IconComponent
name="prompts"
className="h-6 w-6 pl-1 text-primary "
aria-hidden="true"
/>
</BaseModal.Header>
<BaseModal.Content>
<Input
value={code}
readOnly
className="absolute left-[500%] top-[500%]"
id="codeValue"
/>
<div className="flex h-full w-full flex-col transition-all">
<div className="h-full w-full">
<AceEditor
readOnly={readonly}
value={code}
mode="python"
setOptions={{ fontFamily: "monospace" }}
height={height ?? "100%"}
highlightActiveLine={true}
showPrintMargin={false}
fontSize={14}
showGutter
enableLiveAutocompletion
theme={dark ? "twilight" : "github"}
name="CodeEditor"
onChange={(value) => {
setCode(value);
}}
className="h-full w-full rounded-lg border-[1px] border-gray-300 custom-scroll dark:border-gray-600"
/>
</div>
<div
className={
"whitespace-break-spaces transition-all delay-500" +
(error?.detail?.error !== undefined ? "h-2/6" : "h-0")
}
>
<div className="mt-1 h-full max-h-[10rem] w-full overflow-y-auto overflow-x-clip text-left custom-scroll">
<h1 className="text-lg text-destructive">
{error?.detail?.error}
</h1>
<div className="ml-2 w-full text-sm text-status-red word-break-break-word">
<span className="w-full word-break-break-word">
{error?.detail?.traceback}
</span>
</div>
</div>
<div className="flex h-fit w-full justify-end">
<Button
className="mt-3"
onClick={handleClick}
type="submit"
id="checkAndSaveBtn"
disabled={readonly}
>
Check & Save
</Button>
</div>
</div>
</BaseModal.Content>
</BaseModal>
</div>
<div className="flex h-fit w-full justify-end">
<Button
className="mt-3"
onClick={handleClick}
type="submit"
id="checkAndSaveBtn"
disabled={readonly}
>
Check & Save
</Button>
</div>
</div>
</BaseModal.Content>
</BaseModal>
);
}

View file

@ -4,15 +4,15 @@ import "ace-builds/src-noconflict/mode-python";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/theme-twilight";
// import "ace-builds/webpack-resolver";
import { useEffect, useRef, useState } from "react";
import { useEffect, useState } from "react";
import JsonView from "react18-json-view";
import "react18-json-view/src/dark.css";
import "react18-json-view/src/style.css";
import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button";
import { CODE_DICT_DIALOG_SUBTITLE } from "../../constants/constants";
import { useDarkStore } from "../../stores/darkStore";
import BaseModal from "../baseModal";
import { cloneDeep } from "lodash";
export default function DictAreaModal({
children,
@ -25,14 +25,26 @@ export default function DictAreaModal({
}): JSX.Element {
const [open, setOpen] = useState(false);
const isDark = useDarkStore((state) => state.dark);
const ref = useRef(value);
const [myValue, setMyValue] = useState(value);
useEffect(() => {
if (value) ref.current = value;
}, [value]);
setMyValue(value);
}, [value, open]);
return (
<BaseModal size="medium-h-full" open={open} setOpen={setOpen}>
<BaseModal
size="medium-h-full"
open={open}
setOpen={setOpen}
onSubmit={
onChange
? () => {
onChange(myValue);
setOpen(false);
}
: undefined
}
>
<BaseModal.Trigger className="h-full">{children}</BaseModal.Trigger>
<BaseModal.Header
description={onChange ? CODE_DICT_DIALOG_SUBTITLE : null}
@ -54,31 +66,14 @@ export default function DictAreaModal({
className={!isDark ? "json-view-white" : "json-view-dark"}
editable={!!onChange}
enableClipboard
onEdit={(edit) => {
ref.current = edit["src"];
}}
onChange={(edit) => {
ref.current = edit["src"];
setMyValue(edit.src);
}}
src={ref.current}
src={cloneDeep(myValue)}
/>
{onChange && (
<div className="flex h-fit w-full justify-end">
<Button
data-testid="save-dict-button"
className="mt-3"
type="submit"
onClick={() => {
onChange(ref.current);
setOpen(false);
}}
>
Save
</Button>
</div>
)}
</div>
</BaseModal.Content>
<BaseModal.Footer submit={onChange ? { label: "Save" } : undefined} />
</BaseModal>
);
}

View file

@ -6,13 +6,14 @@ import TableToggleCellRender from "../../../components/tableComponent/components
const useColumnDefs = (
myData: any,
handleOnNewValue: (newValue: any, name: string) => void,
handleOnChangeDb: (value: boolean, key: string) => void,
changeAdvanced: (n: string) => void,
open: boolean
open: boolean,
) => {
const columnDefs: ColDef[] = useMemo(
() => [
{
headerName: "Name",
headerName: "Field Name",
field: "display_name",
valueGetter: (params) => {
const templateParam = params.data;
@ -47,12 +48,10 @@ const useColumnDefs = (
value: params.data.value,
nodeClass: myData.node,
handleOnNewValue: handleOnNewValue,
handleOnChangeDb: (value, key) => {
myData.node!.template[key].load_from_db = value;
},
handleOnChangeDb: handleOnChangeDb,
};
},
minWidth: 330,
minWidth: 340,
autoHeight: true,
flex: 1,
resizable: false,
@ -77,7 +76,7 @@ const useColumnDefs = (
cellClass: "no-border",
},
],
[open, myData]
[open, myData],
);
return columnDefs;

View file

@ -9,6 +9,7 @@ import { NodeDataType } from "../../types/flow";
import BaseModal from "../baseModal";
import useColumnDefs from "./hooks/use-column-defs";
import useRowData from "./hooks/use-row-data";
import { cloneDeep } from "lodash";
const EditNodeModal = forwardRef(
(
@ -25,9 +26,9 @@ const EditNodeModal = forwardRef(
// setOpenWDoubleClick: (open: boolean) => void;
data: NodeDataType;
},
ref
ref,
) => {
const myData = useRef(data);
const myData = useRef(cloneDeep(data));
const isDark = useDarkStore((state) => state.dark);
@ -38,8 +39,12 @@ const EditNodeModal = forwardRef(
!myData.current.node!.template[n]?.advanced;
}
const handleOnNewValue = (newValue: any, name) => {
myData.current.node!.template[name].value = newValue;
const handleOnNewValue = (newValue: any, key: string) => {
myData.current.node!.template[key].value = newValue;
};
const handleOnChangeDb = (newValue: boolean, key: string) => {
myData.current.node!.template[key].load_from_db = newValue;
};
const rowData = useRowData(data, open);
@ -47,15 +52,16 @@ const EditNodeModal = forwardRef(
const columnDefs: ColDef[] = useColumnDefs(
data,
handleOnNewValue,
handleOnChangeDb,
changeAdvanced,
open
open,
);
const [gridApi, setGridApi] = useState<GridApi | null>(null);
useEffect(() => {
if (gridApi && open) {
myData.current = data;
myData.current = cloneDeep(data);
gridApi.refreshCells();
}
}, [gridApi, open]);
@ -67,22 +73,7 @@ const EditNodeModal = forwardRef(
// }, []);
return (
<BaseModal
key={data.id}
size="medium-tall"
open={open}
setOpen={setOpen}
onSubmit={() => {
setNode(data.id, (old) => ({
...old,
data: {
...old.data,
node: myData.current.node,
},
}));
setOpen(false);
}}
>
<BaseModal key={data.id} open={open} setOpen={setOpen}>
<BaseModal.Trigger>
<></>
</BaseModal.Trigger>
@ -94,14 +85,6 @@ const EditNodeModal = forwardRef(
</BaseModal.Header>
<BaseModal.Content>
<div className="flex h-full flex-col">
<div className="flex pb-2">
<IconComponent
name="Variable"
className="edit-node-modal-variable "
/>
<span className="edit-node-modal-span">Parameters</span>
</div>
<div className="h-full">
{nodeLength > 0 && (
<TableComponent
@ -118,10 +101,24 @@ const EditNodeModal = forwardRef(
</div>
</BaseModal.Content>
<BaseModal.Footer submit={{ label: "Save Changes" }} />
<BaseModal.Footer
submit={{
label: "Save Changes",
onClick: () => {
setNode(data.id, (old) => ({
...old,
data: {
...old.data,
node: myData.current.node,
},
}));
setOpen(false);
},
}}
/>
</BaseModal>
);
}
},
);
export default EditNodeModal;

View file

@ -45,7 +45,7 @@ const ExportModal = forwardRef(
is_component: false,
},
name!,
description
description,
);
setNoticeData({
title: API_WARNING_NOTICE_ALERT,
@ -61,7 +61,7 @@ const ExportModal = forwardRef(
is_component: false,
}),
name!,
description
description,
);
setOpen(false);
}}
@ -99,9 +99,9 @@ const ExportModal = forwardRef(
</span>
</BaseModal.Content>
<BaseModal.Footer submit={{ label: "Download Flow" }} />
<BaseModal.Footer submit={{ label: "Export" }} />
</BaseModal>
);
}
},
);
export default ExportModal;

View file

@ -37,7 +37,7 @@ export default function FlowLogsModal({
const { columns, rows } = data;
setColumns(columns.map((col) => ({ ...col, editable: true })));
setRows(rows);
}
},
);
}
@ -47,7 +47,7 @@ export default function FlowLogsModal({
.some((template) => template["stream"] && template["stream"].value);
console.log(
haStream,
nodes.map((nodes) => (nodes.data as NodeDataType).node!.template)
nodes.map((nodes) => (nodes.data as NodeDataType).node!.template),
);
if (haStream) {
setNoticeData({

View file

@ -33,7 +33,7 @@ const useFolderSubmit = (setOpen, folderToEdit) => {
getFoldersApi(true);
setOpen(false);
}
}
},
);
} else {
addFolder(data).then(
@ -49,7 +49,7 @@ const useFolderSubmit = (setOpen, folderToEdit) => {
setErrorData({
title: `Error creating folder.`,
});
}
},
);
}
};

Some files were not shown because too many files have changed in this diff Show more