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']; };