Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add new dashboard #2580

Merged
merged 15 commits into from
Aug 6, 2024
3 changes: 3 additions & 0 deletions packages/frontend-2/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ NUXT_PUBLIC_SURVICATE_WORKSPACE_KEY=

# Enable direct preview image loading - way quicker, but requres server & frontend to be on the same origin
NUXT_PUBLIC_ENABLE_DIRECT_PREVIEWS=true

# Ghost API
NUXT_PUBLIC_GHOST_API_KEY=
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
:class="[
'transition grow px-3 inline-flex justify-center items-center outline-none h6 font-medium leading-7',
'rounded shadow bg-foundation- text-foreground dark:text-foreground-on-primary',
'border border-1 border-outline-2 hover:border-outline-3',
'border border-outline-2 hover:border-outline-3',
noVerticalPadding ? '' : 'py-2'
]"
>
Expand Down
80 changes: 80 additions & 0 deletions packages/frontend-2/components/dashboard/ProjectCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<template>
<div
class="bg-foundation border border-outline-3 rounded-xl p-5 pb-4 flex flex-col gap-y-3"
>
<NuxtLink
:to="projectRoute(project.id)"
class="text-heading hover:text-primary truncate"
>
{{ project.name }}
</NuxtLink>
<div class="flex-1">
<p class="text-body-3xs text-foreground-2 capitalize">
{{ project.role?.split(':').reverse()[0] }}
<span class="pl-1 pr-2">•</span>
<span v-tippy="updatedAt.full">
{{ updatedAt.relative }}
</span>
</p>
</div>
<UserAvatarGroup :users="teamUsers" :max-count="4" />
<div>
<FormButton
:to="allProjectModelsRoute(project.id)"
size="sm"
color="outline"
:icon-right="ChevronRightIcon"
>
{{
`${project.models.totalCount} ${
project.models.totalCount === 1 ? 'model' : 'models'
}`
}}
</FormButton>
</div>
</div>
</template>

<script lang="ts" setup>
import { graphql } from '~~/lib/common/generated/gql'
import type { DashboardProjectCard_ProjectFragment } from '~~/lib/common/generated/gql/graphql'
import { projectRoute, allProjectModelsRoute } from '~~/lib/common/helpers/route'
import { ChevronRightIcon } from '@heroicons/vue/20/solid'
import { useGeneralProjectPageUpdateTracking } from '~~/lib/projects/composables/projectPages'

graphql(`
fragment DashboardProjectCard_Project on Project {
id
name
role
updatedAt
models {
totalCount
}
team {
user {
...LimitedUserAvatar
}
}
}
`)

const props = defineProps<{
Mikehrn marked this conversation as resolved.
Show resolved Hide resolved
project: DashboardProjectCard_ProjectFragment
}>()

const projectId = computed(() => props.project.id)

useGeneralProjectPageUpdateTracking(
{ projectId },
{ redirectHomeOnProjectDeletion: false }
)

const teamUsers = computed(() => props.project.team.map((t) => t.user))
const updatedAt = computed(() => {
return {
full: formattedFullDate(props.project.updatedAt),
relative: formattedRelativeDate(props.project.updatedAt, { capitalize: true })
}
})
</script>
41 changes: 41 additions & 0 deletions packages/frontend-2/components/dashboard/TutorialCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<template>
<NuxtLink :to="tutorial.url" target="_blank">
<div
class="bg-foundation border border-outline-3 rounded-xl flex flex-col overflow-hidden hover:border-outline-5 transition"
>
<div
:style="{ backgroundImage: `url(${tutorial.featureImage})` }"
class="bg-foundation-page bg-cover bg-center w-full h-32"
/>
<div class="p-5 pb-4">
<h3 v-if="tutorial.title" class="text-body-2xs text-foreground truncate">
{{ tutorial.title }}
</h3>
<p class="text-body-3xs text-foreground-2 capitalize mt-2">
<span v-tippy="updatedAt.full">
{{ updatedAt.relative }}
</span>
<template v-if="tutorial.readingTime">
<span class="pl-1 pr-2">•</span>
{{ tutorial.readingTime }}m read
</template>
</p>
</div>
</div>
</NuxtLink>
</template>

<script lang="ts" setup>
import type { TutorialItem } from '~~/lib/dashboard/helpers/types'

const props = defineProps<{
tutorial: TutorialItem
}>()

const updatedAt = computed(() => {
return {
full: formattedFullDate(props.tutorial.publishedAt),
relative: formattedRelativeDate(props.tutorial.publishedAt, { capitalize: true })
}
})
</script>
34 changes: 34 additions & 0 deletions packages/frontend-2/components/quick-start/Card.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<template>
<div class="border border-outline-3 rounded-lg p-5 pt-4">
<p class="text-heading-sm text-foreground">{{ title }}</p>
<p class="text-body-xs text-foreground-2 pt-1">{{ description }}</p>

<div
v-if="buttons"
class="flex flex-col flex-wrap md:flex-row gap-y-2 md:gap-x-2 gap-y-0 mt-3"
>
<FormButton
v-for="(button, index) in buttons"
:key="button.id || index"
v-bind="button.props || {}"
:disabled="button.props?.disabled || button.disabled"
:submit="button.props?.submit || button.submit"
size="sm"
color="outline"
@click="($event) => button.onClick?.($event)"
>
{{ button.text }}
</FormButton>
</div>
</div>
</template>

<script lang="ts" setup>
import { type LayoutDialogButton } from '@speckle/ui-components'

defineProps<{
title: string
description: string
buttons?: LayoutDialogButton[]
}>()
</script>
10 changes: 10 additions & 0 deletions packages/frontend-2/lib/common/generated/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const documents = {
"\n fragment AutomateViewerPanel_AutomateRun on AutomateRun {\n id\n functionRuns {\n id\n ...AutomateViewerPanelFunctionRunRow_AutomateFunctionRun\n }\n ...AutomationsStatusOrderedRuns_AutomationRun\n }\n": types.AutomateViewerPanel_AutomateRunFragmentDoc,
"\n fragment AutomateViewerPanelFunctionRunRow_AutomateFunctionRun on AutomateFunctionRun {\n id\n results\n status\n statusMessage\n contextView\n function {\n id\n logo\n name\n }\n createdAt\n updatedAt\n }\n": types.AutomateViewerPanelFunctionRunRow_AutomateFunctionRunFragmentDoc,
"\n fragment CommonModelSelectorModel on Model {\n id\n name\n }\n": types.CommonModelSelectorModelFragmentDoc,
"\n fragment DashboardProjectCard_Project on Project {\n id\n name\n role\n updatedAt\n models {\n totalCount\n }\n team {\n user {\n ...LimitedUserAvatar\n }\n }\n }\n": types.DashboardProjectCard_ProjectFragmentDoc,
"\n fragment FormSelectModels_Model on Model {\n id\n name\n }\n": types.FormSelectModels_ModelFragmentDoc,
"\n fragment FormSelectProjects_Project on Project {\n id\n name\n }\n": types.FormSelectProjects_ProjectFragmentDoc,
"\n fragment FormUsersSelectItem on LimitedUser {\n id\n name\n avatar\n }\n": types.FormUsersSelectItemFragmentDoc,
Expand Down Expand Up @@ -116,6 +117,7 @@ const documents = {
"\n query ServerInfoAllScopes {\n serverInfo {\n scopes {\n name\n description\n }\n }\n }\n": types.ServerInfoAllScopesDocument,
"\n query ProjectModelsSelectorValues($projectId: String!, $cursor: String) {\n project(id: $projectId) {\n id\n models(limit: 100, cursor: $cursor) {\n cursor\n totalCount\n items {\n ...CommonModelSelectorModel\n }\n }\n }\n }\n": types.ProjectModelsSelectorValuesDocument,
"\n query MainServerInfoData {\n serverInfo {\n adminContact\n blobSizeLimitBytes\n canonicalUrl\n company\n description\n guestModeEnabled\n inviteOnly\n name\n termsOfService\n version\n automateUrl\n }\n }\n": types.MainServerInfoDataDocument,
"\n query DashboardProjectsPageQuery {\n activeUser {\n id\n projects(limit: 3) {\n items {\n ...DashboardProjectCard_Project\n }\n }\n }\n }\n": types.DashboardProjectsPageQueryDocument,
"\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,
Expand Down Expand Up @@ -354,6 +356,10 @@ export function graphql(source: "\n fragment AutomateViewerPanelFunctionRunRow_
* 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 CommonModelSelectorModel on Model {\n id\n name\n }\n"): (typeof documents)["\n fragment CommonModelSelectorModel on Model {\n id\n name\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 fragment DashboardProjectCard_Project on Project {\n id\n name\n role\n updatedAt\n models {\n totalCount\n }\n team {\n user {\n ...LimitedUserAvatar\n }\n }\n }\n"): (typeof documents)["\n fragment DashboardProjectCard_Project on Project {\n id\n name\n role\n updatedAt\n models {\n totalCount\n }\n team {\n user {\n ...LimitedUserAvatar\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down Expand Up @@ -670,6 +676,10 @@ export function graphql(source: "\n query ProjectModelsSelectorValues($projectI
* 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 MainServerInfoData {\n serverInfo {\n adminContact\n blobSizeLimitBytes\n canonicalUrl\n company\n description\n guestModeEnabled\n inviteOnly\n name\n termsOfService\n version\n automateUrl\n }\n }\n"): (typeof documents)["\n query MainServerInfoData {\n serverInfo {\n adminContact\n blobSizeLimitBytes\n canonicalUrl\n company\n description\n guestModeEnabled\n inviteOnly\n name\n termsOfService\n version\n automateUrl\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 DashboardProjectsPageQuery {\n activeUser {\n id\n projects(limit: 3) {\n items {\n ...DashboardProjectCard_Project\n }\n }\n }\n }\n"): (typeof documents)["\n query DashboardProjectsPageQuery {\n activeUser {\n id\n projects(limit: 3) {\n items {\n ...DashboardProjectCard_Project\n }\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
Loading