Merge remote-tracking branch 'origin/dev' into NGNMergeDev
This commit is contained in:
commit
88d91c48d8
274 changed files with 11682 additions and 3913 deletions
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/
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
FROM node:14-alpine as frontend_build
|
||||
ARG BACKEND
|
||||
FROM node:20-alpine as frontend_build
|
||||
ARG BACKEND_URL
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
|
||||
COPY ./package.json ./package-lock.json ./tsconfig.json ./vite.config.ts ./index.html ./tailwind.config.js ./postcss.config.js ./prettier.config.js /app/
|
||||
RUN npm install
|
||||
COPY ./src /app/src
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx
|
||||
COPY --from=frontend_build /app/build/ /usr/share/nginx/html
|
||||
COPY /nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY /nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY start-nginx.sh /start-nginx.sh
|
||||
RUN chmod +x /start-nginx.sh
|
||||
ENV BACKEND_URL=$BACKEND_URL
|
||||
CMD ["/start-nginx.sh"]
|
||||
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
|
|
@ -1,18 +1,20 @@
|
|||
server {
|
||||
gzip on;
|
||||
gzip_comp_level 2;
|
||||
gzip_min_length 1000;
|
||||
gzip_types text/xml text/css;
|
||||
gzip_http_version 1.1;
|
||||
gzip_vary on;
|
||||
gzip_disable "MSIE [4-6] \.";
|
||||
gzip on;
|
||||
gzip_comp_level 2;
|
||||
gzip_min_length 1000;
|
||||
gzip_types text/xml text/css;
|
||||
gzip_http_version 1.1;
|
||||
gzip_vary on;
|
||||
gzip_disable "MSIE [4-6] \.";
|
||||
|
||||
listen 80;
|
||||
listen 80;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
|
||||
|
||||
include /etc/nginx/extra-conf.d/*.conf;
|
||||
}
|
||||
|
|
|
|||
528
src/frontend/package-lock.json
generated
528
src/frontend/package-lock.json
generated
|
|
@ -58,7 +58,8 @@
|
|||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-tabs": "^6.0.2",
|
||||
"react-tooltip": "^5.21.1",
|
||||
"reactflow": "^11.8.3",
|
||||
"react18-json-view": "^0.2.3",
|
||||
"reactflow": "^11.9.2",
|
||||
"rehype-mathjax": "^4.0.3",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
|
|
@ -73,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",
|
||||
|
|
@ -1163,14 +1165,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@mui/base": {
|
||||
"version": "5.0.0-beta.18",
|
||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.18.tgz",
|
||||
"integrity": "sha512-e9ZCy/ndhyt5MTshAS3qAUy/40UiO0jX+kAo6a+XirrPJE+rrQW+mKPSI0uyp+5z4Vh+z0pvNoJ2S2gSrNz3BQ==",
|
||||
"version": "5.0.0-beta.17",
|
||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.17.tgz",
|
||||
"integrity": "sha512-xNbk7iOXrglNdIxFBN0k3ySsPIFLWCnFxqsAYl7CIcDkD9low4kJ7IUuy6ctwx/HAy2fenrT3KXHr1sGjAMgpQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.1",
|
||||
"@babel/runtime": "^7.22.15",
|
||||
"@floating-ui/react-dom": "^2.0.2",
|
||||
"@mui/types": "^7.2.5",
|
||||
"@mui/utils": "^5.14.12",
|
||||
"@mui/types": "^7.2.4",
|
||||
"@mui/utils": "^5.14.11",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"clsx": "^2.0.0",
|
||||
"prop-types": "^15.8.1"
|
||||
|
|
@ -1202,25 +1204,25 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@mui/core-downloads-tracker": {
|
||||
"version": "5.14.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.12.tgz",
|
||||
"integrity": "sha512-WZhCkKqhrXaSVBzoC6LNcVkIawS000OOt7gmnp4g9HhyvN0PSclRXc/JrkC7EwfzUAZJh+hiK2LaVsbtOpNuOg==",
|
||||
"version": "5.14.11",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.11.tgz",
|
||||
"integrity": "sha512-uY8FLQURhXe3f3O4dS5OSGML9KDm9+IE226cBu78jarVIzdQGPlXwGIlSI9VJR8MvZDA6C0+6XfWDhWCHruC5Q==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui"
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/material": {
|
||||
"version": "5.14.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.12.tgz",
|
||||
"integrity": "sha512-EelF2L46VcVqhg3KjzIGBBpOtcBgRh0MMy9Efuk6Do81QdcZsFC9RebCVAflo5jIdbHiBmxBs5/l5Q9NjONozg==",
|
||||
"version": "5.14.11",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.11.tgz",
|
||||
"integrity": "sha512-DnSdJzcR7lwG12JA5L2t8JF+RDzMygu5rCNW+logWb/KW2/TRzwLyVWO+CorHTBjBRd38DBxnwOCDiYkDd+N3A==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.1",
|
||||
"@mui/base": "5.0.0-beta.18",
|
||||
"@mui/core-downloads-tracker": "^5.14.12",
|
||||
"@mui/system": "^5.14.12",
|
||||
"@mui/types": "^7.2.5",
|
||||
"@mui/utils": "^5.14.12",
|
||||
"@babel/runtime": "^7.22.15",
|
||||
"@mui/base": "5.0.0-beta.17",
|
||||
"@mui/core-downloads-tracker": "^5.14.11",
|
||||
"@mui/system": "^5.14.11",
|
||||
"@mui/types": "^7.2.4",
|
||||
"@mui/utils": "^5.14.11",
|
||||
"@types/react-transition-group": "^4.4.6",
|
||||
"clsx": "^2.0.0",
|
||||
"csstype": "^3.1.2",
|
||||
|
|
@ -1263,12 +1265,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@mui/private-theming": {
|
||||
"version": "5.14.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.12.tgz",
|
||||
"integrity": "sha512-TWwm+9+BgHFpoR3w04FG+IqID4ALa74A27RuKq2CEaWgxliBZB24EVeI6djfjFt5t4FYmIb8BMw2ZJEir7YjLQ==",
|
||||
"version": "5.14.11",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.11.tgz",
|
||||
"integrity": "sha512-MSnNNzTu9pfKLCKs1ZAKwOTgE4bz+fQA0fNr8Jm7NDmuWmw0CaN9Vq2/MHsatE7+S0A25IAKby46Uv1u53rKVQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.1",
|
||||
"@mui/utils": "^5.14.12",
|
||||
"@babel/runtime": "^7.22.15",
|
||||
"@mui/utils": "^5.14.11",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -1289,11 +1291,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@mui/styled-engine": {
|
||||
"version": "5.14.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.12.tgz",
|
||||
"integrity": "sha512-bocxt1nDmXfB3gpLfCCmFCyJ7sVmscFs+PuheO210QagZwHVp47UIRT1AiswLDYSQo1ZqmVGn7KLEJEYK0d4Xw==",
|
||||
"version": "5.14.11",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.11.tgz",
|
||||
"integrity": "sha512-jdUlqRgTYQ8RMtPX4MbRZqar6W2OiIb6J5KEFbIu4FqvPrk44Each4ppg/LAqp1qNlBYq5i+7Q10MYLMpDxX9A==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.1",
|
||||
"@babel/runtime": "^7.22.15",
|
||||
"@emotion/cache": "^11.11.0",
|
||||
"csstype": "^3.1.2",
|
||||
"prop-types": "^15.8.1"
|
||||
|
|
@ -1320,15 +1322,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@mui/system": {
|
||||
"version": "5.14.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.12.tgz",
|
||||
"integrity": "sha512-6DXfjjLhW0/ia5qU3Crke7j+MnfDbMBOHlLIrqbrEqNs0AuSBv8pXniEGb+kqO0H804NJreRTEJRjCngwOX5CA==",
|
||||
"version": "5.14.11",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.11.tgz",
|
||||
"integrity": "sha512-yl8xV+y0k7j6dzBsHabKwoShmjqLa8kTxrhUI3JpqLG358VRVMJRW/ES0HhvfcCi4IVXde+Tc2P3K1akGL8zoA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.1",
|
||||
"@mui/private-theming": "^5.14.12",
|
||||
"@mui/styled-engine": "^5.14.12",
|
||||
"@mui/types": "^7.2.5",
|
||||
"@mui/utils": "^5.14.12",
|
||||
"@babel/runtime": "^7.22.15",
|
||||
"@mui/private-theming": "^5.14.11",
|
||||
"@mui/styled-engine": "^5.14.11",
|
||||
"@mui/types": "^7.2.4",
|
||||
"@mui/utils": "^5.14.11",
|
||||
"clsx": "^2.0.0",
|
||||
"csstype": "^3.1.2",
|
||||
"prop-types": "^15.8.1"
|
||||
|
|
@ -1380,12 +1382,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@mui/utils": {
|
||||
"version": "5.14.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.12.tgz",
|
||||
"integrity": "sha512-RFNXnhKQlzIkIUig6mmv0r5VbtjPdWoaBPYicq25LETdZux59HAqoRdWw15T7lp3c7gXOoE8y67+hTB8C64m2g==",
|
||||
"version": "5.14.11",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.11.tgz",
|
||||
"integrity": "sha512-fmkIiCPKyDssYrJ5qk+dime1nlO3dmWfCtaPY/uVBqCRMBZ11JhddB9m8sjI2mgqQQwRJG5bq3biaosNdU/s4Q==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.1",
|
||||
"@types/prop-types": "^15.7.7",
|
||||
"@babel/runtime": "^7.22.15",
|
||||
"@types/prop-types": "^15.7.5",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^18.2.0"
|
||||
},
|
||||
|
|
@ -1438,6 +1440,21 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.38.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz",
|
||||
"integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright": "1.38.1"
|
||||
},
|
||||
"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",
|
||||
|
|
@ -2984,9 +3001,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@swc/core": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.92.tgz",
|
||||
"integrity": "sha512-vx0vUrf4YTEw59njOJ46Ha5i0cZTMYdRHQ7KXU29efN1MxcmJH2RajWLPlvQarOP1ab9iv9cApD7SMchDyx2vA==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.91.tgz",
|
||||
"integrity": "sha512-r950d0fdlZ8qbSDyvApn3HyCojiZE8xpgJzQvypeMi32dalYwugdJKWyLB55JIGMRGJ8+lmVvY4MPGkSR3kXgA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
|
|
@ -3001,16 +3018,16 @@
|
|||
"url": "https://opencollective.com/swc"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core-darwin-arm64": "1.3.92",
|
||||
"@swc/core-darwin-x64": "1.3.92",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.3.92",
|
||||
"@swc/core-linux-arm64-gnu": "1.3.92",
|
||||
"@swc/core-linux-arm64-musl": "1.3.92",
|
||||
"@swc/core-linux-x64-gnu": "1.3.92",
|
||||
"@swc/core-linux-x64-musl": "1.3.92",
|
||||
"@swc/core-win32-arm64-msvc": "1.3.92",
|
||||
"@swc/core-win32-ia32-msvc": "1.3.92",
|
||||
"@swc/core-win32-x64-msvc": "1.3.92"
|
||||
"@swc/core-darwin-arm64": "1.3.91",
|
||||
"@swc/core-darwin-x64": "1.3.91",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.3.91",
|
||||
"@swc/core-linux-arm64-gnu": "1.3.91",
|
||||
"@swc/core-linux-arm64-musl": "1.3.91",
|
||||
"@swc/core-linux-x64-gnu": "1.3.91",
|
||||
"@swc/core-linux-x64-musl": "1.3.91",
|
||||
"@swc/core-win32-arm64-msvc": "1.3.91",
|
||||
"@swc/core-win32-ia32-msvc": "1.3.91",
|
||||
"@swc/core-win32-x64-msvc": "1.3.91"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
|
|
@ -3022,9 +3039,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@swc/core-darwin-arm64": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.92.tgz",
|
||||
"integrity": "sha512-v7PqZUBtIF6Q5Cp48gqUiG8zQQnEICpnfNdoiY3xjQAglCGIQCjJIDjreZBoeZQZspB27lQN4eZ43CX18+2SnA==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.91.tgz",
|
||||
"integrity": "sha512-7kHGiQ1he5khcEeJuHDmLZPM3rRL/ith5OTmV6bOPsoHi46kLeixORW+ts1opC3tC9vu6xbk16xgX0QAJchc1w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -3038,9 +3055,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@swc/core-darwin-x64": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.92.tgz",
|
||||
"integrity": "sha512-Q3XIgQfXyxxxms3bPN+xGgvwk0TtG9l89IomApu+yTKzaIIlf051mS+lGngjnh9L0aUiCp6ICyjDLtutWP54fw==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.91.tgz",
|
||||
"integrity": "sha512-8SpU18FbFpZDVzsHsAwdI1thF/picQGxq9UFxa8W+T9SDnbsqwFJv/6RqKJeJoDV6qFdl2OLjuO0OL7xrp0qnQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -3054,9 +3071,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.92.tgz",
|
||||
"integrity": "sha512-tnOCoCpNVXC+0FCfG84PBZJyLlz0Vfj9MQhyhCvlJz9hQmvpf8nTdKH7RHrOn8VfxtUBLdVi80dXgIFgbvl7qA==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.91.tgz",
|
||||
"integrity": "sha512-fOq4Cy8UbwX1yf0WB0d8hWZaIKCnPtPGguRqdXGLfwvhjZ9SIErT6PnmGTGRbQCNCIkOZWHKyTU0r8t2dN3haQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
|
@ -3070,9 +3087,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.92.tgz",
|
||||
"integrity": "sha512-lFfGhX32w8h1j74Iyz0Wv7JByXIwX11OE9UxG+oT7lG0RyXkF4zKyxP8EoxfLrDXse4Oop434p95e3UNC3IfCw==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.91.tgz",
|
||||
"integrity": "sha512-fki4ioRP/Esy4vdp8T34RCV+V9dqkRmOt763pf74pdiyFV2dPLXa5lnw/XvR1RTfPGknrYgjEQLCfZlReTryRw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -3086,9 +3103,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm64-musl": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.92.tgz",
|
||||
"integrity": "sha512-rOZtRcLj57MSAbiecMsqjzBcZDuaCZ8F6l6JDwGkQ7u1NYR57cqF0QDyU7RKS1Jq27Z/Vg21z5cwqoH5fLN+Sg==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.91.tgz",
|
||||
"integrity": "sha512-XrG+DUUqNtfVLcJ20imby7fpBwQNG5VsEQBzQndSonPyUOa2YkTbBb60YDondfQGDABopuHH8gHN8o2H2/VCnQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -3102,9 +3119,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-x64-gnu": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.92.tgz",
|
||||
"integrity": "sha512-qptoMGnBL6v89x/Qpn+l1TH1Y0ed+v0qhNfAEVzZvCvzEMTFXphhlhYbDdpxbzRmCjH6GOGq7Y+xrWt9T1/ARg==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.91.tgz",
|
||||
"integrity": "sha512-d11bYhX+YPBr/Frcjc6eVn3C0LuS/9U1Li9EmQ+6s9EpYtYRl2ygSlC8eueLbaiazBnCVYFnc8bU4o0kc5B9sw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -3118,9 +3135,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-x64-musl": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.92.tgz",
|
||||
"integrity": "sha512-g2KrJ43bZkCZHH4zsIV5ErojuV1OIpUHaEyW1gf7JWKaFBpWYVyubzFPvPkjcxHGLbMsEzO7w/NVfxtGMlFH/Q==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.91.tgz",
|
||||
"integrity": "sha512-2SRp5Dke2P4jCQePkDx9trkkTstnRpZJVw5r3jvYdk0zeO6iC4+ZPvvoWXJLigqQv/fZnIiSUfJ6ssOoaEqTzQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -3134,9 +3151,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-arm64-msvc": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.92.tgz",
|
||||
"integrity": "sha512-3MCRGPAYDoQ8Yyd3WsCMc8eFSyKXY5kQLyg/R5zEqA0uthomo0m0F5/fxAJMZGaSdYkU1DgF73ctOWOf+Z/EzQ==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.91.tgz",
|
||||
"integrity": "sha512-l9qKXikOxj42UIjbeZpz9xtBmr736jOMqInNP8mVF2/U+ws5sI8zJjcOFFtfis4ru7vWCXhB1wtltdlJYO2vGA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -3150,9 +3167,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-ia32-msvc": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.92.tgz",
|
||||
"integrity": "sha512-zqTBKQhgfWm73SVGS8FKhFYDovyRl1f5dTX1IwSKynO0qHkRCqJwauFJv/yevkpJWsI2pFh03xsRs9HncTQKSA==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.91.tgz",
|
||||
"integrity": "sha512-+s+52O0QVPmzOgjEe/rcb0AK6q/J7EHKwAyJCu/FaYO9df5ovE0HJjSKP6HAF0dGPO5hkENrXuNGujofUH9vtQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
|
|
@ -3166,9 +3183,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@swc/core-win32-x64-msvc": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.92.tgz",
|
||||
"integrity": "sha512-41bE66ddr9o/Fi1FBh0sHdaKdENPTuDpv1IFHxSg0dJyM/jX8LbkjnpdInYXHBxhcLVAPraVRrNsC4SaoPw2Pg==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.91.tgz",
|
||||
"integrity": "sha512-7u9HDQhjUC3Gv43EFW84dZtduWCSa4MgltK+Sp9zEGti6WXqDPu/ESjvDsQEVYTBEMEvZs/xVAXPgLVHorV5nQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -3779,9 +3796,9 @@
|
|||
"integrity": "sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ=="
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.2.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.25.tgz",
|
||||
"integrity": "sha512-24xqse6+VByVLIr+xWaQ9muX1B4bXJKXBbjszbld/UEDslGLY53+ZucF44HCmLbMPejTzGG9XgR+3m2/Wqu1kw==",
|
||||
"version": "18.2.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.24.tgz",
|
||||
"integrity": "sha512-Ee0Jt4sbJxMu1iDcetZEIKQr99J1Zfb6D4F3qfUWoR1JpInkY1Wdg4WwCyBjL257D0+jGqSl1twBjV8iCaC0Aw==",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
|
|
@ -3789,9 +3806,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/react-dom": {
|
||||
"version": "18.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.10.tgz",
|
||||
"integrity": "sha512-5VEC5RgXIk1HHdyN1pHlg0cOqnxHzvPGpMMyGAP5qSaDRmyZNDaQ0kkVAkK6NYlDhP6YBID3llaXlmAS/mdgCA==",
|
||||
"version": "18.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.8.tgz",
|
||||
"integrity": "sha512-bAIvO5lN/U8sPGvs1Xm61rlRHHaq5rp5N3kp9C+NJ/Q41P8iqjkXSu0+/qu8POsjH9pNWb0OYabFez7taP7omw==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
|
|
@ -4499,9 +4516,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001546",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz",
|
||||
"integrity": "sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==",
|
||||
"version": "1.0.30001543",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001543.tgz",
|
||||
"integrity": "sha512-qxdO8KPWPQ+Zk6bvNpPeQIOH47qZSYdFZd6dXQzb2KzhnSXju4Kd7H1PkSJx6NICSMgo/IhRZRhhfPTHYpJUCA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -4936,9 +4953,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/daisyui": {
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.9.2.tgz",
|
||||
"integrity": "sha512-yJZ1QjHUaL+r9BkquTdzNHb7KIgAJVFh0zbOXql2Wu0r7zx5qZNLxclhjN0WLoIpY+o2h/8lqXg7ijj8oTigOw==",
|
||||
"version": "3.8.3",
|
||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.8.3.tgz",
|
||||
"integrity": "sha512-64QuR6niTv58E5YyAVBkoXQGngp04jt6XOcKGxnq1C/7r5ZE3bDuOSc4ktT7CPacTRjl5cCeNYY13VxLpZP+7A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"colord": "^2.9",
|
||||
|
|
@ -5219,9 +5236,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.543",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.543.tgz",
|
||||
"integrity": "sha512-t2ZP4AcGE0iKCCQCBx/K2426crYdxD3YU6l0uK2EO3FZH0pbC4pFz/sZm2ruZsND6hQBTcDWWlo/MLpiOdif5g=="
|
||||
"version": "1.4.540",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.540.tgz",
|
||||
"integrity": "sha512-aoCqgU6r9+o9/S7wkcSbmPRFi7OWZWiXS9rtjEd+Ouyu/Xyw5RSq2XN8s5Qp8IaFOLiRrhQCphCIjAxgG3eCAg=="
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
|
|
@ -5695,9 +5712,9 @@
|
|||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -8340,6 +8357,36 @@
|
|||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.38.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz",
|
||||
"integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"playwright-core": "1.38.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.38.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
|
||||
"integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
|
|
@ -9018,6 +9065,14 @@
|
|||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react18-json-view": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/react18-json-view/-/react18-json-view-0.2.5.tgz",
|
||||
"integrity": "sha512-BiCWyRUCVbnaK4kfNay8crOXZnWsZ6XsnY3fwOf5C+ZaY9w9FSTawo2p+h2UG/KcDP8meZuGlkP95klfFG9GfQ==",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/reactflow": {
|
||||
"version": "11.9.2",
|
||||
"resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.9.2.tgz",
|
||||
|
|
@ -10687,9 +10742,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "4.4.11",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz",
|
||||
"integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==",
|
||||
"version": "4.4.10",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.10.tgz",
|
||||
"integrity": "sha512-TzIjiqx9BEXF8yzYdF2NTf1kFFbjMjUSV0LFZ3HyHoI3SGSPLnnFUKiIQtL3gl2AjHvMrprOvQ3amzaHgQlAxw==",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.18.10",
|
||||
"postcss": "^8.4.27",
|
||||
|
|
@ -11335,17 +11390,17 @@
|
|||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.22.4",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
|
||||
"integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==",
|
||||
"version": "3.22.3",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz",
|
||||
"integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/zustand": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.3.tgz",
|
||||
"integrity": "sha512-oRy+X3ZazZvLfmv6viIaQmtLOMeij1noakIsK/Y47PWYhT8glfXzQ4j0YcP5i0P0qI1A4rIB//SGROGyZhx91A==",
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.2.tgz",
|
||||
"integrity": "sha512-qF3/vZHCrjPUX5DvPE3DPDZlh+FiAWRKlP9PI7SlW1MCk8q4vUCDqyWsbF8K41ne0Yx8eeeb0m1cypn1LqUMYQ==",
|
||||
"dependencies": {
|
||||
"use-sync-external-store": "1.2.0"
|
||||
},
|
||||
|
|
@ -12082,14 +12137,14 @@
|
|||
}
|
||||
},
|
||||
"@mui/base": {
|
||||
"version": "5.0.0-beta.18",
|
||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.18.tgz",
|
||||
"integrity": "sha512-e9ZCy/ndhyt5MTshAS3qAUy/40UiO0jX+kAo6a+XirrPJE+rrQW+mKPSI0uyp+5z4Vh+z0pvNoJ2S2gSrNz3BQ==",
|
||||
"version": "5.0.0-beta.17",
|
||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.17.tgz",
|
||||
"integrity": "sha512-xNbk7iOXrglNdIxFBN0k3ySsPIFLWCnFxqsAYl7CIcDkD9low4kJ7IUuy6ctwx/HAy2fenrT3KXHr1sGjAMgpQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.23.1",
|
||||
"@babel/runtime": "^7.22.15",
|
||||
"@floating-ui/react-dom": "^2.0.2",
|
||||
"@mui/types": "^7.2.5",
|
||||
"@mui/utils": "^5.14.12",
|
||||
"@mui/types": "^7.2.4",
|
||||
"@mui/utils": "^5.14.11",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"clsx": "^2.0.0",
|
||||
"prop-types": "^15.8.1"
|
||||
|
|
@ -12103,21 +12158,21 @@
|
|||
}
|
||||
},
|
||||
"@mui/core-downloads-tracker": {
|
||||
"version": "5.14.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.12.tgz",
|
||||
"integrity": "sha512-WZhCkKqhrXaSVBzoC6LNcVkIawS000OOt7gmnp4g9HhyvN0PSclRXc/JrkC7EwfzUAZJh+hiK2LaVsbtOpNuOg=="
|
||||
"version": "5.14.11",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.11.tgz",
|
||||
"integrity": "sha512-uY8FLQURhXe3f3O4dS5OSGML9KDm9+IE226cBu78jarVIzdQGPlXwGIlSI9VJR8MvZDA6C0+6XfWDhWCHruC5Q=="
|
||||
},
|
||||
"@mui/material": {
|
||||
"version": "5.14.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.12.tgz",
|
||||
"integrity": "sha512-EelF2L46VcVqhg3KjzIGBBpOtcBgRh0MMy9Efuk6Do81QdcZsFC9RebCVAflo5jIdbHiBmxBs5/l5Q9NjONozg==",
|
||||
"version": "5.14.11",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.11.tgz",
|
||||
"integrity": "sha512-DnSdJzcR7lwG12JA5L2t8JF+RDzMygu5rCNW+logWb/KW2/TRzwLyVWO+CorHTBjBRd38DBxnwOCDiYkDd+N3A==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.23.1",
|
||||
"@mui/base": "5.0.0-beta.18",
|
||||
"@mui/core-downloads-tracker": "^5.14.12",
|
||||
"@mui/system": "^5.14.12",
|
||||
"@mui/types": "^7.2.5",
|
||||
"@mui/utils": "^5.14.12",
|
||||
"@babel/runtime": "^7.22.15",
|
||||
"@mui/base": "5.0.0-beta.17",
|
||||
"@mui/core-downloads-tracker": "^5.14.11",
|
||||
"@mui/system": "^5.14.11",
|
||||
"@mui/types": "^7.2.4",
|
||||
"@mui/utils": "^5.14.11",
|
||||
"@types/react-transition-group": "^4.4.6",
|
||||
"clsx": "^2.0.0",
|
||||
"csstype": "^3.1.2",
|
||||
|
|
@ -12134,36 +12189,36 @@
|
|||
}
|
||||
},
|
||||
"@mui/private-theming": {
|
||||
"version": "5.14.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.12.tgz",
|
||||
"integrity": "sha512-TWwm+9+BgHFpoR3w04FG+IqID4ALa74A27RuKq2CEaWgxliBZB24EVeI6djfjFt5t4FYmIb8BMw2ZJEir7YjLQ==",
|
||||
"version": "5.14.11",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.11.tgz",
|
||||
"integrity": "sha512-MSnNNzTu9pfKLCKs1ZAKwOTgE4bz+fQA0fNr8Jm7NDmuWmw0CaN9Vq2/MHsatE7+S0A25IAKby46Uv1u53rKVQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.23.1",
|
||||
"@mui/utils": "^5.14.12",
|
||||
"@babel/runtime": "^7.22.15",
|
||||
"@mui/utils": "^5.14.11",
|
||||
"prop-types": "^15.8.1"
|
||||
}
|
||||
},
|
||||
"@mui/styled-engine": {
|
||||
"version": "5.14.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.12.tgz",
|
||||
"integrity": "sha512-bocxt1nDmXfB3gpLfCCmFCyJ7sVmscFs+PuheO210QagZwHVp47UIRT1AiswLDYSQo1ZqmVGn7KLEJEYK0d4Xw==",
|
||||
"version": "5.14.11",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.11.tgz",
|
||||
"integrity": "sha512-jdUlqRgTYQ8RMtPX4MbRZqar6W2OiIb6J5KEFbIu4FqvPrk44Each4ppg/LAqp1qNlBYq5i+7Q10MYLMpDxX9A==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.23.1",
|
||||
"@babel/runtime": "^7.22.15",
|
||||
"@emotion/cache": "^11.11.0",
|
||||
"csstype": "^3.1.2",
|
||||
"prop-types": "^15.8.1"
|
||||
}
|
||||
},
|
||||
"@mui/system": {
|
||||
"version": "5.14.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.12.tgz",
|
||||
"integrity": "sha512-6DXfjjLhW0/ia5qU3Crke7j+MnfDbMBOHlLIrqbrEqNs0AuSBv8pXniEGb+kqO0H804NJreRTEJRjCngwOX5CA==",
|
||||
"version": "5.14.11",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.11.tgz",
|
||||
"integrity": "sha512-yl8xV+y0k7j6dzBsHabKwoShmjqLa8kTxrhUI3JpqLG358VRVMJRW/ES0HhvfcCi4IVXde+Tc2P3K1akGL8zoA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.23.1",
|
||||
"@mui/private-theming": "^5.14.12",
|
||||
"@mui/styled-engine": "^5.14.12",
|
||||
"@mui/types": "^7.2.5",
|
||||
"@mui/utils": "^5.14.12",
|
||||
"@babel/runtime": "^7.22.15",
|
||||
"@mui/private-theming": "^5.14.11",
|
||||
"@mui/styled-engine": "^5.14.11",
|
||||
"@mui/types": "^7.2.4",
|
||||
"@mui/utils": "^5.14.11",
|
||||
"clsx": "^2.0.0",
|
||||
"csstype": "^3.1.2",
|
||||
"prop-types": "^15.8.1"
|
||||
|
|
@ -12183,12 +12238,12 @@
|
|||
"requires": {}
|
||||
},
|
||||
"@mui/utils": {
|
||||
"version": "5.14.12",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.12.tgz",
|
||||
"integrity": "sha512-RFNXnhKQlzIkIUig6mmv0r5VbtjPdWoaBPYicq25LETdZux59HAqoRdWw15T7lp3c7gXOoE8y67+hTB8C64m2g==",
|
||||
"version": "5.14.11",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.11.tgz",
|
||||
"integrity": "sha512-fmkIiCPKyDssYrJ5qk+dime1nlO3dmWfCtaPY/uVBqCRMBZ11JhddB9m8sjI2mgqQQwRJG5bq3biaosNdU/s4Q==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.23.1",
|
||||
"@types/prop-types": "^15.7.7",
|
||||
"@babel/runtime": "^7.22.15",
|
||||
"@types/prop-types": "^15.7.5",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^18.2.0"
|
||||
}
|
||||
|
|
@ -12216,6 +12271,15 @@
|
|||
"fastq": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"@playwright/test": {
|
||||
"version": "1.38.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz",
|
||||
"integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"playwright": "1.38.1"
|
||||
}
|
||||
},
|
||||
"@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
|
|
@ -13037,92 +13101,92 @@
|
|||
}
|
||||
},
|
||||
"@swc/core": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.92.tgz",
|
||||
"integrity": "sha512-vx0vUrf4YTEw59njOJ46Ha5i0cZTMYdRHQ7KXU29efN1MxcmJH2RajWLPlvQarOP1ab9iv9cApD7SMchDyx2vA==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.91.tgz",
|
||||
"integrity": "sha512-r950d0fdlZ8qbSDyvApn3HyCojiZE8xpgJzQvypeMi32dalYwugdJKWyLB55JIGMRGJ8+lmVvY4MPGkSR3kXgA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@swc/core-darwin-arm64": "1.3.92",
|
||||
"@swc/core-darwin-x64": "1.3.92",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.3.92",
|
||||
"@swc/core-linux-arm64-gnu": "1.3.92",
|
||||
"@swc/core-linux-arm64-musl": "1.3.92",
|
||||
"@swc/core-linux-x64-gnu": "1.3.92",
|
||||
"@swc/core-linux-x64-musl": "1.3.92",
|
||||
"@swc/core-win32-arm64-msvc": "1.3.92",
|
||||
"@swc/core-win32-ia32-msvc": "1.3.92",
|
||||
"@swc/core-win32-x64-msvc": "1.3.92",
|
||||
"@swc/core-darwin-arm64": "1.3.91",
|
||||
"@swc/core-darwin-x64": "1.3.91",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.3.91",
|
||||
"@swc/core-linux-arm64-gnu": "1.3.91",
|
||||
"@swc/core-linux-arm64-musl": "1.3.91",
|
||||
"@swc/core-linux-x64-gnu": "1.3.91",
|
||||
"@swc/core-linux-x64-musl": "1.3.91",
|
||||
"@swc/core-win32-arm64-msvc": "1.3.91",
|
||||
"@swc/core-win32-ia32-msvc": "1.3.91",
|
||||
"@swc/core-win32-x64-msvc": "1.3.91",
|
||||
"@swc/counter": "^0.1.1",
|
||||
"@swc/types": "^0.1.5"
|
||||
}
|
||||
},
|
||||
"@swc/core-darwin-arm64": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.92.tgz",
|
||||
"integrity": "sha512-v7PqZUBtIF6Q5Cp48gqUiG8zQQnEICpnfNdoiY3xjQAglCGIQCjJIDjreZBoeZQZspB27lQN4eZ43CX18+2SnA==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.91.tgz",
|
||||
"integrity": "sha512-7kHGiQ1he5khcEeJuHDmLZPM3rRL/ith5OTmV6bOPsoHi46kLeixORW+ts1opC3tC9vu6xbk16xgX0QAJchc1w==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@swc/core-darwin-x64": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.92.tgz",
|
||||
"integrity": "sha512-Q3XIgQfXyxxxms3bPN+xGgvwk0TtG9l89IomApu+yTKzaIIlf051mS+lGngjnh9L0aUiCp6ICyjDLtutWP54fw==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.91.tgz",
|
||||
"integrity": "sha512-8SpU18FbFpZDVzsHsAwdI1thF/picQGxq9UFxa8W+T9SDnbsqwFJv/6RqKJeJoDV6qFdl2OLjuO0OL7xrp0qnQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@swc/core-linux-arm-gnueabihf": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.92.tgz",
|
||||
"integrity": "sha512-tnOCoCpNVXC+0FCfG84PBZJyLlz0Vfj9MQhyhCvlJz9hQmvpf8nTdKH7RHrOn8VfxtUBLdVi80dXgIFgbvl7qA==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.91.tgz",
|
||||
"integrity": "sha512-fOq4Cy8UbwX1yf0WB0d8hWZaIKCnPtPGguRqdXGLfwvhjZ9SIErT6PnmGTGRbQCNCIkOZWHKyTU0r8t2dN3haQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@swc/core-linux-arm64-gnu": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.92.tgz",
|
||||
"integrity": "sha512-lFfGhX32w8h1j74Iyz0Wv7JByXIwX11OE9UxG+oT7lG0RyXkF4zKyxP8EoxfLrDXse4Oop434p95e3UNC3IfCw==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.91.tgz",
|
||||
"integrity": "sha512-fki4ioRP/Esy4vdp8T34RCV+V9dqkRmOt763pf74pdiyFV2dPLXa5lnw/XvR1RTfPGknrYgjEQLCfZlReTryRw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@swc/core-linux-arm64-musl": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.92.tgz",
|
||||
"integrity": "sha512-rOZtRcLj57MSAbiecMsqjzBcZDuaCZ8F6l6JDwGkQ7u1NYR57cqF0QDyU7RKS1Jq27Z/Vg21z5cwqoH5fLN+Sg==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.91.tgz",
|
||||
"integrity": "sha512-XrG+DUUqNtfVLcJ20imby7fpBwQNG5VsEQBzQndSonPyUOa2YkTbBb60YDondfQGDABopuHH8gHN8o2H2/VCnQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@swc/core-linux-x64-gnu": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.92.tgz",
|
||||
"integrity": "sha512-qptoMGnBL6v89x/Qpn+l1TH1Y0ed+v0qhNfAEVzZvCvzEMTFXphhlhYbDdpxbzRmCjH6GOGq7Y+xrWt9T1/ARg==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.91.tgz",
|
||||
"integrity": "sha512-d11bYhX+YPBr/Frcjc6eVn3C0LuS/9U1Li9EmQ+6s9EpYtYRl2ygSlC8eueLbaiazBnCVYFnc8bU4o0kc5B9sw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@swc/core-linux-x64-musl": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.92.tgz",
|
||||
"integrity": "sha512-g2KrJ43bZkCZHH4zsIV5ErojuV1OIpUHaEyW1gf7JWKaFBpWYVyubzFPvPkjcxHGLbMsEzO7w/NVfxtGMlFH/Q==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.91.tgz",
|
||||
"integrity": "sha512-2SRp5Dke2P4jCQePkDx9trkkTstnRpZJVw5r3jvYdk0zeO6iC4+ZPvvoWXJLigqQv/fZnIiSUfJ6ssOoaEqTzQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@swc/core-win32-arm64-msvc": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.92.tgz",
|
||||
"integrity": "sha512-3MCRGPAYDoQ8Yyd3WsCMc8eFSyKXY5kQLyg/R5zEqA0uthomo0m0F5/fxAJMZGaSdYkU1DgF73ctOWOf+Z/EzQ==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.91.tgz",
|
||||
"integrity": "sha512-l9qKXikOxj42UIjbeZpz9xtBmr736jOMqInNP8mVF2/U+ws5sI8zJjcOFFtfis4ru7vWCXhB1wtltdlJYO2vGA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@swc/core-win32-ia32-msvc": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.92.tgz",
|
||||
"integrity": "sha512-zqTBKQhgfWm73SVGS8FKhFYDovyRl1f5dTX1IwSKynO0qHkRCqJwauFJv/yevkpJWsI2pFh03xsRs9HncTQKSA==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.91.tgz",
|
||||
"integrity": "sha512-+s+52O0QVPmzOgjEe/rcb0AK6q/J7EHKwAyJCu/FaYO9df5ovE0HJjSKP6HAF0dGPO5hkENrXuNGujofUH9vtQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@swc/core-win32-x64-msvc": {
|
||||
"version": "1.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.92.tgz",
|
||||
"integrity": "sha512-41bE66ddr9o/Fi1FBh0sHdaKdENPTuDpv1IFHxSg0dJyM/jX8LbkjnpdInYXHBxhcLVAPraVRrNsC4SaoPw2Pg==",
|
||||
"version": "1.3.91",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.91.tgz",
|
||||
"integrity": "sha512-7u9HDQhjUC3Gv43EFW84dZtduWCSa4MgltK+Sp9zEGti6WXqDPu/ESjvDsQEVYTBEMEvZs/xVAXPgLVHorV5nQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
|
|
@ -13670,9 +13734,9 @@
|
|||
"integrity": "sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ=="
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "18.2.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.25.tgz",
|
||||
"integrity": "sha512-24xqse6+VByVLIr+xWaQ9muX1B4bXJKXBbjszbld/UEDslGLY53+ZucF44HCmLbMPejTzGG9XgR+3m2/Wqu1kw==",
|
||||
"version": "18.2.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.24.tgz",
|
||||
"integrity": "sha512-Ee0Jt4sbJxMu1iDcetZEIKQr99J1Zfb6D4F3qfUWoR1JpInkY1Wdg4WwCyBjL257D0+jGqSl1twBjV8iCaC0Aw==",
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
|
|
@ -13680,9 +13744,9 @@
|
|||
}
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"version": "18.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.10.tgz",
|
||||
"integrity": "sha512-5VEC5RgXIk1HHdyN1pHlg0cOqnxHzvPGpMMyGAP5qSaDRmyZNDaQ0kkVAkK6NYlDhP6YBID3llaXlmAS/mdgCA==",
|
||||
"version": "18.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.8.tgz",
|
||||
"integrity": "sha512-bAIvO5lN/U8sPGvs1Xm61rlRHHaq5rp5N3kp9C+NJ/Q41P8iqjkXSu0+/qu8POsjH9pNWb0OYabFez7taP7omw==",
|
||||
"devOptional": true,
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
|
|
@ -14162,9 +14226,9 @@
|
|||
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001546",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz",
|
||||
"integrity": "sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw=="
|
||||
"version": "1.0.30001543",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001543.tgz",
|
||||
"integrity": "sha512-qxdO8KPWPQ+Zk6bvNpPeQIOH47qZSYdFZd6dXQzb2KzhnSXju4Kd7H1PkSJx6NICSMgo/IhRZRhhfPTHYpJUCA=="
|
||||
},
|
||||
"ccount": {
|
||||
"version": "2.0.1",
|
||||
|
|
@ -14469,9 +14533,9 @@
|
|||
}
|
||||
},
|
||||
"daisyui": {
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.9.2.tgz",
|
||||
"integrity": "sha512-yJZ1QjHUaL+r9BkquTdzNHb7KIgAJVFh0zbOXql2Wu0r7zx5qZNLxclhjN0WLoIpY+o2h/8lqXg7ijj8oTigOw==",
|
||||
"version": "3.8.3",
|
||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.8.3.tgz",
|
||||
"integrity": "sha512-64QuR6niTv58E5YyAVBkoXQGngp04jt6XOcKGxnq1C/7r5ZE3bDuOSc4ktT7CPacTRjl5cCeNYY13VxLpZP+7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"colord": "^2.9",
|
||||
|
|
@ -14682,9 +14746,9 @@
|
|||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.4.543",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.543.tgz",
|
||||
"integrity": "sha512-t2ZP4AcGE0iKCCQCBx/K2426crYdxD3YU6l0uK2EO3FZH0pbC4pFz/sZm2ruZsND6hQBTcDWWlo/MLpiOdif5g=="
|
||||
"version": "1.4.540",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.540.tgz",
|
||||
"integrity": "sha512-aoCqgU6r9+o9/S7wkcSbmPRFi7OWZWiXS9rtjEd+Ouyu/Xyw5RSq2XN8s5Qp8IaFOLiRrhQCphCIjAxgG3eCAg=="
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
|
|
@ -15018,9 +15082,9 @@
|
|||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"optional": true
|
||||
},
|
||||
"function-bind": {
|
||||
|
|
@ -16783,6 +16847,22 @@
|
|||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
|
||||
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg=="
|
||||
},
|
||||
"playwright": {
|
||||
"version": "1.38.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz",
|
||||
"integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fsevents": "2.3.2",
|
||||
"playwright-core": "1.38.1"
|
||||
}
|
||||
},
|
||||
"playwright-core": {
|
||||
"version": "1.38.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
|
||||
"integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==",
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
|
|
@ -17173,6 +17253,12 @@
|
|||
"prop-types": "^15.6.2"
|
||||
}
|
||||
},
|
||||
"react18-json-view": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/react18-json-view/-/react18-json-view-0.2.5.tgz",
|
||||
"integrity": "sha512-BiCWyRUCVbnaK4kfNay8crOXZnWsZ6XsnY3fwOf5C+ZaY9w9FSTawo2p+h2UG/KcDP8meZuGlkP95klfFG9GfQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"reactflow": {
|
||||
"version": "11.9.2",
|
||||
"resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.9.2.tgz",
|
||||
|
|
@ -18323,9 +18409,9 @@
|
|||
}
|
||||
},
|
||||
"vite": {
|
||||
"version": "4.4.11",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz",
|
||||
"integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==",
|
||||
"version": "4.4.10",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.10.tgz",
|
||||
"integrity": "sha512-TzIjiqx9BEXF8yzYdF2NTf1kFFbjMjUSV0LFZ3HyHoI3SGSPLnnFUKiIQtL3gl2AjHvMrprOvQ3amzaHgQlAxw==",
|
||||
"requires": {
|
||||
"esbuild": "^0.18.10",
|
||||
"fsevents": "~2.3.2",
|
||||
|
|
@ -18659,14 +18745,14 @@
|
|||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
|
||||
},
|
||||
"zod": {
|
||||
"version": "3.22.4",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
|
||||
"integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg=="
|
||||
"version": "3.22.3",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz",
|
||||
"integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="
|
||||
},
|
||||
"zustand": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.3.tgz",
|
||||
"integrity": "sha512-oRy+X3ZazZvLfmv6viIaQmtLOMeij1noakIsK/Y47PWYhT8glfXzQ4j0YcP5i0P0qI1A4rIB//SGROGyZhx91A==",
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.2.tgz",
|
||||
"integrity": "sha512-qF3/vZHCrjPUX5DvPE3DPDZlh+FiAWRKlP9PI7SlW1MCk8q4vUCDqyWsbF8K41ne0Yx8eeeb0m1cypn1LqUMYQ==",
|
||||
"requires": {
|
||||
"use-sync-external-store": "1.2.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@
|
|||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-tabs": "^6.0.2",
|
||||
"react-tooltip": "^5.21.1",
|
||||
"reactflow": "^11.8.3",
|
||||
"react18-json-view": "^0.2.3",
|
||||
"reactflow": "^11.9.2",
|
||||
"rehype-mathjax": "^4.0.3",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
|
|
@ -72,7 +73,7 @@
|
|||
"start": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview",
|
||||
"format": "npx prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
|
||||
"format": "npx prettier --write \"./**/*.{js,jsx,ts,tsx,json,md}\"",
|
||||
"type-check": "tsc --noEmit --pretty --project tsconfig.json && vite"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
|
@ -95,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,
|
||||
// },
|
||||
});
|
||||
|
|
@ -3,45 +3,85 @@
|
|||
@tailwind utilities;
|
||||
|
||||
.App {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.react-flow__node {
|
||||
width: auto;
|
||||
height: auto;
|
||||
border-radius: auto;
|
||||
min-width: inherit;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
color: #61dafb;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: text-security-disc;
|
||||
src: url("assets/text-security-disc.woff") format("woff");
|
||||
font-family: text-security-disc;
|
||||
src: url("assets/text-security-disc.woff") format("woff");
|
||||
}
|
||||
|
||||
.json-view {
|
||||
height: 370px !important;
|
||||
background-color: #2c2c2c !important;
|
||||
border-radius: 10px !important;
|
||||
padding: 10px !important;
|
||||
}
|
||||
|
||||
.jv-indent {
|
||||
overflow-y: auto !important;
|
||||
max-height: 310px !important;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.jv-indent::-webkit-scrollbar {
|
||||
width: 8px !important;
|
||||
height: 8px !important;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.jv-indent::-webkit-scrollbar-track {
|
||||
background-color: #f1f1f1 !important;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.jv-indent::-webkit-scrollbar-thumb {
|
||||
background-color: #ccc !important;
|
||||
border-radius: 999px !important;
|
||||
}
|
||||
|
||||
.jv-indent::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #bbb !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import React, {
|
|||
import { Handle, Position, useUpdateNodeInternals } from "reactflow";
|
||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||
import CodeAreaComponent from "../../../../components/codeAreaComponent";
|
||||
import DictComponent from "../../../../components/dictComponent";
|
||||
import Dropdown from "../../../../components/dropdownComponent";
|
||||
import FloatComponent from "../../../../components/floatComponent";
|
||||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
|
|
@ -16,15 +17,20 @@ import InputComponent from "../../../../components/inputComponent";
|
|||
import InputFileComponent from "../../../../components/inputFileComponent";
|
||||
import InputListComponent from "../../../../components/inputListComponent";
|
||||
import IntComponent from "../../../../components/intComponent";
|
||||
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";
|
||||
import { ParameterComponentType } from "../../../../types/components";
|
||||
import { TabsState } from "../../../../types/tabs";
|
||||
import {
|
||||
convertObjToArray,
|
||||
convertValuesToNumbers,
|
||||
hasDuplicateKeys,
|
||||
isValidConnection,
|
||||
scapedJSONStringfy,
|
||||
} from "../../../../utils/reactflowUtils";
|
||||
|
|
@ -49,13 +55,14 @@ export default function ParameterComponent({
|
|||
optionalHandle = null,
|
||||
info = "",
|
||||
proxy,
|
||||
showNode,
|
||||
}: ParameterComponentType): JSX.Element {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const refHtml = useRef<HTMLDivElement & ReactNode>(null);
|
||||
const infoHtml = useRef<HTMLDivElement & ReactNode>(null);
|
||||
const updateNodeInternals = useUpdateNodeInternals();
|
||||
const [position, setPosition] = useState(0);
|
||||
const { setTabsState, tabId, save, flows } = useContext(TabsContext);
|
||||
const { setTabsState, tabId, flows } = useContext(TabsContext);
|
||||
|
||||
const flow = flows.find((flow) => flow.id === tabId)?.data?.nodes ?? null;
|
||||
|
||||
|
|
@ -71,7 +78,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()
|
||||
|
|
@ -79,13 +88,18 @@ export default function ParameterComponent({
|
|||
|
||||
const { data: myData } = useContext(typesContext);
|
||||
|
||||
const handleOnNewValue = (newValue: string | string[] | boolean): void => {
|
||||
const handleOnNewValue = (
|
||||
newValue: string | string[] | boolean | Object[]
|
||||
): void => {
|
||||
let newData = cloneDeep(data);
|
||||
newData.node!.template[name].value = newValue;
|
||||
setData(newData);
|
||||
// Set state to pending
|
||||
//@ts-ignore
|
||||
setTabsState((prev: TabsState) => {
|
||||
if (!prev[tabId]) {
|
||||
return prev;
|
||||
}
|
||||
return {
|
||||
...prev,
|
||||
[tabId]: {
|
||||
|
|
@ -98,6 +112,8 @@ export default function ParameterComponent({
|
|||
renderTooltips();
|
||||
};
|
||||
|
||||
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// @ts-ignore
|
||||
infoHtml.current = (
|
||||
|
|
@ -112,7 +128,8 @@ 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
|
||||
|
|
@ -121,45 +138,54 @@ export default function ParameterComponent({
|
|||
nodeIconsLucide[item.family] ?? nodeIconsLucide["unknown"];
|
||||
|
||||
return (
|
||||
<span
|
||||
key={index}
|
||||
className={classNames(
|
||||
index > 0 ? "mt-2 flex items-center" : "flex items-center"
|
||||
<div key={index}>
|
||||
{index === 0 && (
|
||||
<span>
|
||||
{left
|
||||
? "Avaliable input components:"
|
||||
: "Avaliable output components:"}
|
||||
</span>
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className="h-5 w-5"
|
||||
style={{
|
||||
color: nodeColors[item.family],
|
||||
}}
|
||||
<span
|
||||
key={index}
|
||||
className={classNames(
|
||||
index > 0 ? "mt-2 flex items-center" : "mt-3 flex items-center"
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
<div
|
||||
className="h-5 w-5"
|
||||
strokeWidth={1.5}
|
||||
style={{
|
||||
color: nodeColors[item.family] ?? nodeColors.unknown,
|
||||
color: nodeColors[item.family],
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<span className="ps-2 text-xs text-foreground">
|
||||
{nodeNames[item.family] ?? "Other"}
|
||||
<span className="text-xs">
|
||||
{" "}
|
||||
{item.type === "" ? "" : " - "}
|
||||
{item.type.split(", ").length > 2
|
||||
? item.type.split(", ").map((el, index) => (
|
||||
<React.Fragment key={el + index}>
|
||||
<span>
|
||||
{index === item.type.split(", ").length - 1
|
||||
? el
|
||||
: (el += `, `)}
|
||||
</span>
|
||||
</React.Fragment>
|
||||
))
|
||||
: item.type}
|
||||
>
|
||||
<Icon
|
||||
className="h-5 w-5"
|
||||
strokeWidth={1.5}
|
||||
style={{
|
||||
color: nodeColors[item.family] ?? nodeColors.unknown,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<span className="ps-2 text-xs text-foreground">
|
||||
{nodeNames[item.family] ?? "Other"}{" "}
|
||||
<span className="text-xs">
|
||||
{" "}
|
||||
{item.type === "" ? "" : " - "}
|
||||
{item.type.split(", ").length > 2
|
||||
? item.type.split(", ").map((el, index) => (
|
||||
<React.Fragment key={el + index}>
|
||||
<span>
|
||||
{index === item.type.split(", ").length - 1
|
||||
? el
|
||||
: (el += `, `)}
|
||||
</span>
|
||||
</React.Fragment>
|
||||
))
|
||||
: item.type}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
} else {
|
||||
|
|
@ -172,7 +198,56 @@ 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" ||
|
||||
type === "dict" ||
|
||||
type === "NestedDict") &&
|
||||
!optionalHandle ? (
|
||||
<></>
|
||||
) : (
|
||||
<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={
|
||||
proxy
|
||||
? scapedJSONStringfy({ ...id, proxy })
|
||||
: scapedJSONStringfy(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>
|
||||
)
|
||||
) : (
|
||||
<div
|
||||
ref={ref}
|
||||
className="mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2"
|
||||
|
|
@ -214,33 +289,46 @@ export default function ParameterComponent({
|
|||
type === "code" ||
|
||||
type === "prompt" ||
|
||||
type === "file" ||
|
||||
type === "int") &&
|
||||
type === "int" ||
|
||||
type === "dict" ||
|
||||
type === "NestedDict") &&
|
||||
!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={proxy?scapedJSONStringfy({...id,proxy}):scapedJSONStringfy(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={
|
||||
proxy
|
||||
? scapedJSONStringfy({ ...id, proxy })
|
||||
: scapedJSONStringfy(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 &&
|
||||
|
|
@ -330,7 +418,6 @@ export default function ParameterComponent({
|
|||
suffixes={data.node?.template[name].suffixes}
|
||||
onFileChange={(filePath: string) => {
|
||||
data.node!.template[name].file_path = filePath;
|
||||
save();
|
||||
}}
|
||||
></InputFileComponent>
|
||||
</div>
|
||||
|
|
@ -353,11 +440,55 @@ export default function ParameterComponent({
|
|||
field_name={name}
|
||||
setNodeClass={(nodeClass) => {
|
||||
data.node = nodeClass;
|
||||
const clone = cloneDeep(data);
|
||||
clone.node = nodeClass;
|
||||
setData(clone);
|
||||
}}
|
||||
nodeClass={data.node}
|
||||
disabled={disabled}
|
||||
value={data.node?.template[name].value ?? ""}
|
||||
onChange={handleOnNewValue}
|
||||
onChange={(e) => {
|
||||
handleOnNewValue(e);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : left === true && type === "NestedDict" ? (
|
||||
<div className="mt-2 w-full">
|
||||
<DictComponent
|
||||
disabled={disabled}
|
||||
editNode={false}
|
||||
value={
|
||||
!data.node!.template[name].value ||
|
||||
data.node!.template[name].value?.toString() === "{}"
|
||||
? {
|
||||
yourkey: "value",
|
||||
}
|
||||
: data.node!.template[name].value
|
||||
}
|
||||
onChange={(newValue) => {
|
||||
data.node!.template[name].value = newValue;
|
||||
handleOnNewValue(newValue);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : left === true && type === "dict" ? (
|
||||
<div className="mt-2 w-full">
|
||||
<KeypairListComponent
|
||||
disabled={disabled}
|
||||
editNode={false}
|
||||
value={
|
||||
data.node!.template[name].value?.length === 0 ||
|
||||
!data.node!.template[name].value
|
||||
? [{ "": "" }]
|
||||
: convertObjToArray(data.node!.template[name].value)
|
||||
}
|
||||
duplicateKey={errorDuplicateKey}
|
||||
onChange={(newValue) => {
|
||||
const valueToNumbers = convertValuesToNumbers(newValue);
|
||||
data.node!.template[name].value = valueToNumbers;
|
||||
setErrorDuplicateKey(hasDuplicateKeys(valueToNumbers));
|
||||
handleOnNewValue(valueToNumbers);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import { validationStatusType } from "../../types/components";
|
|||
import { NodeDataType } from "../../types/flow";
|
||||
import {
|
||||
cleanEdges,
|
||||
getGroupStatus,
|
||||
handleKeyDown,
|
||||
scapedJSONStringfy,
|
||||
} from "../../utils/reactflowUtils";
|
||||
|
|
@ -36,7 +35,8 @@ 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 [inputName, setInputName] = useState(true);
|
||||
const [nodeName, setNodeName] = useState(data.node!.display_name);
|
||||
|
|
@ -46,6 +46,43 @@ export default function GenericNode({
|
|||
);
|
||||
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(() => {
|
||||
|
|
@ -67,8 +104,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];
|
||||
|
|
@ -79,7 +123,6 @@ export default function GenericNode({
|
|||
setValidationStatus(null);
|
||||
}
|
||||
}, [sseData, data.id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<NodeToolbar>
|
||||
|
|
@ -88,141 +131,257 @@ 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={data.node?.flow?"Ungroup":name}
|
||||
className="generic-node-icon"
|
||||
iconColor={`${nodeColors[types[data.type]]}`}
|
||||
/>
|
||||
<div className="generic-node-tooltip-div">
|
||||
{data.node?.flow && inputName ? (
|
||||
<div>
|
||||
<InputComponent
|
||||
autoFocus
|
||||
onBlur={() => {
|
||||
setInputName(false);
|
||||
if (nodeName.trim() !== "") {
|
||||
setNodeName(nodeName);
|
||||
data.node!.display_name = nodeName;
|
||||
} else {
|
||||
setNodeName(data.node!.display_name);
|
||||
}
|
||||
}}
|
||||
value={nodeName}
|
||||
onChange={setNodeName}
|
||||
password={false}
|
||||
blurOnEnter={true}
|
||||
/>
|
||||
<div>
|
||||
<div
|
||||
className={
|
||||
"generic-node-div-title " +
|
||||
(!showNode
|
||||
? " relative h-24 w-24 rounded-full "
|
||||
: " justify-between rounded-t-lg ")
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
"generic-node-title-arrangement rounded-full" +
|
||||
(!showNode && "justify-center")
|
||||
}
|
||||
>
|
||||
<IconComponent
|
||||
name={data.node?.flow ? "Ungroup" : 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">
|
||||
{data.node?.flow && inputName ? (
|
||||
<div>
|
||||
<InputComponent
|
||||
autoFocus
|
||||
onBlur={() => {
|
||||
setInputName(false);
|
||||
if (nodeName.trim() !== "") {
|
||||
setNodeName(nodeName);
|
||||
data.node!.display_name = nodeName;
|
||||
} else {
|
||||
setNodeName(data.node!.display_name);
|
||||
}
|
||||
}}
|
||||
value={nodeName}
|
||||
onChange={setNodeName}
|
||||
password={false}
|
||||
blurOnEnter={true}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<ShadTooltip content={data.node?.display_name}>
|
||||
<div
|
||||
className="generic-node-tooltip-div text-primary"
|
||||
onDoubleClick={() => setInputName(true)}
|
||||
>
|
||||
{data.node?.display_name}
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<ShadTooltip content={data.node?.display_name}>
|
||||
<div
|
||||
className="generic-node-tooltip-div text-primary"
|
||||
onDoubleClick={() => setInputName(true)}
|
||||
>
|
||||
{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"
|
||||
{!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={scapedJSONStringfy({
|
||||
inputTypes:
|
||||
data.node!.template[templateField].input_types,
|
||||
type: data.node!.template[templateField].type,
|
||||
id: data.id,
|
||||
fieldName: templateField,
|
||||
proxy: data.node!.template[templateField].proxy,
|
||||
})}
|
||||
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={{
|
||||
inputTypes:
|
||||
data.node!.template[templateField].input_types,
|
||||
type: data.node!.template[templateField].type,
|
||||
id: data.id,
|
||||
fieldName: templateField,
|
||||
}}
|
||||
left={true}
|
||||
type={data.node?.template[templateField].type}
|
||||
optionalHandle={
|
||||
data.node?.template[templateField].input_types
|
||||
}
|
||||
proxy={data.node?.template[templateField].proxy}
|
||||
showNode={showNode}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
></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>
|
||||
<ParameterComponent
|
||||
key={scapedJSONStringfy({
|
||||
baseClasses: data.node!.base_classes,
|
||||
id: data.id,
|
||||
dataType: data.type,
|
||||
})}
|
||||
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={{
|
||||
baseClasses: data.node!.base_classes,
|
||||
id: data.id,
|
||||
dataType: data.type,
|
||||
}}
|
||||
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>
|
||||
|
||||
<div className="generic-node-desc">
|
||||
{data.node?.flow && inputDescription ? (
|
||||
<Textarea
|
||||
autoFocus
|
||||
onBlur={() => {
|
||||
setInputDescription(false);
|
||||
if (nodeDescription.trim() !== "") {
|
||||
setNodeDescription(nodeDescription);
|
||||
data.node!.description = nodeDescription;
|
||||
} else {
|
||||
setNodeDescription(data.node!.description);
|
||||
}
|
||||
}}
|
||||
value={nodeDescription}
|
||||
onChange={(e) => setNodeDescription(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, nodeDescription, "");
|
||||
if (
|
||||
e.key === "Enter" &&
|
||||
e.shiftKey === false &&
|
||||
e.ctrlKey === false &&
|
||||
e.altKey === false
|
||||
) {
|
||||
{showNode && (
|
||||
<div
|
||||
className={
|
||||
showNode
|
||||
? "generic-node-desc " +
|
||||
(data.node?.description !== "" ? "py-5" : "pb-5")
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{data.node?.description !== "" &&
|
||||
showNode &&
|
||||
data.node?.flow &&
|
||||
inputDescription ? (
|
||||
<Textarea
|
||||
autoFocus
|
||||
onBlur={() => {
|
||||
setInputDescription(false);
|
||||
if (nodeDescription.trim() !== "") {
|
||||
setNodeDescription(nodeDescription);
|
||||
|
|
@ -230,110 +389,134 @@ export default function GenericNode({
|
|||
} else {
|
||||
setNodeDescription(data.node!.description);
|
||||
}
|
||||
}}
|
||||
value={nodeDescription}
|
||||
onChange={(e) => setNodeDescription(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, nodeDescription, "");
|
||||
if (
|
||||
e.key === "Enter" &&
|
||||
e.shiftKey === false &&
|
||||
e.ctrlKey === false &&
|
||||
e.altKey === false
|
||||
) {
|
||||
setInputDescription(false);
|
||||
if (nodeDescription.trim() !== "") {
|
||||
setNodeDescription(nodeDescription);
|
||||
data.node!.description = nodeDescription;
|
||||
} else {
|
||||
setNodeDescription(data.node!.description);
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="generic-node-desc-text"
|
||||
onDoubleClick={() => setInputDescription(true)}
|
||||
>
|
||||
{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={scapedJSONStringfy({
|
||||
inputTypes:
|
||||
data.node!.template[templateField].input_types,
|
||||
type: data.node!.template[templateField].type,
|
||||
id: data.id,
|
||||
fieldName: templateField,
|
||||
proxy: data.node!.template[templateField].proxy,
|
||||
})}
|
||||
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={{
|
||||
inputTypes:
|
||||
data.node!.template[templateField].input_types,
|
||||
type: data.node!.template[templateField].type,
|
||||
id: data.id,
|
||||
fieldName: templateField,
|
||||
}}
|
||||
left={true}
|
||||
type={data.node?.template[templateField].type}
|
||||
optionalHandle={
|
||||
data.node?.template[templateField].input_types
|
||||
}
|
||||
proxy={data.node?.template[templateField].proxy}
|
||||
showNode={showNode}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div
|
||||
className={classNames(
|
||||
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
|
||||
"flex-max-width justify-center"
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
</div>
|
||||
<ParameterComponent
|
||||
key={scapedJSONStringfy({
|
||||
baseClasses: data.node!.base_classes,
|
||||
id: data.id,
|
||||
dataType: data.type,
|
||||
})}
|
||||
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
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="generic-node-desc-text"
|
||||
onDoubleClick={() => setInputDescription(true)}
|
||||
>
|
||||
{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={scapedJSONStringfy({
|
||||
inputTypes:
|
||||
data.node!.template[templateField].input_types,
|
||||
type: data.node!.template[templateField].type,
|
||||
id: data.id,
|
||||
fieldName: templateField,
|
||||
proxy: data.node!.template[templateField].proxy,
|
||||
})}
|
||||
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={{
|
||||
inputTypes:
|
||||
data.node!.template[templateField].input_types,
|
||||
type: data.node!.template[templateField].type,
|
||||
id: data.id,
|
||||
fieldName: templateField,
|
||||
}}
|
||||
left={true}
|
||||
type={data.node?.template[templateField].type}
|
||||
optionalHandle={
|
||||
data.node?.template[templateField].input_types
|
||||
}
|
||||
proxy={data.node?.template[templateField].proxy}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div
|
||||
className={classNames(
|
||||
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
|
||||
"flex-max-width justify-center"
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
</div>
|
||||
<ParameterComponent
|
||||
key={scapedJSONStringfy({
|
||||
baseClasses: data.node!.base_classes,
|
||||
id: data.id,
|
||||
dataType: data.type,
|
||||
})}
|
||||
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={{
|
||||
baseClasses: data.node!.base_classes,
|
||||
id: data.id,
|
||||
dataType: data.type,
|
||||
}}
|
||||
type={data.node?.base_classes.join("|")}
|
||||
left={false}
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
tooltipTitle={data.node?.base_classes.join("\n")}
|
||||
id={{
|
||||
baseClasses: data.node!.base_classes,
|
||||
id: data.id,
|
||||
dataType: data.type,
|
||||
}}
|
||||
type={data.node?.base_classes.join("|")}
|
||||
left={false}
|
||||
showNode={showNode}
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -36,32 +36,21 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
}
|
||||
if (invalidName !== undefined) {
|
||||
if (!nameLists.current.includes(value)) {
|
||||
setInvalidName(false);
|
||||
setInvalidName!(false);
|
||||
} else {
|
||||
setInvalidName(true);
|
||||
setInvalidName!(true);
|
||||
}
|
||||
|
||||
if (!nameLists.current.includes(value)) {
|
||||
setInvalidName!(false);
|
||||
} else {
|
||||
setInvalidName!(true);
|
||||
}
|
||||
}
|
||||
if (!nameLists.current.includes(value)) {
|
||||
setInvalidName!(false);
|
||||
} else {
|
||||
setInvalidName!(true);
|
||||
}
|
||||
setName(value);
|
||||
setCurrentName(value);
|
||||
};
|
||||
|
||||
const [currentName, setCurrentName] = useState(name);
|
||||
|
||||
const [currentDescription, setCurrentDescription] = useState(description);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentName(name);
|
||||
setCurrentDescription(description);
|
||||
}, [name, description]);
|
||||
|
||||
const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
flows.find((f) => f.id === tabId).description = event.target.value;
|
||||
setCurrentDescription(flows.find((f) => f.id === tabId).description);
|
||||
setDescription(event.target.value);
|
||||
};
|
||||
|
||||
|
|
@ -78,11 +67,11 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
)}
|
||||
</div>
|
||||
<Input
|
||||
className="nopan nodrag noundo nocopy mt-2 font-normal"
|
||||
className="nopan nodelete nodrag noundo nocopy mt-2 font-normal"
|
||||
onChange={handleNameChange}
|
||||
type="text"
|
||||
name="name"
|
||||
value={currentName ?? ""}
|
||||
value={name ?? ""}
|
||||
placeholder="File name"
|
||||
id="name"
|
||||
maxLength={maxLength}
|
||||
|
|
@ -97,7 +86,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
name="description"
|
||||
id="description"
|
||||
onChange={handleDescriptionChange}
|
||||
value={currentDescription}
|
||||
value={description!}
|
||||
placeholder="Flow description"
|
||||
className="mt-2 max-h-[100px] font-normal"
|
||||
rows={3}
|
||||
|
|
|
|||
|
|
@ -12,17 +12,15 @@ import { Button } from "../ui/button";
|
|||
|
||||
export default function PaginatorComponent({
|
||||
pageSize = 10,
|
||||
pageIndex = 0,
|
||||
pageIndex = 1,
|
||||
rowsCount = [10, 20, 50, 100],
|
||||
totalRowsCount = 0,
|
||||
paginate,
|
||||
}: PaginatorComponentType) {
|
||||
const [size, setPageSize] = useState(pageSize);
|
||||
const [index, setPageIndex] = useState(pageIndex);
|
||||
const [maxIndex, setMaxPageIndex] = useState(
|
||||
Math.ceil(totalRowsCount / pageSize)
|
||||
);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
useEffect(() => {
|
||||
setMaxPageIndex(Math.ceil(totalRowsCount / size));
|
||||
|
|
@ -39,8 +37,9 @@ export default function PaginatorComponent({
|
|||
onValueChange={(pageSize: string) => {
|
||||
setPageSize(Number(pageSize));
|
||||
setMaxPageIndex(Math.ceil(totalRowsCount / Number(pageSize)));
|
||||
paginate(Number(pageSize), 0);
|
||||
paginate(Number(pageSize), 1);
|
||||
}}
|
||||
value={pageSize.toString()}
|
||||
>
|
||||
<SelectTrigger className="w-[100px]">
|
||||
<SelectValue placeholder="10" />
|
||||
|
|
@ -55,30 +54,25 @@ export default function PaginatorComponent({
|
|||
</Select>
|
||||
</div>
|
||||
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
|
||||
Page {currentPage} of {maxIndex}
|
||||
Page {pageIndex} of {maxIndex}
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
disabled={index <= 0}
|
||||
disabled={pageIndex <= 1}
|
||||
variant="outline"
|
||||
className="hidden h-8 w-8 p-0 lg:flex"
|
||||
onClick={() => {
|
||||
setPageIndex(0);
|
||||
setCurrentPage(1);
|
||||
paginate(size, 0);
|
||||
paginate(size, 1);
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Go to first page</span>
|
||||
<IconComponent name="ChevronsLeft" className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
disabled={index <= 0}
|
||||
disabled={pageIndex <= 1}
|
||||
onClick={() => {
|
||||
if (index > 0) {
|
||||
const pgIndex = size - index;
|
||||
setCurrentPage(currentPage - 1);
|
||||
setPageIndex(pgIndex);
|
||||
paginate(size, pgIndex);
|
||||
if (pageIndex > 0) {
|
||||
paginate(size, pageIndex - 1);
|
||||
}
|
||||
}}
|
||||
variant="outline"
|
||||
|
|
@ -88,12 +82,9 @@ export default function PaginatorComponent({
|
|||
<IconComponent name="ChevronLeft" className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
disabled={currentPage === maxIndex}
|
||||
disabled={pageIndex === maxIndex}
|
||||
onClick={() => {
|
||||
const pgIndex = size + index;
|
||||
setPageIndex(pgIndex);
|
||||
setCurrentPage(currentPage + 1);
|
||||
paginate(size, pgIndex);
|
||||
paginate(size, pageIndex + 1);
|
||||
}}
|
||||
variant="outline"
|
||||
className="h-8 w-8 p-0"
|
||||
|
|
@ -102,13 +93,11 @@ export default function PaginatorComponent({
|
|||
<IconComponent name="ChevronRight" className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
disabled={currentPage === maxIndex}
|
||||
disabled={pageIndex === maxIndex}
|
||||
variant="outline"
|
||||
className="hidden h-8 w-8 p-0 lg:flex"
|
||||
onClick={() => {
|
||||
setPageIndex(maxIndex - 1);
|
||||
setCurrentPage(maxIndex);
|
||||
paginate(size, size);
|
||||
paginate(size, maxIndex);
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Go to last page</span>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,10 @@ export default function BuildTrigger({
|
|||
if (isBuilding) {
|
||||
return;
|
||||
}
|
||||
const errors = validateNodes(reactFlowInstance!);
|
||||
const errors = validateNodes(
|
||||
reactFlowInstance!.getNodes(),
|
||||
reactFlowInstance!.getEdges()
|
||||
);
|
||||
if (errors.length > 0) {
|
||||
setErrorData({
|
||||
title: "Oops! Looks like you missed something",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { useContext, useEffect, useState } from "react";
|
|||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||
import AccordionComponent from "../../components/AccordionComponent";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import CodeAreaComponent from "../../components/codeAreaComponent";
|
||||
import Dropdown from "../../components/dropdownComponent";
|
||||
import FloatComponent from "../../components/floatComponent";
|
||||
|
|
@ -31,9 +30,16 @@ import {
|
|||
import { darkContext } from "../../contexts/darkContext";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import { codeTabsPropsType } from "../../types/components";
|
||||
import { unselectAllNodes } from "../../utils/reactflowUtils";
|
||||
import {
|
||||
convertObjToArray,
|
||||
convertValuesToNumbers,
|
||||
hasDuplicateKeys,
|
||||
unselectAllNodes,
|
||||
} from "../../utils/reactflowUtils";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import DictComponent from "../dictComponent";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import KeypairListComponent from "../keypairListComponent";
|
||||
|
||||
export default function CodeTabsComponent({
|
||||
flow,
|
||||
|
|
@ -48,6 +54,7 @@ export default function CodeTabsComponent({
|
|||
const [openAccordion, setOpenAccordion] = useState<string[]>([]);
|
||||
const { dark } = useContext(darkContext);
|
||||
const { reactFlowInstance } = useContext(typesContext);
|
||||
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (flow && flow["data"]!["nodes"]) {
|
||||
|
|
@ -248,7 +255,11 @@ export default function CodeTabsComponent({
|
|||
node.data.node.template[templateField]
|
||||
.type === "file" ||
|
||||
node.data.node.template[templateField]
|
||||
.type === "int")
|
||||
.type === "int" ||
|
||||
node.data.node.template[templateField]
|
||||
.type === "dict" ||
|
||||
node.data.node.template[templateField]
|
||||
.type === "NestedDict")
|
||||
)
|
||||
.map((templateField, indx) => {
|
||||
return (
|
||||
|
|
@ -310,55 +321,46 @@ export default function CodeTabsComponent({
|
|||
) : node.data.node.template[
|
||||
templateField
|
||||
].multiline ? (
|
||||
<ShadTooltip
|
||||
content={tweaks.buildContent!(
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].value
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<TextAreaComponent
|
||||
disabled={false}
|
||||
editNode={true}
|
||||
value={
|
||||
!node.data.node
|
||||
.template[
|
||||
<div>
|
||||
<TextAreaComponent
|
||||
disabled={false}
|
||||
editNode={true}
|
||||
value={
|
||||
!node.data.node
|
||||
.template[
|
||||
templateField
|
||||
].value ||
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].value === ""
|
||||
? ""
|
||||
: node.data.node
|
||||
.template[
|
||||
templateField
|
||||
].value
|
||||
}
|
||||
onChange={(target) => {
|
||||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList![
|
||||
i
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value ||
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node
|
||||
.template[
|
||||
templateField
|
||||
].value === ""
|
||||
? ""
|
||||
: node.data.node
|
||||
.template[
|
||||
templateField
|
||||
].value
|
||||
}
|
||||
onChange={(target) => {
|
||||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList![
|
||||
i
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node
|
||||
.template[
|
||||
templateField
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<InputComponent
|
||||
editNode={true}
|
||||
|
|
@ -440,52 +442,35 @@ export default function CodeTabsComponent({
|
|||
) : node.data.node.template[
|
||||
templateField
|
||||
].type === "file" ? (
|
||||
<ShadTooltip
|
||||
content={tweaks.buildContent!(
|
||||
!node.data.node.template[
|
||||
templateField
|
||||
].value ||
|
||||
<div className="mx-auto">
|
||||
<InputFileComponent
|
||||
editNode={true}
|
||||
disabled={false}
|
||||
value={
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].value === ""
|
||||
? ""
|
||||
: node.data.node.template[
|
||||
templateField
|
||||
].value
|
||||
)}
|
||||
>
|
||||
<div className="mx-auto">
|
||||
<InputFileComponent
|
||||
editNode={true}
|
||||
disabled={false}
|
||||
value={
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].value ?? ""
|
||||
}
|
||||
onChange={(
|
||||
target: any
|
||||
) => {}}
|
||||
fileTypes={
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].fileTypes
|
||||
}
|
||||
suffixes={
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].suffixes
|
||||
}
|
||||
onFileChange={(
|
||||
value: any
|
||||
) => {
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].file_path = value;
|
||||
}}
|
||||
></InputFileComponent>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
].value ?? ""
|
||||
}
|
||||
onChange={(target: any) => {}}
|
||||
fileTypes={
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].fileTypes
|
||||
}
|
||||
suffixes={
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].suffixes
|
||||
}
|
||||
onFileChange={(
|
||||
value: any
|
||||
) => {
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].file_path = value;
|
||||
}}
|
||||
></InputFileComponent>
|
||||
</div>
|
||||
) : node.data.node.template[
|
||||
templateField
|
||||
].type === "float" ? (
|
||||
|
|
@ -617,130 +602,197 @@ export default function CodeTabsComponent({
|
|||
) : node.data.node.template[
|
||||
templateField
|
||||
].type === "prompt" ? (
|
||||
<ShadTooltip
|
||||
content={tweaks.buildContent!(
|
||||
!node.data.node.template[
|
||||
templateField
|
||||
].value ||
|
||||
<div className="mx-auto">
|
||||
<PromptAreaComponent
|
||||
readonly={
|
||||
node.data.node?.flow &&
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].dynamic
|
||||
? true
|
||||
: false
|
||||
}
|
||||
editNode={true}
|
||||
disabled={false}
|
||||
value={
|
||||
!node.data.node.template[
|
||||
templateField
|
||||
].value ||
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].value === ""
|
||||
? ""
|
||||
: node.data.node.template[
|
||||
? ""
|
||||
: node.data.node.template[
|
||||
templateField
|
||||
].value
|
||||
}
|
||||
onChange={(target) => {
|
||||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList![
|
||||
i
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value
|
||||
)}
|
||||
>
|
||||
<div className="mx-auto">
|
||||
<PromptAreaComponent
|
||||
readonly={
|
||||
node.data.node?.flow &&
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].dynamic
|
||||
? true
|
||||
: false
|
||||
}
|
||||
editNode={true}
|
||||
disabled={false}
|
||||
value={
|
||||
!node.data.node.template[
|
||||
templateField
|
||||
].value ||
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].value === ""
|
||||
? ""
|
||||
: node.data.node
|
||||
.template[
|
||||
templateField
|
||||
].value
|
||||
}
|
||||
onChange={(target) => {
|
||||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList![
|
||||
i
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : node.data.node.template[
|
||||
templateField
|
||||
].type === "code" ? (
|
||||
<ShadTooltip
|
||||
content={tweaks.buildContent!(
|
||||
tweaks.getValue!(
|
||||
<div className="mx-auto">
|
||||
<CodeAreaComponent
|
||||
disabled={false}
|
||||
editNode={true}
|
||||
readonly={
|
||||
node.data.node?.flow &&
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].value,
|
||||
node.data,
|
||||
].dynamic
|
||||
? true
|
||||
: false
|
||||
}
|
||||
value={
|
||||
!node.data.node.template[
|
||||
templateField
|
||||
].value ||
|
||||
node.data.node.template[
|
||||
templateField
|
||||
]
|
||||
)
|
||||
)}
|
||||
>
|
||||
<div className="mx-auto">
|
||||
<CodeAreaComponent
|
||||
readonly={
|
||||
node.data.node?.flow &&
|
||||
].value === ""
|
||||
? ""
|
||||
: node.data.node.template[
|
||||
templateField
|
||||
].value
|
||||
}
|
||||
onChange={(target) => {
|
||||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList![
|
||||
i
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].dynamic
|
||||
? true
|
||||
: false
|
||||
}
|
||||
disabled={false}
|
||||
editNode={true}
|
||||
value={
|
||||
!node.data.node.template[
|
||||
templateField
|
||||
].value ||
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].value === ""
|
||||
? ""
|
||||
: node.data.node
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : node.data.node.template[
|
||||
templateField
|
||||
].type === "dict" ? (
|
||||
<div className="mx-auto overflow-auto custom-scroll">
|
||||
<KeypairListComponent
|
||||
disabled={false}
|
||||
editNode={true}
|
||||
value={
|
||||
node.data.node!.template[
|
||||
templateField
|
||||
].value?.length === 0 ||
|
||||
!node.data.node!.template[
|
||||
templateField
|
||||
].value
|
||||
? [{ "": "" }]
|
||||
: convertObjToArray(
|
||||
node.data.node!
|
||||
.template[
|
||||
templateField
|
||||
].value
|
||||
}
|
||||
onChange={(target) => {
|
||||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList![
|
||||
i
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
]
|
||||
)
|
||||
}
|
||||
duplicateKey={
|
||||
errorDuplicateKey
|
||||
}
|
||||
onChange={(target) => {
|
||||
const valueToNumbers =
|
||||
convertValuesToNumbers(
|
||||
target
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
node.data.node!.template[
|
||||
templateField
|
||||
].value = valueToNumbers;
|
||||
setErrorDuplicateKey(
|
||||
hasDuplicateKeys(
|
||||
valueToNumbers
|
||||
)
|
||||
);
|
||||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList![
|
||||
i
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : node.data.node.template[
|
||||
templateField
|
||||
].type === "NestedDict" ? (
|
||||
<div className="mx-auto">
|
||||
<DictComponent
|
||||
disabled={false}
|
||||
editNode={true}
|
||||
value={
|
||||
node.data.node!.template[
|
||||
templateField
|
||||
].value.toString() === "{}"
|
||||
? {
|
||||
yourkey: "value",
|
||||
}
|
||||
: node.data.node!
|
||||
.template[
|
||||
templateField
|
||||
].value
|
||||
}
|
||||
onChange={(target) => {
|
||||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList![
|
||||
i
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
node.data.node.template[
|
||||
templateField
|
||||
]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : node.data.node.template[
|
||||
templateField
|
||||
].type === "Any" ? (
|
||||
|
|
|
|||
55
src/frontend/src/components/dictComponent/index.tsx
Normal file
55
src/frontend/src/components/dictComponent/index.tsx
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
import { DictComponentType } from "../../types/components";
|
||||
|
||||
import DictAreaModal from "../../modals/dictAreaModal";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import { Input } from "../ui/input";
|
||||
|
||||
export default function DictComponent({
|
||||
value,
|
||||
onChange,
|
||||
disabled,
|
||||
editNode = false,
|
||||
}: DictComponentType): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
onChange({});
|
||||
}
|
||||
}, [disabled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (value) onChange(value);
|
||||
}, [value]);
|
||||
|
||||
const ref = useRef(value);
|
||||
debugger;
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
value.length > 1 && editNode ? "my-1" : "",
|
||||
"flex flex-col gap-3"
|
||||
)}
|
||||
>
|
||||
{
|
||||
<div className="flex w-full gap-3">
|
||||
<DictAreaModal
|
||||
value={ref.current}
|
||||
onChange={(obj) => {
|
||||
onChange(obj);
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
className={
|
||||
editNode
|
||||
? "input-edit-node input-disable pointer-events-none cursor-pointer"
|
||||
: "input-disable pointer-events-none cursor-pointer"
|
||||
}
|
||||
placeholder="Click to edit your dictionary..."
|
||||
/>
|
||||
</DictAreaModal>
|
||||
</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);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { gradients } from "../../utils/styleUtils";
|
||||
|
||||
export default function GradientChooserComponent({ value, onChange }) {
|
||||
return (
|
||||
<div className="flex flex-wrap items-center justify-center gap-4">
|
||||
{gradients.map((gradient, idx) => (
|
||||
<div
|
||||
onClick={() => {
|
||||
onChange(gradient);
|
||||
}}
|
||||
className={
|
||||
"duration-400 h-12 w-12 cursor-pointer rounded-full transition-all " +
|
||||
gradient +
|
||||
(value === gradient ? " shadow-lg ring-2 ring-primary" : "")
|
||||
}
|
||||
key={idx}
|
||||
></div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -139,7 +139,7 @@ export default function Header(): JSX.Element {
|
|||
<button
|
||||
className={
|
||||
"h-7 w-7 rounded-full focus-visible:outline-0 " +
|
||||
gradients[gradientIndex]
|
||||
(userData?.profile_image ?? gradients[gradientIndex])
|
||||
}
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
|
|
@ -154,6 +154,12 @@ export default function Header(): JSX.Element {
|
|||
Admin Page
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
onClick={() => navigate("/account/settings")}
|
||||
>
|
||||
Profile Settings
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ export default function InputListComponent({
|
|||
}
|
||||
}, [disabled]);
|
||||
|
||||
// @TODO Recursive Character Text Splitter - the value might be in string format, whereas the InputListComponent specifically requires an array format. To ensure smooth operation and prevent potential errors, it's crucial that we handle the conversion from a string to an array with the string as its element.
|
||||
if (typeof value === "string") {
|
||||
value = [value];
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}}
|
||||
|
|
|
|||
117
src/frontend/src/components/keypairListComponent/index.tsx
Normal file
117
src/frontend/src/components/keypairListComponent/index.tsx
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
import { KeyPairListComponentType } from "../../types/components";
|
||||
|
||||
import _ from "lodash";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import { Input } from "../ui/input";
|
||||
|
||||
export default function KeypairListComponent({
|
||||
value,
|
||||
onChange,
|
||||
disabled,
|
||||
editNode = false,
|
||||
duplicateKey,
|
||||
advanced = false,
|
||||
dataValue,
|
||||
}: KeyPairListComponentType): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
onChange([""]);
|
||||
}
|
||||
}, [disabled]);
|
||||
|
||||
const ref = useRef(value.length === 0 ? [{ "": "" }] : value);
|
||||
|
||||
useEffect(() => {
|
||||
if (JSON.stringify(value) !== JSON.stringify(ref.current)) {
|
||||
ref.current = value;
|
||||
onChange(value);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
const handleChangeKey = (event, idx) => {
|
||||
const oldKey = Object.keys(ref.current[idx])[0];
|
||||
const updatedObj = { [event.target.value]: ref.current[idx][oldKey] };
|
||||
ref.current[idx] = updatedObj;
|
||||
onChange(ref.current);
|
||||
};
|
||||
|
||||
const handleChangeValue = (newValue, idx) => {
|
||||
const key = Object.keys(ref.current[idx])[0];
|
||||
ref.current[idx][key] = newValue;
|
||||
onChange(ref.current);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
ref.current?.length > 1 && editNode ? "mx-2 my-1" : "",
|
||||
"flex h-full flex-col gap-3"
|
||||
)}
|
||||
>
|
||||
{ref.current?.map((obj, index) => {
|
||||
return Object.keys(obj).map((key, idx) => {
|
||||
return (
|
||||
<div key={idx} className="flex w-full gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
value={key.trim()}
|
||||
className={classNames(
|
||||
editNode ? "input-edit-node" : "",
|
||||
duplicateKey ? "input-invalid" : ""
|
||||
)}
|
||||
placeholder="Type key..."
|
||||
onChange={(event) => handleChangeKey(event, index)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.ctrlKey && e.key === "Backspace") {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="text"
|
||||
value={obj[key]}
|
||||
className={editNode ? "input-edit-node" : ""}
|
||||
placeholder="Type a value..."
|
||||
onChange={(event) =>
|
||||
handleChangeValue(event.target.value, index)
|
||||
}
|
||||
/>
|
||||
|
||||
{index === ref.current.length - 1 ? (
|
||||
<button
|
||||
onClick={() => {
|
||||
let newInputList = _.cloneDeep(ref.current);
|
||||
newInputList.push({ "": "" });
|
||||
onChange(newInputList);
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="Plus"
|
||||
className={"h-4 w-4 hover:text-accent-foreground"}
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => {
|
||||
let newInputList = _.cloneDeep(ref.current);
|
||||
newInputList.splice(index, 1);
|
||||
onChange(newInputList);
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="X"
|
||||
className="h-4 w-4 hover:text-status-red"
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -4,10 +4,10 @@ export default function LoadingComponent({
|
|||
remSize,
|
||||
}: LoadingComponentProps): JSX.Element {
|
||||
return (
|
||||
<div role="status" className="m-auto w-min">
|
||||
<div role="status" className="flex flex-col items-center justify-center">
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className={`w-${remSize} h-${remSize} mr-2 animate-spin fill-almost-medium-blue text-muted`}
|
||||
className={`w-${remSize} h-${remSize} animate-spin fill-almost-medium-blue text-muted`}
|
||||
viewBox="0 0 100 101"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { useEffect } from "react";
|
|||
import { TypeModal } from "../../constants/enums";
|
||||
import { postValidatePrompt } from "../../controllers/API";
|
||||
import GenericModal from "../../modals/genericModal";
|
||||
import { TextAreaComponentType } from "../../types/components";
|
||||
import { PromptAreaComponentType } from "../../types/components";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
|
||||
export default function PromptAreaComponent({
|
||||
|
|
@ -15,7 +15,7 @@ export default function PromptAreaComponent({
|
|||
disabled,
|
||||
editNode = false,
|
||||
readonly = false,
|
||||
}: TextAreaComponentType): JSX.Element {
|
||||
}: PromptAreaComponentType): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
onChange("");
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ export const SkeletonCardComponent = (): JSX.Element => {
|
|||
<div className="skeleton-card">
|
||||
<div className="skeleton-card-wrapper">
|
||||
<Skeleton className="h-8 w-8 rounded-full" />
|
||||
<Skeleton className="h-4 w-[120px]" />
|
||||
<Skeleton className="h-4 w-[40%]" />
|
||||
</div>
|
||||
<div className="skeleton-card-text">
|
||||
<Skeleton className="h-3 w-[250px]" />
|
||||
<Skeleton className="h-3 w-[200px]" />
|
||||
<Skeleton className="h-3 w-[90%]" />
|
||||
<Skeleton className="h-3 w-[80%]" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||
import { Cross2Icon } from "@radix-ui/react-icons";
|
||||
import * as React from "react";
|
||||
import { cn } from "../../utils/utils";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
|
||||
const Dialog = DialogPrimitive.Root;
|
||||
|
||||
const DialogTrigger = DialogPrimitive.Trigger;
|
||||
|
||||
const DialogPortal = ({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: DialogPrimitive.DialogPortalProps) => (
|
||||
<DialogPrimitive.Portal className={cn(className)} {...props}>
|
||||
<div className="fixed inset-0 z-50 flex items-start justify-center sm:items-center">
|
||||
<DialogPrimitive.Portal {...props}>
|
||||
<div className="nopan nodelete nodrag noundo nocopy fixed inset-0 z-50 flex items-start justify-center sm:items-center">
|
||||
{children}
|
||||
</div>
|
||||
</DialogPrimitive.Portal>
|
||||
|
|
@ -27,7 +26,7 @@ const DialogOverlay = React.forwardRef<
|
|||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed inset-0 bottom-0 left-0 right-0 top-0 z-50 overflow-auto bg-blur-shared backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
"nopan nodelete nodrag noundo nocopy fixed inset-0 bottom-0 left-0 right-0 top-0 z-50 overflow-auto bg-blur-shared backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -44,14 +43,14 @@ const DialogContent = React.forwardRef<
|
|||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"noundo nocopy items-left z-50 flex w-full max-w-lg flex-col justify-center gap-4 overflow-auto border bg-background p-6 text-start shadow-lg duration-200 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-[state=closed]:slide-out-to-bottom-[48%] data-[state=open]:slide-in-from-bottom-[48%] sm:rounded-lg md:w-full",
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<IconComponent name="X" className="h-4 w-4" />
|
||||
<Cross2Icon className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn("nopan nodrag noundo nocopy primary-input", className)}
|
||||
className={cn(
|
||||
"nopan nodelete nodrag noundo nocopy primary-input",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ export default function RenameLabel(props) {
|
|||
ref={inputRef}
|
||||
onInput={resizeInput}
|
||||
className={cn(
|
||||
"nopan nodrag noundo nocopy rounded-md bg-transparent px-2 outline-ring hover:outline focus:border-none focus:outline active:outline",
|
||||
"nopan nodelete nodrag noundo nocopy rounded-md bg-transparent px-2 outline-ring hover:outline focus:border-none focus:outline active:outline",
|
||||
props.className
|
||||
)}
|
||||
onBlur={() => {
|
||||
|
|
|
|||
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,
|
||||
};
|
||||
|
|
@ -8,7 +8,10 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|||
({ className, ...props }, ref) => {
|
||||
return (
|
||||
<textarea
|
||||
className={cn("nopan nodrag noundo nocopy textarea-primary", className)}
|
||||
className={cn(
|
||||
"nopan nodelete nodrag noundo nocopy textarea-primary",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -115,6 +115,9 @@ export const EDIT_DIALOG_SUBTITLE =
|
|||
export const CODE_PROMPT_DIALOG_SUBTITLE =
|
||||
"Edit your Python code. This code snippet accepts module import and a single function definition. Make sure that your function returns a string.";
|
||||
|
||||
export const CODE_DICT_DIALOG_SUBTITLE =
|
||||
"Edit your dictionary. This dialog allows you to create your own customized dictionary. You can add as many key-value pairs as you want. While in edit mode, you can enter ({}) or ([]), and this will result in adding a new object or array.";
|
||||
|
||||
/**
|
||||
* The base text for subtitle of Prompt Dialog
|
||||
* @constant
|
||||
|
|
@ -499,6 +502,21 @@ export const NOUNS: string[] = [
|
|||
*/
|
||||
export const USER_PROJECTS_HEADER = "My Collection";
|
||||
|
||||
/**
|
||||
* Header text for admin page
|
||||
* @constant
|
||||
*
|
||||
*/
|
||||
export const ADMIN_HEADER_TITLE = "Admin Page";
|
||||
|
||||
/**
|
||||
* Header description for admin page
|
||||
* @constant
|
||||
*
|
||||
*/
|
||||
export const ADMIN_HEADER_DESCRIPTION =
|
||||
"Navigate through this section to efficiently oversee all application users. From here, you can seamlessly manage user accounts.";
|
||||
|
||||
/**
|
||||
* URLs excluded from error retries.
|
||||
* @constant
|
||||
|
|
@ -511,7 +529,13 @@ export const URL_EXCLUDED_FROM_ERROR_RETRIES = [
|
|||
"http://localhost:7860/login",
|
||||
];
|
||||
|
||||
export const skipNodeUpdate = ["CustomComponent"];
|
||||
export const skipNodeUpdate = [
|
||||
"CustomComponent",
|
||||
"PromptTemplate",
|
||||
"ChatMessagePromptTemplate",
|
||||
"SystemMessagePromptTemplate",
|
||||
"HumanMessagePromptTemplate",
|
||||
];
|
||||
|
||||
export const CONTROL_INPUT_STATE = {
|
||||
password: "",
|
||||
|
|
@ -519,6 +543,12 @@ export const CONTROL_INPUT_STATE = {
|
|||
username: "",
|
||||
};
|
||||
|
||||
export const CONTROL_PATCH_USER_STATE = {
|
||||
password: "",
|
||||
cnfPassword: "",
|
||||
gradient: "",
|
||||
};
|
||||
|
||||
export const CONTROL_LOGIN_STATE = {
|
||||
username: "",
|
||||
password: "",
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ export const AuthContext = createContext<AuthContextType>(initialValue);
|
|||
export function AuthProvider({ children }): React.ReactElement {
|
||||
const cookies = new Cookies();
|
||||
const [accessToken, setAccessToken] = useState<string | null>(
|
||||
cookies.get("access_token")
|
||||
cookies.get("access_tkn_lflw")
|
||||
);
|
||||
const [refreshToken, setRefreshToken] = useState<string | null>(
|
||||
cookies.get("refresh_token")
|
||||
cookies.get("refresh_tkn_lflw")
|
||||
);
|
||||
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
|
||||
const [isAdmin, setIsAdmin] = useState<boolean>(false);
|
||||
|
|
@ -37,7 +37,7 @@ export function AuthProvider({ children }): React.ReactElement {
|
|||
const [autoLogin, setAutoLogin] = useState<boolean>(false);
|
||||
const { setLoading } = useContext(alertContext);
|
||||
useEffect(() => {
|
||||
const storedAccessToken = cookies.get("access_token");
|
||||
const storedAccessToken = cookies.get("access_tkn_lflw");
|
||||
if (storedAccessToken) {
|
||||
setAccessToken(storedAccessToken);
|
||||
}
|
||||
|
|
@ -63,10 +63,13 @@ export function AuthProvider({ children }): React.ReactElement {
|
|||
.then((user) => {
|
||||
setUserData(user);
|
||||
setLoading(false);
|
||||
const isSuperUser = user.is_superuser;
|
||||
const isSuperUser = user!.is_superuser;
|
||||
setIsAdmin(isSuperUser);
|
||||
})
|
||||
.catch((error) => {});
|
||||
.catch((error) => {
|
||||
console.log("auth context");
|
||||
setLoading(false);
|
||||
});
|
||||
} else {
|
||||
setLoading(false);
|
||||
}
|
||||
|
|
@ -74,23 +77,23 @@ export function AuthProvider({ children }): React.ReactElement {
|
|||
}, []);
|
||||
|
||||
function getAuthentication() {
|
||||
const storedRefreshToken = cookies.get("refresh_token");
|
||||
const storedAccess = cookies.get("access_token");
|
||||
const storedRefreshToken = cookies.get("refresh_tkn_lflw");
|
||||
const storedAccess = cookies.get("access_tkn_lflw");
|
||||
const auth = storedAccess && storedRefreshToken ? true : false;
|
||||
return auth;
|
||||
}
|
||||
|
||||
function login(newAccessToken: string, refreshToken: string) {
|
||||
cookies.set("access_token", newAccessToken, { path: "/" });
|
||||
cookies.set("refresh_token", refreshToken, { path: "/" });
|
||||
cookies.set("access_tkn_lflw", newAccessToken, { path: "/" });
|
||||
cookies.set("refresh_tkn_lflw", refreshToken, { path: "/" });
|
||||
setAccessToken(newAccessToken);
|
||||
setRefreshToken(refreshToken);
|
||||
setIsAuthenticated(true);
|
||||
}
|
||||
|
||||
function logout() {
|
||||
cookies.remove("access_token", { path: "/" });
|
||||
cookies.remove("refresh_token", { path: "/" });
|
||||
cookies.remove("access_tkn_lflw", { path: "/" });
|
||||
cookies.remove("refresh_tkn_lflw", { path: "/" });
|
||||
setIsAdmin(false);
|
||||
setUserData(null);
|
||||
setAccessToken(null);
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ import { typesContext } from "./typesContext";
|
|||
const uid = new ShortUniqueId({ length: 5 });
|
||||
|
||||
const TabsContextInitialValue: TabsContextType = {
|
||||
save: () => {},
|
||||
tabId: "",
|
||||
setTabId: (index: string) => {},
|
||||
isLoading: true,
|
||||
|
|
@ -62,7 +61,7 @@ const TabsContextInitialValue: TabsContextType = {
|
|||
isBuilt: false,
|
||||
setIsBuilt: (state: boolean) => {},
|
||||
hardReset: () => {},
|
||||
saveFlow: async (flow: FlowType) => {},
|
||||
saveFlow: async (flow: FlowType, silent?: boolean) => {},
|
||||
lastCopiedSelection: null,
|
||||
setLastCopiedSelection: (selection: any) => {},
|
||||
tabsState: {},
|
||||
|
|
@ -111,29 +110,6 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
return newNodeId.current;
|
||||
}
|
||||
|
||||
function save() {
|
||||
// added clone deep to avoid mutating the original object
|
||||
let Saveflows = _.cloneDeep(flows);
|
||||
if (Saveflows.length !== 0) {
|
||||
Saveflows.forEach((flow) => {
|
||||
if (flow.data && flow.data?.nodes)
|
||||
flow.data?.nodes.forEach((node) => {
|
||||
//looking for file fields to prevent saving the content and breaking the flow for exceeding the the data limite for local storage
|
||||
Object.keys(node.data.node.template).forEach((key) => {
|
||||
if (node.data.node.template[key].type === "file") {
|
||||
node.data.node.template[key].content = null;
|
||||
node.data.node.template[key].value = "";
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
window.localStorage.setItem(
|
||||
"tabsData",
|
||||
JSON.stringify({ tabId, flows: Saveflows, id })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshFlows() {
|
||||
setIsLoading(true);
|
||||
getTabsDataFromDB().then((DbData) => {
|
||||
|
|
@ -255,7 +231,6 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
function hardReset() {
|
||||
newNodeId.current = uid();
|
||||
setTabId("");
|
||||
|
||||
setFlows([]);
|
||||
setIsLoading(true);
|
||||
setId(uid());
|
||||
|
|
@ -616,13 +591,15 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
});
|
||||
}
|
||||
|
||||
async function saveFlow(newFlow: FlowType) {
|
||||
async function saveFlow(newFlow: FlowType, silent?: boolean) {
|
||||
try {
|
||||
// updates flow in db
|
||||
const updatedFlow = await updateFlowInDatabase(newFlow);
|
||||
if (updatedFlow) {
|
||||
// updates flow in state
|
||||
setSuccessData({ title: "Changes saved successfully" });
|
||||
if (!silent) {
|
||||
setSuccessData({ title: "Changes saved successfully" });
|
||||
}
|
||||
setFlows((prevState) => {
|
||||
const newFlows = [...prevState];
|
||||
const index = newFlows.findIndex((flow) => flow.id === newFlow.id);
|
||||
|
|
@ -666,7 +643,6 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
tabId,
|
||||
setTabId,
|
||||
flows,
|
||||
save,
|
||||
incrementNodeId,
|
||||
removeFlow,
|
||||
addFlow,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Node, ReactFlowInstance } from "reactflow";
|
||||
import { Edge, Node, ReactFlowInstance } from "reactflow";
|
||||
import { getAll, getHealth } from "../controllers/API";
|
||||
import { APIKindType } from "../types/api";
|
||||
import { typesContextType } from "../types/typesContext";
|
||||
|
|
@ -26,6 +26,9 @@ const initialValue: typesContextType = {
|
|||
setData: () => {},
|
||||
setFetchError: () => {},
|
||||
fetchError: false,
|
||||
setFilterEdge: (filter) => {},
|
||||
getFilterEdge: [],
|
||||
deleteEdge: () => {},
|
||||
};
|
||||
|
||||
export const typesContext = createContext<typesContextType>(initialValue);
|
||||
|
|
@ -39,6 +42,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.
|
||||
|
|
@ -89,19 +93,38 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
}
|
||||
|
||||
function deleteNode(idx: string) {
|
||||
function deleteNode(idx: string | Array<string>) {
|
||||
reactFlowInstance!.setNodes(
|
||||
reactFlowInstance!.getNodes().filter((node: Node) => node.id !== idx)
|
||||
reactFlowInstance!
|
||||
.getNodes()
|
||||
.filter((node: Node) =>
|
||||
typeof idx === "string" ? node.id !== idx : !idx.includes(node.id)
|
||||
)
|
||||
);
|
||||
reactFlowInstance!.setEdges(
|
||||
reactFlowInstance!
|
||||
.getEdges()
|
||||
.filter((edge) => edge.source !== idx && edge.target !== idx)
|
||||
.filter((edge) =>
|
||||
typeof idx === "string"
|
||||
? edge.source !== idx && edge.target !== idx
|
||||
: !idx.includes(edge.source) && !idx.includes(edge.target)
|
||||
)
|
||||
);
|
||||
}
|
||||
function deleteEdge(idx: string | Array<string>) {
|
||||
reactFlowInstance!.setEdges(
|
||||
reactFlowInstance!
|
||||
.getEdges()
|
||||
.filter((edge: Edge) =>
|
||||
typeof idx === "string" ? edge.id !== idx : !idx.includes(edge.id)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<typesContext.Provider
|
||||
value={{
|
||||
deleteEdge,
|
||||
types,
|
||||
setTypes,
|
||||
reactFlowInstance,
|
||||
|
|
@ -113,6 +136,8 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
setData,
|
||||
fetchError,
|
||||
setFetchError,
|
||||
setFilterEdge,
|
||||
getFilterEdge,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ function ApiInterceptor() {
|
|||
(response) => response,
|
||||
async (error: AxiosError) => {
|
||||
if (error.response?.status === 401) {
|
||||
const refreshToken = cookies.get("refresh_token");
|
||||
const refreshToken = cookies.get("refresh_tkn_lflw");
|
||||
if (refreshToken && refreshToken !== "auto") {
|
||||
authenticationErrorCount = authenticationErrorCount + 1;
|
||||
if (authenticationErrorCount > 3) {
|
||||
|
|
@ -31,13 +31,16 @@ function ApiInterceptor() {
|
|||
logout();
|
||||
navigate("/login");
|
||||
}
|
||||
|
||||
const res = await renewAccessToken(refreshToken);
|
||||
login(res?.data?.access_token, res?.data?.refresh_token);
|
||||
try {
|
||||
const res = await renewAccessToken(refreshToken);
|
||||
if (res?.data?.access_token && res?.data?.refresh_token) {
|
||||
login(res?.data?.access_token, res?.data?.refresh_token);
|
||||
}
|
||||
if (error?.config?.headers) {
|
||||
delete error.config.headers["Authorization"];
|
||||
error.config.headers["Authorization"] = `Bearer ${accessToken}`;
|
||||
error.config.headers["Authorization"] = `Bearer ${cookies.get(
|
||||
"access_tkn_lflw"
|
||||
)}`;
|
||||
const response = await axios.request(error.config);
|
||||
return response;
|
||||
}
|
||||
|
|
@ -45,6 +48,10 @@ function ApiInterceptor() {
|
|||
if (axios.isAxiosError(error) && error.response?.status === 401) {
|
||||
logout();
|
||||
navigate("/login");
|
||||
} else {
|
||||
console.error(error);
|
||||
logout();
|
||||
navigate("/login");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import {
|
|||
APIObjectType,
|
||||
LoginType,
|
||||
Users,
|
||||
changeUser,
|
||||
resetPasswordType,
|
||||
sendAllProps,
|
||||
} from "../../types/api/index";
|
||||
import { UserInputType } from "../../types/components";
|
||||
|
|
@ -144,8 +146,8 @@ export async function updateFlowInDatabase(
|
|||
description: updatedFlow.description,
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
if (response?.status !== 200) {
|
||||
throw new Error(`HTTP error! status: ${response?.status}`);
|
||||
}
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
|
|
@ -393,16 +395,17 @@ export async function autoLogin() {
|
|||
|
||||
export async function renewAccessToken(token: string) {
|
||||
try {
|
||||
return await api.post(`${BASE_URL_API}refresh?token=${token}`);
|
||||
if (token) {
|
||||
return await api.post(`${BASE_URL_API}refresh?token=${token}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getLoggedUser(): Promise<Users> {
|
||||
export async function getLoggedUser(): Promise<Users | null> {
|
||||
try {
|
||||
const res = await api.get(`${BASE_URL_API}user`);
|
||||
const res = await api.get(`${BASE_URL_API}users/whoami`);
|
||||
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
|
|
@ -411,14 +414,16 @@ export async function getLoggedUser(): Promise<Users> {
|
|||
console.log("Error:", error);
|
||||
throw error;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function addUser(user: UserInputType): Promise<Users> {
|
||||
export async function addUser(user: UserInputType): Promise<Array<Users>> {
|
||||
try {
|
||||
const res = await api.post(`${BASE_URL_API}user`, user);
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
const res = await api.post(`${BASE_URL_API}users/`, user);
|
||||
if (res.status !== 201) {
|
||||
throw new Error(res.data.detail);
|
||||
}
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
console.log("Error:", error);
|
||||
throw error;
|
||||
|
|
@ -428,10 +433,10 @@ export async function addUser(user: UserInputType): Promise<Users> {
|
|||
export async function getUsersPage(
|
||||
skip: number,
|
||||
limit: number
|
||||
): Promise<[Users]> {
|
||||
): 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;
|
||||
|
|
@ -440,11 +445,12 @@ export async function getUsersPage(
|
|||
console.log("Error:", error);
|
||||
throw error;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export async function deleteUser(user_id: string) {
|
||||
try {
|
||||
const res = await api.delete(`${BASE_URL_API}user/${user_id}`);
|
||||
const res = await api.delete(`${BASE_URL_API}users/${user_id}`);
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
}
|
||||
|
|
@ -454,9 +460,24 @@ export async function deleteUser(user_id: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function updateUser(user_id: string, user: Users) {
|
||||
export async function updateUser(user_id: string, user: changeUser) {
|
||||
try {
|
||||
const res = await api.patch(`${BASE_URL_API}user/${user_id}`, user);
|
||||
const res = await api.patch(`${BASE_URL_API}users/${user_id}`, user);
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function resetPassword(user_id: string, user: resetPasswordType) {
|
||||
try {
|
||||
const res = await api.patch(
|
||||
`${BASE_URL_API}users/${user_id}/reset-password`,
|
||||
user
|
||||
);
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
import { InfinityIcon } from "lucide-react";
|
||||
import { forwardRef } from "react";
|
||||
|
||||
const GradientSparkles = forwardRef<SVGSVGElement, React.PropsWithChildren<{}>>(
|
||||
(props, ref) => {
|
||||
return (
|
||||
<>
|
||||
<svg width="0" height="0" style={{ position: "absolute" }}>
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop className="gradient-start" offset="0%" />
|
||||
<stop className="gradient-end" offset="100%" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<InfinityIcon stroke="url(#grad1)" ref={ref} {...props} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default GradientSparkles;
|
||||
export const GradientSparkles = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<>
|
||||
<svg width="0" height="0" style={{ position: "absolute" }}>
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop className="gradient-start" offset="0%" />
|
||||
<stop className="gradient-end" offset="100%" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<InfinityIcon stroke="url(#grad1)" ref={ref} {...props} />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,11 +14,12 @@ 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";
|
||||
import { FlowType, NodeType } from "../../types/flow/index";
|
||||
import { buildTweaks } from "../../utils/reactflowUtils";
|
||||
import { buildTweaks, convertArrayToObj } from "../../utils/reactflowUtils";
|
||||
import {
|
||||
getCurlCode,
|
||||
getPythonApiCode,
|
||||
|
|
@ -33,23 +34,27 @@ const ApiModal = forwardRef(
|
|||
{
|
||||
flow,
|
||||
children,
|
||||
disable,
|
||||
}: {
|
||||
flow: FlowType;
|
||||
children: ReactNode;
|
||||
disable: boolean;
|
||||
},
|
||||
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,
|
||||
|
|
@ -100,7 +105,9 @@ const ApiModal = forwardRef(
|
|||
node.data.node.template[templateField].type === "code" ||
|
||||
node.data.node.template[templateField].type === "prompt" ||
|
||||
node.data.node.template[templateField].type === "file" ||
|
||||
node.data.node.template[templateField].type === "int")
|
||||
node.data.node.template[templateField].type === "int" ||
|
||||
node.data.node.template[templateField].type === "dict" ||
|
||||
node.data.node.template[templateField].type === "NestedDict")
|
||||
)
|
||||
.map((n, i) => {
|
||||
arrNodesWithValues.push(node["id"]);
|
||||
|
|
@ -113,7 +120,7 @@ const ApiModal = forwardRef(
|
|||
}
|
||||
function buildTweakObject(
|
||||
tw: string,
|
||||
changes: string | string[] | boolean | number,
|
||||
changes: string | string[] | boolean | number | Object[] | Object,
|
||||
template: TemplateVariableType
|
||||
) {
|
||||
if (typeof changes === "string" && template.type === "float") {
|
||||
|
|
@ -126,6 +133,14 @@ const ApiModal = forwardRef(
|
|||
changes = changes?.filter((x) => x !== "");
|
||||
}
|
||||
|
||||
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)
|
||||
);
|
||||
|
|
@ -152,10 +167,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;
|
||||
|
|
@ -201,8 +221,8 @@ const ApiModal = forwardRef(
|
|||
}
|
||||
|
||||
return (
|
||||
<BaseModal open={open} setOpen={setOpen} disable={disable}>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal open={open} setOpen={setOpen}>
|
||||
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={EXPORT_CODE_DIALOG}>
|
||||
<span className="pr-2">Code</span>
|
||||
<IconComponent
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import BaseModal from "../baseModal";
|
|||
|
||||
export default function ConfirmationModal({
|
||||
title,
|
||||
asChild,
|
||||
titleHeader,
|
||||
modalContent,
|
||||
modalContentTitle,
|
||||
|
|
@ -22,7 +23,7 @@ export default function ConfirmationModal({
|
|||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<BaseModal size="x-small" open={open} setOpen={setOpen}>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Trigger asChild={asChild}>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={titleHeader}>
|
||||
<span className="pr-2">{title}</span>
|
||||
<Icon
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { ReactNode, forwardRef, useContext, useEffect, useState } from "react";
|
||||
import {
|
||||
ReactNode,
|
||||
forwardRef,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import CodeAreaComponent from "../../components/codeAreaComponent";
|
||||
import DictComponent from "../../components/dictComponent";
|
||||
import Dropdown from "../../components/dropdownComponent";
|
||||
import FloatComponent from "../../components/floatComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
|
|
@ -8,6 +16,7 @@ import InputComponent from "../../components/inputComponent";
|
|||
import InputFileComponent from "../../components/inputFileComponent";
|
||||
import InputListComponent from "../../components/inputListComponent";
|
||||
import IntComponent from "../../components/intComponent";
|
||||
import KeypairListComponent from "../../components/keypairListComponent";
|
||||
import PromptAreaComponent from "../../components/promptComponent";
|
||||
import TextAreaComponent from "../../components/textAreaComponent";
|
||||
import ToggleShadComponent from "../../components/toggleShadComponent";
|
||||
|
|
@ -26,6 +35,11 @@ import { TabsContext } from "../../contexts/tabsContext";
|
|||
import { typesContext } from "../../contexts/typesContext";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
import { TabsState } from "../../types/tabs";
|
||||
import {
|
||||
convertObjToArray,
|
||||
convertValuesToNumbers,
|
||||
hasDuplicateKeys,
|
||||
} from "../../utils/reactflowUtils";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import BaseModal from "../baseModal";
|
||||
|
||||
|
|
@ -36,54 +50,63 @@ 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 [myData, setMyData] = useState(data);
|
||||
const [modalOpen, setModalOpen] = useState(open ?? false);
|
||||
|
||||
const myData = useRef(data);
|
||||
|
||||
const { setTabsState, tabId } = useContext(TabsContext);
|
||||
const { reactFlowInstance } = useContext(typesContext);
|
||||
|
||||
let disabled =
|
||||
reactFlowInstance
|
||||
?.getEdges()
|
||||
.some((edge) => edge.targetHandle === data.id) ?? false;
|
||||
|
||||
function changeAdvanced(templateParam: string): void {
|
||||
setMyData((old) => {
|
||||
let newData = cloneDeep(old);
|
||||
newData.node!.template[templateParam].advanced =
|
||||
!newData.node!.template[templateParam].advanced;
|
||||
return newData;
|
||||
});
|
||||
function changeAdvanced(n) {
|
||||
myData.current.node!.template[n].advanced =
|
||||
!myData.current.node!.template[n].advanced;
|
||||
setAdv(!adv);
|
||||
}
|
||||
|
||||
const handleOnNewValue = (
|
||||
newValue: string | string[] | boolean,
|
||||
name: string
|
||||
) => {
|
||||
setMyData((old) => {
|
||||
let newData = cloneDeep(old);
|
||||
newData.node!.template[name].value = newValue;
|
||||
return newData;
|
||||
});
|
||||
const handleOnNewValue = (newValue: any, name) => {
|
||||
myData.current.node!.template[name].value = newValue;
|
||||
setDataValue(newValue);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setMyData(data); // reset data to what it is on node when opening modal
|
||||
myData.current = data; // reset data to what it is on node when opening modal
|
||||
onClose!(modalOpen);
|
||||
}, [modalOpen]);
|
||||
|
||||
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
|
||||
const [adv, setAdv] = useState<boolean | null>(null);
|
||||
const [dataValue, setDataValue] = useState(data);
|
||||
|
||||
return (
|
||||
<BaseModal size="large-h-full" open={modalOpen} setOpen={setModalOpen}>
|
||||
<BaseModal
|
||||
key={data.id}
|
||||
size="large-h-full"
|
||||
open={modalOpen}
|
||||
setOpen={setModalOpen}
|
||||
onChangeOpenModal={(open) => {
|
||||
myData.current = data;
|
||||
}}
|
||||
>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={myData.node?.description!}>
|
||||
<span className="pr-2">{myData.node?.display_name}</span>
|
||||
<Badge variant="secondary">ID: {myData.id}</Badge>
|
||||
<BaseModal.Header description={myData.current.node?.description!}>
|
||||
<span className="pr-2">{myData.current.type}</span>
|
||||
<Badge variant="secondary">ID: {myData.current.id}</Badge>
|
||||
</BaseModal.Header>
|
||||
<BaseModal.Content>
|
||||
<div className="flex pb-2">
|
||||
|
|
@ -116,64 +139,77 @@ const EditNodeModal = forwardRef(
|
|||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody className="p-0">
|
||||
{Object.keys(myData.node!.template)
|
||||
{Object.keys(myData.current.node!.template)
|
||||
.filter(
|
||||
(templateParam) =>
|
||||
templateParam.charAt(0) !== "_" &&
|
||||
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")
|
||||
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.current.node.template[templateParam]
|
||||
.type === "dict" ||
|
||||
myData.current.node.template[templateParam]
|
||||
.type === "NestedDict")
|
||||
)
|
||||
.map((templateParam, index) => (
|
||||
<TableRow key={index} className="h-10">
|
||||
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
|
||||
{myData.node?.template[templateParam].display_name
|
||||
? myData.node.template[templateParam]
|
||||
{myData.current.node?.template[templateParam]
|
||||
.display_name
|
||||
? myData.current.node.template[templateParam]
|
||||
.display_name
|
||||
: myData.node?.template[templateParam].name}
|
||||
: myData.current.node?.template[templateParam]
|
||||
.name}
|
||||
</TableCell>
|
||||
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
|
||||
{myData.node?.template[templateParam].type ===
|
||||
"str" &&
|
||||
!myData.node.template[templateParam].options ? (
|
||||
{myData.current.node?.template[templateParam]
|
||||
.type === "str" &&
|
||||
!myData.current.node.template[templateParam]
|
||||
.options ? (
|
||||
<div className="mx-auto">
|
||||
{myData.node.template[templateParam].list ? (
|
||||
{myData.current.node.template[templateParam]
|
||||
.list ? (
|
||||
<InputListComponent
|
||||
editNode={true}
|
||||
disabled={disabled}
|
||||
value={
|
||||
!myData.node.template[templateParam]
|
||||
.value ||
|
||||
myData.node.template[templateParam]
|
||||
.value === ""
|
||||
!myData.current.node.template[
|
||||
templateParam
|
||||
].value ||
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value === ""
|
||||
? [""]
|
||||
: myData.node.template[templateParam]
|
||||
.value
|
||||
: myData.current.node.template[
|
||||
templateParam
|
||||
].value
|
||||
}
|
||||
onChange={(value: string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
/>
|
||||
) : myData.node.template[templateParam]
|
||||
.multiline ? (
|
||||
) : myData.current.node.template[
|
||||
templateParam
|
||||
].multiline ? (
|
||||
<TextAreaComponent
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
}
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
|
|
@ -184,12 +220,14 @@ const EditNodeModal = forwardRef(
|
|||
editNode={true}
|
||||
disabled={disabled}
|
||||
password={
|
||||
myData.node.template[templateParam]
|
||||
.password ?? false
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].password ?? false
|
||||
}
|
||||
value={
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
}
|
||||
onChange={(value) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
|
|
@ -197,14 +235,88 @@ const EditNodeModal = forwardRef(
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"bool" ? (
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "NestedDict" ? (
|
||||
<div className=" w-full">
|
||||
<DictComponent
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={
|
||||
myData.current.node!.template[
|
||||
templateParam
|
||||
].value.toString() === "{}"
|
||||
? {
|
||||
yourkey: "value",
|
||||
}
|
||||
: myData.current.node!.template[
|
||||
templateParam
|
||||
].value
|
||||
}
|
||||
onChange={(newValue) => {
|
||||
myData.current.node!.template[
|
||||
templateParam
|
||||
].value = newValue;
|
||||
handleOnNewValue(newValue, templateParam);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "dict" ? (
|
||||
<div
|
||||
className={classNames(
|
||||
"max-h-48 w-full overflow-auto custom-scroll",
|
||||
myData.current.node!.template[templateParam]
|
||||
.value?.length > 1
|
||||
? "my-3"
|
||||
: ""
|
||||
)}
|
||||
>
|
||||
<KeypairListComponent
|
||||
dataValue={dataValue}
|
||||
advanced={adv}
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={
|
||||
myData.current.node!.template[
|
||||
templateParam
|
||||
].value?.length === 0 ||
|
||||
!myData.current.node!.template[
|
||||
templateParam
|
||||
].value
|
||||
? [{ "": "" }]
|
||||
: convertObjToArray(
|
||||
myData.current.node!.template[
|
||||
templateParam
|
||||
].value
|
||||
)
|
||||
}
|
||||
duplicateKey={errorDuplicateKey}
|
||||
onChange={(newValue) => {
|
||||
const valueToNumbers =
|
||||
convertValuesToNumbers(newValue);
|
||||
myData.current.node!.template[
|
||||
templateParam
|
||||
].value = valueToNumbers;
|
||||
setErrorDuplicateKey(
|
||||
hasDuplicateKeys(valueToNumbers)
|
||||
);
|
||||
handleOnNewValue(
|
||||
valueToNumbers,
|
||||
templateParam
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "bool" ? (
|
||||
<div className="ml-auto">
|
||||
{" "}
|
||||
<ToggleShadComponent
|
||||
disabled={disabled}
|
||||
enabled={
|
||||
myData.node.template[templateParam].value
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value
|
||||
}
|
||||
setEnabled={(isEnabled) => {
|
||||
handleOnNewValue(
|
||||
|
|
@ -215,76 +327,84 @@ const EditNodeModal = forwardRef(
|
|||
size="small"
|
||||
/>
|
||||
</div>
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"float" ? (
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "float" ? (
|
||||
<div className="mx-auto">
|
||||
<FloatComponent
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
}
|
||||
onChange={(value) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"str" &&
|
||||
myData.node.template[templateParam].options ? (
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "str" &&
|
||||
myData.current.node.template[templateParam]
|
||||
.options ? (
|
||||
<div className="mx-auto">
|
||||
<Dropdown
|
||||
numberOfOptions={nodeLength}
|
||||
editNode={true}
|
||||
options={
|
||||
myData.node.template[templateParam]
|
||||
.options
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].options
|
||||
}
|
||||
onSelect={(value) =>
|
||||
handleOnNewValue(value, templateParam)
|
||||
}
|
||||
value={
|
||||
myData.node.template[templateParam]
|
||||
.value ?? "Choose an option"
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? "Choose an option"
|
||||
}
|
||||
></Dropdown>
|
||||
</div>
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"int" ? (
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "int" ? (
|
||||
<div className="mx-auto">
|
||||
<IntComponent
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
}
|
||||
onChange={(value) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"file" ? (
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "file" ? (
|
||||
<div className="mx-auto">
|
||||
<InputFileComponent
|
||||
editNode={true}
|
||||
disabled={disabled}
|
||||
value={
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
}
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
fileTypes={
|
||||
myData.node.template[templateParam]
|
||||
.fileTypes
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].fileTypes
|
||||
}
|
||||
suffixes={
|
||||
myData.node.template[templateParam]
|
||||
.suffixes
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].suffixes
|
||||
}
|
||||
onFileChange={(filePath: string) => {
|
||||
data.node!.template[
|
||||
|
|
@ -293,8 +413,8 @@ const EditNodeModal = forwardRef(
|
|||
}}
|
||||
></InputFileComponent>
|
||||
</div>
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"prompt" ? (
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "prompt" ? (
|
||||
<div className="mx-auto">
|
||||
<PromptAreaComponent
|
||||
readonly={
|
||||
|
|
@ -307,21 +427,22 @@ const EditNodeModal = forwardRef(
|
|||
field_name={templateParam}
|
||||
editNode={true}
|
||||
disabled={disabled}
|
||||
nodeClass={myData.node}
|
||||
nodeClass={myData.current.node}
|
||||
setNodeClass={(nodeClass) => {
|
||||
myData.node = nodeClass;
|
||||
myData.current.node = nodeClass;
|
||||
}}
|
||||
value={
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
}
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"code" ? (
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "code" ? (
|
||||
<div className="mx-auto">
|
||||
<CodeAreaComponent
|
||||
readonly={
|
||||
|
|
@ -342,16 +463,17 @@ const EditNodeModal = forwardRef(
|
|||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
myData.current.node.template[
|
||||
templateParam
|
||||
].value ?? ""
|
||||
}
|
||||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : myData.node?.template[templateParam].type ===
|
||||
"Any" ? (
|
||||
) : myData.current.node?.template[templateParam]
|
||||
.type === "Any" ? (
|
||||
"-"
|
||||
) : (
|
||||
<div className="hidden"></div>
|
||||
|
|
@ -361,12 +483,13 @@ const EditNodeModal = forwardRef(
|
|||
<div className="items-center text-center">
|
||||
<ToggleShadComponent
|
||||
enabled={
|
||||
!myData.node?.template[templateParam]
|
||||
.advanced
|
||||
}
|
||||
setEnabled={(e) =>
|
||||
changeAdvanced(templateParam)
|
||||
!myData.current.node?.template[
|
||||
templateParam
|
||||
].advanced
|
||||
}
|
||||
setEnabled={(e) => {
|
||||
changeAdvanced(templateParam);
|
||||
}}
|
||||
disabled={disabled}
|
||||
size="small"
|
||||
/>
|
||||
|
|
@ -386,7 +509,8 @@ const EditNodeModal = forwardRef(
|
|||
<Button
|
||||
className="mt-3"
|
||||
onClick={() => {
|
||||
setData(cloneDeep(myData)); //saves data with actual state of modal
|
||||
const newData = cloneDeep(myData.current);
|
||||
myData.current = newData;
|
||||
//@ts-ignore
|
||||
setTabsState((prev: TabsState) => {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export default function UserManagementModal({
|
|||
data,
|
||||
index,
|
||||
onConfirm,
|
||||
asChild,
|
||||
}: UserManagementType) {
|
||||
const Icon: any = nodeIconsLucide[icon];
|
||||
const [pwdVisible, setPwdVisible] = useState(false);
|
||||
|
|
@ -60,7 +61,7 @@ export default function UserManagementModal({
|
|||
|
||||
return (
|
||||
<BaseModal size="medium-h-full" open={open} setOpen={setOpen}>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Trigger asChild={asChild}>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={titleHeader}>
|
||||
<span className="pr-2">{title}</span>
|
||||
<Icon
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ReactNode } from "react";
|
||||
import { ReactNode, useEffect } from "react";
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
|
|
@ -14,13 +14,25 @@ import { modalHeaderType } from "../../types/components";
|
|||
type ContentProps = { children: ReactNode };
|
||||
type HeaderProps = { children: ReactNode; description: string };
|
||||
type FooterProps = { children: ReactNode };
|
||||
type TriggerProps = { children: ReactNode };
|
||||
type TriggerProps = {
|
||||
children: ReactNode;
|
||||
asChild?: boolean;
|
||||
disable?: boolean;
|
||||
};
|
||||
|
||||
const Content: React.FC<ContentProps> = ({ children }) => {
|
||||
return <div className="h-full w-full">{children}</div>;
|
||||
};
|
||||
const Trigger: React.FC<ContentProps> = ({ children }) => {
|
||||
return <>{children}</>;
|
||||
const Trigger: React.FC<TriggerProps> = ({ children, asChild, disable }) => {
|
||||
return (
|
||||
<DialogTrigger
|
||||
className={asChild ? "" : "w-full"}
|
||||
hidden={children ? false : true}
|
||||
asChild={asChild}
|
||||
>
|
||||
{children}
|
||||
</DialogTrigger>
|
||||
);
|
||||
};
|
||||
|
||||
const Header: React.FC<{ children: ReactNode; description: string | null }> = ({
|
||||
|
|
@ -47,7 +59,6 @@ interface BaseModalProps {
|
|||
];
|
||||
open?: boolean;
|
||||
setOpen?: (open: boolean) => void;
|
||||
disable?: boolean;
|
||||
size?:
|
||||
| "x-small"
|
||||
| "smaller"
|
||||
|
|
@ -57,13 +68,16 @@ interface BaseModalProps {
|
|||
| "large-h-full"
|
||||
| "small-h-full"
|
||||
| "medium-h-full";
|
||||
|
||||
disable?: boolean;
|
||||
onChangeOpenModal?: (open?: boolean) => void;
|
||||
}
|
||||
function BaseModal({
|
||||
open,
|
||||
setOpen,
|
||||
disable = false,
|
||||
children,
|
||||
size = "large",
|
||||
onChangeOpenModal,
|
||||
}: BaseModalProps) {
|
||||
const headerChild = React.Children.toArray(children).find(
|
||||
(child) => (child as React.ReactElement).type === Header
|
||||
|
|
@ -117,20 +131,21 @@ function BaseModal({
|
|||
break;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (onChangeOpenModal) {
|
||||
onChangeOpenModal(open);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
//UPDATE COLORS AND STYLE CLASSSES
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger
|
||||
className={"w-full " + (disable ? "button-disable" : "")}
|
||||
hidden={triggerChild ? false : true}
|
||||
>
|
||||
{triggerChild}
|
||||
</DialogTrigger>
|
||||
{triggerChild}
|
||||
<DialogContent className={minWidth}>
|
||||
<div className="truncate-doubleline word-break-break-word">
|
||||
{headerChild}
|
||||
</div>
|
||||
<div className={`mt-2 flex flex-col ${height} w-full `}>
|
||||
<div className={`mt-2 flex flex-col ${height!} w-full `}>
|
||||
{ContentChild}
|
||||
</div>
|
||||
{ContentFooter && (
|
||||
|
|
|
|||
|
|
@ -166,18 +166,18 @@ export default function CodeAreaModal({
|
|||
</div>
|
||||
<div
|
||||
className={
|
||||
"w-full transition-all delay-500 " +
|
||||
(error?.detail.error !== undefined ? "h-2/6" : "h-0")
|
||||
"whitespace-break-spaces transition-all delay-500" +
|
||||
(error?.detail?.error !== undefined ? "h-2/6" : "h-0")
|
||||
}
|
||||
>
|
||||
<div className="mt-1 h-full w-full overflow-y-auto overflow-x-clip text-left custom-scroll">
|
||||
<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">
|
||||
<pre className="w-full word-break-break-word">
|
||||
<span className="w-full word-break-break-word">
|
||||
{error?.detail?.traceback}
|
||||
</pre>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
71
src/frontend/src/modals/dictAreaModal/index.tsx
Normal file
71
src/frontend/src/modals/dictAreaModal/index.tsx
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import "ace-builds/src-noconflict/ace";
|
||||
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 "ace-builds/webpack-resolver";
|
||||
import { useEffect, useRef, 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 BaseModal from "../baseModal";
|
||||
|
||||
export default function DictAreaModal({
|
||||
children,
|
||||
onChange,
|
||||
value,
|
||||
}): JSX.Element {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const ref = useRef(value);
|
||||
|
||||
useEffect(() => {
|
||||
if (value) ref.current = value;
|
||||
}, [ref]);
|
||||
|
||||
return (
|
||||
<BaseModal size="medium-h-full" open={open} setOpen={setOpen}>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={CODE_DICT_DIALOG_SUBTITLE}>
|
||||
<span className="pr-2">Edit Dictionary</span>
|
||||
<IconComponent
|
||||
name="BookMarked"
|
||||
className="h-6 w-6 pl-1 text-primary "
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</BaseModal.Header>
|
||||
<BaseModal.Content>
|
||||
<div className="flex h-full w-full flex-col transition-all ">
|
||||
<JsonView
|
||||
theme="vscode"
|
||||
dark={true}
|
||||
editable
|
||||
enableClipboard
|
||||
onEdit={(edit) => {
|
||||
ref.current = edit["src"];
|
||||
}}
|
||||
onChange={(edit) => {
|
||||
ref.current = edit["src"];
|
||||
}}
|
||||
src={ref.current}
|
||||
/>
|
||||
<div className="flex h-fit w-full justify-end">
|
||||
<Button
|
||||
className="mt-3"
|
||||
type="submit"
|
||||
onClick={() => {
|
||||
onChange(ref.current);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</BaseModal.Content>
|
||||
</BaseModal>
|
||||
);
|
||||
}
|
||||
|
|
@ -10,15 +10,15 @@ import BaseModal from "../baseModal";
|
|||
|
||||
const ExportModal = forwardRef(
|
||||
(props: { children: ReactNode }, ref): JSX.Element => {
|
||||
const { flows, tabId, updateFlow, downloadFlow } = useContext(TabsContext);
|
||||
const { flows, tabId, downloadFlow } = useContext(TabsContext);
|
||||
const [checked, setChecked] = useState(false);
|
||||
const flow = flows.find((f) => f.id === tabId);
|
||||
useEffect(() => {
|
||||
setName(flow.name);
|
||||
setDescription(flow.description);
|
||||
}, [flow.name, flow.description]);
|
||||
const [name, setName] = useState(flow.name);
|
||||
const [description, setDescription] = useState(flow.description);
|
||||
setName(flow!.name);
|
||||
setDescription(flow!.description);
|
||||
}, [flow!.name, flow!.description]);
|
||||
const [name, setName] = useState(flow!.name);
|
||||
const [description, setDescription] = useState(flow!.description);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
|
|
@ -40,7 +40,6 @@ const ExportModal = forwardRef(
|
|||
tabId={tabId}
|
||||
setName={setName}
|
||||
setDescription={setDescription}
|
||||
updateFlow={updateFlow}
|
||||
/>
|
||||
<div className="mt-3 flex items-center space-x-2">
|
||||
<Checkbox
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import { AuthContext } from "../../contexts/authContext";
|
|||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { getBuildStatus } from "../../controllers/API";
|
||||
import { TabsState } from "../../types/tabs";
|
||||
import { processFlow, validateNodes } from "../../utils/reactflowUtils";
|
||||
import { validateNodes } from "../../utils/reactflowUtils";
|
||||
|
||||
export default function FormModal({
|
||||
flow,
|
||||
|
|
@ -61,6 +61,7 @@ export default function FormModal({
|
|||
});
|
||||
|
||||
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
|
||||
const template = useRef(tabsState[flow.id].formKeysData.template);
|
||||
const { reactFlowInstance } = useContext(typesContext);
|
||||
const { accessToken } = useContext(AuthContext);
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
|
|
@ -125,12 +126,13 @@ export default function FormModal({
|
|||
function updateLastMessage({
|
||||
str,
|
||||
thought,
|
||||
prompt,
|
||||
end = false,
|
||||
files,
|
||||
}: {
|
||||
str?: string;
|
||||
thought?: string;
|
||||
// end param default is false
|
||||
prompt?: string;
|
||||
end?: boolean;
|
||||
files?: Array<any>;
|
||||
}) {
|
||||
|
|
@ -150,12 +152,22 @@ export default function FormModal({
|
|||
if (files) {
|
||||
newChat[newChat.length - 1].files = files;
|
||||
}
|
||||
if (prompt) {
|
||||
newChat[newChat.length - 2].template = prompt;
|
||||
}
|
||||
return newChat;
|
||||
});
|
||||
}
|
||||
|
||||
function handleOnClose(event: CloseEvent): void {
|
||||
if (isOpen.current) {
|
||||
//check if the user has been logged out, if so close the chat when the user is redirected to the login page
|
||||
if (window.location.href.includes("login")) {
|
||||
setOpen(false);
|
||||
ws.current?.close();
|
||||
return;
|
||||
}
|
||||
|
||||
getBuildStatus(flow.id)
|
||||
.then((response) => {
|
||||
if (response.data.built) {
|
||||
|
|
@ -194,16 +206,26 @@ export default function FormModal({
|
|||
}
|
||||
|
||||
function handleWsMessage(data: any) {
|
||||
if (Array.isArray(data)) {
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
//set chat history
|
||||
setChatHistory((_) => {
|
||||
console.log(data);
|
||||
let newChatHistory: ChatMessageType[] = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data[i].type === "prompt" && data[i].prompt) {
|
||||
if (data[i - 1] && !data[i - 1].is_bot) {
|
||||
data[i - 1].prompt = data[i].prompt;
|
||||
template.current = data[i].prompt;
|
||||
}
|
||||
}
|
||||
}
|
||||
data = data.filter((item: any) => item.type !== "prompt");
|
||||
data.forEach(
|
||||
(chatItem: {
|
||||
intermediate_steps?: string;
|
||||
is_bot: boolean;
|
||||
message: string;
|
||||
template: string;
|
||||
prompt?: string;
|
||||
type: string;
|
||||
chatKey: string;
|
||||
files?: Array<any>;
|
||||
|
|
@ -214,7 +236,7 @@ export default function FormModal({
|
|||
? {
|
||||
isSend: !chatItem.is_bot,
|
||||
message: chatItem.message,
|
||||
template: chatItem.template,
|
||||
template: chatItem.prompt,
|
||||
thought: chatItem.intermediate_steps,
|
||||
files: chatItem.files,
|
||||
chatKey: chatItem.chatKey,
|
||||
|
|
@ -222,7 +244,7 @@ export default function FormModal({
|
|||
: {
|
||||
isSend: !chatItem.is_bot,
|
||||
message: chatItem.message,
|
||||
template: chatItem.template,
|
||||
template: chatItem.prompt,
|
||||
thought: chatItem.intermediate_steps,
|
||||
chatKey: chatItem.chatKey,
|
||||
}
|
||||
|
|
@ -239,7 +261,11 @@ export default function FormModal({
|
|||
}
|
||||
if (data.type === "end") {
|
||||
if (data.message) {
|
||||
updateLastMessage({ str: data.message, end: true });
|
||||
updateLastMessage({
|
||||
str: data.message,
|
||||
end: true,
|
||||
prompt: template.current,
|
||||
});
|
||||
}
|
||||
if (data.intermediate_steps) {
|
||||
updateLastMessage({
|
||||
|
|
@ -254,10 +280,12 @@ export default function FormModal({
|
|||
files: data.files,
|
||||
});
|
||||
}
|
||||
|
||||
setLockChat(false);
|
||||
isStream = false;
|
||||
}
|
||||
if (data.type == "prompt" && data.prompt) {
|
||||
template.current = data.prompt;
|
||||
}
|
||||
if (data.type === "stream" && isStream) {
|
||||
updateLastMessage({ str: data.message });
|
||||
}
|
||||
|
|
@ -282,18 +310,8 @@ export default function FormModal({
|
|||
handleOnClose(event);
|
||||
};
|
||||
newWs.onerror = (ev) => {
|
||||
if (flow.id === "") {
|
||||
connectWS();
|
||||
} else {
|
||||
setErrorData({
|
||||
title: "There was an error on web connection, please: ",
|
||||
list: [
|
||||
"Refresh the page",
|
||||
"Use a new flow tab",
|
||||
"Check if the backend is up",
|
||||
],
|
||||
});
|
||||
}
|
||||
console.log(ev);
|
||||
connectWS();
|
||||
};
|
||||
ws.current = newWs;
|
||||
} catch (error) {
|
||||
|
|
@ -313,6 +331,15 @@ export default function FormModal({
|
|||
}
|
||||
};
|
||||
// do not add connectWS on dependencies array
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (ws.current) {
|
||||
console.log("closing ws");
|
||||
ws.current.close();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -355,24 +382,23 @@ export default function FormModal({
|
|||
}, [open]);
|
||||
|
||||
function sendMessage(): void {
|
||||
let nodeValidationErrors = validateNodes(reactFlowInstance!);
|
||||
let nodeValidationErrors = validateNodes(
|
||||
reactFlowInstance!.getNodes(),
|
||||
reactFlowInstance!.getEdges()
|
||||
);
|
||||
if (nodeValidationErrors.length === 0) {
|
||||
setLockChat(true);
|
||||
let inputs = tabsState[id.current].formKeysData.input_keys;
|
||||
setChatValue("");
|
||||
const message = inputs;
|
||||
addChatHistory(
|
||||
message!,
|
||||
true,
|
||||
chatKey!,
|
||||
tabsState[flow.id].formKeysData.template
|
||||
);
|
||||
addChatHistory(message!, true, chatKey!, template.current);
|
||||
sendAll({
|
||||
...reactFlowInstance?.toObject()!,
|
||||
inputs: inputs!,
|
||||
chatHistory,
|
||||
name: flow.name,
|
||||
description: flow.description,
|
||||
chatKey: chatKey!,
|
||||
});
|
||||
//@ts-ignore
|
||||
setTabsState((old: TabsState) => {
|
||||
|
|
@ -390,6 +416,7 @@ export default function FormModal({
|
|||
}
|
||||
function clearChat(): void {
|
||||
setChatHistory([]);
|
||||
template.current = tabsState[id.current].formKeysData.template;
|
||||
ws.current?.send(JSON.stringify({ clear_history: true }));
|
||||
if (lockChat) setLockChat(false);
|
||||
}
|
||||
|
|
@ -403,7 +430,6 @@ export default function FormModal({
|
|||
setChatValue("");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger hidden></DialogTrigger>
|
||||
|
|
|
|||
|
|
@ -17,11 +17,7 @@ import { alertContext } from "../../contexts/alertContext";
|
|||
import { postValidatePrompt } from "../../controllers/API";
|
||||
import { genericModalPropsType } from "../../types/components";
|
||||
import { handleKeyDown } from "../../utils/reactflowUtils";
|
||||
import {
|
||||
classNames,
|
||||
getRandomKeyByssmm,
|
||||
varHighlightHTML,
|
||||
} from "../../utils/utils";
|
||||
import { classNames, varHighlightHTML } from "../../utils/utils";
|
||||
import BaseModal from "../baseModal";
|
||||
|
||||
export default function GenericModal({
|
||||
|
|
@ -38,6 +34,7 @@ export default function GenericModal({
|
|||
}: genericModalPropsType): JSX.Element {
|
||||
const [myButtonText] = useState(buttonText);
|
||||
const [myModalTitle] = useState(modalTitle);
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [myModalType] = useState(type);
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
const [isEdit, setIsEdit] = useState(true);
|
||||
|
|
@ -90,7 +87,7 @@ export default function GenericModal({
|
|||
|
||||
useEffect(() => {
|
||||
setInputValue(value);
|
||||
}, [value]);
|
||||
}, [value, modalOpen]);
|
||||
|
||||
const coloredContent = (inputValue || "")
|
||||
.replace(/</g, "<")
|
||||
|
|
@ -98,19 +95,6 @@ export default function GenericModal({
|
|||
.replace(regexHighlight, varHighlightHTML({ name: "$1" }))
|
||||
.replace(/\n/g, "<br />");
|
||||
|
||||
const TextAreaContentView = (): JSX.Element => {
|
||||
return (
|
||||
<SanitizedHTMLWrapper
|
||||
className={getClassByNumberLength()}
|
||||
content={coloredContent}
|
||||
onClick={() => {
|
||||
setIsEdit(true);
|
||||
}}
|
||||
suppressWarning={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
function getClassByNumberLength(): string {
|
||||
let sumOfCaracteres: number = 0;
|
||||
wordsHighlight.forEach((element) => {
|
||||
|
|
@ -123,8 +107,19 @@ export default function GenericModal({
|
|||
|
||||
function validatePrompt(closeModal: boolean): void {
|
||||
//nodeClass is always null on tweaks
|
||||
|
||||
postValidatePrompt(field_name, inputValue, nodeClass!)
|
||||
.then((apiReturn) => {
|
||||
// if field_name is an empty string, then we need to set it
|
||||
// to the first key of the custom_fields object
|
||||
if (field_name === "") {
|
||||
console.log(apiReturn.data?.frontend_node?.custom_fields);
|
||||
field_name = Array.isArray(
|
||||
apiReturn.data?.frontend_node?.custom_fields?.[""]
|
||||
)
|
||||
? apiReturn.data?.frontend_node?.custom_fields?.[""][0] ?? ""
|
||||
: apiReturn.data?.frontend_node?.custom_fields?.[""] ?? "";
|
||||
}
|
||||
if (apiReturn.data) {
|
||||
let inputVariables = apiReturn.data.input_variables ?? [];
|
||||
if (inputVariables && inputVariables.length === 0) {
|
||||
|
|
@ -132,6 +127,18 @@ export default function GenericModal({
|
|||
setNoticeData({
|
||||
title: "Your template does not have any variables.",
|
||||
});
|
||||
setModalOpen(false);
|
||||
if (
|
||||
JSON.stringify(apiReturn.data?.frontend_node) !==
|
||||
JSON.stringify({})
|
||||
)
|
||||
setNodeClass!(apiReturn.data?.frontend_node);
|
||||
setModalOpen(closeModal);
|
||||
setValue(inputValue);
|
||||
if (field_name !== "") {
|
||||
apiReturn.data.frontend_node["template"][field_name]["value"] =
|
||||
inputValue;
|
||||
}
|
||||
} else {
|
||||
setIsEdit(false);
|
||||
setSuccessData({
|
||||
|
|
@ -144,6 +151,10 @@ export default function GenericModal({
|
|||
setNodeClass!(apiReturn.data?.frontend_node);
|
||||
setModalOpen(closeModal);
|
||||
setValue(inputValue);
|
||||
if (field_name !== "") {
|
||||
apiReturn.data.frontend_node["template"][field_name]["value"] =
|
||||
inputValue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setIsEdit(true);
|
||||
|
|
@ -153,18 +164,21 @@ export default function GenericModal({
|
|||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
setIsEdit(true);
|
||||
return setErrorData({
|
||||
title: "There is something wrong with this prompt, please review it",
|
||||
list: [error?.response?.data?.detail],
|
||||
list: [error.toString()],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<BaseModal open={modalOpen} setOpen={setModalOpen}>
|
||||
<BaseModal
|
||||
onChangeOpenModal={(open) => {}}
|
||||
open={modalOpen}
|
||||
setOpen={setModalOpen}
|
||||
>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header
|
||||
description={(() => {
|
||||
|
|
@ -214,7 +228,14 @@ export default function GenericModal({
|
|||
}}
|
||||
/>
|
||||
) : type === TypeModal.PROMPT && (!isEdit || readonly) ? (
|
||||
<TextAreaContentView />
|
||||
<SanitizedHTMLWrapper
|
||||
className={getClassByNumberLength()}
|
||||
content={coloredContent}
|
||||
onClick={() => {
|
||||
setIsEdit(true);
|
||||
}}
|
||||
suppressWarning={true}
|
||||
/>
|
||||
) : type !== TypeModal.PROMPT ? (
|
||||
<Textarea
|
||||
//@ts-ignore
|
||||
|
|
@ -254,7 +275,7 @@ export default function GenericModal({
|
|||
|
||||
{wordsHighlight.map((word, index) => (
|
||||
<ShadTooltip
|
||||
key={getRandomKeyByssmm() + index}
|
||||
key={index}
|
||||
content={word.replace(/[{}]/g, "")}
|
||||
asChild={false}
|
||||
>
|
||||
|
|
@ -293,9 +314,7 @@ export default function GenericModal({
|
|||
setModalOpen(false);
|
||||
break;
|
||||
case TypeModal.PROMPT:
|
||||
!inputValue || inputValue === ""
|
||||
? setModalOpen(false)
|
||||
: validatePrompt(false);
|
||||
validatePrompt(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -48,13 +48,15 @@ export default function LoginAdminPage() {
|
|||
}
|
||||
|
||||
function getUser() {
|
||||
if (getAuthentication) {
|
||||
if (getAuthentication()) {
|
||||
setTimeout(() => {
|
||||
getLoggedUser()
|
||||
.then((user) => {
|
||||
setUserData(user);
|
||||
})
|
||||
.catch((error) => {});
|
||||
.catch((error) => {
|
||||
console.log("login admin page", error);
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { X } from "lucide-react";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import PaginatorComponent from "../../components/PaginatorComponent";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import Header from "../../components/headerComponent";
|
||||
import LoadingComponent from "../../components/loadingComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { Checkbox } from "../../components/ui/checkbox";
|
||||
import { Input } from "../../components/ui/input";
|
||||
|
|
@ -16,8 +16,13 @@ import {
|
|||
TableHeader,
|
||||
TableRow,
|
||||
} from "../../components/ui/table";
|
||||
import {
|
||||
ADMIN_HEADER_DESCRIPTION,
|
||||
ADMIN_HEADER_TITLE,
|
||||
} from "../../constants/constants";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import {
|
||||
addUser,
|
||||
deleteUser,
|
||||
|
|
@ -33,12 +38,19 @@ export default function AdminPage() {
|
|||
const [inputValue, setInputValue] = useState("");
|
||||
|
||||
const [size, setPageSize] = useState(10);
|
||||
const [index, setPageIndex] = useState(0);
|
||||
const [index, setPageIndex] = useState(1);
|
||||
const [loadingUsers, setLoadingUsers] = useState(true);
|
||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
||||
const { userData } = useContext(AuthContext);
|
||||
const [totalRowsCount, setTotalRowsCount] = useState(0);
|
||||
|
||||
const { setTabId } = useContext(TabsContext);
|
||||
|
||||
// set null id
|
||||
useEffect(() => {
|
||||
setTabId("");
|
||||
}, []);
|
||||
|
||||
const userList = useRef([]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -51,7 +63,7 @@ export default function AdminPage() {
|
|||
|
||||
function getUsers() {
|
||||
setLoadingUsers(true);
|
||||
getUsersPage(index, size)
|
||||
getUsersPage(index - 1, size)
|
||||
.then((users) => {
|
||||
setTotalRowsCount(users["total_count"]);
|
||||
userList.current = users["users"];
|
||||
|
|
@ -65,7 +77,9 @@ export default function AdminPage() {
|
|||
|
||||
function handleChangePagination(pageIndex: number, pageSize: number) {
|
||||
setLoadingUsers(true);
|
||||
getUsersPage(pageIndex, pageSize)
|
||||
setPageSize(pageSize);
|
||||
setPageIndex(pageIndex);
|
||||
getUsersPage(pageSize * (pageIndex - 1), pageSize)
|
||||
.then((users) => {
|
||||
setTotalRowsCount(users["total_count"]);
|
||||
userList.current = users["users"];
|
||||
|
|
@ -78,7 +92,7 @@ export default function AdminPage() {
|
|||
}
|
||||
|
||||
function resetFilter() {
|
||||
setPageIndex(0);
|
||||
setPageIndex(1);
|
||||
setPageSize(10);
|
||||
getUsers();
|
||||
}
|
||||
|
|
@ -168,270 +182,264 @@ export default function AdminPage() {
|
|||
function handleNewUser(user: UserInputType) {
|
||||
addUser(user)
|
||||
.then((res) => {
|
||||
resetFilter();
|
||||
setSuccessData({
|
||||
title: "Success! New user added!",
|
||||
updateUser(res["id"], {
|
||||
is_active: user.is_active,
|
||||
is_superuser: user.is_superuser,
|
||||
}).then((res) => {
|
||||
resetFilter();
|
||||
setSuccessData({
|
||||
title: "Success! New user added!",
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({
|
||||
title: "Error on add new user",
|
||||
list: [error["response"]["data"]["detail"]],
|
||||
title: "Error when adding new user",
|
||||
list: [error.response.data.detail],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col">
|
||||
<Header />
|
||||
{userData && (
|
||||
<div className="main-page-panel">
|
||||
<div className="m-auto flex h-full flex-row justify-center">
|
||||
<div className="basis-5/6">
|
||||
<div className="m-auto flex h-full flex-col space-y-8 p-8 ">
|
||||
<div className="flex items-center justify-between space-y-2">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold tracking-tight">
|
||||
Welcome back!
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Navigate through this section to efficiently oversee all
|
||||
application users. From here, you can seamlessly manage
|
||||
user accounts.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2"></div>
|
||||
</div>
|
||||
|
||||
{userList.current.length === 0 && !loadingUsers && (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<h2>There's no users registered :)</h2>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-1 items-center space-x-2">
|
||||
<Input
|
||||
value={inputValue}
|
||||
placeholder="Filter users..."
|
||||
className="h-8 w-[150px] lg:w-[250px]"
|
||||
onChange={(e) => handleFilterUsers(e.target.value)}
|
||||
/>
|
||||
{inputValue.length > 0 && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setInputValue("");
|
||||
setFilterUserList(userList.current);
|
||||
}}
|
||||
variant="ghost"
|
||||
className="h-8 px-2 lg:px-3"
|
||||
>
|
||||
Reset
|
||||
<X className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<UserManagementModal
|
||||
title="New User"
|
||||
titleHeader={"Add a new user"}
|
||||
cancelText="Cancel"
|
||||
confirmationText="Save"
|
||||
icon={"UserPlus2"}
|
||||
onConfirm={(index, user) => {
|
||||
handleNewUser(user);
|
||||
}}
|
||||
>
|
||||
<Button>New User</Button>
|
||||
</UserManagementModal>
|
||||
</div>
|
||||
</div>
|
||||
{loadingUsers && (
|
||||
<div>
|
||||
<strong>Loading...</strong>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={
|
||||
"max-h-[26rem] min-h-[26rem] overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted custom-scroll" +
|
||||
(loadingUsers ? " border-0" : "")
|
||||
}
|
||||
>
|
||||
<Table className={"table-fixed bg-muted outline-1"}>
|
||||
<TableHeader
|
||||
className={
|
||||
loadingUsers
|
||||
? "hidden"
|
||||
: "table-fixed bg-muted outline-1"
|
||||
}
|
||||
>
|
||||
<TableRow>
|
||||
<TableHead className="h-10">Id</TableHead>
|
||||
<TableHead className="h-10">Username</TableHead>
|
||||
<TableHead className="h-10">Active</TableHead>
|
||||
<TableHead className="h-10">Superuser</TableHead>
|
||||
<TableHead className="h-10">Created At</TableHead>
|
||||
<TableHead className="h-10">Updated At</TableHead>
|
||||
<TableHead className="h-10 w-[100px] text-right"></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
{!loadingUsers && (
|
||||
<TableBody>
|
||||
{filterUserList.map(
|
||||
(user: UserInputType, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="truncate py-2 font-medium">
|
||||
<ShadTooltip content={user.id}>
|
||||
<span className="cursor-default">
|
||||
{user.id}
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2">
|
||||
<ShadTooltip content={user.username}>
|
||||
<span className="cursor-default">
|
||||
{user.username}
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
</TableCell>
|
||||
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
|
||||
<ConfirmationModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.username}`}
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||
cancelText="Cancel"
|
||||
confirmationText="Confirm"
|
||||
icon={"UserCog2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleDisableUser(
|
||||
user.is_active,
|
||||
user.id,
|
||||
user
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
id="is_active"
|
||||
checked={user.is_active}
|
||||
/>
|
||||
</ConfirmationModal>
|
||||
</TableCell>
|
||||
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
|
||||
<ConfirmationModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.username}`}
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||
cancelText="Cancel"
|
||||
confirmationText="Confirm"
|
||||
icon={"UserCog2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleSuperUserEdit(
|
||||
user.is_superuser,
|
||||
user.id,
|
||||
user
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
id="is_superuser"
|
||||
checked={user.is_superuser}
|
||||
/>
|
||||
</ConfirmationModal>
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2 ">
|
||||
{
|
||||
new Date(user.create_at!)
|
||||
.toISOString()
|
||||
.split("T")[0]
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2">
|
||||
{
|
||||
new Date(user.updated_at!)
|
||||
.toISOString()
|
||||
.split("T")[0]
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell className="flex w-[100px] py-2 text-right">
|
||||
<div className="flex">
|
||||
<UserManagementModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.id}`}
|
||||
cancelText="Cancel"
|
||||
confirmationText="Save"
|
||||
icon={"UserPlus2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, editUser) => {
|
||||
handleEditUser(user.id, editUser);
|
||||
}}
|
||||
>
|
||||
<ShadTooltip content="Edit" side="top">
|
||||
<IconComponent
|
||||
name="Pencil"
|
||||
className="h-4 w-4 cursor-pointer"
|
||||
/>
|
||||
</ShadTooltip>
|
||||
</UserManagementModal>
|
||||
|
||||
<ConfirmationModal
|
||||
title="Delete"
|
||||
titleHeader="Delete User"
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you sure you want to delete this user? This action cannot be undone."
|
||||
cancelText="Cancel"
|
||||
confirmationText="Delete"
|
||||
icon={"UserMinus2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleDeleteUser(user);
|
||||
}}
|
||||
>
|
||||
<ShadTooltip
|
||||
content="Delete"
|
||||
side="top"
|
||||
>
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
className="ml-2 h-4 w-4 cursor-pointer"
|
||||
/>
|
||||
</ShadTooltip>
|
||||
</ConfirmationModal>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
)}
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<PaginatorComponent
|
||||
pageIndex={index}
|
||||
pageSize={size}
|
||||
totalRowsCount={totalRowsCount}
|
||||
paginate={(pageIndex, pageSize) => {
|
||||
handleChangePagination(pageSize, pageIndex);
|
||||
}}
|
||||
></PaginatorComponent>
|
||||
</>
|
||||
<Header />
|
||||
{userData && (
|
||||
<div className="admin-page-panel flex h-full flex-col pb-8">
|
||||
<div className="main-page-nav-arrangement">
|
||||
<span className="main-page-nav-title">
|
||||
<IconComponent name="Shield" className="w-6" />
|
||||
{ADMIN_HEADER_TITLE}
|
||||
</span>
|
||||
</div>
|
||||
<span className="admin-page-description-text">
|
||||
{ADMIN_HEADER_DESCRIPTION}
|
||||
</span>
|
||||
<div className="flex w-full justify-between px-4">
|
||||
<div className="flex w-96 items-center gap-4">
|
||||
<Input
|
||||
placeholder="Search Username"
|
||||
value={inputValue}
|
||||
onChange={(e) => handleFilterUsers(e.target.value)}
|
||||
/>
|
||||
{inputValue.length > 0 ? (
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
setInputValue("");
|
||||
setFilterUserList(userList.current);
|
||||
}}
|
||||
>
|
||||
<IconComponent name="X" className="w-6 text-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<IconComponent
|
||||
name="Search"
|
||||
className="w-6 text-foreground"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<UserManagementModal
|
||||
title="New User"
|
||||
titleHeader={"Add a new user"}
|
||||
cancelText="Cancel"
|
||||
confirmationText="Save"
|
||||
icon={"UserPlus2"}
|
||||
onConfirm={(index, user) => {
|
||||
handleNewUser(user);
|
||||
}}
|
||||
asChild
|
||||
>
|
||||
<Button variant="primary">New User</Button>
|
||||
</UserManagementModal>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{loadingUsers ? (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<LoadingComponent remSize={12} />
|
||||
</div>
|
||||
) : userList.current.length === 0 ? (
|
||||
<>
|
||||
<div className="m-4 flex items-center justify-between text-sm">
|
||||
No users registered.
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div
|
||||
className={
|
||||
"m-4 h-full overflow-x-hidden overflow-y-scroll rounded-md border-2 bg-background custom-scroll" +
|
||||
(loadingUsers ? " border-0" : "")
|
||||
}
|
||||
>
|
||||
<Table className={"table-fixed outline-1 "}>
|
||||
<TableHeader
|
||||
className={
|
||||
loadingUsers ? "hidden" : "table-fixed bg-muted outline-1"
|
||||
}
|
||||
>
|
||||
<TableRow>
|
||||
<TableHead className="h-10">Id</TableHead>
|
||||
<TableHead className="h-10">Username</TableHead>
|
||||
<TableHead className="h-10">Active</TableHead>
|
||||
<TableHead className="h-10">Superuser</TableHead>
|
||||
<TableHead className="h-10">Created At</TableHead>
|
||||
<TableHead className="h-10">Updated At</TableHead>
|
||||
<TableHead className="h-10 w-[100px] text-right"></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
{!loadingUsers && (
|
||||
<TableBody>
|
||||
{filterUserList.map((user: UserInputType, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="truncate py-2 font-medium">
|
||||
<ShadTooltip content={user.id}>
|
||||
<span className="cursor-default">{user.id}</span>
|
||||
</ShadTooltip>
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2">
|
||||
<ShadTooltip content={user.username}>
|
||||
<span className="cursor-default">
|
||||
{user.username}
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
</TableCell>
|
||||
<TableCell className="relative left-1 truncate py-2 text-align-last-left">
|
||||
<ConfirmationModal
|
||||
asChild
|
||||
title="Edit"
|
||||
titleHeader={`${user.username}`}
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||
cancelText="Cancel"
|
||||
confirmationText="Confirm"
|
||||
icon={"UserCog2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleDisableUser(
|
||||
user.is_active,
|
||||
user.id,
|
||||
user
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="flex w-fit">
|
||||
<Checkbox
|
||||
id="is_active"
|
||||
checked={user.is_active}
|
||||
/>
|
||||
</div>
|
||||
</ConfirmationModal>
|
||||
</TableCell>
|
||||
<TableCell className="relative left-1 truncate py-2 text-align-last-left">
|
||||
<ConfirmationModal
|
||||
asChild
|
||||
title="Edit"
|
||||
titleHeader={`${user.username}`}
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||
cancelText="Cancel"
|
||||
confirmationText="Confirm"
|
||||
icon={"UserCog2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleSuperUserEdit(
|
||||
user.is_superuser,
|
||||
user.id,
|
||||
user
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="flex w-fit">
|
||||
<Checkbox
|
||||
id="is_superuser"
|
||||
checked={user.is_superuser}
|
||||
/>
|
||||
</div>
|
||||
</ConfirmationModal>
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2 ">
|
||||
{
|
||||
new Date(user.create_at!)
|
||||
.toISOString()
|
||||
.split("T")[0]
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2">
|
||||
{
|
||||
new Date(user.updated_at!)
|
||||
.toISOString()
|
||||
.split("T")[0]
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell className="flex w-[100px] py-2 text-right">
|
||||
<div className="flex">
|
||||
<UserManagementModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.id}`}
|
||||
cancelText="Cancel"
|
||||
confirmationText="Save"
|
||||
icon={"UserPlus2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, editUser) => {
|
||||
handleEditUser(user.id, editUser);
|
||||
}}
|
||||
>
|
||||
<ShadTooltip content="Edit" side="top">
|
||||
<IconComponent
|
||||
name="Pencil"
|
||||
className="h-4 w-4 cursor-pointer"
|
||||
/>
|
||||
</ShadTooltip>
|
||||
</UserManagementModal>
|
||||
|
||||
<ConfirmationModal
|
||||
title="Delete"
|
||||
titleHeader="Delete User"
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you sure you want to delete this user? This action cannot be undone."
|
||||
cancelText="Cancel"
|
||||
confirmationText="Delete"
|
||||
icon={"UserMinus2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleDeleteUser(user);
|
||||
}}
|
||||
>
|
||||
<ShadTooltip content="Delete" side="top">
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
className="ml-2 h-4 w-4 cursor-pointer"
|
||||
/>
|
||||
</ShadTooltip>
|
||||
</ConfirmationModal>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
)}
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<PaginatorComponent
|
||||
pageIndex={index}
|
||||
pageSize={size}
|
||||
totalRowsCount={totalRowsCount}
|
||||
paginate={(pageSize, pageIndex) => {
|
||||
handleChangePagination(pageIndex, pageSize);
|
||||
}}
|
||||
></PaginatorComponent>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { useNavigate } from "react-router-dom";
|
|||
import { CardComponent } from "../../components/cardComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import Header from "../../components/headerComponent";
|
||||
import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
|
||||
import { getExamples } from "../../controllers/API";
|
||||
import { FlowType } from "../../types/flow";
|
||||
export default function CommunityPage(): JSX.Element {
|
||||
|
|
@ -74,7 +75,14 @@ export default function CommunityPage(): JSX.Element {
|
|||
new and powerful features.
|
||||
</span>
|
||||
<div className="community-pages-flows-panel">
|
||||
{!loadingExamples &&
|
||||
{loadingExamples ? (
|
||||
<>
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
</>
|
||||
) : (
|
||||
examples.map((flow, idx) => (
|
||||
<CardComponent
|
||||
key={idx}
|
||||
|
|
@ -99,7 +107,8 @@ export default function CommunityPage(): JSX.Element {
|
|||
</Button>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -71,8 +71,15 @@ export default function Page({
|
|||
setTabsState,
|
||||
tabId,
|
||||
} = useContext(TabsContext);
|
||||
const { types, reactFlowInstance, setReactFlowInstance, templates } =
|
||||
useContext(typesContext);
|
||||
const {
|
||||
types,
|
||||
reactFlowInstance,
|
||||
setReactFlowInstance,
|
||||
templates,
|
||||
setFilterEdge,
|
||||
deleteNode,
|
||||
deleteEdge,
|
||||
} = useContext(typesContext);
|
||||
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { takeSnapshot } = useContext(undoRedoContext);
|
||||
|
|
@ -114,6 +121,16 @@ export default function Page({
|
|||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
if (!isWrappedWithClass(event, "nodelete")) {
|
||||
if (
|
||||
(event.key === "Delete" || event.key === "Backspace") &&
|
||||
lastSelection
|
||||
) {
|
||||
event.preventDefault();
|
||||
deleteNode(lastSelection.nodes.map((node) => node.id));
|
||||
deleteEdge(lastSelection.edges.map((edge) => edge.id));
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleMouseMove = (event) => {
|
||||
setPosition({ x: event.clientX, y: event.clientY });
|
||||
|
|
@ -162,6 +179,27 @@ export default function Page({
|
|||
setExtraNavigation({ title: "Components" });
|
||||
}, [setExtraComponent, setExtraNavigation]);
|
||||
|
||||
const [seconds, setSeconds] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setSeconds((prevSeconds) => {
|
||||
let updatedSeconds = prevSeconds + 1;
|
||||
|
||||
if (updatedSeconds % 30 === 0) {
|
||||
saveFlow(flow, true);
|
||||
updatedSeconds = 0;
|
||||
}
|
||||
|
||||
return updatedSeconds;
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onEdgesChangeMod = useCallback(
|
||||
(change: EdgeChange[]) => {
|
||||
onEdgesChange(change);
|
||||
|
|
@ -312,7 +350,14 @@ export default function Page({
|
|||
setNodes((nds) => nds.concat(newNode));
|
||||
} else if (event.dataTransfer.types.some((types) => types === "Files")) {
|
||||
takeSnapshot();
|
||||
uploadFlow(false, event.dataTransfer.files.item(0)!);
|
||||
if (event.dataTransfer.files.item(0)!.type === "application/json") {
|
||||
uploadFlow(false, event.dataTransfer.files.item(0)!);
|
||||
} else {
|
||||
setErrorData({
|
||||
title: "Invalid file type",
|
||||
list: ["Please upload a JSON file"],
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
// Specify dependencies for useCallback
|
||||
|
|
@ -389,6 +434,10 @@ export default function Page({
|
|||
[]
|
||||
);
|
||||
|
||||
const onPaneClick = useCallback((flow) => {
|
||||
setFilterEdge([]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex h-full overflow-hidden">
|
||||
{!view && <ExtraSidebar />}
|
||||
|
|
@ -427,8 +476,9 @@ export default function Page({
|
|||
connectionLineComponent={ConnectionLineComponent}
|
||||
onDragOver={onDragOver}
|
||||
onDrop={onDrop}
|
||||
onNodesDelete={onDelete}
|
||||
onSelectionChange={onSelectionChange}
|
||||
onNodesDelete={onDelete}
|
||||
deleteKeyCode={[]}
|
||||
className="theme-attribution"
|
||||
minZoom={0.01}
|
||||
maxZoom={8}
|
||||
|
|
@ -436,6 +486,7 @@ export default function Page({
|
|||
zoomOnPinch={!view}
|
||||
panOnDrag={!view}
|
||||
proOptions={{ hideAttribution: true }}
|
||||
onPaneClick={onPaneClick}
|
||||
>
|
||||
<Background className="" />
|
||||
{!view && (
|
||||
|
|
@ -456,7 +507,10 @@ export default function Page({
|
|||
reactFlowInstance!,
|
||||
getRandomName()
|
||||
);
|
||||
const newGroupNode = generateNodeFromFlow(newFlow,getNodeId);
|
||||
const newGroupNode = generateNodeFromFlow(
|
||||
newFlow,
|
||||
getNodeId
|
||||
);
|
||||
setNodes((oldNodes) => [
|
||||
...oldNodes.filter(
|
||||
(oldNodes) =>
|
||||
|
|
|
|||
|
|
@ -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,59 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
setErrorData({ title: " Components with errors: ", list: errors });
|
||||
}, []);
|
||||
|
||||
function handleBlur() {
|
||||
if (!search && search === "") {
|
||||
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">
|
||||
|
|
@ -99,16 +158,20 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
<ShadTooltip content={"Code"} side="top">
|
||||
<div className="side-bar-button">
|
||||
{flow && flow.data && (
|
||||
<ApiModal flow={flow} disable={!isBuilt}>
|
||||
<div className={classNames("extra-side-bar-buttons")}>
|
||||
<IconComponent
|
||||
name="Code2"
|
||||
className={
|
||||
"side-bar-button-size" +
|
||||
(isBuilt ? " " : " extra-side-bar-save-disable")
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<ApiModal flow={flow}>
|
||||
<button
|
||||
className={"w-full " + (!isBuilt ? "button-disable" : "")}
|
||||
>
|
||||
<div className={classNames("extra-side-bar-buttons")}>
|
||||
<IconComponent
|
||||
name="Code2"
|
||||
className={
|
||||
"side-bar-button-size" +
|
||||
(isBuilt ? " " : " extra-side-bar-save-disable")
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</ApiModal>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -137,11 +200,12 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
<Separator />
|
||||
<div className="side-bar-search-div-placement">
|
||||
<Input
|
||||
onFocusCapture={() => handleBlur()}
|
||||
type="text"
|
||||
name="search"
|
||||
id="search"
|
||||
placeholder="Search"
|
||||
className="nopan nodrag noundo nocopy input-search"
|
||||
className="nopan nodelete nodrag noundo nocopy input-search"
|
||||
onChange={(event) => {
|
||||
handleSearchInput(event.target.value);
|
||||
// Set search input state
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
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";
|
||||
|
|
@ -9,13 +15,16 @@ import {
|
|||
expandGroupNode,
|
||||
updateFlowPosition,
|
||||
} from "../../../../utils/reactflowUtils";
|
||||
import { classNames } from "../../../../utils/utils";
|
||||
import { classNames, getRandomKeyByssmm } from "../../../../utils/utils";
|
||||
|
||||
export default function NodeToolbarComponent({
|
||||
data,
|
||||
setData,
|
||||
deleteNode,
|
||||
position,
|
||||
setShowNode,
|
||||
numberOfHandles,
|
||||
showNode,
|
||||
}: nodeToolbarPropsType): JSX.Element {
|
||||
const [nodeLength, setNodeLength] = useState(
|
||||
Object.keys(data.node!.template).filter(
|
||||
|
|
@ -32,10 +41,43 @@ 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 isGroup = data.node?.flow ? true : false;
|
||||
|
||||
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);
|
||||
}
|
||||
if (event.includes("disabled")) {
|
||||
return;
|
||||
}
|
||||
if (event.includes("ungroup")) {
|
||||
updateFlowPosition(position, data.node?.flow!);
|
||||
expandGroupNode(data, reactFlowInstance);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-26 h-10">
|
||||
|
|
@ -103,43 +145,105 @@ export default function NodeToolbarComponent({
|
|||
</a>
|
||||
</ShadTooltip>
|
||||
|
||||
<ShadTooltip content="Edit" side="top">
|
||||
<div>
|
||||
<EditNodeModal
|
||||
data={data}
|
||||
setData={setData}
|
||||
nodeLength={nodeLength}
|
||||
>
|
||||
<div
|
||||
{isMinimal || isGroup ? (
|
||||
<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() +
|
||||
(nodeLength == 0 ? "disabled" : "advanced")
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
"flex " +
|
||||
(nodeLength == 0
|
||||
? "text-muted-foreground"
|
||||
: "text-primary")
|
||||
}
|
||||
>
|
||||
<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>
|
||||
)}
|
||||
{isGroup && (
|
||||
<SelectItem value={getRandomKeyByssmm() + "ungroup"}>
|
||||
<div className="flex">
|
||||
<IconComponent
|
||||
name="Ungroup"
|
||||
className="relative top-0.5 mr-2 h-4 w-4"
|
||||
/>{" "}
|
||||
Ungroup{" "}
|
||||
</div>
|
||||
</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : (
|
||||
<ShadTooltip content="Edit" side="top">
|
||||
<div>
|
||||
<button
|
||||
disabled={nodeLength === 0}
|
||||
onClick={() => setShowModalAdvanced(true)}
|
||||
className={classNames(
|
||||
"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" +
|
||||
"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") +
|
||||
(isGroup ? "" : " rounded-r-md")
|
||||
: " text-foreground")
|
||||
)}
|
||||
>
|
||||
<IconComponent name="Settings2" className="h-4 w-4 " />
|
||||
</div>
|
||||
</EditNodeModal>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
{isGroup && (
|
||||
<ShadTooltip content="Ungroup" side="top">
|
||||
<button
|
||||
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"
|
||||
)}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
updateFlowPosition(position, data.node?.flow!);
|
||||
expandGroupNode(data, reactFlowInstance);
|
||||
}}
|
||||
>
|
||||
<IconComponent name="Ungroup" className="h-4 w-4" />
|
||||
</button>
|
||||
</button>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
|
||||
{showModalAdvanced && (
|
||||
<EditNodeModal
|
||||
data={data}
|
||||
setData={setData}
|
||||
nodeLength={nodeLength}
|
||||
open={showModalAdvanced}
|
||||
onClose={(modal) => {
|
||||
setShowModalAdvanced(modal);
|
||||
}}
|
||||
>
|
||||
<></>
|
||||
</EditNodeModal>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useContext, useEffect } from "react";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import DropdownButton from "../../components/DropdownButtonComponent";
|
||||
import { CardComponent } from "../../components/cardComponent";
|
||||
|
|
@ -7,6 +7,7 @@ import Header from "../../components/headerComponent";
|
|||
import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
export default function HomePage(): JSX.Element {
|
||||
const {
|
||||
|
|
@ -19,6 +20,7 @@ export default function HomePage(): JSX.Element {
|
|||
uploadFlow,
|
||||
isLoading,
|
||||
} = useContext(TabsContext);
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
const dropdownOptions = [
|
||||
{
|
||||
name: "Import from JSON",
|
||||
|
|
@ -35,9 +37,40 @@ export default function HomePage(): JSX.Element {
|
|||
}, []);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
console.log(isLoading);
|
||||
}, [isLoading]);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
const dragOver = (e) => {
|
||||
e.preventDefault();
|
||||
if (e.dataTransfer.types.some((types) => types === "Files")) {
|
||||
setIsDragging(true);
|
||||
}
|
||||
};
|
||||
|
||||
const dragEnter = (e) => {
|
||||
if (e.dataTransfer.types.some((types) => types === "Files")) {
|
||||
setIsDragging(true);
|
||||
}
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const dragLeave = () => {
|
||||
setIsDragging(false);
|
||||
};
|
||||
|
||||
const fileDrop = (e) => {
|
||||
e.preventDefault();
|
||||
setIsDragging(false);
|
||||
if (e.dataTransfer.types.some((types) => types === "Files")) {
|
||||
if (e.dataTransfer.files.item(0).type === "application/json") {
|
||||
uploadFlow(true, e.dataTransfer.files.item(0)!);
|
||||
} else {
|
||||
setErrorData({
|
||||
title: "Invalid file type",
|
||||
list: ["Please upload a JSON file"],
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Personal flows display
|
||||
return (
|
||||
|
|
@ -82,40 +115,63 @@ export default function HomePage(): JSX.Element {
|
|||
<span className="main-page-description-text">
|
||||
Manage your personal projects. Download or upload your collection.
|
||||
</span>
|
||||
<div className="main-page-flows-display">
|
||||
{isLoading && flows.length == 0 ? (
|
||||
<div
|
||||
onDragOver={dragOver}
|
||||
onDragEnter={dragEnter}
|
||||
onDragLeave={dragLeave}
|
||||
onDrop={fileDrop}
|
||||
className={
|
||||
"h-full w-full " +
|
||||
(isDragging
|
||||
? "mb-24 flex flex-col items-center justify-center gap-4 text-2xl font-light"
|
||||
: "")
|
||||
}
|
||||
>
|
||||
{isDragging ? (
|
||||
<>
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<IconComponent
|
||||
name="ArrowUpToLine"
|
||||
className="h-12 w-12 stroke-1"
|
||||
/>
|
||||
Drop your flow here
|
||||
</>
|
||||
) : (
|
||||
flows.map((flow, idx) => (
|
||||
<CardComponent
|
||||
key={idx}
|
||||
flow={flow}
|
||||
id={flow.id}
|
||||
button={
|
||||
<Link to={"/flow/" + flow.id}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="whitespace-nowrap "
|
||||
>
|
||||
<IconComponent
|
||||
name="ExternalLink"
|
||||
className="main-page-nav-button"
|
||||
/>
|
||||
Edit Flow
|
||||
</Button>
|
||||
</Link>
|
||||
}
|
||||
onDelete={() => {
|
||||
removeFlow(flow.id);
|
||||
}}
|
||||
/>
|
||||
))
|
||||
<div className="main-page-flows-display">
|
||||
{isLoading && flows.length == 0 ? (
|
||||
<>
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
</>
|
||||
) : (
|
||||
flows.map((flow, idx) => (
|
||||
<CardComponent
|
||||
key={idx}
|
||||
flow={flow}
|
||||
id={flow.id}
|
||||
button={
|
||||
<Link to={"/flow/" + flow.id}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="whitespace-nowrap "
|
||||
>
|
||||
<IconComponent
|
||||
name="ExternalLink"
|
||||
className="main-page-nav-button"
|
||||
/>
|
||||
Edit Flow
|
||||
</Button>
|
||||
</Link>
|
||||
}
|
||||
onDelete={() => {
|
||||
removeFlow(flow.id);
|
||||
}}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
173
src/frontend/src/pages/ProfileSettingsPage/index.tsx
Normal file
173
src/frontend/src/pages/ProfileSettingsPage/index.tsx
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
import * as Form from "@radix-ui/react-form";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import GradientChooserComponent from "../../components/gradientChooserComponent";
|
||||
import Header from "../../components/headerComponent";
|
||||
import InputComponent from "../../components/inputComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { CONTROL_PATCH_USER_STATE } from "../../constants/constants";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { resetPassword, updateUser } from "../../controllers/API";
|
||||
import {
|
||||
inputHandlerEventType,
|
||||
patchUserInputStateType,
|
||||
} from "../../types/components";
|
||||
import { gradients } from "../../utils/styleUtils";
|
||||
export default function ProfileSettingsPage(): JSX.Element {
|
||||
const { setTabId } = useContext(TabsContext);
|
||||
|
||||
const [inputState, setInputState] = useState<patchUserInputStateType>(
|
||||
CONTROL_PATCH_USER_STATE
|
||||
);
|
||||
|
||||
// set null id
|
||||
useEffect(() => {
|
||||
setTabId("");
|
||||
}, []);
|
||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
||||
const { userData, setUserData } = useContext(AuthContext);
|
||||
const { password, cnfPassword, gradient } = inputState;
|
||||
|
||||
async function handlePatchUser() {
|
||||
if (password !== cnfPassword) {
|
||||
setErrorData({
|
||||
title: "Error changing password",
|
||||
list: ["Passwords do not match"],
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (password !== "") await resetPassword(userData!.id, { password });
|
||||
if (gradient !== "")
|
||||
await updateUser(userData!.id, { profile_image: gradient });
|
||||
if (gradient !== "") {
|
||||
let newUserData = cloneDeep(userData);
|
||||
newUserData!.profile_image = gradient;
|
||||
|
||||
setUserData(newUserData);
|
||||
}
|
||||
handleInput({ target: { name: "password", value: "" } });
|
||||
handleInput({ target: { name: "cnfPassword", value: "" } });
|
||||
setSuccessData({ title: "Changes saved successfully!" });
|
||||
} catch (error) {
|
||||
setErrorData({
|
||||
title: "Error saving changes",
|
||||
list: [(error as any).response.data.detail],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleInput({
|
||||
target: { name, value },
|
||||
}: inputHandlerEventType): void {
|
||||
setInputState((prev) => ({ ...prev, [name]: value }));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
|
||||
<div className="community-page-arrangement">
|
||||
<div className="community-page-nav-arrangement">
|
||||
<span className="community-page-nav-title">
|
||||
<IconComponent name="User" className="w-6" />
|
||||
Profile Settings
|
||||
</span>
|
||||
</div>
|
||||
<span className="community-page-description-text">
|
||||
Change your profile settings like your password and your profile
|
||||
picture.
|
||||
</span>
|
||||
<Form.Root
|
||||
onSubmit={(event) => {
|
||||
handlePatchUser();
|
||||
const data = Object.fromEntries(new FormData(event.currentTarget));
|
||||
event.preventDefault();
|
||||
}}
|
||||
className="flex h-full flex-col px-6 pb-16"
|
||||
>
|
||||
<div className="flex h-full flex-col gap-4">
|
||||
<div className="flex gap-4">
|
||||
<div className="mb-3 w-96">
|
||||
<Form.Field name="password">
|
||||
<Form.Label className="data-[invalid]:label-invalid">
|
||||
Password{" "}
|
||||
</Form.Label>
|
||||
<InputComponent
|
||||
onChange={(value) => {
|
||||
handleInput({ target: { name: "password", value } });
|
||||
}}
|
||||
value={password}
|
||||
isForm
|
||||
password={true}
|
||||
placeholder="Password"
|
||||
className="w-full"
|
||||
/>
|
||||
<Form.Message match="valueMissing" className="field-invalid">
|
||||
Please enter your password
|
||||
</Form.Message>
|
||||
</Form.Field>
|
||||
</div>
|
||||
<div className="mb-3 w-96">
|
||||
<Form.Field name="cnfPassword">
|
||||
<Form.Label className="data-[invalid]:label-invalid">
|
||||
Confirm Password{" "}
|
||||
</Form.Label>
|
||||
|
||||
<InputComponent
|
||||
onChange={(value) => {
|
||||
handleInput({ target: { name: "cnfPassword", value } });
|
||||
}}
|
||||
value={cnfPassword}
|
||||
isForm
|
||||
password={true}
|
||||
placeholder="Confirm Password"
|
||||
className="w-full"
|
||||
/>
|
||||
|
||||
<Form.Message className="field-invalid" match="valueMissing">
|
||||
Please confirm your password
|
||||
</Form.Message>
|
||||
</Form.Field>
|
||||
</div>
|
||||
</div>
|
||||
<Form.Field name="gradient">
|
||||
<Form.Label className="data-[invalid]:label-invalid">
|
||||
Profile Gradient{" "}
|
||||
</Form.Label>
|
||||
|
||||
<div className="mt-4 w-[1010px]">
|
||||
<GradientChooserComponent
|
||||
value={
|
||||
gradient == ""
|
||||
? userData!.profile_image ??
|
||||
gradients[
|
||||
parseInt(userData!.id ?? "", 30) % gradients.length
|
||||
]
|
||||
: gradient
|
||||
}
|
||||
onChange={(value) => {
|
||||
handleInput({ target: { name: "gradient", value } });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Form.Field>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full justify-end">
|
||||
<div className="w-32">
|
||||
<Form.Submit asChild>
|
||||
<Button className="mr-3 mt-6 w-full" type="submit">
|
||||
Save
|
||||
</Button>
|
||||
</Form.Submit>
|
||||
</div>
|
||||
</div>
|
||||
</Form.Root>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -54,11 +54,13 @@ export default function LoginPage(): JSX.Element {
|
|||
setTimeout(() => {
|
||||
getLoggedUser()
|
||||
.then((user) => {
|
||||
const isSuperUser = user.is_superuser;
|
||||
const isSuperUser = user!.is_superuser;
|
||||
setIsAdmin(isSuperUser);
|
||||
setUserData(user);
|
||||
})
|
||||
.catch((error) => {});
|
||||
.catch((error) => {
|
||||
console.log("login page", error);
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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={() => {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import ApiKeysPage from "./pages/ApiKeysPage";
|
|||
import CommunityPage from "./pages/CommunityPage";
|
||||
import FlowPage from "./pages/FlowPage";
|
||||
import HomePage from "./pages/MainPage";
|
||||
import ProfileSettingsPage from "./pages/ProfileSettingsPage";
|
||||
import ViewPage from "./pages/ViewPage";
|
||||
import DeleteAccountPage from "./pages/deleteAccountPage";
|
||||
import LoginPage from "./pages/loginPage";
|
||||
|
|
@ -95,6 +96,14 @@ const Router = () => {
|
|||
/>
|
||||
|
||||
<Route path="/account">
|
||||
<Route
|
||||
path="settings"
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<ProfileSettingsPage />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="delete"
|
||||
element={
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@
|
|||
@apply flex flex-col gap-2 p-2;
|
||||
}
|
||||
.side-bar-components-div-arrangement {
|
||||
@apply w-full overflow-auto scrollbar-hide pb-10;
|
||||
@apply w-full overflow-auto pb-10 scrollbar-hide;
|
||||
}
|
||||
.search-icon {
|
||||
@apply absolute inset-y-0 right-0 flex items-center py-1.5 pr-5;
|
||||
|
|
@ -127,7 +127,7 @@
|
|||
}
|
||||
|
||||
.skeleton-card {
|
||||
@apply bg-background h-48 p-4 border rounded-lg flex flex-col gap-6;
|
||||
@apply flex h-48 flex-col gap-6 rounded-lg border bg-background p-4;
|
||||
}
|
||||
|
||||
.skeleton-card-wrapper {
|
||||
|
|
@ -205,13 +205,17 @@
|
|||
}
|
||||
|
||||
.loading-page-panel {
|
||||
@apply h-full w-full flex justify-center items-center bg-muted;
|
||||
@apply flex h-full w-full items-center justify-center bg-muted;
|
||||
}
|
||||
|
||||
.main-page-panel {
|
||||
@apply flex-max-width h-full flex-col overflow-auto bg-muted px-16;
|
||||
}
|
||||
|
||||
.admin-page-panel {
|
||||
@apply flex-max-width h-full flex-col overflow-auto bg-muted px-16;
|
||||
}
|
||||
|
||||
.main-page-nav-arrangement {
|
||||
@apply flex-max-width justify-between px-6 py-12 pb-2;
|
||||
}
|
||||
|
|
@ -228,6 +232,10 @@
|
|||
@apply flex w-[60%] px-6 pb-14 text-muted-foreground;
|
||||
}
|
||||
|
||||
.admin-page-description-text {
|
||||
@apply flex w-[80%] px-6 pb-8 text-muted-foreground;
|
||||
}
|
||||
|
||||
.main-page-flows-display {
|
||||
@apply grid w-full gap-4 p-4 md:grid-cols-2 lg:grid-cols-4;
|
||||
}
|
||||
|
|
@ -256,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;
|
||||
|
|
@ -303,7 +311,7 @@
|
|||
@apply hover:text-accent-foreground hover:transition-all;
|
||||
}
|
||||
.generic-node-desc {
|
||||
@apply h-full w-full py-5 text-foreground;
|
||||
@apply h-full w-full text-foreground;
|
||||
}
|
||||
.generic-node-desc-text {
|
||||
@apply w-full px-5 pb-3 text-sm text-muted-foreground;
|
||||
|
|
@ -342,7 +350,7 @@
|
|||
@apply alert-icon text-status-green;
|
||||
}
|
||||
.success-alert-message {
|
||||
@apply word-break-break-word alert-font-size text-success-foreground;
|
||||
@apply alert-font-size text-success-foreground word-break-break-word;
|
||||
}
|
||||
|
||||
.card-component-title-display {
|
||||
|
|
@ -352,7 +360,7 @@
|
|||
@apply flex h-7 w-7 items-center justify-center rounded-full text-2xl;
|
||||
}
|
||||
.card-component-title-size {
|
||||
@apply w-full flex-1 word-break-break-word truncate-doubleline;
|
||||
@apply w-full flex-1 truncate-doubleline word-break-break-word;
|
||||
}
|
||||
.card-component-delete-button {
|
||||
@apply flex self-start;
|
||||
|
|
@ -512,7 +520,7 @@
|
|||
@apply inline-flex h-9 items-center justify-center rounded-md border border-input px-3 pr-0 shadow-sm;
|
||||
}
|
||||
.header-waitlist-link-box {
|
||||
@apply inline-flex h-9 items-center justify-center rounded-md border border-input px-2 shadow-sm text-sm font-medium text-muted-foreground ring-offset-background disabled:pointer-events-none disabled:opacity-50 whitespace-nowrap;
|
||||
@apply inline-flex h-9 items-center justify-center whitespace-nowrap rounded-md border border-input px-2 text-sm font-medium text-muted-foreground shadow-sm ring-offset-background disabled:pointer-events-none disabled:opacity-50;
|
||||
}
|
||||
.header-waitlist-link-box:hover {
|
||||
@apply hover:bg-accent hover:text-accent-foreground;
|
||||
|
|
@ -845,7 +853,7 @@
|
|||
}
|
||||
|
||||
.api-modal-tabs {
|
||||
@apply flex h-full flex-col overflow-hidden rounded-md border bg-muted text-center sm:w-[75vw] lg:w-full;
|
||||
@apply flex h-full flex-col max-w-full overflow-hidden rounded-md border bg-muted text-center sm:w-[75vw] md:w-[75vw] lg:w-[75vw] xl:w-[76vw] 2xl:w-full;
|
||||
}
|
||||
.api-modal-tablist-div {
|
||||
@apply flex items-center justify-between px-2;
|
||||
|
|
@ -874,11 +882,11 @@
|
|||
@apply bg-white text-primary;
|
||||
}
|
||||
.code-highlight {
|
||||
@apply block max-h-[64vh] w-full overflow-y-hidden word-break-break-word border-0 px-3 py-2 text-sm outline-0;
|
||||
@apply block max-h-[64vh] w-full overflow-y-hidden border-0 px-3 py-2 text-sm outline-0 word-break-break-word;
|
||||
}
|
||||
|
||||
.code-nohighlight {
|
||||
@apply block max-h-[70vh] w-full overflow-y-hidden word-break-break-word border-0 px-3 py-2 text-sm outline-0;
|
||||
@apply block max-h-[70vh] w-full overflow-y-hidden border-0 px-3 py-2 text-sm outline-0 word-break-break-word;
|
||||
}
|
||||
.form-modal-lockchat {
|
||||
@apply form-input block w-full rounded-md border-border p-4 pr-16 custom-scroll focus:border-ring focus:ring-ring sm:text-sm;
|
||||
|
|
@ -1042,11 +1050,15 @@
|
|||
@apply rounded-md bg-indigo-100 px-0.5 dark:bg-indigo-900;
|
||||
}
|
||||
|
||||
.field-invalid{
|
||||
@apply font-medium text-[0.8rem] text-destructive absolute
|
||||
.field-invalid {
|
||||
@apply absolute text-[0.8rem] font-medium text-destructive;
|
||||
}
|
||||
|
||||
.label-invalid{
|
||||
@apply text-destructive
|
||||
.label-invalid {
|
||||
@apply text-destructive;
|
||||
}
|
||||
|
||||
.input-invalid {
|
||||
@apply border-destructive focus:border-destructive focus:ring-destructive;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ pre {
|
|||
animation: slideUp 300ms ease-out;
|
||||
}
|
||||
|
||||
|
||||
.gradient-end {
|
||||
animation: gradient-motion-end 3s infinite forwards;
|
||||
}
|
||||
|
|
@ -50,4 +49,22 @@ select:-webkit-autofill:focus {
|
|||
-webkit-box-shadow: 0 0 0px 1000px #fff6d0 inset;
|
||||
box-shadow: 0 0 0px 1000px #fff6d0 inset;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
.ace_scrollbar::-webkit-scrollbar {
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.ace_scrollbar::-webkit-scrollbar-track {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
.ace_scrollbar::-webkit-scrollbar-thumb {
|
||||
background-color: #ccc;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.ace_scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #bbb;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ export type APIKindType = { class: APIClassType; [key: string]: APIClassType };
|
|||
export type APITemplateType = {
|
||||
[key: string]: TemplateVariableType;
|
||||
};
|
||||
|
||||
export type CustomFieldsType = {
|
||||
[key: string]: Array<string>;
|
||||
};
|
||||
|
||||
export type APIClassType = {
|
||||
base_classes: Array<string>;
|
||||
description: string;
|
||||
|
|
@ -14,6 +19,7 @@ export type APIClassType = {
|
|||
display_name: string;
|
||||
input_types?: Array<string>;
|
||||
output_types?: Array<string>;
|
||||
custom_fields?: CustomFieldsType;
|
||||
beta?: boolean;
|
||||
documentation: string;
|
||||
error?: string;
|
||||
|
|
@ -24,6 +30,8 @@ export type APIClassType = {
|
|||
| APITemplateType
|
||||
| boolean
|
||||
| FlowType
|
||||
| CustomFieldsType
|
||||
| boolean
|
||||
| undefined;
|
||||
};
|
||||
|
||||
|
|
@ -48,7 +56,7 @@ export type sendAllProps = {
|
|||
description: string;
|
||||
viewport: Viewport;
|
||||
inputs: { text?: string };
|
||||
|
||||
chatKey: string;
|
||||
chatHistory: { message: string | object; isSend: boolean }[];
|
||||
};
|
||||
export type errorsTypeAPI = {
|
||||
|
|
@ -88,11 +96,25 @@ export type LoginAuthType = {
|
|||
token_type?: string;
|
||||
};
|
||||
|
||||
export type changeUser = {
|
||||
username?: string;
|
||||
is_active?: boolean;
|
||||
is_superuser?: boolean;
|
||||
password?: string;
|
||||
profile_image?: string;
|
||||
};
|
||||
|
||||
export type resetPasswordType = {
|
||||
password?: string;
|
||||
profile_image?: string;
|
||||
};
|
||||
|
||||
export type Users = {
|
||||
id: string;
|
||||
username: string;
|
||||
is_active: boolean;
|
||||
is_superuser: boolean;
|
||||
profile_image: string;
|
||||
create_at: Date;
|
||||
updated_at: Date;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,5 +8,6 @@ export type ChatMessageType = {
|
|||
isSend: boolean;
|
||||
thought?: string;
|
||||
files?: Array<{ data: string; type: string; data_type: string }>;
|
||||
prompt?: string;
|
||||
chatKey: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ export type ParameterComponentType = {
|
|||
optionalHandle?: Array<String> | null;
|
||||
info?: string;
|
||||
proxy?: { field: string; id: string };
|
||||
showNode?: boolean;
|
||||
};
|
||||
export type InputListComponentType = {
|
||||
value: string[];
|
||||
|
|
@ -58,6 +59,23 @@ export type InputListComponentType = {
|
|||
editNode?: boolean;
|
||||
};
|
||||
|
||||
export type KeyPairListComponentType = {
|
||||
value: any;
|
||||
onChange: (value: Object[]) => void;
|
||||
disabled: boolean;
|
||||
editNode?: boolean;
|
||||
duplicateKey?: boolean;
|
||||
advanced?: boolean | null;
|
||||
dataValue?: any;
|
||||
};
|
||||
|
||||
export type DictComponentType = {
|
||||
value: any;
|
||||
onChange: (value) => void;
|
||||
disabled: boolean;
|
||||
editNode?: boolean;
|
||||
};
|
||||
|
||||
export type TextAreaComponentType = {
|
||||
field_name?: string;
|
||||
nodeClass?: APIClassType;
|
||||
|
|
@ -69,6 +87,17 @@ export type TextAreaComponentType = {
|
|||
readonly?: boolean;
|
||||
};
|
||||
|
||||
export type PromptAreaComponentType = {
|
||||
field_name?: string;
|
||||
nodeClass?: APIClassType;
|
||||
setNodeClass?: (value: APIClassType) => void;
|
||||
disabled: boolean;
|
||||
onChange: (value: string[] | string) => void;
|
||||
value: string;
|
||||
readonly?: boolean;
|
||||
editNode?: boolean;
|
||||
};
|
||||
|
||||
export type CodeAreaComponentType = {
|
||||
disabled: boolean;
|
||||
onChange: (value: string[] | string) => void;
|
||||
|
|
@ -179,6 +208,7 @@ export type IconComponentProps = {
|
|||
name: string;
|
||||
className?: string;
|
||||
iconColor?: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export type InputProps = {
|
||||
|
|
@ -239,6 +269,7 @@ export type PaginatorComponentType = {
|
|||
export type ConfirmationModalType = {
|
||||
title: string;
|
||||
titleHeader: string;
|
||||
asChild?: boolean;
|
||||
modalContent: string;
|
||||
modalContentTitle: string;
|
||||
cancelText: string;
|
||||
|
|
@ -259,6 +290,7 @@ export type UserManagementType = {
|
|||
icon: string;
|
||||
data?: any;
|
||||
index?: number;
|
||||
asChild?: boolean;
|
||||
onConfirm: (index, data) => void;
|
||||
};
|
||||
|
||||
|
|
@ -267,6 +299,12 @@ export type loginInputStateType = {
|
|||
password: string;
|
||||
};
|
||||
|
||||
export type patchUserInputStateType = {
|
||||
password: string;
|
||||
cnfPassword: string;
|
||||
gradient: string;
|
||||
};
|
||||
|
||||
export type UserInputType = {
|
||||
username: string;
|
||||
password: string;
|
||||
|
|
@ -396,6 +434,9 @@ export type nodeToolbarPropsType = {
|
|||
deleteNode: (idx: string) => void;
|
||||
setData: (newState: NodeDataType) => void;
|
||||
position: XYPosition;
|
||||
setShowNode: (boolean: any) => void;
|
||||
numberOfHandles: boolean[] | [];
|
||||
showNode: boolean;
|
||||
};
|
||||
|
||||
export type parsedDataType = {
|
||||
|
|
@ -520,7 +561,7 @@ export type codeTabsPropsType = {
|
|||
) => string;
|
||||
buildTweakObject?: (
|
||||
tw: string,
|
||||
changes: string | string[] | boolean | number,
|
||||
changes: string | string[] | boolean | number | Object[] | Object,
|
||||
template: TemplateVariableType
|
||||
) => string | void;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ import { tweakType } from "../components";
|
|||
import { FlowType } from "../flow";
|
||||
|
||||
export type TabsContextType = {
|
||||
saveFlow: (flow: FlowType) => Promise<void>;
|
||||
save: () => void;
|
||||
saveFlow: (flow: FlowType, silent?: boolean) => Promise<void>;
|
||||
tabId: string;
|
||||
isLoading: boolean;
|
||||
setTabId: (index: string) => void;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ const data: { [char: string]: string } = {};
|
|||
export type typesContextType = {
|
||||
reactFlowInstance: ReactFlowInstance | null;
|
||||
setReactFlowInstance: (newState: ReactFlowInstance) => void;
|
||||
deleteNode: (idx: string) => void;
|
||||
deleteNode: (idx: string | Array<string>) => void;
|
||||
types: typeof types;
|
||||
setTypes: (newState: {}) => void;
|
||||
templates: typeof template;
|
||||
|
|
@ -18,6 +18,9 @@ export type typesContextType = {
|
|||
setData: (newState: {}) => void;
|
||||
fetchError: boolean;
|
||||
setFetchError: (newState: boolean) => void;
|
||||
setFilterEdge: (newState) => void;
|
||||
getFilterEdge: any[];
|
||||
deleteEdge: (idx: string | Array<string>) => void;
|
||||
};
|
||||
|
||||
export type alertContextType = {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import _, { get } from "lodash";
|
||||
import _ from "lodash";
|
||||
import {
|
||||
Connection,
|
||||
Edge,
|
||||
|
|
@ -25,7 +25,7 @@ import {
|
|||
unselectAllNodesType,
|
||||
updateEdgesHandleIdsType,
|
||||
} from "../types/utils/reactflowUtils";
|
||||
import { toNormalCase, toTitleCase, truncateDisplayName } from "./utils";
|
||||
import { toNormalCase, toTitleCase } from "./utils";
|
||||
const uid = new ShortUniqueId({ length: 5 });
|
||||
|
||||
export function cleanEdges({
|
||||
|
|
@ -54,8 +54,8 @@ export function cleanEdges({
|
|||
id: targetNode.data.id,
|
||||
inputTypes: targetNode.data.node!.template[field]?.input_types,
|
||||
};
|
||||
if(targetNode.data.node!.template[field]?.proxy){
|
||||
id.proxy = targetNode.data.node!.template[field]?.proxy
|
||||
if (targetNode.data.node!.template[field]?.proxy) {
|
||||
id.proxy = targetNode.data.node!.template[field]?.proxy;
|
||||
}
|
||||
if (scapedJSONStringfy(id) !== targetHandle) {
|
||||
newEdges = newEdges.filter((e) => e.id !== edge.id);
|
||||
|
|
@ -213,10 +213,7 @@ export function buildTweaks(flow: FlowType) {
|
|||
}, {});
|
||||
}
|
||||
|
||||
export function validateNode(
|
||||
node: NodeType,
|
||||
reactFlowInstance: ReactFlowInstance
|
||||
): Array<string> {
|
||||
export function validateNode(node: NodeType, edges: Edge[]): Array<string> {
|
||||
if (!node.data?.node?.template || !Object.keys(node.data.node.template)) {
|
||||
return [
|
||||
"We've noticed a potential issue with a node in the flow. Please review it and, if necessary, submit a bug report with your exported flow file. Thank you for your help!",
|
||||
|
|
@ -228,43 +225,58 @@ export function validateNode(
|
|||
node: { template },
|
||||
} = node.data;
|
||||
|
||||
return Object.keys(template).reduce(
|
||||
(errors: Array<string>, t) =>
|
||||
errors.concat(
|
||||
template[t].required &&
|
||||
template[t].show &&
|
||||
(template[t].value === undefined ||
|
||||
template[t].value === null ||
|
||||
template[t].value === "") &&
|
||||
!reactFlowInstance
|
||||
.getEdges()
|
||||
.some(
|
||||
(edge) =>
|
||||
(scapeJSONParse(edge.targetHandle!) as targetHandleType)
|
||||
.fieldName === t &&
|
||||
(scapeJSONParse(edge.targetHandle!) as targetHandleType).id ===
|
||||
node.id
|
||||
)
|
||||
? [
|
||||
`${type} is missing ${
|
||||
template.display_name || toNormalCase(template[t].name)
|
||||
}.`,
|
||||
]
|
||||
: []
|
||||
),
|
||||
[] as string[]
|
||||
);
|
||||
return Object.keys(template).reduce((errors: Array<string>, t) => {
|
||||
if (
|
||||
template[t].required &&
|
||||
template[t].show &&
|
||||
(template[t].value === undefined ||
|
||||
template[t].value === null ||
|
||||
template[t].value === "") &&
|
||||
!edges.some(
|
||||
(edge) =>
|
||||
(scapeJSONParse(edge.targetHandle!) as targetHandleType).fieldName ===
|
||||
t &&
|
||||
(scapeJSONParse(edge.targetHandle!) as targetHandleType).id ===
|
||||
node.id
|
||||
)
|
||||
) {
|
||||
errors.push(
|
||||
`${type} is missing ${
|
||||
template.display_name || toNormalCase(template[t].name)
|
||||
}.`
|
||||
);
|
||||
} else if (
|
||||
template[t].type === "dict" &&
|
||||
template[t].required &&
|
||||
template[t].show &&
|
||||
(template[t].value !== undefined ||
|
||||
template[t].value !== null ||
|
||||
template[t].value !== "")
|
||||
) {
|
||||
if (hasDuplicateKeys(template[t].value))
|
||||
errors.push(
|
||||
`${type} (${
|
||||
template.display_name || template[t].name
|
||||
}) contains duplicate keys with the same values.`
|
||||
);
|
||||
if (hasEmptyKey(template[t].value))
|
||||
errors.push(
|
||||
`${type} (${
|
||||
template.display_name || template[t].name
|
||||
}) field must not be empty.`
|
||||
);
|
||||
}
|
||||
return errors;
|
||||
}, [] as string[]);
|
||||
}
|
||||
|
||||
export function validateNodes(reactFlowInstance: ReactFlowInstance) {
|
||||
if (reactFlowInstance.getNodes().length === 0) {
|
||||
export function validateNodes(nodes: Node[], edges: Edge[]) {
|
||||
if (nodes.length === 0) {
|
||||
return [
|
||||
"No nodes found in the flow. Please add at least one node to the flow.",
|
||||
];
|
||||
}
|
||||
return reactFlowInstance
|
||||
.getNodes()
|
||||
.flatMap((n: NodeType) => validateNode(n, reactFlowInstance));
|
||||
return nodes.flatMap((n: NodeType) => validateNode(n, edges));
|
||||
}
|
||||
|
||||
export function addVersionToDuplicates(flow: FlowType, flows: FlowType[]) {
|
||||
|
|
@ -324,7 +336,9 @@ export function updateEdgesHandleIds({
|
|||
}
|
||||
|
||||
export function handleKeyDown(
|
||||
e: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
e:
|
||||
| React.KeyboardEvent<HTMLInputElement>
|
||||
| React.KeyboardEvent<HTMLTextAreaElement>,
|
||||
inputValue: string | string[] | null,
|
||||
block: string
|
||||
) {
|
||||
|
|
@ -359,6 +373,80 @@ export function getConnectedNodes(
|
|||
return nodes.filter((node) => node.id === targetId || node.id === sourceId);
|
||||
}
|
||||
|
||||
export function convertObjToArray(singleObject: object | string) {
|
||||
if (typeof singleObject === "string") {
|
||||
singleObject = JSON.parse(singleObject);
|
||||
}
|
||||
if (Array.isArray(singleObject)) return singleObject;
|
||||
|
||||
let arrConverted: any[] = [];
|
||||
if (typeof singleObject === "object") {
|
||||
for (const key in singleObject) {
|
||||
if (Object.prototype.hasOwnProperty.call(singleObject, key)) {
|
||||
const newObj = {};
|
||||
newObj[key] = singleObject[key];
|
||||
arrConverted.push(newObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return arrConverted;
|
||||
}
|
||||
|
||||
export function convertArrayToObj(arrayOfObjects) {
|
||||
if (!Array.isArray(arrayOfObjects)) return arrayOfObjects;
|
||||
|
||||
let objConverted = {};
|
||||
for (const obj of arrayOfObjects) {
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
objConverted[key] = obj[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return objConverted;
|
||||
}
|
||||
|
||||
export function hasDuplicateKeys(array) {
|
||||
const keys = {};
|
||||
for (const obj of array) {
|
||||
for (const key in obj) {
|
||||
if (keys[key]) {
|
||||
return true;
|
||||
}
|
||||
keys[key] = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function hasEmptyKey(objArray) {
|
||||
for (const obj of objArray) {
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key) && key === "") {
|
||||
return true; // Found an empty key
|
||||
}
|
||||
}
|
||||
}
|
||||
return false; // No empty keys found
|
||||
}
|
||||
|
||||
export function convertValuesToNumbers(arr) {
|
||||
return arr.map((obj) => {
|
||||
const newObj = {};
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
let value = obj[key];
|
||||
if (/\s/g.test(value)) {
|
||||
value = value.trim();
|
||||
}
|
||||
newObj[key] =
|
||||
value === "" || isNaN(value) ? value.toString() : Number(value);
|
||||
}
|
||||
}
|
||||
return newObj;
|
||||
});
|
||||
}
|
||||
|
||||
export function scapedJSONStringfy(json: object): string {
|
||||
return customStringify(json).replace(/"/g, "œ");
|
||||
}
|
||||
|
|
@ -502,15 +590,15 @@ export function validateSelection(
|
|||
if (selection.edges.length === 0) {
|
||||
selection.edges = edges;
|
||||
}
|
||||
// get only edges that are connected to the nodes in the selection
|
||||
// first creates a set of all the nodes ids
|
||||
let nodesSet = new Set(selection.nodes.map((n) => n.id));
|
||||
// then filter the edges that are connected to the nodes in the set
|
||||
let connectedEdges = selection.edges.filter(
|
||||
(e) => nodesSet.has(e.source) && nodesSet.has(e.target)
|
||||
);
|
||||
// add the edges to the selection
|
||||
selection.edges = connectedEdges;
|
||||
// get only edges that are connected to the nodes in the selection
|
||||
// first creates a set of all the nodes ids
|
||||
let nodesSet = new Set(selection.nodes.map((n) => n.id));
|
||||
// then filter the edges that are connected to the nodes in the set
|
||||
let connectedEdges = selection.edges.filter(
|
||||
(e) => nodesSet.has(e.source) && nodesSet.has(e.target)
|
||||
);
|
||||
// add the edges to the selection
|
||||
selection.edges = connectedEdges;
|
||||
|
||||
let errorsArray: Array<string> = [];
|
||||
// check if there is more than one node
|
||||
|
|
@ -591,11 +679,11 @@ export function mergeNodeTemplates({
|
|||
} else {
|
||||
template[key + "_" + node.id].display_name =
|
||||
//data id already has the node name on it
|
||||
(nodeTemplate[key].display_name
|
||||
nodeTemplate[key].display_name
|
||||
? nodeTemplate[key].display_name
|
||||
: nodeTemplate[key].name
|
||||
? toTitleCase(nodeTemplate[key].name)
|
||||
: toTitleCase(key))
|
||||
: toTitleCase(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -659,15 +747,18 @@ export function generateNodeTemplate(Flow: FlowType) {
|
|||
return template;
|
||||
}
|
||||
|
||||
export function generateNodeFromFlow(flow: FlowType,getNodeId:(type:string)=>string): NodeType {
|
||||
export function generateNodeFromFlow(
|
||||
flow: FlowType,
|
||||
getNodeId: (type: string) => string
|
||||
): NodeType {
|
||||
const { nodes } = flow.data!;
|
||||
const outputNode = _.cloneDeep(findLastNode(flow.data!));
|
||||
const position = getMiddlePoint(nodes);
|
||||
let data = _.cloneDeep(flow);
|
||||
const id = getNodeId(outputNode?.data.type!)
|
||||
const id = getNodeId(outputNode?.data.type!);
|
||||
const newGroupNode: NodeType = {
|
||||
data: {
|
||||
id,
|
||||
id,
|
||||
type: outputNode?.data.type!,
|
||||
node: {
|
||||
output_types: outputNode!.data.node!.output_types,
|
||||
|
|
@ -749,7 +840,7 @@ export function ungroupNode(
|
|||
fieldName: field,
|
||||
type,
|
||||
id: proxyId,
|
||||
inputTypes: inputTypes
|
||||
inputTypes: inputTypes,
|
||||
};
|
||||
if (node.data.node?.flow) {
|
||||
newTargetHandle.proxy = {
|
||||
|
|
@ -816,10 +907,10 @@ export function expandGroupNode(
|
|||
groupNode: NodeDataType,
|
||||
ReactFlowInstance: ReactFlowInstance
|
||||
) {
|
||||
const { template,flow } = _.cloneDeep(groupNode.node!);
|
||||
const { template, flow } = _.cloneDeep(groupNode.node!);
|
||||
const gNodes: NodeType[] = flow?.data?.nodes!;
|
||||
const gEdges = flow!.data!.edges;
|
||||
console.log(gEdges)
|
||||
console.log(gEdges);
|
||||
//redirect edges to correct proxy node
|
||||
let updatedEdges: Edge[] = [];
|
||||
ReactFlowInstance.getEdges().forEach((edge) => {
|
||||
|
|
@ -838,7 +929,7 @@ export function expandGroupNode(
|
|||
fieldName: field,
|
||||
type,
|
||||
id: proxyId,
|
||||
inputTypes: inputTypes
|
||||
inputTypes: inputTypes,
|
||||
};
|
||||
if (node.data.node?.flow) {
|
||||
newTargetHandle.proxy = {
|
||||
|
|
@ -880,7 +971,7 @@ export function expandGroupNode(
|
|||
} else {
|
||||
display_name = gNodes[nodeIndex].data.node!.template[field].name;
|
||||
}
|
||||
if(gNodes[nodeIndex].data.node!.template[field].proxy){
|
||||
if (gNodes[nodeIndex].data.node!.template[field].proxy) {
|
||||
proxy = gNodes[nodeIndex].data.node!.template[field].proxy;
|
||||
}
|
||||
gNodes[nodeIndex].data.node!.template[field] = template[key];
|
||||
|
|
@ -888,11 +979,10 @@ export function expandGroupNode(
|
|||
gNodes[nodeIndex].data.node!.template[field].advanced = advanced;
|
||||
gNodes[nodeIndex].data.node!.template[field].display_name = display_name;
|
||||
gNodes[nodeIndex].selected = false;
|
||||
if(proxy){
|
||||
if (proxy) {
|
||||
gNodes[nodeIndex].data.node!.template[field].proxy = proxy;
|
||||
}
|
||||
else{
|
||||
delete gNodes[nodeIndex].data.node!.template[field].proxy
|
||||
} else {
|
||||
delete gNodes[nodeIndex].data.node!.template[field].proxy;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -908,7 +998,7 @@ export function expandGroupNode(
|
|||
...gEdges,
|
||||
...updatedEdges,
|
||||
];
|
||||
console.log(edges)
|
||||
console.log(edges);
|
||||
ReactFlowInstance.setNodes(nodes);
|
||||
ReactFlowInstance.setEdges(edges);
|
||||
}
|
||||
|
|
@ -931,14 +1021,14 @@ export function getGroupStatus(
|
|||
let status = { valid: true, params: "Built sucessfully ✨" };
|
||||
const { nodes } = flow.data!;
|
||||
const ids = nodes.map((n: NodeType) => n.data.id);
|
||||
ids.forEach((id)=>console.log(ssData[id]))
|
||||
ids.forEach((id) => console.log(ssData[id]));
|
||||
ids.forEach((id) => {
|
||||
if (!ssData[id]) {
|
||||
status = ssData[id]
|
||||
return
|
||||
status = ssData[id];
|
||||
return;
|
||||
}
|
||||
if (!ssData[id].valid) {
|
||||
status = { valid: false, params: ssData[id].params };
|
||||
status = { valid: false, params: ssData[id].params };
|
||||
}
|
||||
});
|
||||
return status;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import {
|
||||
ArrowUpToLine,
|
||||
Bell,
|
||||
BookMarked,
|
||||
Check,
|
||||
CheckCircle2,
|
||||
ChevronDown,
|
||||
|
|
@ -44,10 +46,13 @@ import {
|
|||
Link,
|
||||
Lock,
|
||||
LucideSend,
|
||||
Maximize2,
|
||||
Menu,
|
||||
MessageCircle,
|
||||
MessageSquare,
|
||||
MessagesSquare,
|
||||
Minimize2,
|
||||
Minus,
|
||||
MoonIcon,
|
||||
MoreHorizontal,
|
||||
Paperclip,
|
||||
|
|
@ -59,7 +64,9 @@ import {
|
|||
Scissors,
|
||||
Search,
|
||||
Settings2,
|
||||
Shield,
|
||||
Sparkles,
|
||||
Square,
|
||||
SunIcon,
|
||||
TerminalSquare,
|
||||
Trash2,
|
||||
|
|
@ -67,6 +74,7 @@ import {
|
|||
Ungroup,
|
||||
Unplug,
|
||||
Upload,
|
||||
User,
|
||||
UserCog2,
|
||||
UserMinus2,
|
||||
UserPlus2,
|
||||
|
|
@ -88,7 +96,7 @@ import { EvernoteIcon } from "../icons/Evernote";
|
|||
import { FBIcon } from "../icons/FacebookMessenger";
|
||||
import { GitBookIcon } from "../icons/GitBook";
|
||||
import { GoogleIcon } from "../icons/Google";
|
||||
import GradientSparkles from "../icons/GradientSparkles";
|
||||
import { GradientSparkles } from "../icons/GradientSparkles";
|
||||
import { HuggingFaceIcon } from "../icons/HuggingFace";
|
||||
import { IFixIcon } from "../icons/IFixIt";
|
||||
import { MetaIcon } from "../icons/Meta";
|
||||
|
|
@ -188,6 +196,7 @@ export const nodeNames: { [char: string]: string } = {
|
|||
};
|
||||
|
||||
export const nodeIconsLucide: iconsType = {
|
||||
ArrowUpToLine: ArrowUpToLine,
|
||||
Chroma: ChromaIcon,
|
||||
AirbyteJSONLoader: AirbyteIcon,
|
||||
Anthropic: AnthropicIcon,
|
||||
|
|
@ -222,6 +231,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
ChatVertexAI: VertexAIIcon,
|
||||
VertexAIEmbeddings: VertexAIIcon,
|
||||
agents: Rocket,
|
||||
User,
|
||||
WikipediaAPIWrapper: SvgWikipedia,
|
||||
chains: Link,
|
||||
memories: Cpu,
|
||||
|
|
@ -262,6 +272,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
Bell,
|
||||
ChevronLeft,
|
||||
ChevronDown,
|
||||
Shield,
|
||||
Plus,
|
||||
Redo,
|
||||
Settings2,
|
||||
|
|
@ -306,4 +317,9 @@ export const nodeIconsLucide: iconsType = {
|
|||
Group,
|
||||
ChevronUp,
|
||||
Ungroup,
|
||||
BookMarked,
|
||||
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 ? `, api_key: Optional[str] = None` : ""
|
||||
}) -> dict:
|
||||
"""
|
||||
Run a flow with a given message and optional tweaks.
|
||||
|
||||
|
|
@ -337,16 +340,20 @@ def run_flow(inputs: dict, flow_id: str, tweaks: Optional[dict] = None) -> dict:
|
|||
api_url = f"{BASE_API_URL}/{flow_id}"
|
||||
|
||||
payload = {"inputs": inputs}
|
||||
|
||||
headers = None
|
||||
if tweaks:
|
||||
payload["tweaks"] = tweaks
|
||||
|
||||
response = requests.post(api_url, json=payload)
|
||||
if api_key:
|
||||
headers = {"x-api-key": api_key}
|
||||
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 ? `, api_key=api_key` : ""
|
||||
}))`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -356,6 +363,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 +375,9 @@ export function getCurlCode(
|
|||
${window.location.protocol}//${
|
||||
window.location.host
|
||||
}/api/v1/process/${flowId} \\
|
||||
-H 'Content-Type: application/json' \\
|
||||
-H 'Content-Type: application/json'\\${
|
||||
!isAuth ? `\n -H 'x-api-key: <your api key>'\\` : ""
|
||||
}
|
||||
-d '{"inputs": ${inputs}, "tweaks": ${
|
||||
tweak && tweak.length > 0
|
||||
? buildTweakObject(tweak)
|
||||
|
|
@ -405,7 +415,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 +439,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>`;
|
||||
}
|
||||
|
||||
|
|
|
|||
8
src/frontend/start-nginx.sh
Normal file
8
src/frontend/start-nginx.sh
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Replace the placeholder with the actual value
|
||||
sed -i "s|__BACKEND_URL__|$BACKEND_URL|g" /etc/nginx/conf.d/default.conf
|
||||
|
||||
|
||||
# Start nginx
|
||||
exec nginx -g 'daemon off;'
|
||||
20
src/frontend/tests/custom_component_full.ts
Normal file
20
src/frontend/tests/custom_component_full.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
export const custom = `from langflow import CustomComponent
|
||||
|
||||
from langchain.llms.base import BaseLLM
|
||||
from langchain.chains import LLMChain
|
||||
from langchain.prompts import PromptTemplate
|
||||
from langchain.schema import Document
|
||||
from langchain.field_typing import NestedDict
|
||||
|
||||
import requests
|
||||
|
||||
class YourComponent(CustomComponent):
|
||||
display_name: str = "Custom Component"
|
||||
description: str = "Create any custom component you want!"
|
||||
|
||||
def build_config(self):
|
||||
return { "file": { "file_type": ["json"], } }
|
||||
|
||||
def build(self, url: str,file:str,integer:int,nested:NestedDict,flt:float,boolean:bool,lisst:list[str],dictionary:dict, llm: BaseLLM, prompt: PromptTemplate) -> Document:
|
||||
|
||||
return "test"`;
|
||||
20
src/frontend/tests/flowPage.spec.ts
Normal file
20
src/frontend/tests/flowPage.spec.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { Page, test } from "@playwright/test";
|
||||
|
||||
test.describe("Flow Page tests", () => {
|
||||
async function goToFlowPage(page: Page) {
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.getByRole("button", { name: "New Project" }).click();
|
||||
}
|
||||
|
||||
test("save", async ({ page }) => {
|
||||
await goToFlowPage(page);
|
||||
await page.getByRole("button", { name: "Custom" }).click();
|
||||
await page
|
||||
.locator("div")
|
||||
.filter({ hasText: /^Custom Component$/ })
|
||||
.nth(4)
|
||||
.dragTo(page.locator(".react-flow__pane"));
|
||||
await page.locator("div:nth-child(4) > .extra-side-bar-buttons").click();
|
||||
await page.locator(".success-alert").click();
|
||||
});
|
||||
});
|
||||
32
src/frontend/tests/onlyFront/auto_login.spec.ts
Normal file
32
src/frontend/tests/onlyFront/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/onlyFront/login.spec.ts
Normal file
128
src/frontend/tests/onlyFront/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();
|
||||
});
|
||||
});
|
||||
5
src/frontend/tests/test-1.spec.ts
Normal file
5
src/frontend/tests/test-1.spec.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { test } from "@playwright/test";
|
||||
|
||||
test("test", async ({ page }) => {
|
||||
// Recording...
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue