Merge branch 'dev' into add_cli_options_superuser
This commit is contained in:
commit
6151052b18
31 changed files with 1662 additions and 423 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import ast
|
||||
import inspect
|
||||
import textwrap
|
||||
from typing import Dict, Union
|
||||
|
||||
from langchain.agents.tools import Tool
|
||||
|
|
@ -7,7 +8,7 @@ from loguru import logger
|
|||
|
||||
|
||||
def get_func_tool_params(func, **kwargs) -> Union[Dict, None]:
|
||||
tree = ast.parse(inspect.getsource(func))
|
||||
tree = ast.parse(textwrap.dedent(inspect.getsource(func)))
|
||||
|
||||
# Iterate over the statements in the abstract syntax tree
|
||||
for node in ast.walk(tree):
|
||||
|
|
@ -58,13 +59,7 @@ def get_func_tool_params(func, **kwargs) -> Union[Dict, None]:
|
|||
|
||||
|
||||
def get_class_tool_params(cls, **kwargs) -> Union[Dict, None]:
|
||||
try:
|
||||
tree = ast.parse(inspect.getsource(cls))
|
||||
except IndentationError:
|
||||
logger.error(
|
||||
f"Error parsing class {cls.__name__}. Make sure there are no tabs in the code."
|
||||
)
|
||||
return None
|
||||
tree = ast.parse(textwrap.dedent(inspect.getsource(cls)))
|
||||
|
||||
tool_params = {}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class LangfuseInstance:
|
|||
cls._instance = Langfuse(
|
||||
public_key=settings_manager.settings.LANGFUSE_PUBLIC_KEY,
|
||||
secret_key=settings_manager.settings.LANGFUSE_SECRET_KEY,
|
||||
host=settings_manager.settings.LANGFUSE_HOST,
|
||||
)
|
||||
else:
|
||||
logger.debug("No Langfuse credentials found")
|
||||
|
|
|
|||
27
src/frontend/.github/workflows/playwright.yml
vendored
Normal file
27
src/frontend/.github/workflows/playwright.yml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
name: Playwright Tests
|
||||
on:
|
||||
push:
|
||||
branches: [ main, master ]
|
||||
pull_request:
|
||||
branches: [ main, master ]
|
||||
jobs:
|
||||
test:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
- name: Run Playwright tests
|
||||
run: npx playwright test
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
3
src/frontend/.gitignore
vendored
3
src/frontend/.gitignore
vendored
|
|
@ -21,3 +21,6 @@
|
|||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
|
|
|
|||
229
src/frontend/harFiles/langflow.har
Normal file
229
src/frontend/harFiles/langflow.har
Normal file
File diff suppressed because one or more lines are too long
60
src/frontend/package-lock.json
generated
60
src/frontend/package-lock.json
generated
|
|
@ -74,6 +74,7 @@
|
|||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.38.0",
|
||||
"@swc/cli": "^0.1.62",
|
||||
"@swc/core": "^1.3.80",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
|
|
@ -1435,6 +1436,21 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.0.tgz",
|
||||
"integrity": "sha512-xis/RXXsLxwThKnlIXouxmIvvT3zvQj1JE39GsNieMUrMpb3/GySHDh2j8itCG22qKVD4MYLBp7xB73cUW/UUw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright": "1.38.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
|
|
@ -8195,6 +8211,50 @@
|
|||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.38.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.0.tgz",
|
||||
"integrity": "sha512-fJGw+HO0YY+fU/F1N57DMO+TmXHTrmr905J05zwAQE9xkuwP/QLDk63rVhmyxh03dYnEhnRbsdbH9B0UVVRB3A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright-core": "1.38.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.38.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.0.tgz",
|
||||
"integrity": "sha512-f8z1y8J9zvmHoEhKgspmCvOExF2XdcxMW8jNRuX4vkQFrzV4MlZ55iwb5QeyiFQgOFCUolXiRHgpjSEnqvO48g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright/node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.29",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz",
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@
|
|||
},
|
||||
"proxy": "http://127.0.0.1:7860",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.38.0",
|
||||
"@swc/cli": "^0.1.62",
|
||||
"@swc/core": "^1.3.80",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
|
|
|
|||
77
src/frontend/playwright.config.ts
Normal file
77
src/frontend/playwright.config.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// require('dotenv').config();
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: "./tests",
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: "html",
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
// baseURL: 'http://127.0.0.1:3000',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: "on-first-retry",
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
|
||||
{
|
||||
name: "firefox",
|
||||
use: { ...devices["Desktop Firefox"] },
|
||||
},
|
||||
|
||||
{
|
||||
name: "webkit",
|
||||
use: { ...devices["Desktop Safari"] },
|
||||
},
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: { ...devices['Pixel 5'] },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: { ...devices['iPhone 12'] },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
// webServer: {
|
||||
// command: 'npm run start',
|
||||
// url: 'http://127.0.0.1:3000',
|
||||
// reuseExistingServer: !process.env.CI,
|
||||
// },
|
||||
});
|
||||
|
|
@ -6,6 +6,13 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.react-flow__node {
|
||||
width: auto;
|
||||
height: auto;
|
||||
border-radius: auto;
|
||||
min-width: inherit;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import KeypairListComponent from "../../../../components/keypairListComponent";
|
|||
import PromptAreaComponent from "../../../../components/promptComponent";
|
||||
import TextAreaComponent from "../../../../components/textAreaComponent";
|
||||
import ToggleShadComponent from "../../../../components/toggleShadComponent";
|
||||
import { Button } from "../../../../components/ui/button";
|
||||
import { TOOLTIP_EMPTY } from "../../../../constants/constants";
|
||||
import { TabsContext } from "../../../../contexts/tabsContext";
|
||||
import { typesContext } from "../../../../contexts/typesContext";
|
||||
|
|
@ -52,6 +53,7 @@ export default function ParameterComponent({
|
|||
required = false,
|
||||
optionalHandle = null,
|
||||
info = "",
|
||||
showNode,
|
||||
}: ParameterComponentType): JSX.Element {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const refHtml = useRef<HTMLDivElement & ReactNode>(null);
|
||||
|
|
@ -74,7 +76,9 @@ export default function ParameterComponent({
|
|||
updateNodeInternals(data.id);
|
||||
}, [data.id, position, updateNodeInternals]);
|
||||
|
||||
const { reactFlowInstance } = useContext(typesContext);
|
||||
const groupedEdge = useRef(null);
|
||||
|
||||
const { reactFlowInstance, setFilterEdge } = useContext(typesContext);
|
||||
let disabled =
|
||||
reactFlowInstance?.getEdges().some((edge) => edge.targetHandle === id) ??
|
||||
false;
|
||||
|
|
@ -122,10 +126,10 @@ export default function ParameterComponent({
|
|||
}, [info]);
|
||||
|
||||
function renderTooltips() {
|
||||
let groupedObj = groupByFamily(myData, tooltipTitle!, left, flow!);
|
||||
let groupedObj: any = groupByFamily(myData, tooltipTitle!, left, flow!);
|
||||
groupedEdge.current = groupedObj;
|
||||
|
||||
if (groupedObj && groupedObj.length > 0) {
|
||||
//@ts-ignore
|
||||
//@ts-ignore
|
||||
refHtml.current = groupedObj.map((item, index) => {
|
||||
const Icon: any =
|
||||
|
|
@ -192,7 +196,43 @@ export default function ParameterComponent({
|
|||
renderTooltips();
|
||||
}, [tooltipTitle, flow]);
|
||||
|
||||
return (
|
||||
return !showNode ? (
|
||||
left &&
|
||||
(type === "str" ||
|
||||
type === "bool" ||
|
||||
type === "float" ||
|
||||
type === "code" ||
|
||||
type === "prompt" ||
|
||||
type === "file" ||
|
||||
type === "int") &&
|
||||
!optionalHandle ? (
|
||||
<></>
|
||||
) : (
|
||||
<ShadTooltip
|
||||
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
|
||||
delayDuration={0}
|
||||
content={refHtml.current}
|
||||
side={left ? "left" : "right"}
|
||||
>
|
||||
<Handle
|
||||
type={left ? "target" : "source"}
|
||||
position={left ? Position.Left : Position.Right}
|
||||
id={id}
|
||||
isValidConnection={(connection) =>
|
||||
isValidConnection(connection, reactFlowInstance!)
|
||||
}
|
||||
className={classNames(
|
||||
left ? "my-12 -ml-0.5 " : " my-12 -mr-0.5 ",
|
||||
"h-3 w-3 rounded-full border-2 bg-background"
|
||||
)}
|
||||
style={{
|
||||
borderColor: color,
|
||||
top: position,
|
||||
}}
|
||||
></Handle>
|
||||
</ShadTooltip>
|
||||
)
|
||||
) : (
|
||||
<div
|
||||
ref={ref}
|
||||
className="mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2"
|
||||
|
|
@ -234,29 +274,36 @@ export default function ParameterComponent({
|
|||
!optionalHandle ? (
|
||||
<></>
|
||||
) : (
|
||||
<ShadTooltip
|
||||
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
|
||||
delayDuration={0}
|
||||
content={refHtml.current}
|
||||
side={left ? "left" : "right"}
|
||||
>
|
||||
<Handle
|
||||
type={left ? "target" : "source"}
|
||||
position={left ? Position.Left : Position.Right}
|
||||
id={id}
|
||||
isValidConnection={(connection) =>
|
||||
isValidConnection(connection, reactFlowInstance!)
|
||||
}
|
||||
className={classNames(
|
||||
left ? "-ml-0.5 " : "-mr-0.5 ",
|
||||
"h-3 w-3 rounded-full border-2 bg-background"
|
||||
)}
|
||||
style={{
|
||||
borderColor: color,
|
||||
top: position,
|
||||
}}
|
||||
></Handle>
|
||||
</ShadTooltip>
|
||||
<Button className="h-7 truncate bg-muted p-0 text-sm font-normal text-black hover:bg-muted">
|
||||
<div className="flex">
|
||||
<ShadTooltip
|
||||
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
|
||||
delayDuration={0}
|
||||
content={refHtml.current}
|
||||
side={left ? "left" : "right"}
|
||||
>
|
||||
<Handle
|
||||
type={left ? "target" : "source"}
|
||||
position={left ? Position.Left : Position.Right}
|
||||
id={id}
|
||||
isValidConnection={(connection) =>
|
||||
isValidConnection(connection, reactFlowInstance!)
|
||||
}
|
||||
className={classNames(
|
||||
left ? "-ml-0.5 " : "-mr-0.5 ",
|
||||
"h-3 w-3 rounded-full border-2 bg-background"
|
||||
)}
|
||||
style={{
|
||||
borderColor: color,
|
||||
top: position,
|
||||
}}
|
||||
onClick={() => {
|
||||
setFilterEdge(groupedEdge.current);
|
||||
}}
|
||||
></Handle>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{left === true &&
|
||||
|
|
|
|||
|
|
@ -25,10 +25,48 @@ export default function GenericNode({
|
|||
const [data, setData] = useState(olddata);
|
||||
const { updateFlow, flows, tabId } = useContext(TabsContext);
|
||||
const updateNodeInternals = useUpdateNodeInternals();
|
||||
const { types, deleteNode, reactFlowInstance } = useContext(typesContext);
|
||||
const { types, deleteNode, reactFlowInstance, setFilterEdge, getFilterEdge } =
|
||||
useContext(typesContext);
|
||||
const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
|
||||
const [validationStatus, setValidationStatus] =
|
||||
useState<validationStatusType | null>(null);
|
||||
const [showNode, setShowNode] = useState<boolean>(true);
|
||||
const [handles, setHandles] = useState<boolean[] | []>([]);
|
||||
let numberOfInputs: boolean[] = [];
|
||||
|
||||
function countHandles(): void {
|
||||
numberOfInputs = Object.keys(data.node!.template)
|
||||
.filter((templateField) => templateField.charAt(0) !== "_")
|
||||
.map((templateCamp) => {
|
||||
const { template } = data.node!;
|
||||
if (template[templateCamp].input_types) return true;
|
||||
if (!template[templateCamp].show) return false;
|
||||
switch (template[templateCamp].type) {
|
||||
case "str":
|
||||
return false;
|
||||
case "bool":
|
||||
return false;
|
||||
case "float":
|
||||
return false;
|
||||
case "code":
|
||||
return false;
|
||||
case "prompt":
|
||||
return false;
|
||||
case "file":
|
||||
return false;
|
||||
case "int":
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
});
|
||||
setHandles(numberOfInputs);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
countHandles();
|
||||
}, []);
|
||||
|
||||
// State for outline color
|
||||
const { sseData, isBuilding } = useSSE();
|
||||
useEffect(() => {
|
||||
|
|
@ -50,8 +88,15 @@ export default function GenericNode({
|
|||
});
|
||||
updateFlow(flow);
|
||||
}
|
||||
countHandles();
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
updateNodeInternals(data.id);
|
||||
}, 300);
|
||||
}, [showNode]);
|
||||
|
||||
// New useEffect to watch for changes in sseData and update validation status
|
||||
useEffect(() => {
|
||||
const relevantData = sseData[data.id];
|
||||
|
|
@ -70,192 +115,325 @@ export default function GenericNode({
|
|||
data={data}
|
||||
setData={setData}
|
||||
deleteNode={deleteNode}
|
||||
setShowNode={setShowNode}
|
||||
numberOfHandles={handles}
|
||||
showNode={showNode}
|
||||
></NodeToolbarComponent>
|
||||
</NodeToolbar>
|
||||
|
||||
<div
|
||||
className={classNames(
|
||||
selected ? "border border-ring" : "border",
|
||||
" transition-transform ",
|
||||
showNode
|
||||
? " w-96 scale-100 transform rounded-lg duration-500 ease-in-out "
|
||||
: " transform-width w-26 h-26 scale-90 transform rounded-full duration-500 ",
|
||||
"generic-node-div"
|
||||
)}
|
||||
>
|
||||
{data.node?.beta && (
|
||||
{data.node?.beta && showNode && (
|
||||
<div className="beta-badge-wrapper">
|
||||
<div className="beta-badge-content">BETA</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="generic-node-div-title">
|
||||
<div className="generic-node-title-arrangement">
|
||||
<IconComponent
|
||||
name={name}
|
||||
className="generic-node-icon"
|
||||
iconColor={`${nodeColors[types[data.type]]}`}
|
||||
/>
|
||||
<div className="generic-node-tooltip-div">
|
||||
<ShadTooltip content={data.node?.display_name}>
|
||||
<div className="generic-node-tooltip-div text-primary">
|
||||
{data.node?.display_name}
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className="round-button-div">
|
||||
<div>
|
||||
<Tooltip
|
||||
title={
|
||||
isBuilding ? (
|
||||
<span>Building...</span>
|
||||
) : !validationStatus ? (
|
||||
<span className="flex">
|
||||
Build{" "}
|
||||
<IconComponent
|
||||
name="Zap"
|
||||
className="mx-0.5 h-5 fill-build-trigger stroke-build-trigger stroke-1"
|
||||
/>{" "}
|
||||
flow to validate status.
|
||||
</span>
|
||||
) : (
|
||||
<div className="max-h-96 overflow-auto">
|
||||
{typeof validationStatus.params === "string"
|
||||
? validationStatus.params
|
||||
.split("\n")
|
||||
.map((line: string, index: number) => (
|
||||
<div key={index}>{line}</div>
|
||||
))
|
||||
: ""}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="generic-node-status-position">
|
||||
<div
|
||||
className={classNames(
|
||||
validationStatus && validationStatus.valid
|
||||
? "green-status"
|
||||
: "status-build-animation",
|
||||
"status-div"
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
className={classNames(
|
||||
validationStatus && !validationStatus.valid
|
||||
? "red-status"
|
||||
: "status-build-animation",
|
||||
"status-div"
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
className={classNames(
|
||||
!validationStatus || isBuilding
|
||||
? "yellow-status"
|
||||
: "status-build-animation",
|
||||
"status-div"
|
||||
)}
|
||||
></div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={
|
||||
"generic-node-desc " +
|
||||
(data.node?.description !== "" ? "py-5" : "pb-5")
|
||||
}
|
||||
>
|
||||
{data.node?.description !== "" && (
|
||||
<div className="generic-node-desc-text">
|
||||
{data.node?.description}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<>
|
||||
{Object.keys(data.node!.template)
|
||||
.filter((templateField) => templateField.charAt(0) !== "_")
|
||||
.map((templateField: string, idx) => (
|
||||
<div key={idx}>
|
||||
{data.node!.template[templateField].show &&
|
||||
!data.node!.template[templateField].advanced ? (
|
||||
<ParameterComponent
|
||||
key={
|
||||
(data.node!.template[templateField].input_types?.join(
|
||||
";"
|
||||
) ?? data.node!.template[templateField].type) +
|
||||
"|" +
|
||||
templateField +
|
||||
"|" +
|
||||
data.id
|
||||
}
|
||||
data={data}
|
||||
setData={setData}
|
||||
color={
|
||||
nodeColors[
|
||||
types[data.node?.template[templateField].type!]
|
||||
] ??
|
||||
nodeColors[data.node?.template[templateField].type!] ??
|
||||
nodeColors.unknown
|
||||
}
|
||||
title={
|
||||
data.node?.template[templateField].display_name
|
||||
? data.node.template[templateField].display_name
|
||||
: data.node?.template[templateField].name
|
||||
? toTitleCase(data.node.template[templateField].name)
|
||||
: toTitleCase(templateField)
|
||||
}
|
||||
info={data.node?.template[templateField].info}
|
||||
name={templateField}
|
||||
tooltipTitle={
|
||||
data.node?.template[templateField].input_types?.join(
|
||||
"\n"
|
||||
) ?? data.node?.template[templateField].type
|
||||
}
|
||||
required={data.node?.template[templateField].required}
|
||||
id={
|
||||
(data.node?.template[templateField].input_types?.join(
|
||||
";"
|
||||
) ?? data.node?.template[templateField].type) +
|
||||
"|" +
|
||||
templateField +
|
||||
"|" +
|
||||
data.id
|
||||
}
|
||||
left={true}
|
||||
type={data.node?.template[templateField].type}
|
||||
optionalHandle={
|
||||
data.node?.template[templateField].input_types
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div>
|
||||
<div
|
||||
className={
|
||||
"generic-node-div-title " +
|
||||
(!showNode
|
||||
? " relative h-24 w-24 rounded-full "
|
||||
: " justify-between rounded-t-lg ")
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
|
||||
"flex-max-width justify-center"
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
</div>
|
||||
<ParameterComponent
|
||||
key={[data.type, data.id, ...data.node!.base_classes].join("|")}
|
||||
data={data}
|
||||
setData={setData}
|
||||
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
|
||||
title={
|
||||
data.node?.output_types && data.node.output_types.length > 0
|
||||
? data.node.output_types.join("|")
|
||||
: data.type
|
||||
className={
|
||||
"generic-node-title-arrangement rounded-full" +
|
||||
(!showNode && "justify-center")
|
||||
}
|
||||
tooltipTitle={data.node?.base_classes.join("\n")}
|
||||
id={[data.type, data.id, ...data.node!.base_classes].join("|")}
|
||||
type={data.node?.base_classes.join("|")}
|
||||
left={false}
|
||||
/>
|
||||
</>
|
||||
>
|
||||
<IconComponent
|
||||
name={name}
|
||||
className={
|
||||
"generic-node-icon " +
|
||||
(!showNode && "absolute inset-x-6 h-12 w-12")
|
||||
}
|
||||
iconColor={`${nodeColors[types[data.type]]}`}
|
||||
/>
|
||||
{showNode && (
|
||||
<div className="generic-node-tooltip-div">
|
||||
<ShadTooltip content={data.node?.display_name}>
|
||||
<div className="generic-node-tooltip-div text-primary">
|
||||
{data.node?.display_name}
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
{!showNode && (
|
||||
<>
|
||||
{Object.keys(data.node!.template)
|
||||
.filter((templateField) => templateField.charAt(0) !== "_")
|
||||
.map(
|
||||
(templateField: string, idx) =>
|
||||
data.node!.template[templateField].show &&
|
||||
!data.node!.template[templateField].advanced && (
|
||||
<ParameterComponent
|
||||
key={
|
||||
(data.node!.template[
|
||||
templateField
|
||||
].input_types?.join(";") ??
|
||||
data.node!.template[templateField].type) +
|
||||
"|" +
|
||||
templateField +
|
||||
"|" +
|
||||
data.id
|
||||
}
|
||||
data={data}
|
||||
setData={setData}
|
||||
color={
|
||||
nodeColors[
|
||||
types[data.node?.template[templateField].type!]
|
||||
] ??
|
||||
nodeColors[
|
||||
data.node?.template[templateField].type!
|
||||
] ??
|
||||
nodeColors.unknown
|
||||
}
|
||||
title={
|
||||
data.node?.template[templateField].display_name
|
||||
? data.node.template[templateField].display_name
|
||||
: data.node?.template[templateField].name
|
||||
? toTitleCase(
|
||||
data.node.template[templateField].name
|
||||
)
|
||||
: toTitleCase(templateField)
|
||||
}
|
||||
info={data.node?.template[templateField].info}
|
||||
name={templateField}
|
||||
tooltipTitle={
|
||||
data.node?.template[
|
||||
templateField
|
||||
].input_types?.join("\n") ??
|
||||
data.node?.template[templateField].type
|
||||
}
|
||||
required={
|
||||
data.node?.template[templateField].required
|
||||
}
|
||||
id={
|
||||
(data.node?.template[
|
||||
templateField
|
||||
].input_types?.join(";") ??
|
||||
data.node?.template[templateField].type) +
|
||||
"|" +
|
||||
templateField +
|
||||
"|" +
|
||||
data.id
|
||||
}
|
||||
left={true}
|
||||
type={data.node?.template[templateField].type}
|
||||
optionalHandle={
|
||||
data.node?.template[templateField].input_types
|
||||
}
|
||||
showNode={showNode}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
<ParameterComponent
|
||||
key={[data.type, data.id, ...data.node!.base_classes].join(
|
||||
"|"
|
||||
)}
|
||||
data={data}
|
||||
setData={setData}
|
||||
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
|
||||
title={
|
||||
data.node?.output_types &&
|
||||
data.node.output_types.length > 0
|
||||
? data.node.output_types.join("|")
|
||||
: data.type
|
||||
}
|
||||
tooltipTitle={data.node?.base_classes.join("\n")}
|
||||
id={[data.type, data.id, ...data.node!.base_classes].join(
|
||||
"|"
|
||||
)}
|
||||
type={data.node?.base_classes.join("|")}
|
||||
left={false}
|
||||
showNode={showNode}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{showNode && (
|
||||
<div className="round-button-div">
|
||||
<div>
|
||||
<Tooltip
|
||||
title={
|
||||
isBuilding ? (
|
||||
<span>Building...</span>
|
||||
) : !validationStatus ? (
|
||||
<span className="flex">
|
||||
Build{" "}
|
||||
<IconComponent
|
||||
name="Zap"
|
||||
className="mx-0.5 h-5 fill-build-trigger stroke-build-trigger stroke-1"
|
||||
/>{" "}
|
||||
flow to validate status.
|
||||
</span>
|
||||
) : (
|
||||
<div className="max-h-96 overflow-auto">
|
||||
{typeof validationStatus.params === "string"
|
||||
? validationStatus.params
|
||||
.split("\n")
|
||||
.map((line: string, index: number) => (
|
||||
<div key={index}>{line}</div>
|
||||
))
|
||||
: ""}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="generic-node-status-position">
|
||||
<div
|
||||
className={classNames(
|
||||
validationStatus && validationStatus.valid
|
||||
? "green-status"
|
||||
: "status-build-animation",
|
||||
"status-div"
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
className={classNames(
|
||||
validationStatus && !validationStatus.valid
|
||||
? "red-status"
|
||||
: "status-build-animation",
|
||||
"status-div"
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
className={classNames(
|
||||
!validationStatus || isBuilding
|
||||
? "yellow-status"
|
||||
: "status-build-animation",
|
||||
"status-div"
|
||||
)}
|
||||
></div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showNode && (
|
||||
<div
|
||||
className={
|
||||
showNode
|
||||
? "generic-node-desc " +
|
||||
(data.node?.description !== "" && showNode ? "py-5" : "pb-5")
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{data.node?.description !== "" && showNode && (
|
||||
<div className="generic-node-desc-text">
|
||||
{data.node?.description}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<>
|
||||
{Object.keys(data.node!.template)
|
||||
.filter((templateField) => templateField.charAt(0) !== "_")
|
||||
.map((templateField: string, idx) => (
|
||||
<div key={idx}>
|
||||
{data.node!.template[templateField].show &&
|
||||
!data.node!.template[templateField].advanced ? (
|
||||
<ParameterComponent
|
||||
key={
|
||||
(data.node!.template[templateField].input_types?.join(
|
||||
";"
|
||||
) ?? data.node!.template[templateField].type) +
|
||||
"|" +
|
||||
templateField +
|
||||
"|" +
|
||||
data.id
|
||||
}
|
||||
data={data}
|
||||
setData={setData}
|
||||
color={
|
||||
nodeColors[
|
||||
types[data.node?.template[templateField].type!]
|
||||
] ??
|
||||
nodeColors[
|
||||
data.node?.template[templateField].type!
|
||||
] ??
|
||||
nodeColors.unknown
|
||||
}
|
||||
title={
|
||||
data.node?.template[templateField].display_name
|
||||
? data.node.template[templateField].display_name
|
||||
: data.node?.template[templateField].name
|
||||
? toTitleCase(
|
||||
data.node.template[templateField].name
|
||||
)
|
||||
: toTitleCase(templateField)
|
||||
}
|
||||
info={data.node?.template[templateField].info}
|
||||
name={templateField}
|
||||
tooltipTitle={
|
||||
data.node?.template[templateField].input_types?.join(
|
||||
"\n"
|
||||
) ?? data.node?.template[templateField].type
|
||||
}
|
||||
required={data.node?.template[templateField].required}
|
||||
id={
|
||||
(data.node?.template[templateField].input_types?.join(
|
||||
";"
|
||||
) ?? data.node?.template[templateField].type) +
|
||||
"|" +
|
||||
templateField +
|
||||
"|" +
|
||||
data.id
|
||||
}
|
||||
left={true}
|
||||
type={data.node?.template[templateField].type}
|
||||
optionalHandle={
|
||||
data.node?.template[templateField].input_types
|
||||
}
|
||||
showNode={showNode}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div
|
||||
className={classNames(
|
||||
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
|
||||
"flex-max-width justify-center"
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
</div>
|
||||
<ParameterComponent
|
||||
key={[data.type, data.id, ...data.node!.base_classes].join("|")}
|
||||
data={data}
|
||||
setData={setData}
|
||||
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
|
||||
title={
|
||||
data.node?.output_types && data.node.output_types.length > 0
|
||||
? data.node.output_types.join("|")
|
||||
: data.type
|
||||
}
|
||||
tooltipTitle={data.node?.base_classes.join("\n")}
|
||||
id={[data.type, data.id, ...data.node!.base_classes].join("|")}
|
||||
type={data.node?.base_classes.join("|")}
|
||||
left={false}
|
||||
showNode={showNode}
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ export default function FloatComponent({
|
|||
editNode = false,
|
||||
}: FloatComponentType): JSX.Element {
|
||||
const step = 0.1;
|
||||
const min = 0;
|
||||
const max = 1;
|
||||
const min = -2;
|
||||
const max = 2;
|
||||
|
||||
// Clear component state
|
||||
useEffect(() => {
|
||||
|
|
@ -27,10 +27,10 @@ export default function FloatComponent({
|
|||
step={step}
|
||||
min={min}
|
||||
onInput={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.value < min.toString()) {
|
||||
if (Number(event.target.value) < min) {
|
||||
event.target.value = min.toString();
|
||||
}
|
||||
if (event.target.value > max.toString()) {
|
||||
if (Number(event.target.value) > max) {
|
||||
event.target.value = max.toString();
|
||||
}
|
||||
}}
|
||||
|
|
@ -39,7 +39,7 @@ export default function FloatComponent({
|
|||
disabled={disabled}
|
||||
className={editNode ? "input-edit-node" : ""}
|
||||
placeholder={
|
||||
editNode ? "Number 0 to 1" : "Type a number from zero to one"
|
||||
editNode ? "Number -2 to 2" : "Type a number from minus two to two"
|
||||
}
|
||||
onChange={(event) => {
|
||||
onChange(event.target.value);
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export default function IntComponent({
|
|||
step="1"
|
||||
min={min}
|
||||
onInput={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.value < min.toString()) {
|
||||
if (Number(event.target.value) < min) {
|
||||
event.target.value = min.toString();
|
||||
}
|
||||
}}
|
||||
|
|
|
|||
108
src/frontend/src/components/ui/select-custom.tsx
Normal file
108
src/frontend/src/components/ui/select-custom.tsx
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
"use client";
|
||||
|
||||
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||
import * as React from "react";
|
||||
import { cn } from "../../utils/utils";
|
||||
|
||||
const Select = SelectPrimitive.Root;
|
||||
|
||||
const SelectGroup = SelectPrimitive.Group;
|
||||
|
||||
const SelectValue = SelectPrimitive.Value;
|
||||
|
||||
const SelectTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn("flex w-full items-center justify-between", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild></SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
));
|
||||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
||||
|
||||
const SelectContent = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||
>(({ className, children, position = "popper", ...props }, ref) => (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative z-50 min-w-[8rem] 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
|
||||
)}
|
||||
position={position}
|
||||
{...props}
|
||||
>
|
||||
<SelectPrimitive.Viewport
|
||||
className={cn(
|
||||
"p-1",
|
||||
position === "popper" &&
|
||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</SelectPrimitive.Viewport>
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
));
|
||||
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
||||
|
||||
const SelectLabel = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
||||
|
||||
const SelectItem = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Item
|
||||
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
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
));
|
||||
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
||||
|
||||
const SelectSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
};
|
||||
|
|
@ -26,6 +26,8 @@ const initialValue: typesContextType = {
|
|||
setData: () => {},
|
||||
setFetchError: () => {},
|
||||
fetchError: false,
|
||||
setFilterEdge: (filter) => {},
|
||||
getFilterEdge: [],
|
||||
};
|
||||
|
||||
export const typesContext = createContext<typesContextType>(initialValue);
|
||||
|
|
@ -39,6 +41,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
const [fetchError, setFetchError] = useState(false);
|
||||
const { setLoading } = useContext(alertContext);
|
||||
const { getAuthentication } = useContext(AuthContext);
|
||||
const [getFilterEdge, setFilterEdge] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
// If the user is authenticated, fetch the types. This code is important to check if the user is auth because of the execution order of the useEffect hooks.
|
||||
|
|
@ -113,6 +116,8 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
setData,
|
||||
fetchError,
|
||||
setFetchError,
|
||||
setFilterEdge,
|
||||
getFilterEdge,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@ function ApiInterceptor() {
|
|||
try {
|
||||
if (error?.config?.headers) {
|
||||
delete error.config.headers["Authorization"];
|
||||
error.config.headers["Authorization"] = `Bearer ${accessToken}`;
|
||||
error.config.headers["Authorization"] = `Bearer ${cookies.get(
|
||||
"access_token"
|
||||
)}`;
|
||||
const response = await axios.request(error.config);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
import CodeTabsComponent from "../../components/codeTabsComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { EXPORT_CODE_DIALOG } from "../../constants/constants";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { TemplateVariableType } from "../../types/api";
|
||||
import { tweakType, uniqueTweakType } from "../../types/components";
|
||||
|
|
@ -39,15 +40,21 @@ const ApiModal = forwardRef(
|
|||
},
|
||||
ref
|
||||
) => {
|
||||
const { autoLogin } = useContext(AuthContext);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState("0");
|
||||
const tweak = useRef<tweakType>([]);
|
||||
const tweaksList = useRef<string[]>([]);
|
||||
const { setTweak, getTweak, tabsState } = useContext(TabsContext);
|
||||
const pythonApiCode = getPythonApiCode(flow, tweak.current, tabsState);
|
||||
const curl_code = getCurlCode(flow, tweak.current, tabsState);
|
||||
const pythonApiCode = getPythonApiCode(
|
||||
flow,
|
||||
autoLogin,
|
||||
tweak.current,
|
||||
tabsState
|
||||
);
|
||||
const curl_code = getCurlCode(flow, autoLogin, tweak.current, tabsState);
|
||||
const pythonCode = getPythonCode(flow, tweak.current, tabsState);
|
||||
const widgetCode = getWidgetCode(flow, tabsState);
|
||||
const widgetCode = getWidgetCode(flow, autoLogin, tabsState);
|
||||
const tweaksCode = buildTweaks(flow);
|
||||
const codesArray = [
|
||||
curl_code,
|
||||
|
|
@ -150,10 +157,15 @@ const ApiModal = forwardRef(
|
|||
tweak.current.push(newTweak);
|
||||
}
|
||||
|
||||
const pythonApiCode = getPythonApiCode(flow, tweak.current, tabsState);
|
||||
const curl_code = getCurlCode(flow, tweak.current, tabsState);
|
||||
const pythonApiCode = getPythonApiCode(
|
||||
flow,
|
||||
autoLogin,
|
||||
tweak.current,
|
||||
tabsState
|
||||
);
|
||||
const curl_code = getCurlCode(flow, autoLogin, tweak.current, tabsState);
|
||||
const pythonCode = getPythonCode(flow, tweak.current, tabsState);
|
||||
const widgetCode = getWidgetCode(flow, tabsState);
|
||||
const widgetCode = getWidgetCode(flow, autoLogin, tabsState);
|
||||
|
||||
tabs![0].code = curl_code;
|
||||
tabs![1].code = pythonApiCode;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,5 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import {
|
||||
ReactNode,
|
||||
forwardRef,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { ReactNode, forwardRef, useContext, useEffect, useState } from "react";
|
||||
import CodeAreaComponent from "../../components/codeAreaComponent";
|
||||
import DictComponent from "../../components/dictComponent";
|
||||
import Dropdown from "../../components/dropdownComponent";
|
||||
|
|
@ -49,19 +42,22 @@ const EditNodeModal = forwardRef(
|
|||
setData,
|
||||
nodeLength,
|
||||
children,
|
||||
open,
|
||||
onClose,
|
||||
}: {
|
||||
data: NodeDataType;
|
||||
setData: (data: NodeDataType) => void;
|
||||
nodeLength: number;
|
||||
children: ReactNode;
|
||||
open?: boolean;
|
||||
onClose?: (close: boolean) => void;
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [modalOpen, setModalOpen] = useState(open ?? false);
|
||||
const [myData, setMyData] = useState(data);
|
||||
const { setTabsState, tabId } = useContext(TabsContext);
|
||||
const { reactFlowInstance } = useContext(typesContext);
|
||||
const myData = useRef(data);
|
||||
|
||||
let disabled =
|
||||
reactFlowInstance
|
||||
?.getEdges()
|
||||
|
|
@ -70,17 +66,18 @@ const EditNodeModal = forwardRef(
|
|||
function changeAdvanced(n) {
|
||||
let newData = cloneDeep(data);
|
||||
newData.node!.template[n].advanced = !newData.node!.template[n].advanced;
|
||||
myData.current = newData;
|
||||
setMyData(newData);
|
||||
}
|
||||
|
||||
const handleOnNewValue = (newValue: any, name) => {
|
||||
let newData = cloneDeep(data);
|
||||
newData.node!.template[name].value = newValue;
|
||||
myData.current = newData;
|
||||
setMyData(newData);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
myData.current = data;
|
||||
setMyData(data); // reset data to what it is on node when opening modal
|
||||
onClose!(modalOpen);
|
||||
}, [modalOpen]);
|
||||
|
||||
const [obj, setObj] = useState({
|
||||
|
|
@ -109,11 +106,11 @@ const EditNodeModal = forwardRef(
|
|||
setOpen={setModalOpen}
|
||||
onChangeOpenModal={(open) => {
|
||||
let newData = cloneDeep(data);
|
||||
myData.current = newData;
|
||||
setMyData(newData);
|
||||
}}
|
||||
>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={myData.current.node?.description!}>
|
||||
<BaseModal.Header description={myData.node?.description!}>
|
||||
<span className="pr-2">{myData.type}</span>
|
||||
<Badge variant="secondary">ID: {myData.id}</Badge>
|
||||
</BaseModal.Header>
|
||||
|
|
@ -148,65 +145,58 @@ const EditNodeModal = forwardRef(
|
|||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody className="p-0">
|
||||
{Object.keys(myData.current.node!.template)
|
||||
{Object.keys(myData.node!.template)
|
||||
.filter(
|
||||
(templateParam) =>
|
||||
templateParam.charAt(0) !== "_" &&
|
||||
myData.current.node?.template[templateParam].show &&
|
||||
(myData.current.node.template[templateParam]
|
||||
.type === "str" ||
|
||||
myData.current.node.template[templateParam]
|
||||
.type === "bool" ||
|
||||
myData.current.node.template[templateParam]
|
||||
.type === "float" ||
|
||||
myData.current.node.template[templateParam]
|
||||
.type === "code" ||
|
||||
myData.current.node.template[templateParam]
|
||||
.type === "prompt" ||
|
||||
myData.current.node.template[templateParam]
|
||||
.type === "file" ||
|
||||
myData.current.node.template[templateParam]
|
||||
.type === "int")
|
||||
myData.node?.template[templateParam].show &&
|
||||
(myData.node.template[templateParam].type ===
|
||||
"str" ||
|
||||
myData.node.template[templateParam].type ===
|
||||
"bool" ||
|
||||
myData.node.template[templateParam].type ===
|
||||
"float" ||
|
||||
myData.node.template[templateParam].type ===
|
||||
"code" ||
|
||||
myData.node.template[templateParam].type ===
|
||||
"prompt" ||
|
||||
myData.node.template[templateParam].type ===
|
||||
"file" ||
|
||||
myData.node.template[templateParam].type ===
|
||||
"int")
|
||||
)
|
||||
.map((templateParam, index) => (
|
||||
<TableRow key={index} className="h-10">
|
||||
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
|
||||
{myData.current.node?.template[templateParam].name
|
||||
? myData.current.node.template[templateParam]
|
||||
.name
|
||||
: myData.current.node?.template[templateParam]
|
||||
{myData.node?.template[templateParam].name
|
||||
? myData.node.template[templateParam].name
|
||||
: myData.node?.template[templateParam]
|
||||
.display_name}
|
||||
</TableCell>
|
||||
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
|
||||
{myData.current.node?.template[templateParam]
|
||||
.type === "str" &&
|
||||
!myData.current.node.template[templateParam]
|
||||
.options ? (
|
||||
{myData.node?.template[templateParam].type ===
|
||||
"str" &&
|
||||
!myData.node.template[templateParam].options ? (
|
||||
<div className="mx-auto">
|
||||
{myData.current.node.template[templateParam]
|
||||
.list ? (
|
||||
{myData.node.template[templateParam].list ? (
|
||||
<InputListComponent
|
||||
editNode={true}
|
||||
disabled={disabled}
|
||||
value={
|
||||
!myData.current.node.template[
|
||||
templateParam
|
||||
].value ||
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value === ""
|
||||
!myData.node.template[templateParam]
|
||||
.value ||
|
||||
myData.node.template[templateParam]
|
||||
.value === ""
|
||||
? [""]
|
||||
: myData.current.node.template[
|
||||
templateParam
|
||||
].value
|
||||
: myData.node.template[templateParam]
|
||||
.value
|
||||
}
|
||||
onChange={(value: string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
/>
|
||||
) : myData.current.node?.template[
|
||||
templateParam
|
||||
].type === "NestedDict" ? (
|
||||
) : myData.node?.template[templateParam]
|
||||
.type === "NestedDict" ? (
|
||||
<div className="mt-2 w-full">
|
||||
<DictComponent
|
||||
disabled={disabled}
|
||||
|
|
@ -217,23 +207,20 @@ const EditNodeModal = forwardRef(
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.current.node?.template[
|
||||
templateParam
|
||||
].type === "dict" ? (
|
||||
) : myData.node?.template[templateParam]
|
||||
.type === "dict" ? (
|
||||
<div className="mt-2 w-full">
|
||||
<KeypairListComponent
|
||||
disabled={disabled}
|
||||
editNode={false}
|
||||
value={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value?.length === 0 ||
|
||||
!myData.current.node.template[
|
||||
templateParam
|
||||
].value
|
||||
myData.node.template[templateParam]
|
||||
.value?.length === 0 ||
|
||||
!myData.node.template[templateParam]
|
||||
.value
|
||||
? dictArr
|
||||
: convertObjToArray(
|
||||
myData.current.node.template[
|
||||
myData.node.template[
|
||||
templateParam
|
||||
].value
|
||||
)
|
||||
|
|
@ -247,23 +234,21 @@ const EditNodeModal = forwardRef(
|
|||
setDictArr(newValue);
|
||||
} else {
|
||||
setDictArr(newValue);
|
||||
myData.current.node!.template[
|
||||
myData.node!.template[
|
||||
templateParam
|
||||
].value = newValue;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.current.node.template[
|
||||
templateParam
|
||||
].multiline ? (
|
||||
) : myData.node.template[templateParam]
|
||||
.multiline ? (
|
||||
<TextAreaComponent
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
}
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
|
|
@ -274,14 +259,12 @@ const EditNodeModal = forwardRef(
|
|||
editNode={true}
|
||||
disabled={disabled}
|
||||
password={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].password ?? false
|
||||
myData.node.template[templateParam]
|
||||
.password ?? false
|
||||
}
|
||||
value={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
}
|
||||
onChange={(value) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
|
|
@ -289,16 +272,14 @@ const EditNodeModal = forwardRef(
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "bool" ? (
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"bool" ? (
|
||||
<div className="ml-auto">
|
||||
{" "}
|
||||
<ToggleShadComponent
|
||||
disabled={disabled}
|
||||
enabled={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value
|
||||
myData.node.template[templateParam].value
|
||||
}
|
||||
setEnabled={(isEnabled) => {
|
||||
handleOnNewValue(
|
||||
|
|
@ -309,84 +290,76 @@ const EditNodeModal = forwardRef(
|
|||
size="small"
|
||||
/>
|
||||
</div>
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "float" ? (
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"float" ? (
|
||||
<div className="mx-auto">
|
||||
<FloatComponent
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
}
|
||||
onChange={(value) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "str" &&
|
||||
myData.current.node.template[templateParam]
|
||||
.options ? (
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"str" &&
|
||||
myData.node.template[templateParam].options ? (
|
||||
<div className="mx-auto">
|
||||
<Dropdown
|
||||
numberOfOptions={nodeLength}
|
||||
editNode={true}
|
||||
options={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].options
|
||||
myData.node.template[templateParam]
|
||||
.options
|
||||
}
|
||||
onSelect={(value) =>
|
||||
handleOnNewValue(value, templateParam)
|
||||
}
|
||||
value={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? "Choose an option"
|
||||
myData.node.template[templateParam]
|
||||
.value ?? "Choose an option"
|
||||
}
|
||||
></Dropdown>
|
||||
</div>
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "int" ? (
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"int" ? (
|
||||
<div className="mx-auto">
|
||||
<IntComponent
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
}
|
||||
onChange={(value) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "file" ? (
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"file" ? (
|
||||
<div className="mx-auto">
|
||||
<InputFileComponent
|
||||
editNode={true}
|
||||
disabled={disabled}
|
||||
value={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
}
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
fileTypes={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].fileTypes
|
||||
myData.node.template[templateParam]
|
||||
.fileTypes
|
||||
}
|
||||
suffixes={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].suffixes
|
||||
myData.node.template[templateParam]
|
||||
.suffixes
|
||||
}
|
||||
onFileChange={(filePath: string) => {
|
||||
data.node!.template[
|
||||
|
|
@ -395,29 +368,28 @@ const EditNodeModal = forwardRef(
|
|||
}}
|
||||
></InputFileComponent>
|
||||
</div>
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "prompt" ? (
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"prompt" ? (
|
||||
<div className="mx-auto">
|
||||
<PromptAreaComponent
|
||||
field_name={templateParam}
|
||||
editNode={true}
|
||||
disabled={disabled}
|
||||
nodeClass={myData.current.node}
|
||||
nodeClass={myData.node}
|
||||
setNodeClass={(nodeClass) => {
|
||||
myData.current.node = nodeClass;
|
||||
myData.node = nodeClass;
|
||||
}}
|
||||
value={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
}
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "code" ? (
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"code" ? (
|
||||
<div className="mx-auto">
|
||||
<CodeAreaComponent
|
||||
dynamic={
|
||||
|
|
@ -431,17 +403,16 @@ const EditNodeModal = forwardRef(
|
|||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
}
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "Any" ? (
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"Any" ? (
|
||||
"-"
|
||||
) : (
|
||||
<div className="hidden"></div>
|
||||
|
|
@ -451,9 +422,8 @@ const EditNodeModal = forwardRef(
|
|||
<div className="items-center text-center">
|
||||
<ToggleShadComponent
|
||||
enabled={
|
||||
!myData.current.node?.template[
|
||||
templateParam
|
||||
].advanced
|
||||
!myData.node?.template[templateParam]
|
||||
.advanced
|
||||
}
|
||||
setEnabled={(e) =>
|
||||
changeAdvanced(templateParam)
|
||||
|
|
|
|||
|
|
@ -64,8 +64,13 @@ export default function Page({
|
|||
setTabsState,
|
||||
tabId,
|
||||
} = useContext(TabsContext);
|
||||
const { types, reactFlowInstance, setReactFlowInstance, templates } =
|
||||
useContext(typesContext);
|
||||
const {
|
||||
types,
|
||||
reactFlowInstance,
|
||||
setReactFlowInstance,
|
||||
templates,
|
||||
setFilterEdge,
|
||||
} = useContext(typesContext);
|
||||
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { takeSnapshot } = useContext(undoRedoContext);
|
||||
|
|
@ -382,6 +387,10 @@ export default function Page({
|
|||
[]
|
||||
);
|
||||
|
||||
const onPaneClick = useCallback((flow) => {
|
||||
setFilterEdge([]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex h-full overflow-hidden">
|
||||
{!view && <ExtraSidebar />}
|
||||
|
|
@ -429,6 +438,7 @@ export default function Page({
|
|||
zoomOnPinch={!view}
|
||||
panOnDrag={!view}
|
||||
proOptions={{ hideAttribution: true }}
|
||||
onPaneClick={onPaneClick}
|
||||
>
|
||||
<Background className="" />
|
||||
{!view && (
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
|
|
@ -18,7 +19,8 @@ import { classNames } from "../../../../utils/utils";
|
|||
import DisclosureComponent from "../DisclosureComponent";
|
||||
|
||||
export default function ExtraSidebar(): JSX.Element {
|
||||
const { data, templates } = useContext(typesContext);
|
||||
const { data, templates, getFilterEdge, setFilterEdge } =
|
||||
useContext(typesContext);
|
||||
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } =
|
||||
useContext(TabsContext);
|
||||
const { setSuccessData, setErrorData } = useContext(alertContext);
|
||||
|
|
@ -42,6 +44,10 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
|
||||
// Handle showing components after use search input
|
||||
function handleSearchInput(e: string) {
|
||||
if (e === "") {
|
||||
setFilterData(data);
|
||||
return;
|
||||
}
|
||||
setFilterData((_) => {
|
||||
let ret = {};
|
||||
Object.keys(data).forEach((d: keyof APIObjectType, i) => {
|
||||
|
|
@ -69,6 +75,57 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
setErrorData({ title: " Components with errors: ", list: errors });
|
||||
}, []);
|
||||
|
||||
function handleBlur() {
|
||||
setFilterData(data);
|
||||
setFilterEdge([]);
|
||||
setSearch("");
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (getFilterEdge.length === 0 && search === "") {
|
||||
setFilterData(data);
|
||||
setFilterEdge([]);
|
||||
setSearch("");
|
||||
}
|
||||
}, [getFilterEdge]);
|
||||
|
||||
useEffect(() => {
|
||||
if (getFilterEdge?.length > 0) {
|
||||
setFilterData((_) => {
|
||||
let dataClone = cloneDeep(data);
|
||||
let ret = {};
|
||||
Object.keys(dataClone).forEach((d: keyof APIObjectType, i) => {
|
||||
ret[d] = {};
|
||||
if (getFilterEdge.some((x) => x.family === d)) {
|
||||
ret[d] = dataClone[d];
|
||||
|
||||
const filtered = getFilterEdge
|
||||
.filter((x) => x.family === d)
|
||||
.pop()
|
||||
.type.split(",");
|
||||
|
||||
for (let i = 0; i < filtered.length; i++) {
|
||||
filtered[i] = filtered[i].trimStart();
|
||||
}
|
||||
|
||||
if (filtered.some((x) => x !== "")) {
|
||||
let keys = Object.keys(dataClone[d]).filter((nd) =>
|
||||
filtered.includes(nd)
|
||||
);
|
||||
Object.keys(dataClone[d]).forEach((element) => {
|
||||
if (!keys.includes(element)) {
|
||||
delete ret[d][element];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
setSearch("search");
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
}, [getFilterEdge]);
|
||||
|
||||
return (
|
||||
<div className="side-bar-arrangement">
|
||||
<div className="side-bar-buttons-arrangement">
|
||||
|
|
@ -141,6 +198,7 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
<Separator />
|
||||
<div className="side-bar-search-div-placement">
|
||||
<Input
|
||||
onFocusCapture={() => handleBlur()}
|
||||
type="text"
|
||||
name="search"
|
||||
id="search"
|
||||
|
|
|
|||
|
|
@ -1,16 +1,25 @@
|
|||
import { useContext, useState } from "react";
|
||||
import { useReactFlow } from "reactflow";
|
||||
import { useReactFlow, useUpdateNodeInternals } from "reactflow";
|
||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
} from "../../../../components/ui/select-custom";
|
||||
import { TabsContext } from "../../../../contexts/tabsContext";
|
||||
import EditNodeModal from "../../../../modals/EditNodeModal";
|
||||
import { nodeToolbarPropsType } from "../../../../types/components";
|
||||
import { classNames } from "../../../../utils/utils";
|
||||
import { classNames, getRandomKeyByssmm } from "../../../../utils/utils";
|
||||
|
||||
export default function NodeToolbarComponent({
|
||||
data,
|
||||
setData,
|
||||
deleteNode,
|
||||
setShowNode,
|
||||
numberOfHandles,
|
||||
showNode,
|
||||
}: nodeToolbarPropsType): JSX.Element {
|
||||
const [nodeLength, setNodeLength] = useState(
|
||||
Object.keys(data.node!.template).filter(
|
||||
|
|
@ -27,9 +36,34 @@ export default function NodeToolbarComponent({
|
|||
data.node.template[templateField].type === "int")
|
||||
).length
|
||||
);
|
||||
const updateNodeInternals = useUpdateNodeInternals();
|
||||
|
||||
function canMinimize() {
|
||||
let countHandles: number = 0;
|
||||
numberOfHandles.forEach((bool) => {
|
||||
if (bool) countHandles += 1;
|
||||
});
|
||||
if (countHandles > 1) return false;
|
||||
return true;
|
||||
}
|
||||
const isMinimal = canMinimize();
|
||||
const { paste } = useContext(TabsContext);
|
||||
const reactFlowInstance = useReactFlow();
|
||||
const [showModalAdvanced, setShowModalAdvanced] = useState(false);
|
||||
const [selectedValue, setSelectedValue] = useState("");
|
||||
|
||||
const handleSelectChange = (event) => {
|
||||
setSelectedValue(event);
|
||||
if (event.includes("advanced")) {
|
||||
return setShowModalAdvanced(true);
|
||||
}
|
||||
setShowModalAdvanced(false);
|
||||
if (event.includes("show")) {
|
||||
setShowNode((prev) => !prev);
|
||||
updateNodeInternals(data.id);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-26 h-10">
|
||||
|
|
@ -97,6 +131,83 @@ export default function NodeToolbarComponent({
|
|||
</a>
|
||||
</ShadTooltip>
|
||||
|
||||
{isMinimal ? (
|
||||
<Select onValueChange={handleSelectChange} value={selectedValue}>
|
||||
<ShadTooltip content="More" side="top">
|
||||
<SelectTrigger>
|
||||
<div>
|
||||
<div
|
||||
className={classNames(
|
||||
"relative -ml-px inline-flex h-8 w-[31px] items-center rounded-r-md bg-background text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" +
|
||||
(nodeLength == 0
|
||||
? " text-muted-foreground"
|
||||
: " text-foreground")
|
||||
)}
|
||||
>
|
||||
<IconComponent
|
||||
name="MoreHorizontal"
|
||||
className="relative left-2 h-4 w-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</SelectTrigger>
|
||||
</ShadTooltip>
|
||||
<SelectContent>
|
||||
<SelectItem value={getRandomKeyByssmm() + "advanced"}>
|
||||
<div className="flex">
|
||||
<IconComponent
|
||||
name="Settings2"
|
||||
className="relative top-0.5 mr-2 h-4 w-4"
|
||||
/>{" "}
|
||||
Edit{" "}
|
||||
</div>{" "}
|
||||
</SelectItem>
|
||||
{isMinimal && (
|
||||
<SelectItem value={getRandomKeyByssmm() + "show"}>
|
||||
<div className="flex">
|
||||
<IconComponent
|
||||
name={showNode ? "Minimize2" : "Maximize2"}
|
||||
className="relative top-0.5 mr-2 h-4 w-4"
|
||||
/>
|
||||
{showNode ? "Minimize" : "Expand"}
|
||||
</div>
|
||||
</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : (
|
||||
<ShadTooltip content="Edit" side="top">
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setShowModalAdvanced(true)}
|
||||
className={classNames(
|
||||
"relative -ml-px inline-flex items-center rounded-r-md bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" +
|
||||
(nodeLength == 0
|
||||
? " text-muted-foreground"
|
||||
: " text-foreground")
|
||||
)}
|
||||
>
|
||||
<IconComponent name="Settings2" className="h-4 w-4 " />
|
||||
</button>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
|
||||
{showModalAdvanced && (
|
||||
<EditNodeModal
|
||||
data={data}
|
||||
setData={setData}
|
||||
nodeLength={nodeLength}
|
||||
open={showModalAdvanced}
|
||||
onClose={(modal) => {
|
||||
setShowModalAdvanced(modal);
|
||||
}}
|
||||
>
|
||||
<></>
|
||||
</EditNodeModal>
|
||||
)}
|
||||
|
||||
{/*
|
||||
<ShadTooltip content="Edit" side="top">
|
||||
<div>
|
||||
<EditNodeModal
|
||||
|
|
@ -106,7 +217,8 @@ export default function NodeToolbarComponent({
|
|||
>
|
||||
<div
|
||||
className={classNames(
|
||||
"relative -ml-px inline-flex items-center rounded-r-md bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" +
|
||||
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10" +
|
||||
(!canMinimize() && " rounded-r-md ") +
|
||||
(nodeLength == 0
|
||||
? " text-muted-foreground"
|
||||
: " text-foreground")
|
||||
|
|
@ -116,7 +228,7 @@ export default function NodeToolbarComponent({
|
|||
</div>
|
||||
</EditNodeModal>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
</ShadTooltip> */}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import * as Form from "@radix-ui/react-form";
|
||||
import { FormEvent, useContext, useState } from "react";
|
||||
import { FormEvent, useContext, useEffect, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import InputComponent from "../../components/inputComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
|
|
@ -20,6 +20,8 @@ export default function SignUp(): JSX.Element {
|
|||
const [inputState, setInputState] =
|
||||
useState<signUpInputStateType>(CONTROL_INPUT_STATE);
|
||||
|
||||
const [isDisabled, setDisableBtn] = useState<boolean>(true);
|
||||
|
||||
const { password, cnfPassword, username } = inputState;
|
||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
||||
const navigate = useNavigate();
|
||||
|
|
@ -30,6 +32,13 @@ export default function SignUp(): JSX.Element {
|
|||
setInputState((prev) => ({ ...prev, [name]: value }));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (password !== cnfPassword) return setDisableBtn(true);
|
||||
if (password === "" || cnfPassword === "") return setDisableBtn(true);
|
||||
if (username === "") return setDisableBtn(true);
|
||||
setDisableBtn(false);
|
||||
}, [password, cnfPassword, username, handleInput]);
|
||||
|
||||
function handleSignup(): void {
|
||||
const { username, password } = inputState;
|
||||
const newUser: UserInputType = {
|
||||
|
|
@ -158,6 +167,7 @@ export default function SignUp(): JSX.Element {
|
|||
<div className="w-full">
|
||||
<Form.Submit asChild>
|
||||
<Button
|
||||
disabled={isDisabled}
|
||||
type="submit"
|
||||
className="mr-3 mt-6 w-full"
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -264,10 +264,10 @@
|
|||
@apply grid w-full gap-4 p-4 md:grid-cols-2 lg:grid-cols-4;
|
||||
}
|
||||
.generic-node-div {
|
||||
@apply relative flex w-96 flex-col justify-center rounded-lg bg-background;
|
||||
@apply relative flex flex-col justify-center bg-background;
|
||||
}
|
||||
.generic-node-div-title {
|
||||
@apply flex w-full items-center justify-between gap-8 rounded-t-lg border-b bg-muted p-4;
|
||||
@apply flex w-full items-center gap-8 border-b bg-muted p-4;
|
||||
}
|
||||
.generic-node-title-arrangement {
|
||||
@apply flex-max-width items-center truncate;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ export type ParameterComponentType = {
|
|||
dataContext?: typesContextType;
|
||||
optionalHandle?: Array<String> | null;
|
||||
info?: string;
|
||||
showNode?: boolean;
|
||||
};
|
||||
export type InputListComponentType = {
|
||||
value: string[];
|
||||
|
|
@ -198,6 +199,7 @@ export type IconComponentProps = {
|
|||
name: string;
|
||||
className?: string;
|
||||
iconColor?: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export type InputProps = {
|
||||
|
|
@ -422,6 +424,9 @@ export type nodeToolbarPropsType = {
|
|||
data: NodeDataType;
|
||||
deleteNode: (idx: string) => void;
|
||||
setData: (newState: NodeDataType) => void;
|
||||
setShowNode: (boolean: any) => void;
|
||||
numberOfHandles: boolean[] | [];
|
||||
showNode: boolean;
|
||||
};
|
||||
|
||||
export type parsedDataType = {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ export type typesContextType = {
|
|||
setData: (newState: {}) => void;
|
||||
fetchError: boolean;
|
||||
setFetchError: (newState: boolean) => void;
|
||||
setFilterEdge: (newState) => void;
|
||||
getFilterEdge: any[];
|
||||
};
|
||||
|
||||
export type alertContextType = {
|
||||
|
|
|
|||
|
|
@ -45,10 +45,13 @@ import {
|
|||
Link,
|
||||
Lock,
|
||||
LucideSend,
|
||||
Maximize2,
|
||||
Menu,
|
||||
MessageCircle,
|
||||
MessageSquare,
|
||||
MessagesSquare,
|
||||
Minimize2,
|
||||
Minus,
|
||||
MoonIcon,
|
||||
MoreHorizontal,
|
||||
Paperclip,
|
||||
|
|
@ -62,6 +65,7 @@ import {
|
|||
Settings2,
|
||||
Shield,
|
||||
Sparkles,
|
||||
Square,
|
||||
SunIcon,
|
||||
TerminalSquare,
|
||||
Trash2,
|
||||
|
|
@ -310,4 +314,8 @@ export const nodeIconsLucide: iconsType = {
|
|||
Unplug,
|
||||
BookMarked,
|
||||
ChevronUp,
|
||||
Minus,
|
||||
Square,
|
||||
Minimize2,
|
||||
Maximize2,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -299,6 +299,7 @@ export function getChatInputField(flow: FlowType, tabsState?: TabsState) {
|
|||
*/
|
||||
export function getPythonApiCode(
|
||||
flow: FlowType,
|
||||
isAuth: boolean,
|
||||
tweak?: any[],
|
||||
tabsState?: TabsState
|
||||
): string {
|
||||
|
|
@ -325,7 +326,9 @@ TWEAKS = ${
|
|||
: JSON.stringify(tweaks, null, 2)
|
||||
}
|
||||
|
||||
def run_flow(inputs: dict, flow_id: str, tweaks: Optional[dict] = None) -> dict:
|
||||
def run_flow(inputs: dict, flow_id: str, tweaks: Optional[dict] = None${
|
||||
!isAuth ? `, apiKey: str=""` : ""
|
||||
}) -> dict:
|
||||
"""
|
||||
Run a flow with a given message and optional tweaks.
|
||||
|
||||
|
|
@ -340,13 +343,16 @@ def run_flow(inputs: dict, flow_id: str, tweaks: Optional[dict] = None) -> dict:
|
|||
|
||||
if tweaks:
|
||||
payload["tweaks"] = tweaks
|
||||
|
||||
response = requests.post(api_url, json=payload)
|
||||
${!isAuth ? 'headers = {"api-key": apiKey}' : ""}
|
||||
response = requests.post(api_url, json=payload,headers=headers)
|
||||
return response.json()
|
||||
|
||||
# Setup any tweaks you want to apply to the flow
|
||||
inputs = ${inputs}
|
||||
print(run_flow(inputs, flow_id=FLOW_ID, tweaks=TWEAKS))`;
|
||||
${!isAuth ? `api_key = "<your api key>"` : ""}
|
||||
print(run_flow(inputs, flow_id=FLOW_ID, tweaks=TWEAKS${
|
||||
!isAuth ? `, apiKey=api_key` : ""
|
||||
}))`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -356,6 +362,7 @@ print(run_flow(inputs, flow_id=FLOW_ID, tweaks=TWEAKS))`;
|
|||
*/
|
||||
export function getCurlCode(
|
||||
flow: FlowType,
|
||||
isAuth: boolean,
|
||||
tweak?: any[],
|
||||
tabsState?: TabsState
|
||||
): string {
|
||||
|
|
@ -367,7 +374,8 @@ export function getCurlCode(
|
|||
${window.location.protocol}//${
|
||||
window.location.host
|
||||
}/api/v1/process/${flowId} \\
|
||||
-H 'Content-Type: application/json' \\
|
||||
-H 'Content-Type: application/json'\\
|
||||
${!isAuth ? `-H 'api-key: <your api key>'\\` : ""}
|
||||
-d '{"inputs": ${inputs}, "tweaks": ${
|
||||
tweak && tweak.length > 0
|
||||
? buildTweakObject(tweak)
|
||||
|
|
@ -405,7 +413,11 @@ flow(inputs)`;
|
|||
* @param {string} flow - The current flow.
|
||||
* @returns {string} - The widget code
|
||||
*/
|
||||
export function getWidgetCode(flow: FlowType, tabsState?: TabsState): string {
|
||||
export function getWidgetCode(
|
||||
flow: FlowType,
|
||||
isAuth: boolean,
|
||||
tabsState?: TabsState
|
||||
): string {
|
||||
const flowId = flow.id;
|
||||
const flowName = flow.name;
|
||||
const inputs = buildInputs(tabsState!, flow.id);
|
||||
|
|
@ -425,7 +437,13 @@ chat_input_field: Input key that you want the chat to send the user message with
|
|||
chat_input_field="${chat_input_field}"
|
||||
`
|
||||
: ""
|
||||
}host_url="http://localhost:7860"
|
||||
}host_url="http://localhost:7860"${
|
||||
!isAuth
|
||||
? `
|
||||
api_key="..."`
|
||||
: ""
|
||||
}
|
||||
|
||||
></langflow-chat>`;
|
||||
}
|
||||
|
||||
|
|
|
|||
32
src/frontend/tests/auto_login.spec.ts
Normal file
32
src/frontend/tests/auto_login.spec.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test.describe("Auto_login tests", () => {
|
||||
test("auto_login sign in", async ({ page }) => {
|
||||
await page.routeFromHAR("harFiles/langflow.har", {
|
||||
url: "**/api/v1/**",
|
||||
update: false,
|
||||
});
|
||||
await page.goto("http:localhost:3000/");
|
||||
await page.getByRole("button", { name: "Community Examples" }).click();
|
||||
await page.waitForSelector(".community-pages-flows-panel");
|
||||
expect(
|
||||
await page
|
||||
.locator(".community-pages-flows-panel")
|
||||
.evaluate((el) => el.children)
|
||||
).toBeTruthy();
|
||||
});
|
||||
test("auto_login block_admin", async ({ page }) => {
|
||||
await page.routeFromHAR("harFiles/langflow.har", {
|
||||
url: "**/api/v1/**",
|
||||
update: false,
|
||||
});
|
||||
await page.goto("http:localhost:3000/");
|
||||
await page.getByRole("button", { name: "Community Examples" }).click();
|
||||
await page.goto("http:localhost:3000/login");
|
||||
await page.getByRole("button", { name: "Community Examples" }).click();
|
||||
await page.goto("http:localhost:3000/admin");
|
||||
await page.getByRole("button", { name: "Community Examples" }).click();
|
||||
await page.goto("http:localhost:3000/admin/login");
|
||||
await page.getByRole("button", { name: "Community Examples" }).click();
|
||||
});
|
||||
});
|
||||
128
src/frontend/tests/login.spec.ts
Normal file
128
src/frontend/tests/login.spec.ts
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test.describe("Login Tests", () => {
|
||||
test("Login_Success", async ({ page }) => {
|
||||
await page.route("**/api/v1/login", async (route) => {
|
||||
const json = {
|
||||
access_token:
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhMWNlM2FkOS1iZTE2LTRiNjgtOGRhYi1hYjA4YTVjMmZjZTkiLCJleHAiOjE2OTUyNTIwNTh9.MBYFwMhTcZnsW_L7p4qavUhSDylCllJQWUCJdU1wX8o",
|
||||
refresh_token:
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhMWNlM2FkOS1iZTE2LTRiNjgtOGRhYi1hYjA4YTVjMmZjZTkiLCJ0eXBlIjoicmYiLCJleHAiOjE2OTUyNTI2NTh9.a4wL9-XK_zyTyrXduBFgCsODFXrqiByVr5HOeiCbiQA",
|
||||
token_type: "bearer",
|
||||
};
|
||||
await route.fulfill({ json });
|
||||
});
|
||||
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.waitForURL("http://localhost:3000/login");
|
||||
await page.waitForURL("http://localhost:3000/login", { timeout: 100 });
|
||||
await page.getByPlaceholder("Username").click();
|
||||
await page.getByPlaceholder("Username").fill("test");
|
||||
await page.getByPlaceholder("Password").click();
|
||||
await page.getByPlaceholder("Password").fill("test");
|
||||
await page.getByRole("button", { name: "Sign in" }).click();
|
||||
await page.getByRole("button", { name: "Community Examples" }).click();
|
||||
await page.waitForSelector(".community-pages-flows-panel");
|
||||
expect(
|
||||
await page
|
||||
.locator(".community-pages-flows-panel")
|
||||
.evaluate((el) => el.children)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test("Login Error", async ({ page }) => {
|
||||
await page.route("**/api/v1/login", async (route) => {
|
||||
const json = { detail: "Incorrect username or password" };
|
||||
await route.fulfill({ json, status: 401 });
|
||||
});
|
||||
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.waitForURL("http://localhost:3000/login");
|
||||
await page.waitForURL("http://localhost:3000/login", { timeout: 100 });
|
||||
await page.getByPlaceholder("Username").click();
|
||||
await page.getByPlaceholder("Username").fill("test");
|
||||
await page.getByPlaceholder("Password").click();
|
||||
await page.getByPlaceholder("Password").fill("test5");
|
||||
await page.getByRole("button", { name: "Sign in" }).click();
|
||||
await page.getByRole("heading", { name: "Error signing in" }).click();
|
||||
});
|
||||
|
||||
test("Login create account wrong form", async ({ page }) => {
|
||||
const fullfillForm = async (username, password, confirmPassword) => {
|
||||
await page.getByPlaceholder("Username").click();
|
||||
await page.getByPlaceholder("Username").fill(username);
|
||||
await page.getByPlaceholder("Password", { exact: true }).click();
|
||||
await page.getByPlaceholder("Password", { exact: true }).fill(password);
|
||||
await page.getByPlaceholder("Confirm your password").click();
|
||||
await page
|
||||
.getByPlaceholder("Confirm your password")
|
||||
.fill(confirmPassword);
|
||||
};
|
||||
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.waitForURL("http://localhost:3000/login");
|
||||
await page.waitForURL("http://localhost:3000/login", { timeout: 100 });
|
||||
await page
|
||||
.getByRole("button", { name: "Don't have an account? Sign Up" })
|
||||
.click();
|
||||
await page.getByText("Sign up to Langflow").click();
|
||||
await page.goto("http://localhost:3000/signup");
|
||||
await page.getByText("Sign up to Langflow").click();
|
||||
|
||||
await fullfillForm("name", "vazz", "vazz5");
|
||||
expect(
|
||||
await page.getByRole("button", { name: "Sign up" }).isDisabled()
|
||||
).toBeTruthy();
|
||||
await fullfillForm("", "vazz", "vazz");
|
||||
expect(
|
||||
await page.getByRole("button", { name: "Sign up" }).isDisabled()
|
||||
).toBeTruthy();
|
||||
await fullfillForm("name", "", "");
|
||||
expect(
|
||||
await page.getByRole("button", { name: "Sign up" }).isDisabled()
|
||||
).toBeTruthy();
|
||||
await fullfillForm("", "", "");
|
||||
expect(
|
||||
await page.getByRole("button", { name: "Sign up" }).isDisabled()
|
||||
).toBeTruthy();
|
||||
});
|
||||
test("Login create account success", async ({ page }) => {
|
||||
await page.route("**/api/v1/users/", async (route) => {
|
||||
const json = {
|
||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
||||
username: "teste",
|
||||
profile_image: null,
|
||||
is_active: false,
|
||||
is_superuser: false,
|
||||
create_at: "2023-09-21T01:45:51.873303",
|
||||
updated_at: "2023-09-21T01:45:51.873305",
|
||||
last_login_at: null,
|
||||
};
|
||||
await route.fulfill({ json, status: 201 });
|
||||
});
|
||||
const submitForm = async (username, password, confirmPassword) => {
|
||||
await page.getByPlaceholder("Username").click();
|
||||
await page.getByPlaceholder("Username").fill(username);
|
||||
await page.getByPlaceholder("Password", { exact: true }).click();
|
||||
await page.getByPlaceholder("Password", { exact: true }).fill(password);
|
||||
await page.getByPlaceholder("Confirm your password").click();
|
||||
await page
|
||||
.getByPlaceholder("Confirm your password")
|
||||
.fill(confirmPassword);
|
||||
};
|
||||
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.waitForURL("http://localhost:3000/login");
|
||||
await page.waitForURL("http://localhost:3000/login", { timeout: 100 });
|
||||
await page
|
||||
.getByRole("button", { name: "Don't have an account? Sign Up" })
|
||||
.click();
|
||||
await page.getByText("Sign up to Langflow").click();
|
||||
await page.goto("http://localhost:3000/signup");
|
||||
await page.getByText("Sign up to Langflow").click();
|
||||
await submitForm("teste", "pass", "pass");
|
||||
await page.getByRole("button", { name: "Sign up" }).click();
|
||||
await page.waitForURL("http://localhost:3000/login", { timeout: 1000 });
|
||||
await page.getByText("Account created! Await admin activation.").click();
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue