WEB-2466 fix(automate): include workspace in function breadcrumb (#3926)

* fix(automate): repair workspace function breadcrumb

* fix(automate): use route helper correctly
This commit is contained in:
Chuck Driesler
2025-02-04 09:58:45 +00:00
committed by GitHub
parent 9dd5989b14
commit 272c136a17
13 changed files with 101 additions and 29 deletions
@@ -2,7 +2,7 @@
<Component
:is="noButtons ? NuxtLink : 'div'"
:class="classes"
:to="noButtons ? automationFunctionRoute(fn.id) : undefined"
:to="noButtons ? automateFunctionRoute(fn.id) : undefined"
:external="externalMoreInfo"
:target="externalMoreInfo ? '_blank' : undefined"
class="rounded-lg border border-outline-3 bg-foundation overflow-hidden"
@@ -19,7 +19,7 @@
>
<Component
:is="noButtons ? 'div' : NuxtLink"
:to="automationFunctionRoute(fn.id)"
:to="automateFunctionRoute(fn.id)"
:target="externalMoreInfo ? '_blank' : undefined"
class="truncate"
>
@@ -52,7 +52,7 @@
</FormButton>
<FormButton
color="subtle"
:to="automationFunctionRoute(fn.id)"
:to="automateFunctionRoute(fn.id)"
:external="externalMoreInfo"
:target="externalMoreInfo ? '_blank' : undefined"
>
@@ -80,7 +80,7 @@
import { graphql } from '~/lib/common/generated/gql'
import type { AutomationsFunctionsCard_AutomateFunctionFragment } from '~/lib/common/generated/gql/graphql'
import { CheckIcon } from '@heroicons/vue/24/outline'
import { automationFunctionRoute } from '~/lib/common/helpers/route'
import { automateFunctionRoute } from '~/lib/common/helpers/route'
import { useMarkdown } from '~/lib/common/composables/markdown'
graphql(`
@@ -52,7 +52,7 @@ import type {
} from '~/lib/automate/helpers/functions'
import {
automateGithubAppAuthorizationRoute,
automationFunctionRoute
automateFunctionRoute
} from '~/lib/common/helpers/route'
import { useEnumSteps, useEnumStepsWidgetSetup } from '~/lib/form/composables/steps'
import { useForm } from 'vee-validate'
@@ -281,7 +281,7 @@ const buttons = computed((): LayoutDialogButton[] => {
iconRight: ArrowRightIcon,
fullWidth: true,
to: createdFunction.value?.id
? automationFunctionRoute(createdFunction.value.id)
? automateFunctionRoute(createdFunction.value.id)
: undefined
}
}
@@ -1,12 +1,26 @@
<template>
<div class="pt-4 flex gap-4 flex-col sm:flex-row sm:items-center sm:justify-between">
<Portal to="navigation">
<HeaderNavLink
:to="automationFunctionsRoute"
:separator="false"
name="Automate functions"
/>
<HeaderNavLink :to="automationFunctionRoute(fn.id)" :name="fn.name" />
<template v-if="fnWorkspace">
<HeaderNavLink
:to="workspaceRoute(fnWorkspace.slug)"
:separator="false"
:name="fnWorkspace.name"
/>
<HeaderNavLink
:to="workspaceFunctionsRoute(fnWorkspace.slug)"
name="Functions"
/>
<HeaderNavLink :to="automateFunctionRoute(fn.id)" :name="fn.name" />
</template>
<template v-else>
<HeaderNavLink
:to="publicAutomateFunctionsRoute"
:separator="false"
name="Functions"
/>
<HeaderNavLink :to="automateFunctionRoute(fn.id)" :name="fn.name" />
</template>
</Portal>
<div class="flex items-center gap-4">
<AutomateFunctionLogo :logo="fn.logo" />
@@ -21,10 +35,15 @@
</template>
<script setup lang="ts">
import { graphql } from '~/lib/common/generated/gql'
import type { AutomateFunctionPageHeader_FunctionFragment } from '~/lib/common/generated/gql/graphql'
import type {
AutomateFunctionPageHeader_FunctionFragment,
AutomateFunctionPageHeader_WorkspaceFragment
} from '~/lib/common/generated/gql/graphql'
import {
automationFunctionRoute,
automationFunctionsRoute
automateFunctionRoute,
publicAutomateFunctionsRoute,
workspaceFunctionsRoute,
workspaceRoute
} from '~/lib/common/helpers/route'
graphql(`
@@ -43,10 +62,17 @@ graphql(`
}
workspaceIds
}
fragment AutomateFunctionPageHeader_Workspace on Workspace {
id
name
slug
}
`)
defineProps<{
fn: AutomateFunctionPageHeader_FunctionFragment
fnWorkspace?: AutomateFunctionPageHeader_WorkspaceFragment
isOwner: boolean
}>()
@@ -57,7 +57,7 @@
v-if="revisionFn"
text
target="_blank"
:to="automationFunctionRoute(revisionFn.release.function.id)"
:to="automateFunctionRoute(revisionFn.release.function.id)"
>
View function
</FormButton>
@@ -75,7 +75,7 @@
</template>
<script setup lang="ts">
import type { MaybeNullOrUndefined, Optional } from '@speckle/shared'
import { automationFunctionRoute } from '~/lib/common/helpers/route'
import { automateFunctionRoute } from '~/lib/common/helpers/route'
import {
useJsonFormsChangeHandler,
hasJsonFormErrors as hasFormErrors
@@ -16,7 +16,7 @@
</template>
<script setup lang="ts">
import {
automationFunctionsRoute,
publicAutomateFunctionsRoute,
workspaceFunctionsRoute
} from '~/lib/common/helpers/route'
import type { CreateAutomationSelectableFunction } from '~/lib/automate/helpers/automations'
@@ -58,7 +58,7 @@ onMounted(() => {
const functionsGalleryRoute = computed(() =>
props.workspaceSlug
? workspaceFunctionsRoute(props.workspaceSlug)
: automationFunctionsRoute
: publicAutomateFunctionsRoute
)
const isVisibleAction = (action: LayoutDialogButton): boolean => {
@@ -31,7 +31,7 @@
<script setup lang="ts">
import { useDebouncedTextInput } from '@speckle/ui-components'
import {
automationFunctionsRoute,
publicAutomateFunctionsRoute,
workspaceFunctionsRoute
} from '~/lib/common/helpers/route'
@@ -51,7 +51,7 @@ const exploreFunctionsMessage = computed(() =>
const exploreFunctionsRoute = computed(() =>
props.workspaceSlug
? workspaceFunctionsRoute(props.workspaceSlug)
: automationFunctionsRoute
: publicAutomateFunctionsRoute
)
const search = defineModel<string>('search')
@@ -31,7 +31,7 @@ const documents = {
"\n fragment AutomateFunctionEditDialog_Workspace on Workspace {\n id\n name\n }\n": types.AutomateFunctionEditDialog_WorkspaceFragmentDoc,
"\n fragment AutomateFunctionCreateDialogDoneStep_AutomateFunction on AutomateFunction {\n id\n repo {\n id\n url\n owner\n name\n }\n ...AutomationsFunctionsCard_AutomateFunction\n }\n": types.AutomateFunctionCreateDialogDoneStep_AutomateFunctionFragmentDoc,
"\n fragment AutomateFunctionCreateDialogTemplateStep_AutomateFunctionTemplate on AutomateFunctionTemplate {\n id\n title\n logo\n url\n }\n": types.AutomateFunctionCreateDialogTemplateStep_AutomateFunctionTemplateFragmentDoc,
"\n fragment AutomateFunctionPageHeader_Function on AutomateFunction {\n id\n name\n logo\n repo {\n id\n url\n owner\n name\n }\n releases(limit: 1) {\n totalCount\n }\n workspaceIds\n }\n": types.AutomateFunctionPageHeader_FunctionFragmentDoc,
"\n fragment AutomateFunctionPageHeader_Function on AutomateFunction {\n id\n name\n logo\n repo {\n id\n url\n owner\n name\n }\n releases(limit: 1) {\n totalCount\n }\n workspaceIds\n }\n\n fragment AutomateFunctionPageHeader_Workspace on Workspace {\n id\n name\n slug\n }\n": types.AutomateFunctionPageHeader_FunctionFragmentDoc,
"\n fragment AutomateFunctionPageInfo_AutomateFunction on AutomateFunction {\n id\n repo {\n id\n url\n owner\n name\n }\n description\n releases(limit: 1) {\n items {\n id\n inputSchema\n createdAt\n commitId\n ...AutomateFunctionPageParametersDialog_AutomateFunctionRelease\n }\n }\n }\n": types.AutomateFunctionPageInfo_AutomateFunctionFragmentDoc,
"\n fragment AutomateFunctionPageParametersDialog_AutomateFunctionRelease on AutomateFunctionRelease {\n id\n inputSchema\n }\n": types.AutomateFunctionPageParametersDialog_AutomateFunctionReleaseFragmentDoc,
"\n fragment AutomateFunctionsPageHeader_Query on Query {\n activeUser {\n id\n role\n automateInfo {\n hasAutomateGithubApp\n availableGithubOrgs\n }\n }\n serverInfo {\n automate {\n availableFunctionTemplates {\n ...AutomateFunctionCreateDialogTemplateStep_AutomateFunctionTemplate\n }\n }\n }\n }\n": types.AutomateFunctionsPageHeader_QueryFragmentDoc,
@@ -381,6 +381,7 @@ const documents = {
"\n query ResolveCommentLink($commentId: String!, $projectId: String!) {\n project(id: $projectId) {\n comment(id: $commentId) {\n id\n ...LinkableComment\n }\n }\n }\n": types.ResolveCommentLinkDocument,
"\n fragment AutomateFunctionPage_AutomateFunction on AutomateFunction {\n id\n name\n description\n logo\n supportedSourceApps\n tags\n ...AutomateFunctionPageHeader_Function\n ...AutomateFunctionPageInfo_AutomateFunction\n ...AutomateAutomationCreateDialog_AutomateFunction\n creator {\n id\n }\n }\n": types.AutomateFunctionPage_AutomateFunctionFragmentDoc,
"\n query AutomateFunctionPage($functionId: ID!) {\n automateFunction(id: $functionId) {\n ...AutomateFunctionPage_AutomateFunction\n }\n activeUser {\n workspaces {\n items {\n ...AutomateFunctionCreateDialog_Workspace\n ...AutomateFunctionEditDialog_Workspace\n }\n }\n }\n }\n": types.AutomateFunctionPageDocument,
"\n query AutomateFunctionPageWorkspace($workspaceId: String!) {\n workspace(id: $workspaceId) {\n id\n ...AutomateFunctionPageHeader_Workspace\n }\n }\n": types.AutomateFunctionPageWorkspaceDocument,
"\n query AutomateFunctionsPage($search: String, $cursor: String = null) {\n ...AutomateFunctionsPageItems_Query\n ...AutomateFunctionsPageHeader_Query\n }\n": types.AutomateFunctionsPageDocument,
"\n fragment ProjectPageProject on Project {\n id\n createdAt\n modelCount: models(limit: 0) {\n totalCount\n }\n commentThreadCount: commentThreads(limit: 0) {\n totalCount\n }\n workspace {\n id\n }\n ...ProjectPageTeamInternals_Project\n ...ProjectPageProjectHeader\n ...ProjectPageTeamDialog\n ...ProjectsMoveToWorkspaceDialog_Project\n }\n": types.ProjectPageProjectFragmentDoc,
"\n fragment ProjectPageAutomationPage_Automation on Automation {\n id\n ...ProjectPageAutomationHeader_Automation\n ...ProjectPageAutomationFunctions_Automation\n ...ProjectPageAutomationRuns_Automation\n }\n": types.ProjectPageAutomationPage_AutomationFragmentDoc,
@@ -483,7 +484,7 @@ export function graphql(source: "\n fragment AutomateFunctionCreateDialogTempla
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment AutomateFunctionPageHeader_Function on AutomateFunction {\n id\n name\n logo\n repo {\n id\n url\n owner\n name\n }\n releases(limit: 1) {\n totalCount\n }\n workspaceIds\n }\n"): (typeof documents)["\n fragment AutomateFunctionPageHeader_Function on AutomateFunction {\n id\n name\n logo\n repo {\n id\n url\n owner\n name\n }\n releases(limit: 1) {\n totalCount\n }\n workspaceIds\n }\n"];
export function graphql(source: "\n fragment AutomateFunctionPageHeader_Function on AutomateFunction {\n id\n name\n logo\n repo {\n id\n url\n owner\n name\n }\n releases(limit: 1) {\n totalCount\n }\n workspaceIds\n }\n\n fragment AutomateFunctionPageHeader_Workspace on Workspace {\n id\n name\n slug\n }\n"): (typeof documents)["\n fragment AutomateFunctionPageHeader_Function on AutomateFunction {\n id\n name\n logo\n repo {\n id\n url\n owner\n name\n }\n releases(limit: 1) {\n totalCount\n }\n workspaceIds\n }\n\n fragment AutomateFunctionPageHeader_Workspace on Workspace {\n id\n name\n slug\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1880,6 +1881,10 @@ export function graphql(source: "\n fragment AutomateFunctionPage_AutomateFunct
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query AutomateFunctionPage($functionId: ID!) {\n automateFunction(id: $functionId) {\n ...AutomateFunctionPage_AutomateFunction\n }\n activeUser {\n workspaces {\n items {\n ...AutomateFunctionCreateDialog_Workspace\n ...AutomateFunctionEditDialog_Workspace\n }\n }\n }\n }\n"): (typeof documents)["\n query AutomateFunctionPage($functionId: ID!) {\n automateFunction(id: $functionId) {\n ...AutomateFunctionPage_AutomateFunction\n }\n activeUser {\n workspaces {\n items {\n ...AutomateFunctionCreateDialog_Workspace\n ...AutomateFunctionEditDialog_Workspace\n }\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query AutomateFunctionPageWorkspace($workspaceId: String!) {\n workspace(id: $workspaceId) {\n id\n ...AutomateFunctionPageHeader_Workspace\n }\n }\n"): (typeof documents)["\n query AutomateFunctionPageWorkspace($workspaceId: String!) {\n workspace(id: $workspaceId) {\n id\n ...AutomateFunctionPageHeader_Workspace\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
File diff suppressed because one or more lines are too long
@@ -112,10 +112,10 @@ export const automateGithubAppAuthorizationRoute = (workspaceSlug?: string) => {
}`
}
export const automationFunctionsRoute = '/functions'
export const publicAutomateFunctionsRoute = '/functions'
export const automationFunctionRoute = (functionId: string) =>
`${automationFunctionsRoute}/${functionId}`
export const automateFunctionRoute = (functionId: string) =>
`${publicAutomateFunctionsRoute}/${functionId}`
export const workspaceRoute = (slug: string) => `/workspaces/${slug}`
export const workspaceSsoRoute = (slug: string) => `/workspaces/${slug}/sso`
@@ -4,6 +4,7 @@
<template v-if="!loading && fn">
<AutomateFunctionPageHeader
:fn="fn"
:fn-workspace="fnWorkspace"
:is-owner="isOwner"
class="mb-12"
@create-automation="showNewAutomationDialog = true"
@@ -70,6 +71,15 @@ const pageQuery = graphql(`
}
`)
const functionWorkspaceQuery = graphql(`
query AutomateFunctionPageWorkspace($workspaceId: String!) {
workspace(id: $workspaceId) {
id
...AutomateFunctionPageHeader_Workspace
}
}
`)
definePageMeta({
middleware: ['auth', 'require-valid-function']
})
@@ -95,6 +105,19 @@ const showNewAutomationDialog = ref(false)
const fn = computed(() => result.value?.automateFunction)
const fnWorkspaceId = computed(() => fn.value?.workspaceIds?.at(0))
const { result: functionWorkspaceResult } = useQuery(
functionWorkspaceQuery,
() => ({
workspaceId: fnWorkspaceId.value as string
}),
() => ({
enabled: !!fnWorkspaceId.value
})
)
const fnWorkspace = computed(() => functionWorkspaceResult.value?.workspace)
const isOwner = computed(
() =>
!!(
@@ -2,7 +2,7 @@
<div>
<Portal to="navigation">
<HeaderNavLink
:to="automationFunctionsRoute"
:to="publicAutomateFunctionsRoute"
name="Functions"
:separator="false"
/>
@@ -44,7 +44,7 @@ import {
usePaginatedQuery
} from '~/lib/common/composables/graphql'
import { graphql } from '~/lib/common/generated/gql'
import { automationFunctionsRoute } from '~/lib/common/helpers/route'
import { publicAutomateFunctionsRoute } from '~/lib/common/helpers/route'
definePageMeta({
middleware: ['auth', 'requires-automate-enabled']
@@ -325,6 +325,9 @@ type CreateFunctionWithoutVersionBody = {
speckleServerAuthenticationPayload: AuthCodePayloadWithOrigin
functionName: string
description: string
repositoryUrl: string
supportedSourceApps: string[]
tags: string[]
}
type CreateFunctionWithoutVersionResponse = {
@@ -556,7 +556,11 @@ export = (FF_AUTOMATE_MODULE_ENABLED
origin: getServerOrigin()
},
functionName: args.input.name,
description: args.input.description
description: args.input.description,
repositoryUrl:
'https://github.com/specklesystems/speckle_automate_python_example',
supportedSourceApps: [],
tags: []
}
})
},