diff --git a/packages/frontend-2/components/automate/automation/visual-editor/VisualEditor.vue b/packages/frontend-2/components/automate/automation/visual-editor/VisualEditor.vue
new file mode 100644
index 000000000..45b2511d1
--- /dev/null
+++ b/packages/frontend-2/components/automate/automation/visual-editor/VisualEditor.vue
@@ -0,0 +1,10 @@
+
+
+
diff --git a/packages/frontend-2/components/project/page/automations/EmptyState.vue b/packages/frontend-2/components/project/page/automations/EmptyState.vue
index c4a02d5d2..063d0efee 100644
--- a/packages/frontend-2/components/project/page/automations/EmptyState.vue
+++ b/packages/frontend-2/components/project/page/automations/EmptyState.vue
@@ -85,6 +85,7 @@ defineEmits<{
const props = defineProps<{
functions?: ProjectPageAutomationsEmptyState_QueryFragment
isAutomateEnabled: boolean
+ isVisualAutomateEnabled: boolean
disabledCreateBecauseOf?: string
}>()
diff --git a/packages/frontend-2/components/project/page/automations/Tab.vue b/packages/frontend-2/components/project/page/automations/Tab.vue
index 6ca8e536b..731277923 100644
--- a/packages/frontend-2/components/project/page/automations/Tab.vue
+++ b/packages/frontend-2/components/project/page/automations/Tab.vue
@@ -55,9 +55,11 @@ import type { CreateAutomationSelectableFunction } from '~/lib/automate/helpers/
import { usePaginatedQuery } from '~/lib/common/composables/graphql'
const route = useRoute()
+const router = useRouter()
const projectId = computed(() => route.params.id as string)
const search = ref('')
const isAutomateEnabled = useIsAutomateModuleEnabled()
+const isVisualAutomateEnabled = useIsVisualAutomateModuleEnabled()
const pageFetchPolicy = usePageQueryStandardFetchPolicy()
// Base tab query (no pagination)
@@ -121,7 +123,11 @@ const allowNewCreation = computed(() => {
})
const onNewAutomation = (fn?: CreateAutomationSelectableFunction) => {
- newAutomationTargetFn.value = fn
- showNewAutomationDialog.value = true
+ if (isVisualAutomateEnabled.value) {
+ router.push(`${route.path}/new`)
+ } else {
+ newAutomationTargetFn.value = fn
+ showNewAutomationDialog.value = true
+ }
}
diff --git a/packages/frontend-2/composables/globals.ts b/packages/frontend-2/composables/globals.ts
index d51c2b28b..3833631f3 100644
--- a/packages/frontend-2/composables/globals.ts
+++ b/packages/frontend-2/composables/globals.ts
@@ -6,10 +6,16 @@ export const useIsAutomateModuleEnabled = () => {
const {
public: { FF_AUTOMATE_MODULE_ENABLED }
} = useRuntimeConfig()
-
return ref(FF_AUTOMATE_MODULE_ENABLED)
}
+export const useIsVisualAutomateModuleEnabled = () => {
+ const {
+ public: { FF_VISUAL_AUTOMATE_MODULE_ENABLED }
+ } = useRuntimeConfig()
+ return ref(FF_VISUAL_AUTOMATE_MODULE_ENABLED)
+}
+
export const useIsGendoModuleEnabled = () => {
const {
public: { FF_GENDOAI_MODULE_ENABLED }
diff --git a/packages/frontend-2/lib/common/generated/gql/graphql.ts b/packages/frontend-2/lib/common/generated/gql/graphql.ts
index 2678d75c0..9a2249cc5 100644
--- a/packages/frontend-2/lib/common/generated/gql/graphql.ts
+++ b/packages/frontend-2/lib/common/generated/gql/graphql.ts
@@ -68,6 +68,7 @@ export type AdminQueries = {
projectList: ProjectCollection;
serverStatistics: ServerStatistics;
userList: AdminUserList;
+ workspaceList: WorkspaceCollection;
};
@@ -94,6 +95,13 @@ export type AdminQueriesUserListArgs = {
role?: InputMaybe;
};
+
+export type AdminQueriesWorkspaceListArgs = {
+ cursor?: InputMaybe;
+ limit?: Scalars['Int']['input'];
+ query?: InputMaybe;
+};
+
export type AdminUserList = {
__typename?: 'AdminUserList';
cursor?: Maybe;
@@ -1305,6 +1313,7 @@ export type Mutation = {
webhookDelete: Scalars['String']['output'];
/** Updates an existing webhook */
webhookUpdate: Scalars['String']['output'];
+ workspaceMutations: WorkspaceMutations;
};
@@ -1686,6 +1695,22 @@ export type PendingStreamCollaborator = {
user?: Maybe;
};
+export type PendingWorkspaceCollaborator = {
+ __typename?: 'PendingWorkspaceCollaborator';
+ id: Scalars['ID']['output'];
+ inviteId: Scalars['String']['output'];
+ invitedBy: LimitedUser;
+ role: Scalars['String']['output'];
+ /** E-mail address or name of the invited user */
+ title: Scalars['String']['output'];
+ /** Only available if the active user is the pending workspace collaborator */
+ token?: Maybe;
+ /** Set only if user is registered */
+ user?: Maybe;
+ workspaceId: Scalars['String']['output'];
+ workspaceName: Scalars['String']['output'];
+};
+
export type Project = {
__typename?: 'Project';
allowPublicComments: Scalars['Boolean']['output'];
@@ -1737,6 +1762,7 @@ export type Project = {
viewerResources: Array;
visibility: ProjectVisibility;
webhooks: WebhookCollection;
+ workspace?: Maybe;
};
@@ -2383,6 +2409,7 @@ export type Query = {
* The query looks for matches in name & email
*/
userSearch: UserSearchResultCollection;
+ workspace: Workspace;
};
@@ -2508,6 +2535,11 @@ export type QueryUserSearchArgs = {
query: Scalars['String']['input'];
};
+
+export type QueryWorkspaceArgs = {
+ id: Scalars['String']['input'];
+};
+
/** Deprecated: Used by old stream-based mutations */
export type ReplyCreateInput = {
/** IDs of uploaded blobs that should be attached to this reply */
@@ -2610,6 +2642,7 @@ export type ServerInfo = {
serverRoles: Array;
termsOfService?: Maybe;
version?: Maybe;
+ workspaces: ServerWorkspacesInfo;
};
export type ServerInfoUpdateInput = {
@@ -2678,6 +2711,15 @@ export type ServerStats = {
userHistory?: Maybe>>;
};
+export type ServerWorkspacesInfo = {
+ __typename?: 'ServerWorkspacesInfo';
+ /**
+ * This is a backend control variable for the workspaces feature set.
+ * Since workspaces need a backend logic to be enabled, this is not enough as a feature flag.
+ */
+ workspacesEnabled: Scalars['Boolean']['output'];
+};
+
export type SmartTextEditorValue = {
__typename?: 'SmartTextEditorValue';
/** File attachments, if any */
@@ -3298,6 +3340,8 @@ export type User = {
* Note: Only count resolution is currently implemented
*/
versions: CountOnlyCollection;
+ /** Get the workspaces for the user */
+ workspaces: WorkspaceCollection;
};
@@ -3385,6 +3429,17 @@ export type UserVersionsArgs = {
limit?: Scalars['Int']['input'];
};
+
+/**
+ * Full user type, should only be used in the context of admin operations or
+ * when a user is reading/writing info about himself
+ */
+export type UserWorkspacesArgs = {
+ cursor?: InputMaybe;
+ filter?: InputMaybe;
+ limit?: Scalars['Int']['input'];
+};
+
export type UserAutomateInfo = {
__typename?: 'UserAutomateInfo';
availableGithubOrgs: Array;
@@ -3435,6 +3490,10 @@ export type UserUpdateInput = {
name?: InputMaybe;
};
+export type UserWorkspacesFilter = {
+ search?: InputMaybe;
+};
+
export type Version = {
__typename?: 'Version';
authorUser?: Maybe;
@@ -3647,6 +3706,153 @@ export type WebhookUpdateInput = {
url?: InputMaybe;
};
+export type Workspace = {
+ __typename?: 'Workspace';
+ createdAt: Scalars['DateTime']['output'];
+ description?: Maybe;
+ id: Scalars['ID']['output'];
+ invitedTeam?: Maybe>;
+ name: Scalars['String']['output'];
+ projects: ProjectCollection;
+ /** Active user's role for this workspace. `null` if request is not authenticated, or the workspace is not explicitly shared with you. */
+ role?: Maybe;
+ team: Array;
+ updatedAt: Scalars['DateTime']['output'];
+};
+
+
+export type WorkspaceProjectsArgs = {
+ cursor?: InputMaybe;
+ filter?: InputMaybe;
+ limit?: Scalars['Int']['input'];
+};
+
+export type WorkspaceCollaborator = {
+ __typename?: 'WorkspaceCollaborator';
+ id: Scalars['ID']['output'];
+ role: Scalars['String']['output'];
+ user: LimitedUser;
+};
+
+export type WorkspaceCollection = {
+ __typename?: 'WorkspaceCollection';
+ cursor?: Maybe;
+ items: Array;
+ totalCount: Scalars['Int']['output'];
+};
+
+export type WorkspaceCreateInput = {
+ description?: InputMaybe;
+ logoUrl?: InputMaybe;
+ name: Scalars['String']['input'];
+};
+
+export type WorkspaceInviteCreateInput = {
+ /** Either this or userId must be filled */
+ email?: InputMaybe;
+ /** Defaults to the member role, if not specified */
+ role?: InputMaybe;
+ /** Either this or email must be filled */
+ userId?: InputMaybe;
+};
+
+export type WorkspaceInviteMutations = {
+ __typename?: 'WorkspaceInviteMutations';
+ batchCreate: Workspace;
+ cancel: Workspace;
+ create: Workspace;
+ use: Scalars['Boolean']['output'];
+};
+
+
+export type WorkspaceInviteMutationsBatchCreateArgs = {
+ input: Array;
+ workspaceId: Scalars['String']['input'];
+};
+
+
+export type WorkspaceInviteMutationsCancelArgs = {
+ inviteId: Scalars['String']['input'];
+ workspaceId: Scalars['String']['input'];
+};
+
+
+export type WorkspaceInviteMutationsCreateArgs = {
+ input: WorkspaceInviteCreateInput;
+ workspaceId: Scalars['String']['input'];
+};
+
+
+export type WorkspaceInviteMutationsUseArgs = {
+ input: WorkspaceInviteUseInput;
+};
+
+export type WorkspaceInviteUseInput = {
+ accept: Scalars['Boolean']['input'];
+ token: Scalars['String']['input'];
+ workspaceId: Scalars['String']['input'];
+};
+
+export type WorkspaceMutations = {
+ __typename?: 'WorkspaceMutations';
+ create: Workspace;
+ delete: Workspace;
+ deleteRole: Scalars['Boolean']['output'];
+ invites: WorkspaceInviteMutations;
+ update: Workspace;
+ /** TODO: `@hasWorkspaceRole(role: WORKSPACE_ADMIN)` for role changes */
+ updateRole: Scalars['Boolean']['output'];
+};
+
+
+export type WorkspaceMutationsCreateArgs = {
+ input: WorkspaceCreateInput;
+};
+
+
+export type WorkspaceMutationsDeleteArgs = {
+ workspaceId: Scalars['String']['input'];
+};
+
+
+export type WorkspaceMutationsDeleteRoleArgs = {
+ input: WorkspaceRoleDeleteInput;
+};
+
+
+export type WorkspaceMutationsUpdateArgs = {
+ input: WorkspaceUpdateInput;
+};
+
+
+export type WorkspaceMutationsUpdateRoleArgs = {
+ input: WorkspaceRoleUpdateInput;
+};
+
+export enum WorkspaceRole {
+ Admin = 'ADMIN',
+ Guest = 'GUEST',
+ Member = 'MEMBER'
+}
+
+export type WorkspaceRoleDeleteInput = {
+ userId: Scalars['String']['input'];
+ workspaceId: Scalars['String']['input'];
+};
+
+export type WorkspaceRoleUpdateInput = {
+ role: WorkspaceRole;
+ userId: Scalars['String']['input'];
+ workspaceId: Scalars['String']['input'];
+};
+
+export type WorkspaceUpdateInput = {
+ description?: InputMaybe;
+ id: Scalars['String']['input'];
+ logoUrl?: InputMaybe;
+ name?: InputMaybe;
+};
+
export type AuthRegisterPanelServerInfoFragment = { __typename?: 'ServerInfo', inviteOnly?: boolean | null };
export type RegisterPanelServerInviteQueryVariables = Exact<{
diff --git a/packages/frontend-2/pages/projects/[id]/index/automations/new.vue b/packages/frontend-2/pages/projects/[id]/index/automations/new.vue
new file mode 100644
index 000000000..17b52583a
--- /dev/null
+++ b/packages/frontend-2/pages/projects/[id]/index/automations/new.vue
@@ -0,0 +1,3 @@
+
+
+
diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts
index e81628202..837fac495 100644
--- a/packages/server/modules/core/graph/generated/graphql.ts
+++ b/packages/server/modules/core/graph/generated/graphql.ts
@@ -3813,6 +3813,7 @@ export type WorkspaceMutations = {
deleteRole: Scalars['Boolean']['output'];
invites: WorkspaceInviteMutations;
update: Workspace;
+ /** TODO: `@hasWorkspaceRole(role: WORKSPACE_ADMIN)` for role changes */
updateRole: Scalars['Boolean']['output'];
};
diff --git a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts
index bc72fba38..d19ca8df6 100644
--- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts
+++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts
@@ -3803,6 +3803,7 @@ export type WorkspaceMutations = {
deleteRole: Scalars['Boolean']['output'];
invites: WorkspaceInviteMutations;
update: Workspace;
+ /** TODO: `@hasWorkspaceRole(role: WORKSPACE_ADMIN)` for role changes */
updateRole: Scalars['Boolean']['output'];
};
diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts
index 01fd97ed4..ed4da398a 100644
--- a/packages/server/test/graphql/generated/graphql.ts
+++ b/packages/server/test/graphql/generated/graphql.ts
@@ -3804,6 +3804,7 @@ export type WorkspaceMutations = {
deleteRole: Scalars['Boolean']['output'];
invites: WorkspaceInviteMutations;
update: Workspace;
+ /** TODO: `@hasWorkspaceRole(role: WORKSPACE_ADMIN)` for role changes */
updateRole: Scalars['Boolean']['output'];
};