Feat: List dashboards on projects (#5718)

This commit is contained in:
Mike
2025-10-08 20:26:59 +02:00
committed by GitHub
parent 978fc8b6fb
commit 8e7bfd138c
9 changed files with 265 additions and 54 deletions
@@ -1,19 +1,37 @@
<template>
<div class="flex flex-col gap-y-6">
<section class="flex items-center gap-2 justify-between">
<h1 class="text-heading-sm md:text-heading">Dashboards</h1>
<div class="flex flex-col gap-y-6 pt-3">
<section class="flex flex-col md:flex-row md:items-center gap-2 justify-between">
<h1 class="text-heading-lg">Dashboards</h1>
<FormButton color="outline" @click="showCreateDashboardDialog = true">
Add dashboard
</FormButton>
<div class="flex space-x-2 items-center">
<FormTextInput
v-model="localSearch"
name="dashboardsearch"
:show-label="false"
placeholder="Search dashboards..."
color="foundation"
wrapper-classes="grow min-w-40"
:show-clear="localSearch !== ''"
/>
<FormButton color="outline" @click="showCreateDashboardDialog = true">
Add dashboard
</FormButton>
</div>
</section>
<div
v-if="!isVeryFirstLoading && !result?.workspaceBySlug?.dashboards.items.length"
v-if="!isVeryFirstLoading && !dashboards?.items.length"
class="flex flex-col items-center justify-center gap-y-4 mx-auto my-14"
>
<h2 class="text-heading-sm text-foreground-2">
This workspace has no dashboards yet
{{
localSearch.trim()
? 'No dashboards found matching your search'
: props.projectId
? 'This project has no dashboards yet'
: 'This workspace has no dashboards yet'
}}
</h2>
<FormButton
v-if="canCreateDashboards"
@@ -24,25 +42,28 @@
</FormButton>
</div>
<div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div
v-for="dashboard in result?.workspaceBySlug?.dashboards.items"
:key="dashboard.id"
>
<DashboardsCard :dashboard="dashboard" :active-workspace-slug="workspaceSlug" />
<div v-for="dashboard in dashboards?.items" :key="dashboard.id">
<DashboardsCard
:dashboard="dashboard"
:active-workspace-slug="effectiveWorkspaceSlug"
/>
</div>
</div>
<InfiniteLoading :settings="{ identifier }" @infinite="onInfiniteLoad" />
<DashboardsCreateDialog
v-model:open="showCreateDashboardDialog"
:workspace-slug="workspaceSlug"
:workspace-slug="effectiveWorkspaceSlug"
/>
</div>
</template>
<script setup lang="ts">
import { usePaginatedQuery } from '~/lib/common/composables/graphql'
import { workspaceDashboardsQuery } from '~/lib/dashboards/graphql/queries'
import {
workspaceDashboardsQuery,
projectDashboardsQuery
} from '~/lib/dashboards/graphql/queries'
import type { Nullable } from '@speckle/shared'
import { graphql } from '~~/lib/common/generated/gql'
import { useQuery } from '@vue/apollo-composable'
@@ -59,29 +80,44 @@ const canCreateDashboardsQuery = graphql(`
}
`)
const props = defineProps<{
workspaceSlug?: string
projectId?: string
}>()
const route = useRoute()
const workspaceSlug = computed(() => route.params.slug as string)
const { result: canCreateDashboardsResult } = useQuery(
canCreateDashboardsQuery,
() => ({
slug: workspaceSlug.value
})
const showCreateDashboardDialog = ref(false)
const localSearch = ref('')
const workspaceSlug = computed(
() => props.workspaceSlug || (route.params.slug as string)
)
const effectiveWorkspaceSlug = computed(() => {
if (props.workspaceSlug) return props.workspaceSlug
if (props.projectId) return projectResult.value?.project?.workspace?.slug
return route.params.slug as string
})
const {
identifier,
onInfiniteLoad,
isVeryFirstLoading,
query: { result }
identifier: workspaceIdentifier,
onInfiniteLoad: onWorkspaceInfiniteLoad,
isVeryFirstLoading: isWorkspaceLoading,
query: { result: workspaceResult }
} = usePaginatedQuery({
query: workspaceDashboardsQuery,
options: computed(() => ({
enabled: !!workspaceSlug.value
enabled: !!props.workspaceSlug && !!workspaceSlug.value
})),
baseVariables: computed(() => ({
workspaceSlug: workspaceSlug.value || '',
cursor: null as Nullable<string>
cursor: null as Nullable<string>,
filter: {
search: localSearch.value.trim() || null
}
})),
resolveKey: () => [''],
resolveKey: () => ['workspace', localSearch.value],
resolveCurrentResult: (res) =>
res?.workspaceBySlug?.dashboards
? {
@@ -96,10 +132,68 @@ const {
resolveCursorFromVariables: (vars) => vars.cursor
})
const showCreateDashboardDialog = ref(false)
const canCreateDashboards = computed(
() =>
canCreateDashboardsResult.value?.workspaceBySlug?.permissions?.canCreateDashboards
?.authorized
const {
identifier: projectIdentifier,
onInfiniteLoad: onProjectInfiniteLoad,
isVeryFirstLoading: isProjectLoading,
query: { result: projectResult }
} = usePaginatedQuery({
query: projectDashboardsQuery,
options: computed(() => ({
enabled: !!props.projectId
})),
baseVariables: computed(() => ({
projectId: props.projectId || '',
cursor: null as Nullable<string>,
filter: {
search: localSearch.value.trim() || null
}
})),
resolveKey: () => ['project', localSearch.value],
resolveCurrentResult: (res) =>
res?.project?.dashboards
? {
totalCount: res.project.dashboards.items.length,
items: res.project.dashboards.items
}
: undefined,
resolveNextPageVariables: (baseVars, cursor) => ({
...baseVars,
cursor
}),
resolveCursorFromVariables: (vars) => vars.cursor
})
const { result: canCreateDashboardsResult } = useQuery(
canCreateDashboardsQuery,
() => ({
slug: effectiveWorkspaceSlug.value || ''
}),
{
enabled: computed(() => !!effectiveWorkspaceSlug.value)
}
)
const dashboards = computed(() =>
props.workspaceSlug
? workspaceResult.value?.workspaceBySlug?.dashboards
: projectResult.value?.project?.dashboards
)
const identifier = computed(() =>
props.workspaceSlug ? workspaceIdentifier.value : projectIdentifier.value
)
const isVeryFirstLoading = computed(() =>
props.workspaceSlug ? isWorkspaceLoading.value : isProjectLoading.value
)
const onInfiniteLoad = computed(() =>
props.workspaceSlug ? onWorkspaceInfiniteLoad : onProjectInfiniteLoad
)
const canCreateDashboards = computed(() => {
return canCreateDashboardsResult.value?.workspaceBySlug?.permissions
?.canCreateDashboards?.authorized
})
</script>
@@ -0,0 +1,15 @@
<template>
<div>
<DashboardsList :project-id="projectId" />
</div>
</template>
<script setup lang="ts">
import type { ProjectPageProjectFragment } from '~~/lib/common/generated/gql/graphql'
const attrs = useAttrs() as {
project: ProjectPageProjectFragment
}
const projectId = computed(() => attrs.project.id)
</script>
@@ -289,7 +289,8 @@ type Documents = {
"\n mutation DeleteDashboard($id: String!) {\n dashboardMutations {\n delete(id: $id)\n }\n }\n": typeof types.DeleteDashboardDocument,
"\n query DashboardAccessCheck($id: String!) {\n dashboard(id: $id) {\n id\n }\n }\n": typeof types.DashboardAccessCheckDocument,
"\n query Dashboard($id: String!) {\n dashboard(id: $id) {\n id\n ...WorkspaceDashboards_Dashboard\n }\n }\n": typeof types.DashboardDocument,
"\n query WorkspaceDashboards($workspaceSlug: String!, $cursor: String) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n dashboards(cursor: $cursor) {\n cursor\n items {\n id\n ...DashboardsCard_Dashboard\n }\n }\n }\n }\n": typeof types.WorkspaceDashboardsDocument,
"\n query WorkspaceDashboards(\n $workspaceSlug: String!\n $cursor: String\n $filter: WorkspaceDashboardsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n dashboards(cursor: $cursor, filter: $filter) {\n cursor\n items {\n id\n ...DashboardsCard_Dashboard\n }\n }\n }\n }\n": typeof types.WorkspaceDashboardsDocument,
"\n query ProjectDashboards(\n $projectId: String!\n $cursor: String\n $filter: ProjectDashboardsFilter\n ) {\n project(id: $projectId) {\n id\n workspace {\n slug\n }\n dashboards(cursor: $cursor, filter: $filter) {\n cursor\n items {\n id\n ...DashboardsCard_Dashboard\n }\n }\n }\n }\n": typeof types.ProjectDashboardsDocument,
"\n mutation DeleteAccessToken($token: String!) {\n apiTokenRevoke(token: $token)\n }\n": typeof types.DeleteAccessTokenDocument,
"\n mutation CreateAccessToken($token: ApiTokenCreateInput!) {\n apiTokenCreate(token: $token)\n }\n": typeof types.CreateAccessTokenDocument,
"\n mutation DeleteApplication($appId: String!) {\n appDelete(appId: $appId)\n }\n": typeof types.DeleteApplicationDocument,
@@ -553,7 +554,7 @@ type Documents = {
"\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": typeof 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": typeof types.AutomateFunctionPageDocument,
"\n query AutomateFunctionPageWorkspace($workspaceId: String!) {\n workspace(id: $workspaceId) {\n id\n ...AutomateFunctionPageHeader_Workspace\n }\n }\n": typeof types.AutomateFunctionPageWorkspaceDocument,
"\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 permissions {\n canReadSettings {\n ...FullPermissionCheckResult\n }\n canUpdate {\n ...FullPermissionCheckResult\n }\n canMoveToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n ...ProjectPageTeamInternals_Project\n ...ProjectPageProjectHeader\n ...ProjectPageTeamDialog\n ...WorkspaceMoveProjectManager_ProjectBase\n ...ProjectPageSettingsTab_Project\n ...WorkspaceMoveProject_Project\n }\n": typeof types.ProjectPageProjectFragmentDoc,
"\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 permissions {\n canReadSettings {\n ...FullPermissionCheckResult\n }\n canUpdate {\n ...FullPermissionCheckResult\n }\n canMoveToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n ...ProjectPageTeamInternals_Project\n ...ProjectPageProjectHeader\n ...ProjectPageTeamDialog\n ...WorkspaceMoveProjectManager_ProjectBase\n ...ProjectPageSettingsTab_Project\n ...WorkspaceMoveProject_Project\n hasAccessToDashboards: hasAccessToFeature(featureName: dashboards)\n }\n": typeof types.ProjectPageProjectFragmentDoc,
"\n fragment ProjectPageAutomationPage_Automation on Automation {\n id\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...ProjectPageAutomationHeader_Automation\n ...ProjectPageAutomationFunctions_Automation\n ...ProjectPageAutomationRuns_Automation\n }\n": typeof types.ProjectPageAutomationPage_AutomationFragmentDoc,
"\n fragment ProjectPageAutomationPage_Project on Project {\n id\n workspaceId\n ...ProjectPageAutomationHeader_Project\n }\n": typeof types.ProjectPageAutomationPage_ProjectFragmentDoc,
"\n fragment ProjectPageSettingsTab_Project on Project {\n id\n name\n permissions {\n canReadWebhooks {\n ...FullPermissionCheckResult\n }\n canReadEmbedTokens {\n ...FullPermissionCheckResult\n }\n canReadAccIntegrationSettings {\n ...FullPermissionCheckResult\n }\n }\n }\n": typeof types.ProjectPageSettingsTab_ProjectFragmentDoc,
@@ -845,7 +846,8 @@ const documents: Documents = {
"\n mutation DeleteDashboard($id: String!) {\n dashboardMutations {\n delete(id: $id)\n }\n }\n": types.DeleteDashboardDocument,
"\n query DashboardAccessCheck($id: String!) {\n dashboard(id: $id) {\n id\n }\n }\n": types.DashboardAccessCheckDocument,
"\n query Dashboard($id: String!) {\n dashboard(id: $id) {\n id\n ...WorkspaceDashboards_Dashboard\n }\n }\n": types.DashboardDocument,
"\n query WorkspaceDashboards($workspaceSlug: String!, $cursor: String) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n dashboards(cursor: $cursor) {\n cursor\n items {\n id\n ...DashboardsCard_Dashboard\n }\n }\n }\n }\n": types.WorkspaceDashboardsDocument,
"\n query WorkspaceDashboards(\n $workspaceSlug: String!\n $cursor: String\n $filter: WorkspaceDashboardsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n dashboards(cursor: $cursor, filter: $filter) {\n cursor\n items {\n id\n ...DashboardsCard_Dashboard\n }\n }\n }\n }\n": types.WorkspaceDashboardsDocument,
"\n query ProjectDashboards(\n $projectId: String!\n $cursor: String\n $filter: ProjectDashboardsFilter\n ) {\n project(id: $projectId) {\n id\n workspace {\n slug\n }\n dashboards(cursor: $cursor, filter: $filter) {\n cursor\n items {\n id\n ...DashboardsCard_Dashboard\n }\n }\n }\n }\n": types.ProjectDashboardsDocument,
"\n mutation DeleteAccessToken($token: String!) {\n apiTokenRevoke(token: $token)\n }\n": types.DeleteAccessTokenDocument,
"\n mutation CreateAccessToken($token: ApiTokenCreateInput!) {\n apiTokenCreate(token: $token)\n }\n": types.CreateAccessTokenDocument,
"\n mutation DeleteApplication($appId: String!) {\n appDelete(appId: $appId)\n }\n": types.DeleteApplicationDocument,
@@ -1109,7 +1111,7 @@ const documents: Documents = {
"\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 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 permissions {\n canReadSettings {\n ...FullPermissionCheckResult\n }\n canUpdate {\n ...FullPermissionCheckResult\n }\n canMoveToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n ...ProjectPageTeamInternals_Project\n ...ProjectPageProjectHeader\n ...ProjectPageTeamDialog\n ...WorkspaceMoveProjectManager_ProjectBase\n ...ProjectPageSettingsTab_Project\n ...WorkspaceMoveProject_Project\n }\n": types.ProjectPageProjectFragmentDoc,
"\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 permissions {\n canReadSettings {\n ...FullPermissionCheckResult\n }\n canUpdate {\n ...FullPermissionCheckResult\n }\n canMoveToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n ...ProjectPageTeamInternals_Project\n ...ProjectPageProjectHeader\n ...ProjectPageTeamDialog\n ...WorkspaceMoveProjectManager_ProjectBase\n ...ProjectPageSettingsTab_Project\n ...WorkspaceMoveProject_Project\n hasAccessToDashboards: hasAccessToFeature(featureName: dashboards)\n }\n": types.ProjectPageProjectFragmentDoc,
"\n fragment ProjectPageAutomationPage_Automation on Automation {\n id\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...ProjectPageAutomationHeader_Automation\n ...ProjectPageAutomationFunctions_Automation\n ...ProjectPageAutomationRuns_Automation\n }\n": types.ProjectPageAutomationPage_AutomationFragmentDoc,
"\n fragment ProjectPageAutomationPage_Project on Project {\n id\n workspaceId\n ...ProjectPageAutomationHeader_Project\n }\n": types.ProjectPageAutomationPage_ProjectFragmentDoc,
"\n fragment ProjectPageSettingsTab_Project on Project {\n id\n name\n permissions {\n canReadWebhooks {\n ...FullPermissionCheckResult\n }\n canReadEmbedTokens {\n ...FullPermissionCheckResult\n }\n canReadAccIntegrationSettings {\n ...FullPermissionCheckResult\n }\n }\n }\n": types.ProjectPageSettingsTab_ProjectFragmentDoc,
@@ -2243,7 +2245,11 @@ export function graphql(source: "\n query Dashboard($id: String!) {\n dashbo
/**
* 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 WorkspaceDashboards($workspaceSlug: String!, $cursor: String) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n dashboards(cursor: $cursor) {\n cursor\n items {\n id\n ...DashboardsCard_Dashboard\n }\n }\n }\n }\n"): (typeof documents)["\n query WorkspaceDashboards($workspaceSlug: String!, $cursor: String) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n dashboards(cursor: $cursor) {\n cursor\n items {\n id\n ...DashboardsCard_Dashboard\n }\n }\n }\n }\n"];
export function graphql(source: "\n query WorkspaceDashboards(\n $workspaceSlug: String!\n $cursor: String\n $filter: WorkspaceDashboardsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n dashboards(cursor: $cursor, filter: $filter) {\n cursor\n items {\n id\n ...DashboardsCard_Dashboard\n }\n }\n }\n }\n"): (typeof documents)["\n query WorkspaceDashboards(\n $workspaceSlug: String!\n $cursor: String\n $filter: WorkspaceDashboardsFilter\n ) {\n workspaceBySlug(slug: $workspaceSlug) {\n id\n dashboards(cursor: $cursor, filter: $filter) {\n cursor\n items {\n id\n ...DashboardsCard_Dashboard\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 ProjectDashboards(\n $projectId: String!\n $cursor: String\n $filter: ProjectDashboardsFilter\n ) {\n project(id: $projectId) {\n id\n workspace {\n slug\n }\n dashboards(cursor: $cursor, filter: $filter) {\n cursor\n items {\n id\n ...DashboardsCard_Dashboard\n }\n }\n }\n }\n"): (typeof documents)["\n query ProjectDashboards(\n $projectId: String!\n $cursor: String\n $filter: ProjectDashboardsFilter\n ) {\n project(id: $projectId) {\n id\n workspace {\n slug\n }\n dashboards(cursor: $cursor, filter: $filter) {\n cursor\n items {\n id\n ...DashboardsCard_Dashboard\n }\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -3299,7 +3305,7 @@ export function graphql(source: "\n query AutomateFunctionPageWorkspace($worksp
/**
* 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 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 permissions {\n canReadSettings {\n ...FullPermissionCheckResult\n }\n canUpdate {\n ...FullPermissionCheckResult\n }\n canMoveToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n ...ProjectPageTeamInternals_Project\n ...ProjectPageProjectHeader\n ...ProjectPageTeamDialog\n ...WorkspaceMoveProjectManager_ProjectBase\n ...ProjectPageSettingsTab_Project\n ...WorkspaceMoveProject_Project\n }\n"): (typeof documents)["\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 permissions {\n canReadSettings {\n ...FullPermissionCheckResult\n }\n canUpdate {\n ...FullPermissionCheckResult\n }\n canMoveToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n ...ProjectPageTeamInternals_Project\n ...ProjectPageProjectHeader\n ...ProjectPageTeamDialog\n ...WorkspaceMoveProjectManager_ProjectBase\n ...ProjectPageSettingsTab_Project\n ...WorkspaceMoveProject_Project\n }\n"];
export function graphql(source: "\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 permissions {\n canReadSettings {\n ...FullPermissionCheckResult\n }\n canUpdate {\n ...FullPermissionCheckResult\n }\n canMoveToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n ...ProjectPageTeamInternals_Project\n ...ProjectPageProjectHeader\n ...ProjectPageTeamDialog\n ...WorkspaceMoveProjectManager_ProjectBase\n ...ProjectPageSettingsTab_Project\n ...WorkspaceMoveProject_Project\n hasAccessToDashboards: hasAccessToFeature(featureName: dashboards)\n }\n"): (typeof documents)["\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 permissions {\n canReadSettings {\n ...FullPermissionCheckResult\n }\n canUpdate {\n ...FullPermissionCheckResult\n }\n canMoveToWorkspace {\n ...FullPermissionCheckResult\n }\n }\n ...ProjectPageTeamInternals_Project\n ...ProjectPageProjectHeader\n ...ProjectPageTeamDialog\n ...WorkspaceMoveProjectManager_ProjectBase\n ...ProjectPageSettingsTab_Project\n ...WorkspaceMoveProject_Project\n hasAccessToDashboards: hasAccessToFeature(featureName: dashboards)\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
@@ -98,7 +98,14 @@ export const settingsWorkspaceRoutes = {
export const projectRoute = (
id: string,
tab?: 'models' | 'discussions' | 'automations' | 'collaborators' | 'settings' | 'acc'
tab?:
| 'models'
| 'discussions'
| 'automations'
| 'collaborators'
| 'settings'
| 'acc'
| 'dashboards'
) => {
let res = `/projects/${id}`
if (tab && tab !== 'models') {
@@ -18,10 +18,36 @@ export const dashboardQuery = graphql(`
`)
export const workspaceDashboardsQuery = graphql(`
query WorkspaceDashboards($workspaceSlug: String!, $cursor: String) {
query WorkspaceDashboards(
$workspaceSlug: String!
$cursor: String
$filter: WorkspaceDashboardsFilter
) {
workspaceBySlug(slug: $workspaceSlug) {
id
dashboards(cursor: $cursor) {
dashboards(cursor: $cursor, filter: $filter) {
cursor
items {
id
...DashboardsCard_Dashboard
}
}
}
}
`)
export const projectDashboardsQuery = graphql(`
query ProjectDashboards(
$projectId: String!
$cursor: String
$filter: ProjectDashboardsFilter
) {
project(id: $projectId) {
id
workspace {
slug
}
dashboards(cursor: $cursor, filter: $filter) {
cursor
items {
id
@@ -117,6 +117,7 @@ graphql(`
...WorkspaceMoveProjectManager_ProjectBase
...ProjectPageSettingsTab_Project
...WorkspaceMoveProject_Project
hasAccessToDashboards: hasAccessToFeature(featureName: dashboards)
}
`)
@@ -266,6 +267,13 @@ const pageTabItems = computed((): LayoutPageTabItem[] => {
})
}
if (project.value?.hasAccessToDashboards) {
items.push({
title: 'Dashboards',
id: 'dashboards'
})
}
return items
})
@@ -286,6 +294,7 @@ const activePageTab = computed({
if (/\/discussions\/?$/i.test(path)) return findTabById('discussions')
if (/\/automations\/?.*$/i.test(path)) return findTabById('automations')
if (/\/acc\/?.*$/i.test(path)) return findTabById('acc')
if (/\/dashboards\/?/i.test(path)) return findTabById('dashboards')
if (/\/collaborators\/?/i.test(path) && canReadSettings.value?.authorized)
return findTabById('collaborators')
if (/\/settings\/?/i.test(path) && canReadSettings.value?.authorized)
@@ -312,6 +321,11 @@ const activePageTab = computed({
router.push({ path: projectRoute(projectId.value, 'collaborators') })
}
break
case 'dashboards':
if (project.value?.hasAccessToDashboards) {
router.push({ path: projectRoute(projectId.value, 'dashboards') })
}
break
case 'settings':
if (canReadSettings.value?.authorized) {
router.push({ path: projectRoute(projectId.value, 'settings') })
@@ -0,0 +1,19 @@
<template>
<ProjectPageDashboards />
</template>
<script setup lang="ts">
import type { ProjectPageProjectFragment } from '~~/lib/common/generated/gql/graphql'
const attrs = useAttrs() as {
project: ProjectPageProjectFragment
}
const projectName = computed(() =>
attrs.project.name.length ? attrs.project.name : ''
)
useHead({
title: `Dashboards | ${projectName.value}`
})
</script>
@@ -9,7 +9,7 @@
</Portal>
<div>
<DashboardsList />
<DashboardsList :workspace-slug="activeWorkspaceSlug" />
</div>
<DashboardsCreateDialog