Compare commits
42 Commits
AUT-991
...
AUT-157-AU
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8f3ecb6d4d | ||
![]() |
47caa5aa37 | ||
![]() |
725b38c697 | ||
![]() |
402a0fdf3b | ||
![]() |
078364ffa1 | ||
![]() |
f64d5ec4fc | ||
![]() |
12194a50e1 | ||
![]() |
82ee592699 | ||
![]() |
1b4fb2ce6e | ||
![]() |
ebea8d12d1 | ||
![]() |
f842dd77df | ||
![]() |
a6ec7a6c99 | ||
![]() |
369c72282c | ||
![]() |
6f30c1a509 | ||
![]() |
abfd1116c7 | ||
![]() |
017854955d | ||
![]() |
1405cddea1 | ||
![]() |
00dd3164c9 | ||
![]() |
d5cbc0f611 | ||
![]() |
5d2e9ccc67 | ||
![]() |
017a881494 | ||
![]() |
52994970e6 | ||
![]() |
ebae629e5c | ||
![]() |
4d79220b0c | ||
![]() |
96fba7fbb8 | ||
![]() |
e0d610071d | ||
![]() |
ab0966c005 | ||
![]() |
751eb41e72 | ||
![]() |
f08dc25711 | ||
![]() |
737eb31776 | ||
![]() |
d6abf283bc | ||
![]() |
bac4ab5aa4 | ||
![]() |
b5839390fd | ||
![]() |
d19271dae1 | ||
![]() |
ef5a09314e | ||
![]() |
ba52e298eb | ||
![]() |
b3c3998189 | ||
![]() |
782f9b5c04 | ||
![]() |
3079d8c605 | ||
![]() |
c5202d7b3e | ||
![]() |
fbae83f4de | ||
![]() |
1dc9646894 |
@@ -20,7 +20,6 @@
|
|||||||
"db:migrate": "node ./bin/database/convert-migrations.js && knex migrate:latest"
|
"db:migrate": "node ./bin/database/convert-migrations.js && knex migrate:latest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@atproto/api": "^0.12.13",
|
|
||||||
"@bull-board/express": "^3.10.1",
|
"@bull-board/express": "^3.10.1",
|
||||||
"@casl/ability": "^6.5.0",
|
"@casl/ability": "^6.5.0",
|
||||||
"@graphql-tools/graphql-file-loader": "^7.3.4",
|
"@graphql-tools/graphql-file-loader": "^7.3.4",
|
||||||
|
@@ -1,41 +0,0 @@
|
|||||||
import { BskyAgent, RichText } from '@atproto/api';
|
|
||||||
|
|
||||||
import defineAction from '../../../../helpers/define-action.js';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Create post',
|
|
||||||
key: 'createPost',
|
|
||||||
description: 'Creates a new post.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Text',
|
|
||||||
key: 'text',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
variables: true,
|
|
||||||
description: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const text = $.step.parameters.text;
|
|
||||||
const agent = new BskyAgent({ service: 'https://bsky.social/xrpc' });
|
|
||||||
const richText = new RichText({ text });
|
|
||||||
await richText.detectFacets(agent);
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
repo: $.auth.data.did,
|
|
||||||
collection: 'app.bsky.feed.post',
|
|
||||||
record: {
|
|
||||||
$type: 'app.bsky.feed.post',
|
|
||||||
text: richText.text,
|
|
||||||
facets: richText.facets,
|
|
||||||
createdAt: new Date().toISOString(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data } = await $.http.post('/com.atproto.repo.createRecord', body);
|
|
||||||
|
|
||||||
$.setActionItem({ raw: data });
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,4 +0,0 @@
|
|||||||
import createPost from './create-post/index.js';
|
|
||||||
import searchPostByUrl from './search-post-by-url/index.js';
|
|
||||||
|
|
||||||
export default [createPost, searchPostByUrl];
|
|
@@ -1,35 +0,0 @@
|
|||||||
import defineAction from '../../../../helpers/define-action.js';
|
|
||||||
|
|
||||||
export default defineAction({
|
|
||||||
name: 'Search post by url',
|
|
||||||
key: 'searchPostByUrl',
|
|
||||||
description: 'Searches a post in a thread by url.',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
label: 'Post Url',
|
|
||||||
key: 'postUrl',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
variables: true,
|
|
||||||
description: 'Enter whole post url here.',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
async run($) {
|
|
||||||
const postUrl = $.step.parameters.postUrl;
|
|
||||||
const urlParts = postUrl.split('/');
|
|
||||||
const handle = urlParts[urlParts.length - 3];
|
|
||||||
const postId = urlParts[urlParts.length - 1];
|
|
||||||
const uri = `at://${handle}/app.bsky.feed.post/${postId}`;
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
uri,
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data } = await $.http.get('/app.bsky.feed.getPostThread', {
|
|
||||||
params,
|
|
||||||
});
|
|
||||||
|
|
||||||
$.setActionItem({ raw: data });
|
|
||||||
},
|
|
||||||
});
|
|
@@ -1,4 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 -50 430 390" fill="#1185fd" aria-hidden="true">
|
|
||||||
<path d="M180 141.964C163.699 110.262 119.308 51.1817 78.0347 22.044C38.4971 -5.86834 23.414 -1.03207 13.526 3.43594C2.08093 8.60755 0 26.1785 0 36.5164C0 46.8542 5.66748 121.272 9.36416 133.694C21.5786 174.738 65.0603 188.607 105.104 184.156C107.151 183.852 109.227 183.572 111.329 183.312C109.267 183.642 107.19 183.924 105.104 184.156C46.4204 192.847 -5.69621 214.233 62.6582 290.33C137.848 368.18 165.705 273.637 180 225.702C194.295 273.637 210.76 364.771 295.995 290.33C360 225.702 313.58 192.85 254.896 184.158C252.81 183.926 250.733 183.645 248.671 183.315C250.773 183.574 252.849 183.855 254.896 184.158C294.94 188.61 338.421 174.74 350.636 133.697C354.333 121.275 360 46.8568 360 36.519C360 26.1811 357.919 8.61012 346.474 3.43851C336.586 -1.02949 321.503 -5.86576 281.965 22.0466C240.692 51.1843 196.301 110.262 180 141.964Z">
|
|
||||||
</path>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 956 B |
@@ -1,34 +0,0 @@
|
|||||||
import verifyCredentials from './verify-credentials.js';
|
|
||||||
import isStillVerified from './is-still-verified.js';
|
|
||||||
import refreshToken from './refresh-token.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
key: 'handle',
|
|
||||||
label: 'Your Bluesky Handle',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: '',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'password',
|
|
||||||
label: 'Your Bluesky Password',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
readOnly: false,
|
|
||||||
value: null,
|
|
||||||
placeholder: null,
|
|
||||||
description: '',
|
|
||||||
clickToCopy: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
verifyCredentials,
|
|
||||||
isStillVerified,
|
|
||||||
refreshToken,
|
|
||||||
};
|
|
@@ -1,8 +0,0 @@
|
|||||||
import getCurrentUser from '../common/get-current-user.js';
|
|
||||||
|
|
||||||
const isStillVerified = async ($) => {
|
|
||||||
const currentUser = await getCurrentUser($);
|
|
||||||
return !!currentUser.did;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default isStillVerified;
|
|
@@ -1,24 +0,0 @@
|
|||||||
const refreshToken = async ($) => {
|
|
||||||
const { refreshJwt } = $.auth.data;
|
|
||||||
|
|
||||||
const { data } = await $.http.post(
|
|
||||||
'/com.atproto.server.refreshSession',
|
|
||||||
null,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${refreshJwt}`,
|
|
||||||
},
|
|
||||||
additionalProperties: {
|
|
||||||
skipAddingAuthHeader: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
accessJwt: data.accessJwt,
|
|
||||||
refreshJwt: data.refreshJwt,
|
|
||||||
did: data.did,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default refreshToken;
|
|
@@ -1,20 +0,0 @@
|
|||||||
const verifyCredentials = async ($) => {
|
|
||||||
const handle = $.auth.data.handle;
|
|
||||||
const password = $.auth.data.password;
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
identifier: handle,
|
|
||||||
password,
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data } = await $.http.post('/com.atproto.server.createSession', body);
|
|
||||||
|
|
||||||
await $.auth.set({
|
|
||||||
accessJwt: data.accessJwt,
|
|
||||||
refreshJwt: data.refreshJwt,
|
|
||||||
did: data.did,
|
|
||||||
screenName: data.handle,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default verifyCredentials;
|
|
@@ -1,12 +0,0 @@
|
|||||||
const addAuthHeader = ($, requestConfig) => {
|
|
||||||
if (requestConfig.additionalProperties?.skipAddingAuthHeader)
|
|
||||||
return requestConfig;
|
|
||||||
|
|
||||||
if ($.auth.data?.accessJwt) {
|
|
||||||
requestConfig.headers.Authorization = `Bearer ${$.auth.data.accessJwt}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default addAuthHeader;
|
|
@@ -1,15 +0,0 @@
|
|||||||
const getCurrentUser = async ($) => {
|
|
||||||
const handle = $.auth.data.handle;
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
actor: handle,
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data: currentUser } = await $.http.get('/app.bsky.actor.getProfile', {
|
|
||||||
params,
|
|
||||||
});
|
|
||||||
|
|
||||||
return currentUser;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getCurrentUser;
|
|
@@ -1,18 +0,0 @@
|
|||||||
import defineApp from '../../helpers/define-app.js';
|
|
||||||
import addAuthHeader from './common/add-auth-header.js';
|
|
||||||
import auth from './auth/index.js';
|
|
||||||
import actions from './actions/index.js';
|
|
||||||
|
|
||||||
export default defineApp({
|
|
||||||
name: 'Bluesky',
|
|
||||||
key: 'bluesky',
|
|
||||||
iconUrl: '{BASE_URL}/apps/bluesky/assets/favicon.svg',
|
|
||||||
authDocUrl: '{DOCS_URL}/apps/bluesky/connection',
|
|
||||||
supportsConnections: true,
|
|
||||||
baseUrl: 'https://bluesky.app',
|
|
||||||
apiBaseUrl: 'https://bsky.social/xrpc',
|
|
||||||
primaryColor: '1185fd',
|
|
||||||
beforeRequest: [addAuthHeader],
|
|
||||||
auth,
|
|
||||||
actions,
|
|
||||||
});
|
|
@@ -52,7 +52,7 @@ const appConfig = {
|
|||||||
isDev: appEnv === 'development',
|
isDev: appEnv === 'development',
|
||||||
isTest: appEnv === 'test',
|
isTest: appEnv === 'test',
|
||||||
isProd: appEnv === 'production',
|
isProd: appEnv === 'production',
|
||||||
version: '0.11.0',
|
version: '0.12.0',
|
||||||
postgresDatabase: process.env.POSTGRES_DATABASE || 'automatisch_development',
|
postgresDatabase: process.env.POSTGRES_DATABASE || 'automatisch_development',
|
||||||
postgresSchema: process.env.POSTGRES_SCHEMA || 'public',
|
postgresSchema: process.env.POSTGRES_SCHEMA || 'public',
|
||||||
postgresPort: parseInt(process.env.POSTGRES_PORT || '5432'),
|
postgresPort: parseInt(process.env.POSTGRES_PORT || '5432'),
|
||||||
|
@@ -10,7 +10,7 @@ describe('GET /api/v1/automatisch/version', () => {
|
|||||||
|
|
||||||
const expectedPayload = {
|
const expectedPayload = {
|
||||||
data: {
|
data: {
|
||||||
version: '0.11.0',
|
version: '0.12.0',
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
count: 1,
|
count: 1,
|
||||||
|
@@ -0,0 +1,11 @@
|
|||||||
|
export async function up(knex) {
|
||||||
|
return knex.schema.alterTable('datastore', (table) => {
|
||||||
|
table.text('value').alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex) {
|
||||||
|
return knex.schema.alterTable('datastore', (table) => {
|
||||||
|
table.string('value').alter();
|
||||||
|
});
|
||||||
|
}
|
@@ -33,8 +33,8 @@ class User extends Base {
|
|||||||
fullName: { type: 'string', minLength: 1 },
|
fullName: { type: 'string', minLength: 1 },
|
||||||
email: { type: 'string', format: 'email', minLength: 1, maxLength: 255 },
|
email: { type: 'string', format: 'email', minLength: 1, maxLength: 255 },
|
||||||
password: { type: 'string' },
|
password: { type: 'string' },
|
||||||
resetPasswordToken: { type: 'string' },
|
resetPasswordToken: { type: ['string', 'null'] },
|
||||||
resetPasswordTokenSentAt: { type: 'string' },
|
resetPasswordTokenSentAt: { type: ['string', 'null'], format: 'date-time' },
|
||||||
trialExpiryDate: { type: 'string' },
|
trialExpiryDate: { type: 'string' },
|
||||||
roleId: { type: 'string', format: 'uuid' },
|
roleId: { type: 'string', format: 'uuid' },
|
||||||
deletedAt: { type: 'string' },
|
deletedAt: { type: 'string' },
|
||||||
|
@@ -40,6 +40,7 @@ export const worker = new Worker(
|
|||||||
await user.$relatedQuery('usageData').withSoftDeleted().hardDelete();
|
await user.$relatedQuery('usageData').withSoftDeleted().hardDelete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await user.$relatedQuery('accessTokens').withSoftDeleted().hardDelete();
|
||||||
await user.$query().withSoftDeleted().hardDelete();
|
await user.$query().withSoftDeleted().hardDelete();
|
||||||
},
|
},
|
||||||
{ connection: redisConfig }
|
{ connection: redisConfig }
|
||||||
|
@@ -50,15 +50,6 @@ export default defineConfig({
|
|||||||
{ text: 'Connection', link: '/apps/appwrite/connection' },
|
{ text: 'Connection', link: '/apps/appwrite/connection' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
text: 'Bluesky',
|
|
||||||
collapsible: true,
|
|
||||||
collapsed: true,
|
|
||||||
items: [
|
|
||||||
{ text: 'Actions', link: '/apps/bluesky/actions' },
|
|
||||||
{ text: 'Connection', link: '/apps/bluesky/connection' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
text: 'Carbone',
|
text: 'Carbone',
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
favicon: /favicons/bluesky.svg
|
|
||||||
items:
|
|
||||||
- name: Create post
|
|
||||||
desc: Creates a new post.
|
|
||||||
- name: Search post by url
|
|
||||||
desc: Searches a post in a thread by url.
|
|
||||||
---
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import CustomListing from '../../components/CustomListing.vue'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CustomListing />
|
|
@@ -1,10 +0,0 @@
|
|||||||
# Bluesky
|
|
||||||
|
|
||||||
:::info
|
|
||||||
This page explains the steps you need to follow to set up the Bluesky connection in Automatisch. If any of the steps are outdated, please let us know!
|
|
||||||
:::
|
|
||||||
|
|
||||||
1. Enter your `Bluesky Handle` from the page to the `Your Bluesky Handle` field on Automatisch.
|
|
||||||
1. Enter your `Bluesky Password` from the page to the `Your Bluesky Password` field on Automatisch.
|
|
||||||
1. Click **Submit** button on Automatisch.
|
|
||||||
1. Congrats! Start using your new Bluesky connection within the flows.
|
|
@@ -6,16 +6,12 @@ We use `lerna` with `yarn workspaces` to manage the mono repository. We have the
|
|||||||
.
|
.
|
||||||
├── packages
|
├── packages
|
||||||
│ ├── backend
|
│ ├── backend
|
||||||
│ ├── cli
|
|
||||||
│ ├── docs
|
│ ├── docs
|
||||||
│ ├── e2e-tests
|
│ ├── e2e-tests
|
||||||
│ ├── types
|
|
||||||
│ └── web
|
│ └── web
|
||||||
```
|
```
|
||||||
|
|
||||||
- `backend` - The backend package contains the backend application and all integrations.
|
- `backend` - The backend package contains the backend application and all integrations.
|
||||||
- `cli` - The cli package contains the CLI application of Automatisch.
|
|
||||||
- `docs` - The docs package contains the documentation website.
|
- `docs` - The docs package contains the documentation website.
|
||||||
- `e2e-tests` - The e2e-tests package contains the end-to-end tests for the internal usage.
|
- `e2e-tests` - The e2e-tests package contains the end-to-end tests for the internal usage.
|
||||||
- `types` - The types package contains the shared types for both the backend and web packages.
|
|
||||||
- `web` - The web package contains the frontend application of Automatisch.
|
- `web` - The web package contains the frontend application of Automatisch.
|
||||||
|
@@ -4,7 +4,6 @@ The following integrations are currently supported by Automatisch.
|
|||||||
|
|
||||||
- [Airtable](/apps/airtable/actions)
|
- [Airtable](/apps/airtable/actions)
|
||||||
- [Appwrite](/apps/appwrite/triggers)
|
- [Appwrite](/apps/appwrite/triggers)
|
||||||
- [Bluesky](/apps/bluesky/actions)
|
|
||||||
- [Carbone](/apps/carbone/actions)
|
- [Carbone](/apps/carbone/actions)
|
||||||
- [Datastore](/apps/datastore/actions)
|
- [Datastore](/apps/datastore/actions)
|
||||||
- [DeepL](/apps/deepl/actions)
|
- [DeepL](/apps/deepl/actions)
|
||||||
|
@@ -1,4 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 -50 430 390" fill="#1185fd" aria-hidden="true">
|
|
||||||
<path d="M180 141.964C163.699 110.262 119.308 51.1817 78.0347 22.044C38.4971 -5.86834 23.414 -1.03207 13.526 3.43594C2.08093 8.60755 0 26.1785 0 36.5164C0 46.8542 5.66748 121.272 9.36416 133.694C21.5786 174.738 65.0603 188.607 105.104 184.156C107.151 183.852 109.227 183.572 111.329 183.312C109.267 183.642 107.19 183.924 105.104 184.156C46.4204 192.847 -5.69621 214.233 62.6582 290.33C137.848 368.18 165.705 273.637 180 225.702C194.295 273.637 210.76 364.771 295.995 290.33C360 225.702 313.58 192.85 254.896 184.158C252.81 183.926 250.733 183.645 248.671 183.315C250.773 183.574 252.849 183.855 254.896 184.158C294.94 188.61 338.421 174.74 350.636 133.697C354.333 121.275 360 46.8568 360 36.519C360 26.1811 357.919 8.61012 346.474 3.43851C336.586 -1.02949 321.503 -5.86576 281.965 22.0466C240.692 51.1843 196.301 110.262 180 141.964Z">
|
|
||||||
</path>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 956 B |
@@ -7,6 +7,7 @@
|
|||||||
"@apollo/client": "^3.6.9",
|
"@apollo/client": "^3.6.9",
|
||||||
"@casl/ability": "^6.5.0",
|
"@casl/ability": "^6.5.0",
|
||||||
"@casl/react": "^3.1.0",
|
"@casl/react": "^3.1.0",
|
||||||
|
"@dagrejs/dagre": "^1.1.2",
|
||||||
"@emotion/react": "^11.4.1",
|
"@emotion/react": "^11.4.1",
|
||||||
"@emotion/styled": "^11.3.0",
|
"@emotion/styled": "^11.3.0",
|
||||||
"@hookform/resolvers": "^2.8.8",
|
"@hookform/resolvers": "^2.8.8",
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
"react-router-dom": "^6.0.2",
|
"react-router-dom": "^6.0.2",
|
||||||
"react-scripts": "5.0.0",
|
"react-scripts": "5.0.0",
|
||||||
"react-window": "^1.8.9",
|
"react-window": "^1.8.9",
|
||||||
|
"reactflow": "^11.11.2",
|
||||||
"slate": "^0.94.1",
|
"slate": "^0.94.1",
|
||||||
"slate-history": "^0.93.0",
|
"slate-history": "^0.93.0",
|
||||||
"slate-react": "^0.94.2",
|
"slate-react": "^0.94.2",
|
||||||
|
@@ -68,7 +68,10 @@ function AccountDropdownMenu(props) {
|
|||||||
AccountDropdownMenu.propTypes = {
|
AccountDropdownMenu.propTypes = {
|
||||||
open: PropTypes.bool.isRequired,
|
open: PropTypes.bool.isRequired,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
anchorEl: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
|
anchorEl: PropTypes.oneOfType([
|
||||||
|
PropTypes.func,
|
||||||
|
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
|
||||||
|
]),
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -36,7 +36,7 @@ function AdminApplicationSettings(props) {
|
|||||||
|
|
||||||
const handleSubmit = async (values) => {
|
const handleSubmit = async (values) => {
|
||||||
try {
|
try {
|
||||||
if (!appConfig.data) {
|
if (!appConfig?.data) {
|
||||||
await createAppConfig({
|
await createAppConfig({
|
||||||
variables: {
|
variables: {
|
||||||
input: { key: props.appKey, ...values },
|
input: { key: props.appKey, ...values },
|
||||||
@@ -69,6 +69,7 @@ function AdminApplicationSettings(props) {
|
|||||||
}),
|
}),
|
||||||
[appConfig?.data],
|
[appConfig?.data],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
defaultValues={defaultValues}
|
defaultValues={defaultValues}
|
||||||
|
@@ -8,6 +8,7 @@ import * as URLS from 'config/urls';
|
|||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import { ConnectionPropType } from 'propTypes/propTypes';
|
import { ConnectionPropType } from 'propTypes/propTypes';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
import Can from 'components/Can';
|
||||||
|
|
||||||
function ContextMenu(props) {
|
function ContextMenu(props) {
|
||||||
const {
|
const {
|
||||||
@@ -44,21 +45,35 @@ function ContextMenu(props) {
|
|||||||
hideBackdrop={false}
|
hideBackdrop={false}
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
>
|
>
|
||||||
|
<Can I="read" a="Flow" passThrough>
|
||||||
|
{(allowed) => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
component={Link}
|
component={Link}
|
||||||
to={URLS.APP_FLOWS_FOR_CONNECTION(appKey, connection.id)}
|
to={URLS.APP_FLOWS_FOR_CONNECTION(appKey, connection.id)}
|
||||||
onClick={createActionHandler({ type: 'viewFlows' })}
|
onClick={createActionHandler({ type: 'viewFlows' })}
|
||||||
|
disabled={!allowed}
|
||||||
>
|
>
|
||||||
{formatMessage('connection.viewFlows')}
|
{formatMessage('connection.viewFlows')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
</Can>
|
||||||
|
|
||||||
<MenuItem onClick={createActionHandler({ type: 'test' })}>
|
<Can I="update" a="Connection" passThrough>
|
||||||
|
{(allowed) => (
|
||||||
|
<MenuItem
|
||||||
|
onClick={createActionHandler({ type: 'test' })}
|
||||||
|
disabled={!allowed}
|
||||||
|
>
|
||||||
{formatMessage('connection.testConnection')}
|
{formatMessage('connection.testConnection')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
</Can>
|
||||||
|
|
||||||
|
<Can I="create" a="Connection" passThrough>
|
||||||
|
{(allowed) => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
component={Link}
|
component={Link}
|
||||||
disabled={disableReconnection}
|
disabled={!allowed || disableReconnection}
|
||||||
to={URLS.APP_RECONNECT_CONNECTION(
|
to={URLS.APP_RECONNECT_CONNECTION(
|
||||||
appKey,
|
appKey,
|
||||||
connection.id,
|
connection.id,
|
||||||
@@ -68,10 +83,19 @@ function ContextMenu(props) {
|
|||||||
>
|
>
|
||||||
{formatMessage('connection.reconnect')}
|
{formatMessage('connection.reconnect')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
</Can>
|
||||||
|
|
||||||
<MenuItem onClick={createActionHandler({ type: 'delete' })}>
|
<Can I="delete" a="Connection" passThrough>
|
||||||
|
{(allowed) => (
|
||||||
|
<MenuItem
|
||||||
|
onClick={createActionHandler({ type: 'delete' })}
|
||||||
|
disabled={!allowed}
|
||||||
|
>
|
||||||
{formatMessage('connection.delete')}
|
{formatMessage('connection.delete')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
</Can>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
import AppConnectionRow from 'components/AppConnectionRow';
|
import AppConnectionRow from 'components/AppConnectionRow';
|
||||||
import NoResultFound from 'components/NoResultFound';
|
import NoResultFound from 'components/NoResultFound';
|
||||||
|
import Can from 'components/Can';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import useAppConnections from 'hooks/useAppConnections';
|
import useAppConnections from 'hooks/useAppConnections';
|
||||||
@@ -16,11 +17,15 @@ function AppConnections(props) {
|
|||||||
|
|
||||||
if (!hasConnections) {
|
if (!hasConnections) {
|
||||||
return (
|
return (
|
||||||
|
<Can I="create" a="Connection" passThrough>
|
||||||
|
{(allowed) => (
|
||||||
<NoResultFound
|
<NoResultFound
|
||||||
to={URLS.APP_ADD_CONNECTION(appKey)}
|
|
||||||
text={formatMessage('app.noConnections')}
|
text={formatMessage('app.noConnections')}
|
||||||
data-test="connections-no-results"
|
data-test="connections-no-results"
|
||||||
|
{...(allowed && { to: URLS.APP_ADD_CONNECTION(appKey) })}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
</Can>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,6 +5,7 @@ import PaginationItem from '@mui/material/PaginationItem';
|
|||||||
|
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import AppFlowRow from 'components/FlowRow';
|
import AppFlowRow from 'components/FlowRow';
|
||||||
|
import Can from 'components/Can';
|
||||||
import NoResultFound from 'components/NoResultFound';
|
import NoResultFound from 'components/NoResultFound';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import useConnectionFlows from 'hooks/useConnectionFlows';
|
import useConnectionFlows from 'hooks/useConnectionFlows';
|
||||||
@@ -36,11 +37,20 @@ function AppFlows(props) {
|
|||||||
|
|
||||||
if (!hasFlows) {
|
if (!hasFlows) {
|
||||||
return (
|
return (
|
||||||
|
<Can I="create" a="Flow" passThrough>
|
||||||
|
{(allowed) => (
|
||||||
<NoResultFound
|
<NoResultFound
|
||||||
to={URLS.CREATE_FLOW_WITH_APP_AND_CONNECTION(appKey, connectionId)}
|
|
||||||
text={formatMessage('app.noFlows')}
|
text={formatMessage('app.noFlows')}
|
||||||
data-test="flows-no-results"
|
data-test="flows-no-results"
|
||||||
|
{...(allowed && {
|
||||||
|
to: URLS.CREATE_FLOW_WITH_APP_AND_CONNECTION(
|
||||||
|
appKey,
|
||||||
|
connectionId
|
||||||
|
),
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
</Can>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -165,6 +165,7 @@ function ChooseAppAndEventSubstep(props) {
|
|||||||
value={getOption(appOptions, step.appKey) || null}
|
value={getOption(appOptions, step.appKey) || null}
|
||||||
onChange={onAppChange}
|
onChange={onAppChange}
|
||||||
data-test="choose-app-autocomplete"
|
data-test="choose-app-autocomplete"
|
||||||
|
componentsProps={{ popper: { className: 'nowheel' } }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{step.appKey && (
|
{step.appKey && (
|
||||||
@@ -227,6 +228,7 @@ function ChooseAppAndEventSubstep(props) {
|
|||||||
value={getOption(actionOrTriggerOptions, step.key) || null}
|
value={getOption(actionOrTriggerOptions, step.key) || null}
|
||||||
onChange={onEventChange}
|
onChange={onEventChange}
|
||||||
data-test="choose-event-autocomplete"
|
data-test="choose-event-autocomplete"
|
||||||
|
componentsProps={{ popper: { className: 'nowheel' } }}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
@@ -240,6 +240,7 @@ function ChooseConnectionSubstep(props) {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
loading={isAppConnectionsLoading}
|
loading={isAppConnectionsLoading}
|
||||||
data-test="choose-connection-autocomplete"
|
data-test="choose-connection-autocomplete"
|
||||||
|
componentsProps={{ popper: { className: 'nowheel' } }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
@@ -32,9 +32,11 @@ function ControlledAutocomplete(props) {
|
|||||||
...autocompleteProps
|
...autocompleteProps
|
||||||
} = props;
|
} = props;
|
||||||
let dependsOnValues = [];
|
let dependsOnValues = [];
|
||||||
|
|
||||||
if (dependsOn?.length) {
|
if (dependsOn?.length) {
|
||||||
dependsOnValues = watch(dependsOn);
|
dependsOnValues = watch(dependsOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const hasDependencies = dependsOnValues.length;
|
const hasDependencies = dependsOnValues.length;
|
||||||
const allDepsSatisfied = dependsOnValues.every(Boolean);
|
const allDepsSatisfied = dependsOnValues.every(Boolean);
|
||||||
@@ -44,6 +46,7 @@ function ControlledAutocomplete(props) {
|
|||||||
resetField(name);
|
resetField(name);
|
||||||
}
|
}
|
||||||
}, dependsOnValues);
|
}, dependsOnValues);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Controller
|
<Controller
|
||||||
rules={{ required }}
|
rules={{ required }}
|
||||||
|
@@ -21,7 +21,9 @@ const CustomOptions = (props) => {
|
|||||||
label,
|
label,
|
||||||
initialTabIndex,
|
initialTabIndex,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [activeTabIndex, setActiveTabIndex] = React.useState(undefined);
|
const [activeTabIndex, setActiveTabIndex] = React.useState(undefined);
|
||||||
|
|
||||||
React.useEffect(
|
React.useEffect(
|
||||||
function applyInitialActiveTabIndex() {
|
function applyInitialActiveTabIndex() {
|
||||||
setActiveTabIndex((currentActiveTabIndex) => {
|
setActiveTabIndex((currentActiveTabIndex) => {
|
||||||
@@ -33,6 +35,7 @@ const CustomOptions = (props) => {
|
|||||||
},
|
},
|
||||||
[initialTabIndex],
|
[initialTabIndex],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popper
|
<Popper
|
||||||
open={open}
|
open={open}
|
||||||
@@ -47,6 +50,7 @@ const CustomOptions = (props) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
className="nowheel"
|
||||||
>
|
>
|
||||||
<Paper elevation={5} sx={{ width: '100%' }}>
|
<Paper elevation={5} sx={{ width: '100%' }}>
|
||||||
<Tabs
|
<Tabs
|
||||||
@@ -75,7 +79,10 @@ const CustomOptions = (props) => {
|
|||||||
|
|
||||||
CustomOptions.propTypes = {
|
CustomOptions.propTypes = {
|
||||||
open: PropTypes.bool.isRequired,
|
open: PropTypes.bool.isRequired,
|
||||||
anchorEl: PropTypes.oneOfType([PropTypes.element, PropTypes.func]).isRequired,
|
anchorEl: PropTypes.oneOfType([
|
||||||
|
PropTypes.func,
|
||||||
|
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
|
||||||
|
]),
|
||||||
data: PropTypes.arrayOf(
|
data: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
|
@@ -61,6 +61,7 @@ function ControlledCustomAutocomplete(props) {
|
|||||||
const [isSingleChoice, setSingleChoice] = React.useState(undefined);
|
const [isSingleChoice, setSingleChoice] = React.useState(undefined);
|
||||||
const priorStepsWithExecutions = React.useContext(StepExecutionsContext);
|
const priorStepsWithExecutions = React.useContext(StepExecutionsContext);
|
||||||
const editorRef = React.useRef(null);
|
const editorRef = React.useRef(null);
|
||||||
|
const mountedRef = React.useRef(false);
|
||||||
|
|
||||||
const renderElement = React.useCallback(
|
const renderElement = React.useCallback(
|
||||||
(props) => <Element {...props} disabled={disabled} />,
|
(props) => <Element {...props} disabled={disabled} />,
|
||||||
@@ -94,11 +95,15 @@ function ControlledCustomAutocomplete(props) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
if (mountedRef.current) {
|
||||||
const hasDependencies = dependsOnValues.length;
|
const hasDependencies = dependsOnValues.length;
|
||||||
if (hasDependencies) {
|
if (hasDependencies) {
|
||||||
// Reset the field when a dependent has been updated
|
// Reset the field when a dependent has been updated
|
||||||
resetEditor(editor);
|
resetEditor(editor);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
mountedRef.current = true;
|
||||||
|
}
|
||||||
}, dependsOnValues);
|
}, dependsOnValues);
|
||||||
|
|
||||||
React.useEffect(
|
React.useEffect(
|
||||||
|
@@ -64,11 +64,19 @@ function DynamicField(props) {
|
|||||||
<Stack
|
<Stack
|
||||||
direction={{ xs: 'column', sm: 'row' }}
|
direction={{ xs: 'column', sm: 'row' }}
|
||||||
spacing={{ xs: 2 }}
|
spacing={{ xs: 2 }}
|
||||||
sx={{ display: 'flex', flex: 1 }}
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 0,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{fields.map((fieldSchema, fieldSchemaIndex) => (
|
{fields.map((fieldSchema, fieldSchemaIndex) => (
|
||||||
<Box
|
<Box
|
||||||
sx={{ display: 'flex', flex: '1 0 0px' }}
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flex: '1 0 0px',
|
||||||
|
minWidth: 0,
|
||||||
|
}}
|
||||||
key={`field-${field.__id}-${fieldSchemaIndex}`}
|
key={`field-${field.__id}-${fieldSchemaIndex}`}
|
||||||
>
|
>
|
||||||
<InputCreator
|
<InputCreator
|
||||||
|
@@ -8,6 +8,7 @@ import Tooltip from '@mui/material/Tooltip';
|
|||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
|
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
|
||||||
import Snackbar from '@mui/material/Snackbar';
|
import Snackbar from '@mui/material/Snackbar';
|
||||||
|
import { ReactFlowProvider } from 'reactflow';
|
||||||
|
|
||||||
import { EditorProvider } from 'contexts/Editor';
|
import { EditorProvider } from 'contexts/Editor';
|
||||||
import EditableTypography from 'components/EditableTypography';
|
import EditableTypography from 'components/EditableTypography';
|
||||||
@@ -20,6 +21,9 @@ import * as URLS from 'config/urls';
|
|||||||
import { TopBar } from './style';
|
import { TopBar } from './style';
|
||||||
import useFlow from 'hooks/useFlow';
|
import useFlow from 'hooks/useFlow';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
import EditorNew from 'components/EditorNew/EditorNew';
|
||||||
|
|
||||||
|
const useNewFlowEditor = process.env.REACT_APP_USE_NEW_FLOW_EDITOR === 'true';
|
||||||
|
|
||||||
export default function EditorLayout() {
|
export default function EditorLayout() {
|
||||||
const { flowId } = useParams();
|
const { flowId } = useParams();
|
||||||
@@ -55,6 +59,7 @@ export default function EditorLayout() {
|
|||||||
|
|
||||||
const onFlowStatusUpdate = React.useCallback(
|
const onFlowStatusUpdate = React.useCallback(
|
||||||
async (active) => {
|
async (active) => {
|
||||||
|
try {
|
||||||
await updateFlowStatus({
|
await updateFlowStatus({
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
@@ -72,6 +77,7 @@ export default function EditorLayout() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await queryClient.invalidateQueries({ queryKey: ['flows', flowId] });
|
await queryClient.invalidateQueries({ queryKey: ['flows', flowId] });
|
||||||
|
} catch (err) {}
|
||||||
},
|
},
|
||||||
[flowId, queryClient],
|
[flowId, queryClient],
|
||||||
);
|
);
|
||||||
@@ -131,15 +137,28 @@ export default function EditorLayout() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</TopBar>
|
</TopBar>
|
||||||
|
|
||||||
|
{useNewFlowEditor ? (
|
||||||
|
<Stack direction="column" height="100%" flexGrow={1}>
|
||||||
|
<Stack direction="column" flexGrow={1}>
|
||||||
|
<EditorProvider value={{ readOnly: !!flow?.active }}>
|
||||||
|
<ReactFlowProvider>
|
||||||
|
{!flow && !isFlowLoading && 'not found'}
|
||||||
|
{flow && <EditorNew flow={flow} />}
|
||||||
|
</ReactFlowProvider>
|
||||||
|
</EditorProvider>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
) : (
|
||||||
<Stack direction="column" height="100%">
|
<Stack direction="column" height="100%">
|
||||||
<Container maxWidth="md">
|
<Container maxWidth="md">
|
||||||
<EditorProvider value={{ readOnly: !!flow?.active }}>
|
<EditorProvider value={{ readOnly: !!flow?.active }}>
|
||||||
{!flow && !isFlowLoading && 'not found'}
|
{!flow && !isFlowLoading && 'not found'}
|
||||||
|
|
||||||
{flow && <Editor flow={flow} />}
|
{flow && <Editor flow={flow} />}
|
||||||
</EditorProvider>
|
</EditorProvider>
|
||||||
</Container>
|
</Container>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
)}
|
||||||
|
|
||||||
<Snackbar
|
<Snackbar
|
||||||
data-test="flow-cannot-edit-info-snackbar"
|
data-test="flow-cannot-edit-info-snackbar"
|
||||||
|
57
packages/web/src/components/EditorNew/Edge/Edge.jsx
Normal file
57
packages/web/src/components/EditorNew/Edge/Edge.jsx
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { EdgeLabelRenderer, getStraightPath } from 'reactflow';
|
||||||
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
|
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import { EdgesContext } from '../EditorNew';
|
||||||
|
|
||||||
|
export default function Edge({
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
source,
|
||||||
|
data: { laidOut },
|
||||||
|
}) {
|
||||||
|
const { stepCreationInProgress, flowActive, onAddStep } =
|
||||||
|
useContext(EdgesContext);
|
||||||
|
|
||||||
|
const [edgePath, labelX, labelY] = getStraightPath({
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EdgeLabelRenderer>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => onAddStep(source)}
|
||||||
|
color="primary"
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
||||||
|
pointerEvents: 'all',
|
||||||
|
visibility: laidOut ? 'visible' : 'hidden',
|
||||||
|
}}
|
||||||
|
disabled={stepCreationInProgress || flowActive}
|
||||||
|
>
|
||||||
|
<AddIcon />
|
||||||
|
</IconButton>
|
||||||
|
</EdgeLabelRenderer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Edge.propTypes = {
|
||||||
|
sourceX: PropTypes.number.isRequired,
|
||||||
|
sourceY: PropTypes.number.isRequired,
|
||||||
|
targetX: PropTypes.number.isRequired,
|
||||||
|
targetY: PropTypes.number.isRequired,
|
||||||
|
source: PropTypes.string.isRequired,
|
||||||
|
data: PropTypes.shape({
|
||||||
|
laidOut: PropTypes.bool,
|
||||||
|
}).isRequired,
|
||||||
|
};
|
277
packages/web/src/components/EditorNew/EditorNew.jsx
Normal file
277
packages/web/src/components/EditorNew/EditorNew.jsx
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
import { useEffect, useCallback, createContext, useRef } from 'react';
|
||||||
|
import { useMutation } from '@apollo/client';
|
||||||
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { FlowPropType } from 'propTypes/propTypes';
|
||||||
|
import ReactFlow, { useNodesState, useEdgesState } from 'reactflow';
|
||||||
|
import 'reactflow/dist/style.css';
|
||||||
|
import { UPDATE_STEP } from 'graphql/mutations/update-step';
|
||||||
|
import { CREATE_STEP } from 'graphql/mutations/create-step';
|
||||||
|
|
||||||
|
import { useAutoLayout } from './useAutoLayout';
|
||||||
|
import { useScrollBoundaries } from './useScrollBoundaries';
|
||||||
|
import FlowStepNode from './FlowStepNode/FlowStepNode';
|
||||||
|
import Edge from './Edge/Edge';
|
||||||
|
import InvisibleNode from './InvisibleNode/InvisibleNode';
|
||||||
|
import { EditorWrapper } from './style';
|
||||||
|
import {
|
||||||
|
generateEdgeId,
|
||||||
|
generateInitialEdges,
|
||||||
|
generateInitialNodes,
|
||||||
|
updatedCollapsedNodes,
|
||||||
|
} from './utils';
|
||||||
|
import { EDGE_TYPES, INVISIBLE_NODE_ID, NODE_TYPES } from './constants';
|
||||||
|
|
||||||
|
export const EdgesContext = createContext();
|
||||||
|
export const NodesContext = createContext();
|
||||||
|
|
||||||
|
const nodeTypes = {
|
||||||
|
[NODE_TYPES.FLOW_STEP]: FlowStepNode,
|
||||||
|
[NODE_TYPES.INVISIBLE]: InvisibleNode,
|
||||||
|
};
|
||||||
|
|
||||||
|
const edgeTypes = {
|
||||||
|
[EDGE_TYPES.ADD_NODE_EDGE]: Edge,
|
||||||
|
};
|
||||||
|
|
||||||
|
const EditorNew = ({ flow }) => {
|
||||||
|
const [updateStep] = useMutation(UPDATE_STEP);
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const [createStep, { loading: stepCreationInProgress }] =
|
||||||
|
useMutation(CREATE_STEP);
|
||||||
|
|
||||||
|
const [nodes, setNodes, onNodesChange] = useNodesState(
|
||||||
|
generateInitialNodes(flow),
|
||||||
|
);
|
||||||
|
const [edges, setEdges, onEdgesChange] = useEdgesState(
|
||||||
|
generateInitialEdges(flow),
|
||||||
|
);
|
||||||
|
|
||||||
|
useAutoLayout();
|
||||||
|
useScrollBoundaries();
|
||||||
|
|
||||||
|
const createdStepIdRef = useRef(null);
|
||||||
|
|
||||||
|
const openNextStep = useCallback(
|
||||||
|
(currentStepId) => {
|
||||||
|
setNodes((nodes) => {
|
||||||
|
const currentStepIndex = nodes.findIndex(
|
||||||
|
(node) => node.id === currentStepId,
|
||||||
|
);
|
||||||
|
if (currentStepIndex >= 0) {
|
||||||
|
const nextStep = nodes[currentStepIndex + 1];
|
||||||
|
return updatedCollapsedNodes(nodes, nextStep.id);
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[setNodes],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onStepClose = useCallback(() => {
|
||||||
|
setNodes((nodes) => updatedCollapsedNodes(nodes));
|
||||||
|
}, [setNodes]);
|
||||||
|
|
||||||
|
const onStepOpen = useCallback(
|
||||||
|
(stepId) => {
|
||||||
|
setNodes((nodes) => updatedCollapsedNodes(nodes, stepId));
|
||||||
|
},
|
||||||
|
[setNodes],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onStepChange = useCallback(
|
||||||
|
async (step) => {
|
||||||
|
const mutationInput = {
|
||||||
|
id: step.id,
|
||||||
|
key: step.key,
|
||||||
|
parameters: step.parameters,
|
||||||
|
connection: {
|
||||||
|
id: step.connection?.id,
|
||||||
|
},
|
||||||
|
flow: {
|
||||||
|
id: flow.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (step.appKey) {
|
||||||
|
mutationInput.appKey = step.appKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateStep({
|
||||||
|
variables: { input: mutationInput },
|
||||||
|
});
|
||||||
|
await queryClient.invalidateQueries({
|
||||||
|
queryKey: ['steps', step.id, 'connection'],
|
||||||
|
});
|
||||||
|
await queryClient.invalidateQueries({ queryKey: ['flows', flow.id] });
|
||||||
|
},
|
||||||
|
[flow.id, updateStep, queryClient],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onAddStep = useCallback(
|
||||||
|
async (previousStepId) => {
|
||||||
|
const mutationInput = {
|
||||||
|
previousStep: {
|
||||||
|
id: previousStepId,
|
||||||
|
},
|
||||||
|
flow: {
|
||||||
|
id: flow.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: { createStep: createdStep },
|
||||||
|
} = await createStep({
|
||||||
|
variables: { input: mutationInput },
|
||||||
|
});
|
||||||
|
|
||||||
|
const createdStepId = createdStep.id;
|
||||||
|
await queryClient.invalidateQueries({ queryKey: ['flows', flow.id] });
|
||||||
|
createdStepIdRef.current = createdStepId;
|
||||||
|
},
|
||||||
|
[flow.id, createStep, queryClient],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (flow.steps.length + 1 !== nodes.length) {
|
||||||
|
setNodes((nodes) => {
|
||||||
|
const newNodes = flow.steps.map((step) => {
|
||||||
|
const createdStepId = createdStepIdRef.current;
|
||||||
|
const prevNode = nodes.find(({ id }) => id === step.id);
|
||||||
|
if (prevNode) {
|
||||||
|
return {
|
||||||
|
...prevNode,
|
||||||
|
zIndex: createdStepId ? 0 : prevNode.zIndex,
|
||||||
|
data: {
|
||||||
|
...prevNode.data,
|
||||||
|
collapsed: createdStepId ? true : prevNode.data.collapsed,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
id: step.id,
|
||||||
|
type: NODE_TYPES.FLOW_STEP,
|
||||||
|
position: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
zIndex: 1,
|
||||||
|
data: {
|
||||||
|
collapsed: false,
|
||||||
|
laidOut: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const prevInvisible = nodes.find(({ id }) => id === INVISIBLE_NODE_ID);
|
||||||
|
return [
|
||||||
|
...newNodes,
|
||||||
|
{
|
||||||
|
id: INVISIBLE_NODE_ID,
|
||||||
|
type: NODE_TYPES.INVISIBLE,
|
||||||
|
position: {
|
||||||
|
x: prevInvisible?.position.x || 0,
|
||||||
|
y: prevInvisible?.position.y || 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
setEdges((edges) => {
|
||||||
|
const newEdges = flow.steps
|
||||||
|
.map((step, i) => {
|
||||||
|
const sourceId = step.id;
|
||||||
|
const targetId = flow.steps[i + 1]?.id;
|
||||||
|
const edge = edges?.find(
|
||||||
|
(edge) => edge.id === generateEdgeId(sourceId, targetId),
|
||||||
|
);
|
||||||
|
if (targetId) {
|
||||||
|
return {
|
||||||
|
id: generateEdgeId(sourceId, targetId),
|
||||||
|
source: sourceId,
|
||||||
|
target: targetId,
|
||||||
|
type: 'addNodeEdge',
|
||||||
|
data: {
|
||||||
|
laidOut: edge ? edge?.data.laidOut : false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter((edge) => !!edge);
|
||||||
|
|
||||||
|
const lastStep = flow.steps[flow.steps.length - 1];
|
||||||
|
const lastEdge = edges[edges.length - 1];
|
||||||
|
|
||||||
|
return lastStep
|
||||||
|
? [
|
||||||
|
...newEdges,
|
||||||
|
{
|
||||||
|
id: generateEdgeId(lastStep.id, INVISIBLE_NODE_ID),
|
||||||
|
source: lastStep.id,
|
||||||
|
target: INVISIBLE_NODE_ID,
|
||||||
|
type: 'addNodeEdge',
|
||||||
|
data: {
|
||||||
|
laidOut:
|
||||||
|
lastEdge?.id ===
|
||||||
|
generateEdgeId(lastStep.id, INVISIBLE_NODE_ID)
|
||||||
|
? lastEdge?.data.laidOut
|
||||||
|
: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: newEdges;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (createdStepIdRef.current) {
|
||||||
|
createdStepIdRef.current = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [flow.steps]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NodesContext.Provider
|
||||||
|
value={{
|
||||||
|
openNextStep,
|
||||||
|
onStepOpen,
|
||||||
|
onStepClose,
|
||||||
|
onStepChange,
|
||||||
|
flowId: flow.id,
|
||||||
|
steps: flow.steps,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EdgesContext.Provider
|
||||||
|
value={{
|
||||||
|
stepCreationInProgress,
|
||||||
|
onAddStep,
|
||||||
|
flowActive: flow.active,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EditorWrapper direction="column">
|
||||||
|
<ReactFlow
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
|
onNodesChange={onNodesChange}
|
||||||
|
onEdgesChange={onEdgesChange}
|
||||||
|
nodeTypes={nodeTypes}
|
||||||
|
edgeTypes={edgeTypes}
|
||||||
|
panOnScroll
|
||||||
|
panOnScrollMode="vertical"
|
||||||
|
panOnDrag={false}
|
||||||
|
zoomOnScroll={false}
|
||||||
|
zoomOnPinch={false}
|
||||||
|
zoomOnDoubleClick={false}
|
||||||
|
panActivationKeyCode={null}
|
||||||
|
proOptions={{ hideAttribution: true }}
|
||||||
|
/>
|
||||||
|
</EditorWrapper>
|
||||||
|
</EdgesContext.Provider>
|
||||||
|
</NodesContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
EditorNew.propTypes = {
|
||||||
|
flow: FlowPropType.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditorNew;
|
@@ -0,0 +1,60 @@
|
|||||||
|
import { Handle, Position } from 'reactflow';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import FlowStep from 'components/FlowStep';
|
||||||
|
|
||||||
|
import { NodeWrapper, NodeInnerWrapper } from './style.js';
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import { NodesContext } from '../EditorNew.jsx';
|
||||||
|
|
||||||
|
function FlowStepNode({ data: { collapsed, laidOut }, id }) {
|
||||||
|
const { openNextStep, onStepOpen, onStepClose, onStepChange, flowId, steps } =
|
||||||
|
useContext(NodesContext);
|
||||||
|
|
||||||
|
const step = steps.find(({ id: stepId }) => stepId === id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NodeWrapper
|
||||||
|
className="nodrag"
|
||||||
|
sx={{
|
||||||
|
visibility: laidOut ? 'visible' : 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NodeInnerWrapper>
|
||||||
|
<Handle
|
||||||
|
type="target"
|
||||||
|
position={Position.Top}
|
||||||
|
isConnectable={false}
|
||||||
|
style={{ visibility: 'hidden' }}
|
||||||
|
/>
|
||||||
|
{step && (
|
||||||
|
<FlowStep
|
||||||
|
step={step}
|
||||||
|
collapsed={collapsed}
|
||||||
|
onOpen={() => onStepOpen(step.id)}
|
||||||
|
onClose={onStepClose}
|
||||||
|
onChange={onStepChange}
|
||||||
|
flowId={flowId}
|
||||||
|
onContinue={() => openNextStep(step.id)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
position={Position.Bottom}
|
||||||
|
isConnectable={false}
|
||||||
|
style={{ visibility: 'hidden' }}
|
||||||
|
/>
|
||||||
|
</NodeInnerWrapper>
|
||||||
|
</NodeWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlowStepNode.propTypes = {
|
||||||
|
id: PropTypes.string,
|
||||||
|
data: PropTypes.shape({
|
||||||
|
collapsed: PropTypes.bool.isRequired,
|
||||||
|
laidOut: PropTypes.bool.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FlowStepNode;
|
14
packages/web/src/components/EditorNew/FlowStepNode/style.js
Normal file
14
packages/web/src/components/EditorNew/FlowStepNode/style.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import { Box } from '@mui/material';
|
||||||
|
|
||||||
|
export const NodeWrapper = styled(Box)(({ theme }) => ({
|
||||||
|
width: '100vw',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: theme.spacing(0, 2.5),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const NodeInnerWrapper = styled(Box)(({ theme }) => ({
|
||||||
|
maxWidth: 900,
|
||||||
|
flex: 1,
|
||||||
|
}));
|
@@ -0,0 +1,19 @@
|
|||||||
|
import { Handle, Position } from 'reactflow';
|
||||||
|
import { Box } from '@mui/material';
|
||||||
|
|
||||||
|
// This node is used for adding an edge with add node button after the last flow step node
|
||||||
|
function InvisibleNode() {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
maxWidth={900}
|
||||||
|
width="100vw"
|
||||||
|
className="nodrag"
|
||||||
|
sx={{ visibility: 'hidden' }}
|
||||||
|
>
|
||||||
|
<Handle type="target" position={Position.Top} isConnectable={false} />
|
||||||
|
Invisible node
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InvisibleNode;
|
10
packages/web/src/components/EditorNew/constants.js
Normal file
10
packages/web/src/components/EditorNew/constants.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export const INVISIBLE_NODE_ID = 'invisible-node';
|
||||||
|
|
||||||
|
export const NODE_TYPES = {
|
||||||
|
FLOW_STEP: 'flowStep',
|
||||||
|
INVISIBLE: 'invisible',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EDGE_TYPES = {
|
||||||
|
ADD_NODE_EDGE: 'addNodeEdge',
|
||||||
|
};
|
13
packages/web/src/components/EditorNew/style.js
Normal file
13
packages/web/src/components/EditorNew/style.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Stack } from '@mui/material';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
|
||||||
|
export const EditorWrapper = styled(Stack)(({ theme }) => ({
|
||||||
|
flexGrow: 1,
|
||||||
|
'& > div': {
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
|
|
||||||
|
'& .react-flow__pane, & .react-flow__node': {
|
||||||
|
cursor: 'auto !important',
|
||||||
|
},
|
||||||
|
}));
|
69
packages/web/src/components/EditorNew/useAutoLayout.js
Normal file
69
packages/web/src/components/EditorNew/useAutoLayout.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { useCallback, useEffect } from 'react';
|
||||||
|
import Dagre from '@dagrejs/dagre';
|
||||||
|
import { usePrevious } from 'hooks/usePrevious';
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
import { useNodesInitialized, useNodes, useReactFlow } from 'reactflow';
|
||||||
|
|
||||||
|
const getLaidOutElements = (nodes, edges) => {
|
||||||
|
const graph = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
|
||||||
|
graph.setGraph({
|
||||||
|
rankdir: 'TB',
|
||||||
|
marginy: 60,
|
||||||
|
ranksep: 64,
|
||||||
|
});
|
||||||
|
edges.forEach((edge) => graph.setEdge(edge.source, edge.target));
|
||||||
|
nodes.forEach((node) => graph.setNode(node.id, node));
|
||||||
|
|
||||||
|
Dagre.layout(graph);
|
||||||
|
|
||||||
|
return {
|
||||||
|
nodes: nodes.map((node) => {
|
||||||
|
const { x, y, width, height } = graph.node(node.id);
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
position: { x: x - width / 2, y: y - height / 2 },
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
edges,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAutoLayout = () => {
|
||||||
|
const nodes = useNodes();
|
||||||
|
const prevNodes = usePrevious(nodes);
|
||||||
|
const nodesInitialized = useNodesInitialized();
|
||||||
|
const { getEdges, setNodes, setEdges } = useReactFlow();
|
||||||
|
|
||||||
|
const onLayout = useCallback(
|
||||||
|
(nodes, edges) => {
|
||||||
|
const laidOutElements = getLaidOutElements(nodes, edges);
|
||||||
|
|
||||||
|
setNodes([
|
||||||
|
...laidOutElements.nodes.map((node) => ({
|
||||||
|
...node,
|
||||||
|
data: { ...node.data, laidOut: true },
|
||||||
|
})),
|
||||||
|
]);
|
||||||
|
setEdges([
|
||||||
|
...laidOutElements.edges.map((edge) => ({
|
||||||
|
...edge,
|
||||||
|
data: { ...edge.data, laidOut: true },
|
||||||
|
})),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
[setEdges, setNodes],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const shouldAutoLayout =
|
||||||
|
nodesInitialized &&
|
||||||
|
!isEqual(
|
||||||
|
nodes.map(({ width, height }) => ({ width, height })),
|
||||||
|
prevNodes.map(({ width, height }) => ({ width, height })),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (shouldAutoLayout) {
|
||||||
|
onLayout(nodes, getEdges());
|
||||||
|
}
|
||||||
|
}, [nodes]);
|
||||||
|
};
|
13
packages/web/src/components/EditorNew/useScrollBoundaries.js
Normal file
13
packages/web/src/components/EditorNew/useScrollBoundaries.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useViewport, useReactFlow } from 'reactflow';
|
||||||
|
|
||||||
|
export const useScrollBoundaries = () => {
|
||||||
|
const { setViewport } = useReactFlow();
|
||||||
|
const { x, y, zoom } = useViewport();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (y > 0) {
|
||||||
|
setViewport({ x, y: 0, zoom });
|
||||||
|
}
|
||||||
|
}, [y]);
|
||||||
|
};
|
88
packages/web/src/components/EditorNew/utils.js
Normal file
88
packages/web/src/components/EditorNew/utils.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import { INVISIBLE_NODE_ID, NODE_TYPES } from './constants';
|
||||||
|
|
||||||
|
export const generateEdgeId = (sourceId, targetId) => `${sourceId}-${targetId}`;
|
||||||
|
|
||||||
|
export const updatedCollapsedNodes = (nodes, openStepId) => {
|
||||||
|
return nodes.map((node) => {
|
||||||
|
if (node.type !== NODE_TYPES.FLOW_STEP) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const collapsed = node.id !== openStepId;
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
zIndex: collapsed ? 0 : 1,
|
||||||
|
data: { ...node.data, collapsed },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generateInitialNodes = (flow) => {
|
||||||
|
const newNodes = flow.steps.map((step, index) => {
|
||||||
|
const collapsed = index !== 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: step.id,
|
||||||
|
type: NODE_TYPES.FLOW_STEP,
|
||||||
|
position: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
zIndex: collapsed ? 0 : 1,
|
||||||
|
data: {
|
||||||
|
collapsed,
|
||||||
|
laidOut: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
...newNodes,
|
||||||
|
{
|
||||||
|
id: INVISIBLE_NODE_ID,
|
||||||
|
type: NODE_TYPES.INVISIBLE,
|
||||||
|
position: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generateInitialEdges = (flow) => {
|
||||||
|
const newEdges = flow.steps
|
||||||
|
.map((step, i) => {
|
||||||
|
const sourceId = step.id;
|
||||||
|
const targetId = flow.steps[i + 1]?.id;
|
||||||
|
if (targetId) {
|
||||||
|
return {
|
||||||
|
id: generateEdgeId(sourceId, targetId),
|
||||||
|
source: sourceId,
|
||||||
|
target: targetId,
|
||||||
|
type: 'addNodeEdge',
|
||||||
|
data: {
|
||||||
|
laidOut: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter((edge) => !!edge);
|
||||||
|
|
||||||
|
const lastStep = flow.steps[flow.steps.length - 1];
|
||||||
|
|
||||||
|
return lastStep
|
||||||
|
? [
|
||||||
|
...newEdges,
|
||||||
|
{
|
||||||
|
id: generateEdgeId(lastStep.id, INVISIBLE_NODE_ID),
|
||||||
|
source: lastStep.id,
|
||||||
|
target: INVISIBLE_NODE_ID,
|
||||||
|
type: 'addNodeEdge',
|
||||||
|
data: {
|
||||||
|
laidOut: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: newEdges;
|
||||||
|
};
|
@@ -28,9 +28,12 @@ function ContextMenu(props) {
|
|||||||
variables: { input: { id: flowId } },
|
variables: { input: { id: flowId } },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (appKey) {
|
||||||
await queryClient.invalidateQueries({
|
await queryClient.invalidateQueries({
|
||||||
queryKey: ['apps', appKey, 'flows'],
|
queryKey: ['apps', appKey, 'flows'],
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
enqueueSnackbar(formatMessage('flow.successfullyDuplicated'), {
|
enqueueSnackbar(formatMessage('flow.successfullyDuplicated'), {
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
SnackbarProps: {
|
SnackbarProps: {
|
||||||
@@ -56,9 +59,12 @@ function ContextMenu(props) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (appKey) {
|
||||||
await queryClient.invalidateQueries({
|
await queryClient.invalidateQueries({
|
||||||
queryKey: ['apps', appKey, 'flows'],
|
queryKey: ['apps', appKey, 'flows'],
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
enqueueSnackbar(formatMessage('flow.successfullyDeleted'), {
|
enqueueSnackbar(formatMessage('flow.successfullyDeleted'), {
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
});
|
});
|
||||||
@@ -110,7 +116,7 @@ ContextMenu.propTypes = {
|
|||||||
]).isRequired,
|
]).isRequired,
|
||||||
onDeleteFlow: PropTypes.func,
|
onDeleteFlow: PropTypes.func,
|
||||||
onDuplicateFlow: PropTypes.func,
|
onDuplicateFlow: PropTypes.func,
|
||||||
appKey: PropTypes.string.isRequired,
|
appKey: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ContextMenu;
|
export default ContextMenu;
|
||||||
|
@@ -38,20 +38,24 @@ function FlowRow(props) {
|
|||||||
const contextButtonRef = React.useRef(null);
|
const contextButtonRef = React.useRef(null);
|
||||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||||
const { flow, onDuplicateFlow, onDeleteFlow, appKey } = props;
|
const { flow, onDuplicateFlow, onDeleteFlow, appKey } = props;
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onContextMenuClick = (event) => {
|
const onContextMenuClick = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.nativeEvent.stopImmediatePropagation();
|
event.nativeEvent.stopImmediatePropagation();
|
||||||
setAnchorEl(contextButtonRef.current);
|
setAnchorEl(contextButtonRef.current);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createdAt = DateTime.fromMillis(parseInt(flow.createdAt, 10));
|
const createdAt = DateTime.fromMillis(parseInt(flow.createdAt, 10));
|
||||||
const updatedAt = DateTime.fromMillis(parseInt(flow.updatedAt, 10));
|
const updatedAt = DateTime.fromMillis(parseInt(flow.updatedAt, 10));
|
||||||
const isUpdated = updatedAt > createdAt;
|
const isUpdated = updatedAt > createdAt;
|
||||||
const relativeCreatedAt = createdAt.toRelative();
|
const relativeCreatedAt = createdAt.toRelative();
|
||||||
const relativeUpdatedAt = updatedAt.toRelative();
|
const relativeUpdatedAt = updatedAt.toRelative();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card sx={{ mb: 1 }} data-test="flow-row">
|
<Card sx={{ mb: 1 }} data-test="flow-row">
|
||||||
@@ -127,7 +131,7 @@ FlowRow.propTypes = {
|
|||||||
flow: FlowPropType.isRequired,
|
flow: FlowPropType.isRequired,
|
||||||
onDeleteFlow: PropTypes.func,
|
onDeleteFlow: PropTypes.func,
|
||||||
onDuplicateFlow: PropTypes.func,
|
onDuplicateFlow: PropTypes.func,
|
||||||
appKey: PropTypes.string.isRequired,
|
appKey: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FlowRow;
|
export default FlowRow;
|
||||||
|
@@ -11,9 +11,6 @@ import IconButton from '@mui/material/IconButton';
|
|||||||
import ErrorIcon from '@mui/icons-material/Error';
|
import ErrorIcon from '@mui/icons-material/Error';
|
||||||
import CircularProgress from '@mui/material/CircularProgress';
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
|
||||||
import * as yup from 'yup';
|
|
||||||
|
|
||||||
import { EditorContext } from 'contexts/Editor';
|
import { EditorContext } from 'contexts/Editor';
|
||||||
import { StepExecutionsProvider } from 'contexts/StepExecutions';
|
import { StepExecutionsProvider } from 'contexts/StepExecutions';
|
||||||
import TestSubstep from 'components/TestSubstep';
|
import TestSubstep from 'components/TestSubstep';
|
||||||
@@ -33,77 +30,18 @@ import {
|
|||||||
Header,
|
Header,
|
||||||
Wrapper,
|
Wrapper,
|
||||||
} from './style';
|
} from './style';
|
||||||
import isEmpty from 'helpers/isEmpty';
|
|
||||||
import { StepPropType } from 'propTypes/propTypes';
|
import { StepPropType } from 'propTypes/propTypes';
|
||||||
import useTriggers from 'hooks/useTriggers';
|
import useTriggers from 'hooks/useTriggers';
|
||||||
import useActions from 'hooks/useActions';
|
import useActions from 'hooks/useActions';
|
||||||
import useTriggerSubsteps from 'hooks/useTriggerSubsteps';
|
import useTriggerSubsteps from 'hooks/useTriggerSubsteps';
|
||||||
import useActionSubsteps from 'hooks/useActionSubsteps';
|
import useActionSubsteps from 'hooks/useActionSubsteps';
|
||||||
import useStepWithTestExecutions from 'hooks/useStepWithTestExecutions';
|
import useStepWithTestExecutions from 'hooks/useStepWithTestExecutions';
|
||||||
|
import { validationSchemaResolver } from './validation';
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
const validIcon = <CheckCircleIcon color="success" />;
|
const validIcon = <CheckCircleIcon color="success" />;
|
||||||
const errorIcon = <ErrorIcon color="error" />;
|
const errorIcon = <ErrorIcon color="error" />;
|
||||||
|
|
||||||
function generateValidationSchema(substeps) {
|
|
||||||
const fieldValidations = substeps?.reduce(
|
|
||||||
(allValidations, { arguments: args }) => {
|
|
||||||
if (!args || !Array.isArray(args)) return allValidations;
|
|
||||||
const substepArgumentValidations = {};
|
|
||||||
for (const arg of args) {
|
|
||||||
const { key, required } = arg;
|
|
||||||
// base validation for the field if not exists
|
|
||||||
if (!substepArgumentValidations[key]) {
|
|
||||||
substepArgumentValidations[key] = yup.mixed();
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
typeof substepArgumentValidations[key] === 'object' &&
|
|
||||||
(arg.type === 'string' || arg.type === 'dropdown')
|
|
||||||
) {
|
|
||||||
// if the field is required, add the required validation
|
|
||||||
if (required) {
|
|
||||||
substepArgumentValidations[key] = substepArgumentValidations[key]
|
|
||||||
.required(`${key} is required.`)
|
|
||||||
.test(
|
|
||||||
'empty-check',
|
|
||||||
`${key} must be not empty`,
|
|
||||||
(value) => !isEmpty(value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// if the field depends on another field, add the dependsOn required validation
|
|
||||||
if (Array.isArray(arg.dependsOn) && arg.dependsOn.length > 0) {
|
|
||||||
for (const dependsOnKey of arg.dependsOn) {
|
|
||||||
const missingDependencyValueMessage = `We're having trouble loading '${key}' data as required field '${dependsOnKey}' is missing.`;
|
|
||||||
// TODO: make `dependsOnKey` agnostic to the field. However, nested validation schema is not supported.
|
|
||||||
// So the fields under the `parameters` key are subject to their siblings only and thus, `parameters.` is removed.
|
|
||||||
substepArgumentValidations[key] = substepArgumentValidations[
|
|
||||||
key
|
|
||||||
].when(`${dependsOnKey.replace('parameters.', '')}`, {
|
|
||||||
is: (value) => Boolean(value) === false,
|
|
||||||
then: (schema) =>
|
|
||||||
schema
|
|
||||||
.notOneOf([''], missingDependencyValueMessage)
|
|
||||||
.required(missingDependencyValueMessage),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...allValidations,
|
|
||||||
...substepArgumentValidations,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
const validationSchema = yup.object({
|
|
||||||
parameters: yup.object(fieldValidations),
|
|
||||||
});
|
|
||||||
|
|
||||||
return yupResolver(validationSchema);
|
|
||||||
}
|
|
||||||
|
|
||||||
function FlowStep(props) {
|
function FlowStep(props) {
|
||||||
const { collapsed, onChange, onContinue, flowId } = props;
|
const { collapsed, onChange, onContinue, flowId } = props;
|
||||||
const editorContext = React.useContext(EditorContext);
|
const editorContext = React.useContext(EditorContext);
|
||||||
@@ -114,6 +52,10 @@ function FlowStep(props) {
|
|||||||
const isAction = step.type === 'action';
|
const isAction = step.type === 'action';
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const [currentSubstep, setCurrentSubstep] = React.useState(0);
|
const [currentSubstep, setCurrentSubstep] = React.useState(0);
|
||||||
|
const [formResolverContext, setFormResolverContext] = React.useState({
|
||||||
|
substeps: [],
|
||||||
|
additionalFields: {},
|
||||||
|
});
|
||||||
const useAppsOptions = {};
|
const useAppsOptions = {};
|
||||||
|
|
||||||
if (isTrigger) {
|
if (isTrigger) {
|
||||||
@@ -168,6 +110,12 @@ function FlowStep(props) {
|
|||||||
? triggerSubstepsData
|
? triggerSubstepsData
|
||||||
: actionSubstepsData || [];
|
: actionSubstepsData || [];
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!isEqual(substeps, formResolverContext.substeps)) {
|
||||||
|
setFormResolverContext({ substeps, additionalFields: {} });
|
||||||
|
}
|
||||||
|
}, [substeps]);
|
||||||
|
|
||||||
const handleChange = React.useCallback(({ step }) => {
|
const handleChange = React.useCallback(({ step }) => {
|
||||||
onChange(step);
|
onChange(step);
|
||||||
}, []);
|
}, []);
|
||||||
@@ -180,11 +128,6 @@ function FlowStep(props) {
|
|||||||
handleChange({ step: val });
|
handleChange({ step: val });
|
||||||
};
|
};
|
||||||
|
|
||||||
const stepValidationSchema = React.useMemo(
|
|
||||||
() => generateValidationSchema(substeps),
|
|
||||||
[substeps],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!apps?.data) {
|
if (!apps?.data) {
|
||||||
return (
|
return (
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
@@ -213,6 +156,15 @@ function FlowStep(props) {
|
|||||||
value !== substepIndex ? substepIndex : null,
|
value !== substepIndex ? substepIndex : null,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const addAdditionalFieldsValidation = (additionalFields) => {
|
||||||
|
if (additionalFields) {
|
||||||
|
setFormResolverContext((prev) => ({
|
||||||
|
...prev,
|
||||||
|
additionalFields: { ...prev.additionalFields, ...additionalFields },
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const validationStatusIcon =
|
const validationStatusIcon =
|
||||||
step.status === 'completed' ? validIcon : errorIcon;
|
step.status === 'completed' ? validIcon : errorIcon;
|
||||||
|
|
||||||
@@ -266,7 +218,8 @@ function FlowStep(props) {
|
|||||||
<Form
|
<Form
|
||||||
defaultValues={step}
|
defaultValues={step}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
resolver={stepValidationSchema}
|
resolver={validationSchemaResolver}
|
||||||
|
context={formResolverContext}
|
||||||
>
|
>
|
||||||
<ChooseAppAndEventSubstep
|
<ChooseAppAndEventSubstep
|
||||||
expanded={currentSubstep === 0}
|
expanded={currentSubstep === 0}
|
||||||
@@ -330,6 +283,9 @@ function FlowStep(props) {
|
|||||||
onSubmit={expandNextStep}
|
onSubmit={expandNextStep}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
step={step}
|
step={step}
|
||||||
|
addAdditionalFieldsValidation={
|
||||||
|
addAdditionalFieldsValidation
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
@@ -360,7 +316,6 @@ function FlowStep(props) {
|
|||||||
FlowStep.propTypes = {
|
FlowStep.propTypes = {
|
||||||
collapsed: PropTypes.bool,
|
collapsed: PropTypes.bool,
|
||||||
step: StepPropType.isRequired,
|
step: StepPropType.isRequired,
|
||||||
index: PropTypes.number,
|
|
||||||
onOpen: PropTypes.func,
|
onOpen: PropTypes.func,
|
||||||
onClose: PropTypes.func,
|
onClose: PropTypes.func,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
|
120
packages/web/src/components/FlowStep/validation.js
Normal file
120
packages/web/src/components/FlowStep/validation.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import * as yup from 'yup';
|
||||||
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
|
import isEmpty from 'helpers/isEmpty';
|
||||||
|
|
||||||
|
function addRequiredValidation({ required, schema, key }) {
|
||||||
|
// if the field is required, add the required validation
|
||||||
|
if (required) {
|
||||||
|
return schema
|
||||||
|
.required(`${key} is required.`)
|
||||||
|
.test(
|
||||||
|
'empty-check',
|
||||||
|
`${key} must be not empty`,
|
||||||
|
(value) => !isEmpty(value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDependsOnValidation({ schema, dependsOn, key, args }) {
|
||||||
|
// if the field depends on another field, add the dependsOn required validation
|
||||||
|
if (Array.isArray(dependsOn) && dependsOn.length > 0) {
|
||||||
|
for (const dependsOnKey of dependsOn) {
|
||||||
|
const dependsOnKeyShort = dependsOnKey.replace('parameters.', '');
|
||||||
|
const dependsOnField = args.find(({ key }) => key === dependsOnKeyShort);
|
||||||
|
|
||||||
|
if (dependsOnField?.required) {
|
||||||
|
const missingDependencyValueMessage = `We're having trouble loading '${key}' data as required field '${dependsOnKey}' is missing.`;
|
||||||
|
|
||||||
|
// TODO: make `dependsOnKey` agnostic to the field. However, nested validation schema is not supported.
|
||||||
|
// So the fields under the `parameters` key are subject to their siblings only and thus, `parameters.` is removed.
|
||||||
|
return schema.when(dependsOnKeyShort, {
|
||||||
|
is: (dependsOnValue) => Boolean(dependsOnValue) === false,
|
||||||
|
then: (schema) =>
|
||||||
|
schema
|
||||||
|
.notOneOf([''], missingDependencyValueMessage)
|
||||||
|
.required(missingDependencyValueMessage),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validationSchemaResolver(data, context, options) {
|
||||||
|
const { substeps = [], additionalFields = {} } = context;
|
||||||
|
|
||||||
|
const fieldValidations = [
|
||||||
|
...substeps,
|
||||||
|
{
|
||||||
|
arguments: Object.values(additionalFields)
|
||||||
|
.filter((field) => !!field)
|
||||||
|
.flat(),
|
||||||
|
},
|
||||||
|
].reduce((allValidations, { arguments: args }) => {
|
||||||
|
if (!args || !Array.isArray(args)) return allValidations;
|
||||||
|
|
||||||
|
const substepArgumentValidations = {};
|
||||||
|
|
||||||
|
for (const arg of args) {
|
||||||
|
const { key, required } = arg;
|
||||||
|
|
||||||
|
// base validation for the field if not exists
|
||||||
|
if (!substepArgumentValidations[key]) {
|
||||||
|
substepArgumentValidations[key] = yup.mixed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.type === 'dynamic') {
|
||||||
|
const fieldsSchema = {};
|
||||||
|
|
||||||
|
for (const field of arg.fields) {
|
||||||
|
fieldsSchema[field.key] = yup.mixed();
|
||||||
|
|
||||||
|
fieldsSchema[field.key] = addRequiredValidation({
|
||||||
|
required: field.required,
|
||||||
|
schema: fieldsSchema[field.key],
|
||||||
|
key: field.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
fieldsSchema[field.key] = addDependsOnValidation({
|
||||||
|
schema: fieldsSchema[field.key],
|
||||||
|
dependsOn: field.dependsOn,
|
||||||
|
key: field.key,
|
||||||
|
args,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
substepArgumentValidations[key] = yup
|
||||||
|
.array()
|
||||||
|
.of(yup.object(fieldsSchema));
|
||||||
|
} else if (
|
||||||
|
typeof substepArgumentValidations[key] === 'object' &&
|
||||||
|
(arg.type === 'string' || arg.type === 'dropdown')
|
||||||
|
) {
|
||||||
|
substepArgumentValidations[key] = addRequiredValidation({
|
||||||
|
required,
|
||||||
|
schema: substepArgumentValidations[key],
|
||||||
|
key,
|
||||||
|
});
|
||||||
|
|
||||||
|
substepArgumentValidations[key] = addDependsOnValidation({
|
||||||
|
schema: substepArgumentValidations[key],
|
||||||
|
dependsOn: arg.dependsOn,
|
||||||
|
key,
|
||||||
|
args,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...allValidations,
|
||||||
|
...substepArgumentValidations,
|
||||||
|
};
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const validationSchema = yup.object({
|
||||||
|
parameters: yup.object(fieldValidations),
|
||||||
|
});
|
||||||
|
|
||||||
|
return yupResolver(validationSchema)(data, context, options);
|
||||||
|
}
|
@@ -43,7 +43,10 @@ function FlowStepContextMenu(props) {
|
|||||||
FlowStepContextMenu.propTypes = {
|
FlowStepContextMenu.propTypes = {
|
||||||
stepId: PropTypes.string.isRequired,
|
stepId: PropTypes.string.isRequired,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
anchorEl: PropTypes.element.isRequired,
|
anchorEl: PropTypes.oneOfType([
|
||||||
|
PropTypes.func,
|
||||||
|
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
|
||||||
|
]).isRequired,
|
||||||
deletable: PropTypes.bool.isRequired,
|
deletable: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -19,7 +19,9 @@ function FlowSubstep(props) {
|
|||||||
onCollapse,
|
onCollapse,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
step,
|
step,
|
||||||
|
addAdditionalFieldsValidation,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { name, arguments: args } = substep;
|
const { name, arguments: args } = substep;
|
||||||
const editorContext = React.useContext(EditorContext);
|
const editorContext = React.useContext(EditorContext);
|
||||||
const formContext = useFormContext();
|
const formContext = useFormContext();
|
||||||
@@ -54,6 +56,7 @@ function FlowSubstep(props) {
|
|||||||
stepId={step.id}
|
stepId={step.id}
|
||||||
disabled={editorContext.readOnly}
|
disabled={editorContext.readOnly}
|
||||||
showOptionValue={true}
|
showOptionValue={true}
|
||||||
|
addAdditionalFieldsValidation={addAdditionalFieldsValidation}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { FormProvider, useForm, useWatch } from 'react-hook-form';
|
import { FormProvider, useForm, useWatch } from 'react-hook-form';
|
||||||
|
|
||||||
const noop = () => null;
|
const noop = () => null;
|
||||||
|
|
||||||
export default function Form(props) {
|
export default function Form(props) {
|
||||||
const {
|
const {
|
||||||
children,
|
children,
|
||||||
@@ -9,24 +11,31 @@ export default function Form(props) {
|
|||||||
resolver,
|
resolver,
|
||||||
render,
|
render,
|
||||||
mode = 'all',
|
mode = 'all',
|
||||||
|
context,
|
||||||
...formProps
|
...formProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const methods = useForm({
|
const methods = useForm({
|
||||||
defaultValues,
|
defaultValues,
|
||||||
reValidateMode: 'onBlur',
|
reValidateMode: 'onBlur',
|
||||||
resolver,
|
resolver,
|
||||||
mode,
|
mode,
|
||||||
|
context,
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = useWatch({ control: methods.control });
|
const form = useWatch({ control: methods.control });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For fields having `dependsOn` fields, we need to re-validate the form.
|
* For fields having `dependsOn` fields, we need to re-validate the form.
|
||||||
*/
|
*/
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
methods.trigger();
|
methods.trigger();
|
||||||
}, [methods.trigger, form]);
|
}, [methods.trigger, form]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
methods.reset(defaultValues);
|
methods.reset(defaultValues);
|
||||||
}, [defaultValues]);
|
}, [defaultValues]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<form onSubmit={methods.handleSubmit(onSubmit)} {...formProps}>
|
<form onSubmit={methods.handleSubmit(onSubmit)} {...formProps}>
|
||||||
|
@@ -23,7 +23,9 @@ export default function InputCreator(props) {
|
|||||||
disabled,
|
disabled,
|
||||||
showOptionValue,
|
showOptionValue,
|
||||||
shouldUnregister,
|
shouldUnregister,
|
||||||
|
addAdditionalFieldsValidation,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
key: name,
|
key: name,
|
||||||
label,
|
label,
|
||||||
@@ -33,6 +35,7 @@ export default function InputCreator(props) {
|
|||||||
description,
|
description,
|
||||||
type,
|
type,
|
||||||
} = schema;
|
} = schema;
|
||||||
|
|
||||||
const { data, loading } = useDynamicData(stepId, schema);
|
const { data, loading } = useDynamicData(stepId, schema);
|
||||||
const { data: additionalFieldsData, isLoading: isDynamicFieldsLoading } =
|
const { data: additionalFieldsData, isLoading: isDynamicFieldsLoading } =
|
||||||
useDynamicFields(stepId, schema);
|
useDynamicFields(stepId, schema);
|
||||||
@@ -40,6 +43,10 @@ export default function InputCreator(props) {
|
|||||||
|
|
||||||
const computedName = namePrefix ? `${namePrefix}.${name}` : name;
|
const computedName = namePrefix ? `${namePrefix}.${name}` : name;
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
addAdditionalFieldsValidation?.({ [name]: additionalFields });
|
||||||
|
}, [additionalFields]);
|
||||||
|
|
||||||
if (type === 'dynamic') {
|
if (type === 'dynamic') {
|
||||||
return (
|
return (
|
||||||
<DynamicField
|
<DynamicField
|
||||||
@@ -80,6 +87,7 @@ export default function InputCreator(props) {
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
showOptionValue={showOptionValue}
|
showOptionValue={showOptionValue}
|
||||||
shouldUnregister={shouldUnregister}
|
shouldUnregister={shouldUnregister}
|
||||||
|
componentsProps={{ popper: { className: 'nowheel' } }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@@ -5,8 +5,10 @@ import AddCircleIcon from '@mui/icons-material/AddCircle';
|
|||||||
import CardActionArea from '@mui/material/CardActionArea';
|
import CardActionArea from '@mui/material/CardActionArea';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { CardContent } from './style';
|
import { CardContent } from './style';
|
||||||
|
|
||||||
export default function NoResultFound(props) {
|
export default function NoResultFound(props) {
|
||||||
const { text, to } = props;
|
const { text, to } = props;
|
||||||
|
|
||||||
const ActionAreaLink = React.useMemo(
|
const ActionAreaLink = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
React.forwardRef(function InlineLink(linkProps, ref) {
|
React.forwardRef(function InlineLink(linkProps, ref) {
|
||||||
@@ -15,12 +17,12 @@ export default function NoResultFound(props) {
|
|||||||
}),
|
}),
|
||||||
[to],
|
[to],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card elevation={0}>
|
<Card elevation={0}>
|
||||||
<CardActionArea component={ActionAreaLink} {...props}>
|
<CardActionArea component={ActionAreaLink} {...props}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{!!to && <AddCircleIcon color="primary" />}
|
{!!to && <AddCircleIcon color="primary" />}
|
||||||
|
|
||||||
<Typography variant="body1">{text}</Typography>
|
<Typography variant="body1">{text}</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</CardActionArea>
|
</CardActionArea>
|
||||||
|
@@ -17,6 +17,7 @@ import { StepExecutionsContext } from 'contexts/StepExecutions';
|
|||||||
import Popper from './Popper';
|
import Popper from './Popper';
|
||||||
import { processStepWithExecutions } from './data';
|
import { processStepWithExecutions } from './data';
|
||||||
import { ChildrenWrapper, FakeInput, InputLabelWrapper } from './style';
|
import { ChildrenWrapper, FakeInput, InputLabelWrapper } from './style';
|
||||||
|
|
||||||
const PowerInput = (props) => {
|
const PowerInput = (props) => {
|
||||||
const { control } = useFormContext();
|
const { control } = useFormContext();
|
||||||
const {
|
const {
|
||||||
@@ -31,33 +32,41 @@ const PowerInput = (props) => {
|
|||||||
} = props;
|
} = props;
|
||||||
const priorStepsWithExecutions = React.useContext(StepExecutionsContext);
|
const priorStepsWithExecutions = React.useContext(StepExecutionsContext);
|
||||||
const editorRef = React.useRef(null);
|
const editorRef = React.useRef(null);
|
||||||
|
|
||||||
const renderElement = React.useCallback(
|
const renderElement = React.useCallback(
|
||||||
(props) => <Element {...props} />,
|
(props) => <Element {...props} />,
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [editor] = React.useState(() => customizeEditor(createEditor()));
|
const [editor] = React.useState(() => customizeEditor(createEditor()));
|
||||||
|
|
||||||
const [showVariableSuggestions, setShowVariableSuggestions] =
|
const [showVariableSuggestions, setShowVariableSuggestions] =
|
||||||
React.useState(false);
|
React.useState(false);
|
||||||
|
|
||||||
const disappearSuggestionsOnShift = (event) => {
|
const disappearSuggestionsOnShift = (event) => {
|
||||||
if (event.code === 'Tab') {
|
if (event.code === 'Tab') {
|
||||||
setShowVariableSuggestions(false);
|
setShowVariableSuggestions(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const stepsWithVariables = React.useMemo(() => {
|
const stepsWithVariables = React.useMemo(() => {
|
||||||
return processStepWithExecutions(priorStepsWithExecutions);
|
return processStepWithExecutions(priorStepsWithExecutions);
|
||||||
}, [priorStepsWithExecutions]);
|
}, [priorStepsWithExecutions]);
|
||||||
|
|
||||||
const handleBlur = React.useCallback(
|
const handleBlur = React.useCallback(
|
||||||
(value) => {
|
(value) => {
|
||||||
onBlur?.(value);
|
onBlur?.(value);
|
||||||
},
|
},
|
||||||
[onBlur],
|
[onBlur],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleVariableSuggestionClick = React.useCallback(
|
const handleVariableSuggestionClick = React.useCallback(
|
||||||
(variable) => {
|
(variable) => {
|
||||||
insertVariable(editor, variable, stepsWithVariables);
|
insertVariable(editor, variable, stepsWithVariables);
|
||||||
},
|
},
|
||||||
[stepsWithVariables],
|
[stepsWithVariables],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Controller
|
<Controller
|
||||||
rules={{ required }}
|
rules={{ required }}
|
||||||
@@ -127,6 +136,7 @@ const PowerInput = (props) => {
|
|||||||
anchorEl={editorRef.current}
|
anchorEl={editorRef.current}
|
||||||
data={stepsWithVariables}
|
data={stepsWithVariables}
|
||||||
onSuggestionClick={handleVariableSuggestionClick}
|
onSuggestionClick={handleVariableSuggestionClick}
|
||||||
|
className="nowheel"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormHelperText variant="outlined">{description}</FormHelperText>
|
<FormHelperText variant="outlined">{description}</FormHelperText>
|
||||||
|
@@ -7,6 +7,7 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import api from 'helpers/api';
|
import api from 'helpers/api';
|
||||||
|
|
||||||
const variableRegExp = /({.*?})/;
|
const variableRegExp = /({.*?})/;
|
||||||
|
|
||||||
// TODO: extract this function to a separate file
|
// TODO: extract this function to a separate file
|
||||||
function computeArguments(args, getValues) {
|
function computeArguments(args, getValues) {
|
||||||
const initialValue = {};
|
const initialValue = {};
|
||||||
|
9
packages/web/src/hooks/usePrevious.js
Normal file
9
packages/web/src/hooks/usePrevious.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
export const usePrevious = (value) => {
|
||||||
|
const ref = useRef();
|
||||||
|
useEffect(() => {
|
||||||
|
ref.current = value;
|
||||||
|
});
|
||||||
|
return ref.current;
|
||||||
|
};
|
@@ -1,4 +1,6 @@
|
|||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import { Settings } from 'luxon';
|
||||||
|
|
||||||
import ThemeProvider from 'components/ThemeProvider';
|
import ThemeProvider from 'components/ThemeProvider';
|
||||||
import IntlProvider from 'components/IntlProvider';
|
import IntlProvider from 'components/IntlProvider';
|
||||||
import ApolloProvider from 'components/ApolloProvider';
|
import ApolloProvider from 'components/ApolloProvider';
|
||||||
@@ -10,6 +12,9 @@ import Router from 'components/Router';
|
|||||||
import routes from 'routes';
|
import routes from 'routes';
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from './reportWebVitals';
|
||||||
|
|
||||||
|
// Sets the default locale to English for all luxon DateTime instances created afterwards.
|
||||||
|
Settings.defaultLocale = 'en';
|
||||||
|
|
||||||
const container = document.getElementById('root');
|
const container = document.getElementById('root');
|
||||||
const root = createRoot(container);
|
const root = createRoot(container);
|
||||||
|
|
||||||
|
@@ -30,6 +30,7 @@ import AppIcon from 'components/AppIcon';
|
|||||||
import Container from 'components/Container';
|
import Container from 'components/Container';
|
||||||
import PageTitle from 'components/PageTitle';
|
import PageTitle from 'components/PageTitle';
|
||||||
import useApp from 'hooks/useApp';
|
import useApp from 'hooks/useApp';
|
||||||
|
import Can from 'components/Can';
|
||||||
|
|
||||||
const ReconnectConnection = (props) => {
|
const ReconnectConnection = (props) => {
|
||||||
const { application, onClose } = props;
|
const { application, onClose } = props;
|
||||||
@@ -92,7 +93,7 @@ export default function Application() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}, [appKey, appConfig?.data, currentUserAbility]);
|
}, [appKey, appConfig?.data, currentUserAbility, formatMessage]);
|
||||||
|
|
||||||
if (loading) return null;
|
if (loading) return null;
|
||||||
|
|
||||||
@@ -118,6 +119,8 @@ export default function Application() {
|
|||||||
<Route
|
<Route
|
||||||
path={`${URLS.FLOWS}/*`}
|
path={`${URLS.FLOWS}/*`}
|
||||||
element={
|
element={
|
||||||
|
<Can I="create" a="Flow" passThrough>
|
||||||
|
{(allowed) => (
|
||||||
<ConditionalIconButton
|
<ConditionalIconButton
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
@@ -130,18 +133,23 @@ export default function Application() {
|
|||||||
)}
|
)}
|
||||||
fullWidth
|
fullWidth
|
||||||
icon={<AddIcon />}
|
icon={<AddIcon />}
|
||||||
disabled={!currentUserAbility.can('create', 'Flow')}
|
disabled={!allowed}
|
||||||
>
|
>
|
||||||
{formatMessage('app.createFlow')}
|
{formatMessage('app.createFlow')}
|
||||||
</ConditionalIconButton>
|
</ConditionalIconButton>
|
||||||
|
)}
|
||||||
|
</Can>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={`${URLS.CONNECTIONS}/*`}
|
path={`${URLS.CONNECTIONS}/*`}
|
||||||
element={
|
element={
|
||||||
|
<Can I="create" a="Connection" passThrough>
|
||||||
|
{(allowed) => (
|
||||||
<SplitButton
|
<SplitButton
|
||||||
disabled={
|
disabled={
|
||||||
|
!allowed ||
|
||||||
(appConfig?.data &&
|
(appConfig?.data &&
|
||||||
!appConfig?.data?.canConnect &&
|
!appConfig?.data?.canConnect &&
|
||||||
!appConfig?.data?.canCustomConnect) ||
|
!appConfig?.data?.canCustomConnect) ||
|
||||||
@@ -149,6 +157,8 @@ export default function Application() {
|
|||||||
}
|
}
|
||||||
options={connectionOptions}
|
options={connectionOptions}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
</Can>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
@@ -169,17 +179,20 @@ export default function Application() {
|
|||||||
label={formatMessage('app.connections')}
|
label={formatMessage('app.connections')}
|
||||||
to={URLS.APP_CONNECTIONS(appKey)}
|
to={URLS.APP_CONNECTIONS(appKey)}
|
||||||
value={URLS.APP_CONNECTIONS_PATTERN}
|
value={URLS.APP_CONNECTIONS_PATTERN}
|
||||||
disabled={!app.supportsConnections}
|
disabled={
|
||||||
|
!currentUserAbility.can('read', 'Connection') ||
|
||||||
|
!app.supportsConnections
|
||||||
|
}
|
||||||
component={Link}
|
component={Link}
|
||||||
data-test="connections-tab"
|
data-test="connections-tab"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
label={formatMessage('app.flows')}
|
label={formatMessage('app.flows')}
|
||||||
to={URLS.APP_FLOWS(appKey)}
|
to={URLS.APP_FLOWS(appKey)}
|
||||||
value={URLS.APP_FLOWS_PATTERN}
|
value={URLS.APP_FLOWS_PATTERN}
|
||||||
component={Link}
|
component={Link}
|
||||||
data-test="flows-tab"
|
data-test="flows-tab"
|
||||||
|
disabled={!currentUserAbility.can('read', 'Flow')}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -187,14 +200,20 @@ export default function Application() {
|
|||||||
<Routes>
|
<Routes>
|
||||||
<Route
|
<Route
|
||||||
path={`${URLS.FLOWS}/*`}
|
path={`${URLS.FLOWS}/*`}
|
||||||
element={<AppFlows appKey={appKey} />}
|
element={
|
||||||
|
<Can I="read" a="Flow">
|
||||||
|
<AppFlows appKey={appKey} />
|
||||||
|
</Can>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={`${URLS.CONNECTIONS}/*`}
|
path={`${URLS.CONNECTIONS}/*`}
|
||||||
element={<AppConnections appKey={appKey} />}
|
element={
|
||||||
|
<Can I="read" a="Connection">
|
||||||
|
<AppConnections appKey={appKey} />
|
||||||
|
</Can>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="/"
|
path="/"
|
||||||
element={
|
element={
|
||||||
@@ -218,17 +237,24 @@ export default function Application() {
|
|||||||
<Route
|
<Route
|
||||||
path="/connections/add"
|
path="/connections/add"
|
||||||
element={
|
element={
|
||||||
<AddAppConnection onClose={goToApplicationPage} application={app} />
|
<Can I="create" a="Connection">
|
||||||
|
<AddAppConnection
|
||||||
|
onClose={goToApplicationPage}
|
||||||
|
application={app}
|
||||||
|
/>
|
||||||
|
</Can>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="/connections/:connectionId/reconnect"
|
path="/connections/:connectionId/reconnect"
|
||||||
element={
|
element={
|
||||||
|
<Can I="create" a="Connection">
|
||||||
<ReconnectConnection
|
<ReconnectConnection
|
||||||
application={app}
|
application={app}
|
||||||
onClose={goToApplicationPage}
|
onClose={goToApplicationPage}
|
||||||
/>
|
/>
|
||||||
|
</Can>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
@@ -84,11 +84,15 @@ export default function Applications() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{!isLoading && !hasApps && (
|
{!isLoading && !hasApps && (
|
||||||
|
<Can I="create" a="Connection" passThrough>
|
||||||
|
{(allowed) => (
|
||||||
<NoResultFound
|
<NoResultFound
|
||||||
text={formatMessage('apps.noConnections')}
|
text={formatMessage('apps.noConnections')}
|
||||||
to={URLS.NEW_APP_CONNECTION}
|
{...(allowed && { to: URLS.NEW_APP_CONNECTION })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
</Can>
|
||||||
|
)}
|
||||||
|
|
||||||
{!isLoading &&
|
{!isLoading &&
|
||||||
apps?.map((app) => (
|
apps?.map((app) => (
|
||||||
|
@@ -7,13 +7,15 @@ import * as URLS from 'config/urls';
|
|||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import { CREATE_FLOW } from 'graphql/mutations/create-flow';
|
import { CREATE_FLOW } from 'graphql/mutations/create-flow';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
|
|
||||||
export default function CreateFlow() {
|
export default function CreateFlow() {
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const [createFlow] = useMutation(CREATE_FLOW);
|
const [createFlow, { error }] = useMutation(CREATE_FLOW);
|
||||||
const appKey = searchParams.get('appKey');
|
const appKey = searchParams.get('appKey');
|
||||||
const connectionId = searchParams.get('connectionId');
|
const connectionId = searchParams.get('connectionId');
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
async function initiate() {
|
async function initiate() {
|
||||||
const variables = {};
|
const variables = {};
|
||||||
@@ -33,6 +35,11 @@ export default function CreateFlow() {
|
|||||||
}
|
}
|
||||||
initiate();
|
initiate();
|
||||||
}, [createFlow, navigate, appKey, connectionId]);
|
}, [createFlow, navigate, appKey, connectionId]);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@@ -45,7 +52,6 @@ export default function CreateFlow() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CircularProgress size={16} thickness={7.5} />
|
<CircularProgress size={16} thickness={7.5} />
|
||||||
|
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
{formatMessage('createFlow.creating')}
|
{formatMessage('createFlow.creating')}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@@ -17,6 +17,7 @@ import Container from 'components/Container';
|
|||||||
import PageTitle from 'components/PageTitle';
|
import PageTitle from 'components/PageTitle';
|
||||||
import SearchInput from 'components/SearchInput';
|
import SearchInput from 'components/SearchInput';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
import useCurrentUserAbility from 'hooks/useCurrentUserAbility';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import useLazyFlows from 'hooks/useLazyFlows';
|
import useLazyFlows from 'hooks/useLazyFlows';
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ export default function Flows() {
|
|||||||
const page = parseInt(searchParams.get('page') || '', 10) || 1;
|
const page = parseInt(searchParams.get('page') || '', 10) || 1;
|
||||||
const [flowName, setFlowName] = React.useState('');
|
const [flowName, setFlowName] = React.useState('');
|
||||||
const [isLoading, setIsLoading] = React.useState(false);
|
const [isLoading, setIsLoading] = React.useState(false);
|
||||||
|
const currentUserAbility = useCurrentUserAbility();
|
||||||
|
|
||||||
const { data, mutate: fetchFlows } = useLazyFlows(
|
const { data, mutate: fetchFlows } = useLazyFlows(
|
||||||
{ flowName, page },
|
{ flowName, page },
|
||||||
@@ -124,7 +126,9 @@ export default function Flows() {
|
|||||||
{!isLoading && !hasFlows && (
|
{!isLoading && !hasFlows && (
|
||||||
<NoResultFound
|
<NoResultFound
|
||||||
text={formatMessage('flows.noFlows')}
|
text={formatMessage('flows.noFlows')}
|
||||||
to={URLS.CREATE_FLOW}
|
{...(currentUserAbility.can('create', 'Flow') && {
|
||||||
|
to: URLS.CREATE_FLOW,
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!isLoading && pageInfo && pageInfo.totalPages > 1 && (
|
{!isLoading && pageInfo && pageInfo.totalPages > 1 && (
|
||||||
|
@@ -3,7 +3,7 @@ services:
|
|||||||
name: automatisch-main
|
name: automatisch-main
|
||||||
env: docker
|
env: docker
|
||||||
dockerfilePath: ./docker/Dockerfile
|
dockerfilePath: ./docker/Dockerfile
|
||||||
dockerContext: ./docker
|
dockerContext: .
|
||||||
repo: https://github.com/automatisch/automatisch
|
repo: https://github.com/automatisch/automatisch
|
||||||
autoDeploy: false
|
autoDeploy: false
|
||||||
envVars:
|
envVars:
|
||||||
@@ -47,7 +47,7 @@ services:
|
|||||||
name: automatisch-worker
|
name: automatisch-worker
|
||||||
env: docker
|
env: docker
|
||||||
dockerfilePath: ./docker/Dockerfile
|
dockerfilePath: ./docker/Dockerfile
|
||||||
dockerContext: ./docker
|
dockerContext: .
|
||||||
repo: https://github.com/automatisch/automatisch
|
repo: https://github.com/automatisch/automatisch
|
||||||
autoDeploy: false
|
autoDeploy: false
|
||||||
envVars:
|
envVars:
|
||||||
|
460
yarn.lock
460
yarn.lock
@@ -170,52 +170,6 @@
|
|||||||
tslib "^2.3.0"
|
tslib "^2.3.0"
|
||||||
zen-observable-ts "~1.1.0"
|
zen-observable-ts "~1.1.0"
|
||||||
|
|
||||||
"@atproto/api@^0.12.13":
|
|
||||||
version "0.12.13"
|
|
||||||
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.12.13.tgz#269d6c57ea894e23f20b28bd3cbfed944bd28528"
|
|
||||||
integrity sha512-pRSID6w8AUiZJoCxgctMPRTSGVFHq7wphAnxEbRLBP3OQ1g+BRZUcqFw+e+17Pd3wrc8VImjiD4HCWtCJvCx3w==
|
|
||||||
dependencies:
|
|
||||||
"@atproto/common-web" "^0.3.0"
|
|
||||||
"@atproto/lexicon" "^0.4.0"
|
|
||||||
"@atproto/syntax" "^0.3.0"
|
|
||||||
"@atproto/xrpc" "^0.5.0"
|
|
||||||
multiformats "^9.9.0"
|
|
||||||
tlds "^1.234.0"
|
|
||||||
|
|
||||||
"@atproto/common-web@^0.3.0":
|
|
||||||
version "0.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@atproto/common-web/-/common-web-0.3.0.tgz#36da8c2c31d8cf8a140c3c8f03223319bf4430bb"
|
|
||||||
integrity sha512-67VnV6JJyX+ZWyjV7xFQMypAgDmjVaR9ZCuU/QW+mqlqI7fex2uL4Fv+7/jHadgzhuJHVd6OHOvNn0wR5WZYtA==
|
|
||||||
dependencies:
|
|
||||||
graphemer "^1.4.0"
|
|
||||||
multiformats "^9.9.0"
|
|
||||||
uint8arrays "3.0.0"
|
|
||||||
zod "^3.21.4"
|
|
||||||
|
|
||||||
"@atproto/lexicon@^0.4.0":
|
|
||||||
version "0.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@atproto/lexicon/-/lexicon-0.4.0.tgz#63e8829945d80c25524882caa8ed27b1151cc576"
|
|
||||||
integrity sha512-RvCBKdSI4M8qWm5uTNz1z3R2yIvIhmOsMuleOj8YR6BwRD+QbtUBy3l+xQ7iXf4M5fdfJFxaUNa6Ty0iRwdKqQ==
|
|
||||||
dependencies:
|
|
||||||
"@atproto/common-web" "^0.3.0"
|
|
||||||
"@atproto/syntax" "^0.3.0"
|
|
||||||
iso-datestring-validator "^2.2.2"
|
|
||||||
multiformats "^9.9.0"
|
|
||||||
zod "^3.21.4"
|
|
||||||
|
|
||||||
"@atproto/syntax@^0.3.0":
|
|
||||||
version "0.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.3.0.tgz#fafa2dbea9add37253005cb663e7373e05e618b3"
|
|
||||||
integrity sha512-Weq0ZBxffGHDXHl9U7BQc2BFJi/e23AL+k+i5+D9hUq/bzT4yjGsrCejkjq0xt82xXDjmhhvQSZ0LqxyZ5woxA==
|
|
||||||
|
|
||||||
"@atproto/xrpc@^0.5.0":
|
|
||||||
version "0.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.5.0.tgz#dacbfd8f7b13f0ab5bd56f8fdd4b460e132a6032"
|
|
||||||
integrity sha512-swu+wyOLvYW4l3n+VAuJbHcPcES+tin2Lsrp8Bw5aIXIICiuFn1YMFlwK9JwVUzTH21Py1s1nHEjr4CJeElJog==
|
|
||||||
dependencies:
|
|
||||||
"@atproto/lexicon" "^0.4.0"
|
|
||||||
zod "^3.21.4"
|
|
||||||
|
|
||||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.8.3":
|
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.8.3":
|
||||||
version "7.16.7"
|
version "7.16.7"
|
||||||
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz"
|
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz"
|
||||||
@@ -1501,6 +1455,18 @@
|
|||||||
enabled "2.0.x"
|
enabled "2.0.x"
|
||||||
kuler "^2.0.0"
|
kuler "^2.0.0"
|
||||||
|
|
||||||
|
"@dagrejs/dagre@^1.1.2":
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@dagrejs/dagre/-/dagre-1.1.2.tgz#5ec339979447091f48d2144deed8c70dfadae374"
|
||||||
|
integrity sha512-F09dphqvHsbe/6C2t2unbmpr5q41BNPEfJCdn8Z7aEBpVSy/zFQ/b4SWsweQjWNsYMDvE2ffNUN8X0CeFsEGNw==
|
||||||
|
dependencies:
|
||||||
|
"@dagrejs/graphlib" "2.2.2"
|
||||||
|
|
||||||
|
"@dagrejs/graphlib@2.2.2":
|
||||||
|
version "2.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@dagrejs/graphlib/-/graphlib-2.2.2.tgz#74154d5cb880a23b4fae71034a09b4b5aef06feb"
|
||||||
|
integrity sha512-CbyGpCDKsiTg/wuk79S7Muoj8mghDGAESWGxcSyhHX5jD35vYMBZochYVFzlHxynpE9unpu6O+4ZuhrLxASsOg==
|
||||||
|
|
||||||
"@docsearch/css@3.2.1", "@docsearch/css@^3.2.1":
|
"@docsearch/css@3.2.1", "@docsearch/css@^3.2.1":
|
||||||
version "3.2.1"
|
version "3.2.1"
|
||||||
resolved "https://registry.npmjs.org/@docsearch/css/-/css-3.2.1.tgz"
|
resolved "https://registry.npmjs.org/@docsearch/css/-/css-3.2.1.tgz"
|
||||||
@@ -3379,6 +3345,72 @@
|
|||||||
resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz"
|
resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz"
|
||||||
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
|
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
|
||||||
|
|
||||||
|
"@reactflow/background@11.3.12":
|
||||||
|
version "11.3.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@reactflow/background/-/background-11.3.12.tgz#9c9491cce4659bae13074fcdb48ac25664879d3f"
|
||||||
|
integrity sha512-jBuWVb43JQy5h4WOS7G0PU8voGTEJNA+qDmx8/jyBtrjbasTesLNfQvboTGjnQYYiJco6mw5vrtQItAJDNoIqw==
|
||||||
|
dependencies:
|
||||||
|
"@reactflow/core" "11.11.2"
|
||||||
|
classcat "^5.0.3"
|
||||||
|
zustand "^4.4.1"
|
||||||
|
|
||||||
|
"@reactflow/controls@11.2.12":
|
||||||
|
version "11.2.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@reactflow/controls/-/controls-11.2.12.tgz#85e2aa5de17e2af28a5ecf6a75bb9c828a20640b"
|
||||||
|
integrity sha512-L9F3+avFRShoprdT+5oOijm5gVsz2rqWCXBzOAgD923L1XFGIspdiHLLf8IlPGsT+mfl0GxbptZhaEeEzl1e3g==
|
||||||
|
dependencies:
|
||||||
|
"@reactflow/core" "11.11.2"
|
||||||
|
classcat "^5.0.3"
|
||||||
|
zustand "^4.4.1"
|
||||||
|
|
||||||
|
"@reactflow/core@11.11.2":
|
||||||
|
version "11.11.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@reactflow/core/-/core-11.11.2.tgz#c62f78297bda9d2e86a12228617ec3f91fbd4b22"
|
||||||
|
integrity sha512-+GfgyskweL1PsgRSguUwfrT2eDotlFgaKfDLm7x0brdzzPJY2qbCzVetaxedaiJmIli3817iYbILvE9qLKwbRA==
|
||||||
|
dependencies:
|
||||||
|
"@types/d3" "^7.4.0"
|
||||||
|
"@types/d3-drag" "^3.0.1"
|
||||||
|
"@types/d3-selection" "^3.0.3"
|
||||||
|
"@types/d3-zoom" "^3.0.1"
|
||||||
|
classcat "^5.0.3"
|
||||||
|
d3-drag "^3.0.0"
|
||||||
|
d3-selection "^3.0.0"
|
||||||
|
d3-zoom "^3.0.0"
|
||||||
|
zustand "^4.4.1"
|
||||||
|
|
||||||
|
"@reactflow/minimap@11.7.12":
|
||||||
|
version "11.7.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@reactflow/minimap/-/minimap-11.7.12.tgz#6b2fc671ee17e37ccd3bc038ae8d2121d0ce6291"
|
||||||
|
integrity sha512-SRDU77c2PCF54PV/MQfkz7VOW46q7V1LZNOQlXAp7dkNyAOI6R+tb9qBUtUJOvILB+TCN6pRfD9fQ+2T99bW3Q==
|
||||||
|
dependencies:
|
||||||
|
"@reactflow/core" "11.11.2"
|
||||||
|
"@types/d3-selection" "^3.0.3"
|
||||||
|
"@types/d3-zoom" "^3.0.1"
|
||||||
|
classcat "^5.0.3"
|
||||||
|
d3-selection "^3.0.0"
|
||||||
|
d3-zoom "^3.0.0"
|
||||||
|
zustand "^4.4.1"
|
||||||
|
|
||||||
|
"@reactflow/node-resizer@2.2.12":
|
||||||
|
version "2.2.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@reactflow/node-resizer/-/node-resizer-2.2.12.tgz#df82a7dfba883afea6a01a9c8210008a1ddba01f"
|
||||||
|
integrity sha512-6LHJGuI1zHyRrZHw5gGlVLIWnvVxid9WIqw8FMFSg+oF2DuS3pAPwSoZwypy7W22/gDNl9eD1Dcl/OtFtDFQ+w==
|
||||||
|
dependencies:
|
||||||
|
"@reactflow/core" "11.11.2"
|
||||||
|
classcat "^5.0.4"
|
||||||
|
d3-drag "^3.0.0"
|
||||||
|
d3-selection "^3.0.0"
|
||||||
|
zustand "^4.4.1"
|
||||||
|
|
||||||
|
"@reactflow/node-toolbar@1.3.12":
|
||||||
|
version "1.3.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@reactflow/node-toolbar/-/node-toolbar-1.3.12.tgz#89e7aa9d34b6213bb5e64c344d4e2e3cb7af3163"
|
||||||
|
integrity sha512-4kJRvNna/E3y2MZW9/80wTKwkhw4pLJiz3D5eQrD13XcmojSb1rArO9CiwyrI+rMvs5gn6NlCFB4iN1F+Q+lxQ==
|
||||||
|
dependencies:
|
||||||
|
"@reactflow/core" "11.11.2"
|
||||||
|
classcat "^5.0.3"
|
||||||
|
zustand "^4.4.1"
|
||||||
|
|
||||||
"@rollup/plugin-babel@^5.2.0":
|
"@rollup/plugin-babel@^5.2.0":
|
||||||
version "5.3.0"
|
version "5.3.0"
|
||||||
resolved "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz"
|
resolved "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz"
|
||||||
@@ -3869,6 +3901,216 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/d3-array@*":
|
||||||
|
version "3.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.2.1.tgz#1f6658e3d2006c4fceac53fde464166859f8b8c5"
|
||||||
|
integrity sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==
|
||||||
|
|
||||||
|
"@types/d3-axis@*":
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-3.0.6.tgz#e760e5765b8188b1defa32bc8bb6062f81e4c795"
|
||||||
|
integrity sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==
|
||||||
|
dependencies:
|
||||||
|
"@types/d3-selection" "*"
|
||||||
|
|
||||||
|
"@types/d3-brush@*":
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-3.0.6.tgz#c2f4362b045d472e1b186cdbec329ba52bdaee6c"
|
||||||
|
integrity sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==
|
||||||
|
dependencies:
|
||||||
|
"@types/d3-selection" "*"
|
||||||
|
|
||||||
|
"@types/d3-chord@*":
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-3.0.6.tgz#1706ca40cf7ea59a0add8f4456efff8f8775793d"
|
||||||
|
integrity sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==
|
||||||
|
|
||||||
|
"@types/d3-color@*":
|
||||||
|
version "3.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2"
|
||||||
|
integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==
|
||||||
|
|
||||||
|
"@types/d3-contour@*":
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-contour/-/d3-contour-3.0.6.tgz#9ada3fa9c4d00e3a5093fed0356c7ab929604231"
|
||||||
|
integrity sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==
|
||||||
|
dependencies:
|
||||||
|
"@types/d3-array" "*"
|
||||||
|
"@types/geojson" "*"
|
||||||
|
|
||||||
|
"@types/d3-delaunay@*":
|
||||||
|
version "6.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz#185c1a80cc807fdda2a3fe960f7c11c4a27952e1"
|
||||||
|
integrity sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==
|
||||||
|
|
||||||
|
"@types/d3-dispatch@*":
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz#096efdf55eb97480e3f5621ff9a8da552f0961e7"
|
||||||
|
integrity sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==
|
||||||
|
|
||||||
|
"@types/d3-drag@*", "@types/d3-drag@^3.0.1":
|
||||||
|
version "3.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-3.0.7.tgz#b13aba8b2442b4068c9a9e6d1d82f8bcea77fc02"
|
||||||
|
integrity sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/d3-selection" "*"
|
||||||
|
|
||||||
|
"@types/d3-dsv@*":
|
||||||
|
version "3.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-3.0.7.tgz#0a351f996dc99b37f4fa58b492c2d1c04e3dac17"
|
||||||
|
integrity sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==
|
||||||
|
|
||||||
|
"@types/d3-ease@*":
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-3.0.2.tgz#e28db1bfbfa617076f7770dd1d9a48eaa3b6c51b"
|
||||||
|
integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==
|
||||||
|
|
||||||
|
"@types/d3-fetch@*":
|
||||||
|
version "3.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-fetch/-/d3-fetch-3.0.7.tgz#c04a2b4f23181aa376f30af0283dbc7b3b569980"
|
||||||
|
integrity sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==
|
||||||
|
dependencies:
|
||||||
|
"@types/d3-dsv" "*"
|
||||||
|
|
||||||
|
"@types/d3-force@*":
|
||||||
|
version "3.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-3.0.9.tgz#dd96ccefba4386fe4ff36b8e4ee4e120c21fcf29"
|
||||||
|
integrity sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==
|
||||||
|
|
||||||
|
"@types/d3-format@*":
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-3.0.4.tgz#b1e4465644ddb3fdf3a263febb240a6cd616de90"
|
||||||
|
integrity sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==
|
||||||
|
|
||||||
|
"@types/d3-geo@*":
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-3.1.0.tgz#b9e56a079449174f0a2c8684a9a4df3f60522440"
|
||||||
|
integrity sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/geojson" "*"
|
||||||
|
|
||||||
|
"@types/d3-hierarchy@*":
|
||||||
|
version "3.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz#6023fb3b2d463229f2d680f9ac4b47466f71f17b"
|
||||||
|
integrity sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==
|
||||||
|
|
||||||
|
"@types/d3-interpolate@*":
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c"
|
||||||
|
integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==
|
||||||
|
dependencies:
|
||||||
|
"@types/d3-color" "*"
|
||||||
|
|
||||||
|
"@types/d3-path@*":
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.1.0.tgz#2b907adce762a78e98828f0b438eaca339ae410a"
|
||||||
|
integrity sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==
|
||||||
|
|
||||||
|
"@types/d3-polygon@*":
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-3.0.2.tgz#dfae54a6d35d19e76ac9565bcb32a8e54693189c"
|
||||||
|
integrity sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==
|
||||||
|
|
||||||
|
"@types/d3-quadtree@*":
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz#d4740b0fe35b1c58b66e1488f4e7ed02952f570f"
|
||||||
|
integrity sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==
|
||||||
|
|
||||||
|
"@types/d3-random@*":
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-3.0.3.tgz#ed995c71ecb15e0cd31e22d9d5d23942e3300cfb"
|
||||||
|
integrity sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==
|
||||||
|
|
||||||
|
"@types/d3-scale-chromatic@*":
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz#fc0db9c10e789c351f4c42d96f31f2e4df8f5644"
|
||||||
|
integrity sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==
|
||||||
|
|
||||||
|
"@types/d3-scale@*":
|
||||||
|
version "4.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.8.tgz#d409b5f9dcf63074464bf8ddfb8ee5a1f95945bb"
|
||||||
|
integrity sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/d3-time" "*"
|
||||||
|
|
||||||
|
"@types/d3-selection@*", "@types/d3-selection@^3.0.3":
|
||||||
|
version "3.0.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.10.tgz#98cdcf986d0986de6912b5892e7c015a95ca27fe"
|
||||||
|
integrity sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==
|
||||||
|
|
||||||
|
"@types/d3-shape@*":
|
||||||
|
version "3.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.6.tgz#65d40d5a548f0a023821773e39012805e6e31a72"
|
||||||
|
integrity sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==
|
||||||
|
dependencies:
|
||||||
|
"@types/d3-path" "*"
|
||||||
|
|
||||||
|
"@types/d3-time-format@*":
|
||||||
|
version "4.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-4.0.3.tgz#d6bc1e6b6a7db69cccfbbdd4c34b70632d9e9db2"
|
||||||
|
integrity sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==
|
||||||
|
|
||||||
|
"@types/d3-time@*":
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.3.tgz#3c186bbd9d12b9d84253b6be6487ca56b54f88be"
|
||||||
|
integrity sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==
|
||||||
|
|
||||||
|
"@types/d3-timer@*":
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70"
|
||||||
|
integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==
|
||||||
|
|
||||||
|
"@types/d3-transition@*":
|
||||||
|
version "3.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.8.tgz#677707f5eed5b24c66a1918cde05963021351a8f"
|
||||||
|
integrity sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/d3-selection" "*"
|
||||||
|
|
||||||
|
"@types/d3-zoom@*", "@types/d3-zoom@^3.0.1":
|
||||||
|
version "3.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz#dccb32d1c56b1e1c6e0f1180d994896f038bc40b"
|
||||||
|
integrity sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==
|
||||||
|
dependencies:
|
||||||
|
"@types/d3-interpolate" "*"
|
||||||
|
"@types/d3-selection" "*"
|
||||||
|
|
||||||
|
"@types/d3@^7.4.0":
|
||||||
|
version "7.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/d3/-/d3-7.4.3.tgz#d4550a85d08f4978faf0a4c36b848c61eaac07e2"
|
||||||
|
integrity sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==
|
||||||
|
dependencies:
|
||||||
|
"@types/d3-array" "*"
|
||||||
|
"@types/d3-axis" "*"
|
||||||
|
"@types/d3-brush" "*"
|
||||||
|
"@types/d3-chord" "*"
|
||||||
|
"@types/d3-color" "*"
|
||||||
|
"@types/d3-contour" "*"
|
||||||
|
"@types/d3-delaunay" "*"
|
||||||
|
"@types/d3-dispatch" "*"
|
||||||
|
"@types/d3-drag" "*"
|
||||||
|
"@types/d3-dsv" "*"
|
||||||
|
"@types/d3-ease" "*"
|
||||||
|
"@types/d3-fetch" "*"
|
||||||
|
"@types/d3-force" "*"
|
||||||
|
"@types/d3-format" "*"
|
||||||
|
"@types/d3-geo" "*"
|
||||||
|
"@types/d3-hierarchy" "*"
|
||||||
|
"@types/d3-interpolate" "*"
|
||||||
|
"@types/d3-path" "*"
|
||||||
|
"@types/d3-polygon" "*"
|
||||||
|
"@types/d3-quadtree" "*"
|
||||||
|
"@types/d3-random" "*"
|
||||||
|
"@types/d3-scale" "*"
|
||||||
|
"@types/d3-scale-chromatic" "*"
|
||||||
|
"@types/d3-selection" "*"
|
||||||
|
"@types/d3-shape" "*"
|
||||||
|
"@types/d3-time" "*"
|
||||||
|
"@types/d3-time-format" "*"
|
||||||
|
"@types/d3-timer" "*"
|
||||||
|
"@types/d3-transition" "*"
|
||||||
|
"@types/d3-zoom" "*"
|
||||||
|
|
||||||
"@types/debug@^4.1.7":
|
"@types/debug@^4.1.7":
|
||||||
version "4.1.8"
|
version "4.1.8"
|
||||||
resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz"
|
resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz"
|
||||||
@@ -3959,6 +4201,11 @@
|
|||||||
"@types/qs" "*"
|
"@types/qs" "*"
|
||||||
"@types/serve-static" "*"
|
"@types/serve-static" "*"
|
||||||
|
|
||||||
|
"@types/geojson@*":
|
||||||
|
version "7946.0.14"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.14.tgz#319b63ad6df705ee2a65a73ef042c8271e696613"
|
||||||
|
integrity sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==
|
||||||
|
|
||||||
"@types/graceful-fs@^4.1.2":
|
"@types/graceful-fs@^4.1.2":
|
||||||
version "4.1.5"
|
version "4.1.5"
|
||||||
resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz"
|
resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz"
|
||||||
@@ -6090,6 +6337,11 @@ cjs-module-lexer@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz"
|
resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz"
|
||||||
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
|
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
|
||||||
|
|
||||||
|
classcat@^5.0.3, classcat@^5.0.4:
|
||||||
|
version "5.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/classcat/-/classcat-5.0.5.tgz#8c209f359a93ac302404a10161b501eba9c09c77"
|
||||||
|
integrity sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==
|
||||||
|
|
||||||
clean-css@^5.2.2:
|
clean-css@^5.2.2:
|
||||||
version "5.2.2"
|
version "5.2.2"
|
||||||
resolved "https://registry.npmjs.org/clean-css/-/clean-css-5.2.2.tgz"
|
resolved "https://registry.npmjs.org/clean-css/-/clean-css-5.2.2.tgz"
|
||||||
@@ -6875,6 +7127,68 @@ csstype@^3.1.1:
|
|||||||
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz"
|
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz"
|
||||||
integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
|
integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
|
||||||
|
|
||||||
|
"d3-color@1 - 3":
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2"
|
||||||
|
integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
|
||||||
|
|
||||||
|
"d3-dispatch@1 - 3":
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
|
||||||
|
integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
|
||||||
|
|
||||||
|
"d3-drag@2 - 3", d3-drag@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
|
||||||
|
integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
|
||||||
|
dependencies:
|
||||||
|
d3-dispatch "1 - 3"
|
||||||
|
d3-selection "3"
|
||||||
|
|
||||||
|
"d3-ease@1 - 3":
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
|
||||||
|
integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
|
||||||
|
|
||||||
|
"d3-interpolate@1 - 3":
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
|
||||||
|
integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
|
||||||
|
dependencies:
|
||||||
|
d3-color "1 - 3"
|
||||||
|
|
||||||
|
"d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
|
||||||
|
integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
|
||||||
|
|
||||||
|
"d3-timer@1 - 3":
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
|
||||||
|
integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
|
||||||
|
|
||||||
|
"d3-transition@2 - 3":
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f"
|
||||||
|
integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==
|
||||||
|
dependencies:
|
||||||
|
d3-color "1 - 3"
|
||||||
|
d3-dispatch "1 - 3"
|
||||||
|
d3-ease "1 - 3"
|
||||||
|
d3-interpolate "1 - 3"
|
||||||
|
d3-timer "1 - 3"
|
||||||
|
|
||||||
|
d3-zoom@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3"
|
||||||
|
integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
|
||||||
|
dependencies:
|
||||||
|
d3-dispatch "1 - 3"
|
||||||
|
d3-drag "2 - 3"
|
||||||
|
d3-interpolate "1 - 3"
|
||||||
|
d3-selection "2 - 3"
|
||||||
|
d3-transition "2 - 3"
|
||||||
|
|
||||||
damerau-levenshtein@^1.0.7:
|
damerau-levenshtein@^1.0.7:
|
||||||
version "1.0.8"
|
version "1.0.8"
|
||||||
resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz"
|
resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz"
|
||||||
@@ -8985,11 +9299,6 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6
|
|||||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz"
|
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz"
|
||||||
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
|
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
|
||||||
|
|
||||||
graphemer@^1.4.0:
|
|
||||||
version "1.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
|
|
||||||
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
|
|
||||||
|
|
||||||
graphql-executor@0.0.18:
|
graphql-executor@0.0.18:
|
||||||
version "0.0.18"
|
version "0.0.18"
|
||||||
resolved "https://registry.npmjs.org/graphql-executor/-/graphql-executor-0.0.18.tgz"
|
resolved "https://registry.npmjs.org/graphql-executor/-/graphql-executor-0.0.18.tgz"
|
||||||
@@ -9927,11 +10236,6 @@ isexe@^3.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d"
|
resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d"
|
||||||
integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==
|
integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==
|
||||||
|
|
||||||
iso-datestring-validator@^2.2.2:
|
|
||||||
version "2.2.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz#2daa80d2900b7a954f9f731d42f96ee0c19a6895"
|
|
||||||
integrity sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==
|
|
||||||
|
|
||||||
isobject@^3.0.1:
|
isobject@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz"
|
resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz"
|
||||||
@@ -11648,11 +11952,6 @@ multicast-dns@^6.0.1:
|
|||||||
dns-packet "^1.3.1"
|
dns-packet "^1.3.1"
|
||||||
thunky "^1.0.2"
|
thunky "^1.0.2"
|
||||||
|
|
||||||
multiformats@^9.4.2, multiformats@^9.9.0:
|
|
||||||
version "9.9.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37"
|
|
||||||
integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==
|
|
||||||
|
|
||||||
multimatch@^5.0.0:
|
multimatch@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz"
|
resolved "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz"
|
||||||
@@ -13870,6 +14169,18 @@ react@^18.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
|
|
||||||
|
reactflow@^11.11.2:
|
||||||
|
version "11.11.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/reactflow/-/reactflow-11.11.2.tgz#4968866a9372e6004ad1e424a2141996f0ba769a"
|
||||||
|
integrity sha512-o1fT3stSdhzW+SedCGNSmEvZvULZygZIMLyW67NcWNZrgwx1wuJfzLg5fuQ0Nzf389wItumZX/zP3zdaPX7lEw==
|
||||||
|
dependencies:
|
||||||
|
"@reactflow/background" "11.3.12"
|
||||||
|
"@reactflow/controls" "11.2.12"
|
||||||
|
"@reactflow/core" "11.11.2"
|
||||||
|
"@reactflow/minimap" "11.7.12"
|
||||||
|
"@reactflow/node-resizer" "2.2.12"
|
||||||
|
"@reactflow/node-toolbar" "1.3.12"
|
||||||
|
|
||||||
read-cmd-shim@^2.0.0:
|
read-cmd-shim@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz"
|
||||||
@@ -15635,11 +15946,6 @@ tinyspy@^2.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.0.tgz#9dc04b072746520b432f77ea2c2d17933de5d6ce"
|
resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.0.tgz#9dc04b072746520b432f77ea2c2d17933de5d6ce"
|
||||||
integrity sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==
|
integrity sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==
|
||||||
|
|
||||||
tlds@^1.234.0:
|
|
||||||
version "1.252.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.252.0.tgz#71d9617f4ef4cc7347843bee72428e71b8b0f419"
|
|
||||||
integrity sha512-GA16+8HXvqtfEnw/DTcwB0UU354QE1n3+wh08oFjr6Znl7ZLAeUgYzCcK+/CCrOyE0vnHR8/pu3XXG3vDijXpQ==
|
|
||||||
|
|
||||||
tmp@^0.0.33:
|
tmp@^0.0.33:
|
||||||
version "0.0.33"
|
version "0.0.33"
|
||||||
resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz"
|
resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz"
|
||||||
@@ -15889,13 +16195,6 @@ uid-number@0.0.6:
|
|||||||
resolved "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz"
|
resolved "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz"
|
||||||
integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=
|
integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=
|
||||||
|
|
||||||
uint8arrays@3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.0.0.tgz#260869efb8422418b6f04e3fac73a3908175c63b"
|
|
||||||
integrity sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==
|
|
||||||
dependencies:
|
|
||||||
multiformats "^9.4.2"
|
|
||||||
|
|
||||||
umask@^1.1.0:
|
umask@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz"
|
resolved "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz"
|
||||||
@@ -16050,6 +16349,11 @@ url-parse-lax@^3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
prepend-http "^2.0.0"
|
prepend-http "^2.0.0"
|
||||||
|
|
||||||
|
use-sync-external-store@1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
||||||
|
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
||||||
|
|
||||||
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
|
||||||
@@ -17022,7 +17326,9 @@ zen-observable@0.8.15:
|
|||||||
resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz"
|
resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz"
|
||||||
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==
|
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==
|
||||||
|
|
||||||
zod@^3.21.4:
|
zustand@^4.4.1:
|
||||||
version "3.23.8"
|
version "4.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
|
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.2.tgz#fddbe7cac1e71d45413b3682cdb47b48034c3848"
|
||||||
integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
|
integrity sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==
|
||||||
|
dependencies:
|
||||||
|
use-sync-external-store "1.2.0"
|
||||||
|
Reference in New Issue
Block a user