Feat: Remove workspace promobanner (#3185)
This commit is contained in:
@@ -26,7 +26,9 @@
|
||||
class="absolute z-40 lg:static h-full flex w-[17rem] shrink-0 transition-all"
|
||||
:class="isOpenMobile ? '' : '-translate-x-[17rem] lg:translate-x-0'"
|
||||
>
|
||||
<LayoutSidebar class="border-r border-outline-3 px-2 py-3 bg-foundation-page">
|
||||
<LayoutSidebar
|
||||
class="border-r border-outline-3 px-2 pt-3 pb-2 bg-foundation-page"
|
||||
>
|
||||
<LayoutSidebarMenu>
|
||||
<LayoutSidebarMenuGroup>
|
||||
<NuxtLink :to="homeRoute" @click="isOpenMobile = false">
|
||||
@@ -156,6 +158,14 @@
|
||||
</NuxtLink>
|
||||
</LayoutSidebarMenuGroup>
|
||||
</LayoutSidebarMenu>
|
||||
<template #promo>
|
||||
<LayoutSidebarPromo
|
||||
title="SpeckleCon 2024"
|
||||
text="Join us in London on Nov 13-14 for the ultimate community event."
|
||||
button-text="Get tickets"
|
||||
@on-click="onPromoClick"
|
||||
/>
|
||||
</template>
|
||||
</LayoutSidebar>
|
||||
</div>
|
||||
</template>
|
||||
@@ -173,6 +183,7 @@
|
||||
import {
|
||||
FormButton,
|
||||
LayoutSidebar,
|
||||
LayoutSidebarPromo,
|
||||
LayoutSidebarMenu,
|
||||
LayoutSidebarMenuGroup,
|
||||
LayoutSidebarMenuGroupItem
|
||||
@@ -249,6 +260,15 @@ onWorkspaceResult((result) => {
|
||||
}
|
||||
})
|
||||
|
||||
const onPromoClick = () => {
|
||||
mixpanel.track('Promo Banner Clicked', {
|
||||
source: 'sidebar',
|
||||
campaign: 'specklecon2024'
|
||||
})
|
||||
|
||||
window.open('https://conf.speckle.systems/', '_blank')
|
||||
}
|
||||
|
||||
const openFeedbackDialog = () => {
|
||||
showFeedbackDialog.value = true
|
||||
isOpenMobile.value = false
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<div class="position left-2 sm:left-auto right-2 bottom-2 fixed z-[45]">
|
||||
<div
|
||||
v-if="showBanner"
|
||||
class="rounded-lg flex flex-col w-full sm:max-w-96 border border-outline-2 shadow-md bg-foundation-3 dark:bg-foundation"
|
||||
>
|
||||
<img :src="bannerImage" class="w-full" alt="Try workspaces" />
|
||||
<div class="px-5 py-6 flex flex-col gap-y-2">
|
||||
<h5 class="text-body-xs md:text-heading-sm text-foreground font-medium">
|
||||
Still not using workspaces?
|
||||
</h5>
|
||||
<p class="text-body-2xs leading-5 md:text-body-xs text-foreground-2">
|
||||
Be the first to reach more security options, data control, and better
|
||||
project management with your team.
|
||||
</p>
|
||||
<div class="flex items-center gap-x-2 mt-2">
|
||||
<FormButton color="primary" size="sm" @click="openWorkspaceCreateDialog">
|
||||
Start for free
|
||||
</FormButton>
|
||||
<FormButton color="subtle" size="sm" @click="dismissedCookie = true">
|
||||
Dismiss
|
||||
</FormButton>
|
||||
</div>
|
||||
</div>
|
||||
<WorkspaceCreateDialog
|
||||
v-model:open="showWorkspaceCreateDialog"
|
||||
navigate-on-success
|
||||
event-source="promo-banner"
|
||||
@created="dismissedCookie = true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// This is a temporary component, to meassure if in app-notifications can be succesful
|
||||
// It will be remove after a certain period, if we continue with in-app notification we should further develop this
|
||||
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useSynchronizedCookie } from '~~/lib/common/composables/reactiveCookie'
|
||||
import { CookieKeys } from '~/lib/common/helpers/constants'
|
||||
import { useIsWorkspacesEnabled } from '~/composables/globals'
|
||||
import { settingsSidebarQuery } from '~/lib/settings/graphql/queries'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { useTheme } from '~~/lib/core/composables/theme'
|
||||
import { useBreakpoints } from '@vueuse/core'
|
||||
import { TailwindBreakpoints } from '~~/lib/common/helpers/tailwind'
|
||||
import imageLight from '~/assets/images/banners/workspace-promo-light.png'
|
||||
import imageDark from '~/assets/images/banners/workspace-promo-dark.png'
|
||||
import imageMobileLight from '~/assets/images/banners/workspace-promo-mobile-light.png'
|
||||
import imageMobileDark from '~/assets/images/banners/workspace-promo-mobile-dark.png'
|
||||
|
||||
const { isLoggedIn } = useActiveUser()
|
||||
const breakpoints = useBreakpoints(TailwindBreakpoints)
|
||||
const { isDarkTheme } = useTheme()
|
||||
const isWorkspacesEnabled = useIsWorkspacesEnabled()
|
||||
const mixpanel = useMixpanel()
|
||||
const dismissedCookie = useSynchronizedCookie<boolean>(
|
||||
CookieKeys.DismissedWorkspaceBanner,
|
||||
{
|
||||
default: () => false
|
||||
}
|
||||
)
|
||||
const { result } = useQuery(settingsSidebarQuery, null, {
|
||||
enabled: isWorkspacesEnabled.value
|
||||
})
|
||||
|
||||
const showWorkspaceCreateDialog = ref(false)
|
||||
const isMobile = breakpoints.smaller('md')
|
||||
|
||||
const bannerImage = computed(() => {
|
||||
if (isMobile.value) {
|
||||
return isDarkTheme.value ? imageMobileDark : imageMobileLight
|
||||
}
|
||||
return isDarkTheme.value ? imageDark : imageLight
|
||||
})
|
||||
const hasWorkspaces = computed(() =>
|
||||
result.value?.activeUser?.workspaces.items
|
||||
? result.value.activeUser.workspaces.items.length > 0
|
||||
: false
|
||||
)
|
||||
const showBanner = computed(
|
||||
() =>
|
||||
isWorkspacesEnabled.value &&
|
||||
isLoggedIn.value &&
|
||||
!hasWorkspaces.value &&
|
||||
(import.meta.client ? !dismissedCookie.value : false)
|
||||
)
|
||||
|
||||
const openWorkspaceCreateDialog = () => {
|
||||
showWorkspaceCreateDialog.value = true
|
||||
mixpanel.track('Create Workspace Button Clicked', {
|
||||
source: 'promo-banner'
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
showBanner,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
mixpanel.track('Workspace Promo Banner Viewed', {
|
||||
source: 'promo-banner'
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
</script>
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<HeaderNavBar />
|
||||
<PromoBannersWorkspace />
|
||||
<div class="h-dvh w-dvh overflow-hidden flex flex-col">
|
||||
<!-- Static Spacer to allow for absolutely positioned HeaderNavBar -->
|
||||
<div class="h-12 w-full shrink-0"></div>
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
<Portal to="navigation">
|
||||
<HeaderNavLink :to="homeRoute" name="Dashboard" hide-chevron :separator="false" />
|
||||
</Portal>
|
||||
<PromoBannersWrapper v-if="promoBanners.length" :banners="promoBanners" />
|
||||
<PromoBannersWrapper
|
||||
v-if="promoBanners && promoBanners.length"
|
||||
:banners="promoBanners"
|
||||
/>
|
||||
<ProjectsDashboardHeader
|
||||
:projects-invites="projectsResult?.activeUser || undefined"
|
||||
:workspaces-invites="workspacesResult?.activeUser || undefined"
|
||||
@@ -72,7 +75,6 @@ import { downloadManager } from '~~/lib/common/utils/downloadManager'
|
||||
import { ToastNotificationType, useGlobalToast } from '~~/lib/common/composables/toast'
|
||||
import type { LayoutDialogButton } from '@speckle/ui-components'
|
||||
import type { PromoBanner } from '~/lib/promo-banners/types'
|
||||
import speckleconTicketsImage from '~/assets/images/banners/grab-your-tickets.gif'
|
||||
|
||||
useHead({ title: 'Dashboard' })
|
||||
|
||||
@@ -187,14 +189,5 @@ const onDownloadManager = (extension: ManagerExtension) => {
|
||||
}
|
||||
}
|
||||
|
||||
const promoBanners = ref<PromoBanner[]>([
|
||||
{
|
||||
primaryText: 'Specklecon - Grab your tickets',
|
||||
url: 'https://conf.speckle.systems/',
|
||||
priority: 1,
|
||||
expiryDate: '2024-11-14',
|
||||
image: speckleconTicketsImage,
|
||||
isBackgroundImage: true
|
||||
}
|
||||
])
|
||||
const promoBanners = ref<PromoBanner[]>()
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col gap-y-1 sm:gap-y-2 border border-outline-3 rounded-lg py-2 px-3 sm:p-4 select-none"
|
||||
>
|
||||
<h6
|
||||
v-if="title"
|
||||
class="text-body-xs sm:text-heading-sm font-medium text-foreground"
|
||||
>
|
||||
{{ title }}
|
||||
</h6>
|
||||
<p v-if="text" class="text-body-2xs sm:text-body-xs text-foreground-2 !leading-5">
|
||||
{{ text }}
|
||||
</p>
|
||||
<FormButton
|
||||
v-if="buttonText"
|
||||
size="sm"
|
||||
class="mt-1"
|
||||
:to="to"
|
||||
:target="to ? '_blank' : undefined"
|
||||
@click="$emit('onClick')"
|
||||
>
|
||||
{{ buttonText }}
|
||||
</FormButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FormButton from '~~/src/components/form/Button.vue'
|
||||
|
||||
defineEmits(['onClick'])
|
||||
|
||||
defineProps<{
|
||||
title?: string
|
||||
text?: string
|
||||
to?: string
|
||||
buttonText?: string
|
||||
}>()
|
||||
</script>
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3'
|
||||
import LayoutSidebar from '~~/src/components/layout/sidebar/Sidebar.vue'
|
||||
import LayoutSidebarPromo from '~~/src/components/layout/sidebar/Promo.vue'
|
||||
import LayoutSidebarMenu from '~~/src/components/layout/sidebar/menu/Menu.vue'
|
||||
import LayoutSidebarMenuGroup from '~~/src/components/layout/sidebar/menu/group/Group.vue'
|
||||
import LayoutSidebarMenuGroupItem from '~~/src/components/layout/sidebar/menu/group/Item.vue'
|
||||
@@ -20,6 +21,7 @@ export const Dashboard: StoryObj = {
|
||||
render: (args) => ({
|
||||
components: {
|
||||
LayoutSidebar,
|
||||
LayoutSidebarPromo,
|
||||
LayoutSidebarMenu,
|
||||
LayoutSidebarMenuGroup,
|
||||
LayoutSidebarMenuGroupItem,
|
||||
@@ -68,6 +70,9 @@ export const Dashboard: StoryObj = {
|
||||
</LayoutSidebarMenuGroupItem>
|
||||
</LayoutSidebarMenuGroup>
|
||||
</LayoutSidebarMenu>
|
||||
<template #promo>
|
||||
<LayoutSidebarPromo title="Example Title" text="An example piece of text" />
|
||||
</template>
|
||||
</LayoutSidebar>
|
||||
`
|
||||
})
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
<template>
|
||||
<!-- If promo content is defined, scroll the menu items. If not, scroll the whole aside -->
|
||||
<aside
|
||||
class="flex flex-col justify-between h-full w-full overflow-y-auto overflow-x-hidden simple-scrollbar"
|
||||
class="flex flex-col justify-between h-full w-full"
|
||||
:class="$slots.promo ? '' : 'overflow-y-auto overflow-x-hidden simple-scrollbar'"
|
||||
>
|
||||
<slot></slot>
|
||||
<div
|
||||
class="flex flex-col h-full w-full"
|
||||
:class="$slots.promo ? 'overflow-y-auto overflow-x-hidden simple-scrollbar' : ''"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div v-if="$slots.promo" class="shrink-0 pt-2">
|
||||
<slot name="promo"></slot>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
@@ -65,6 +65,7 @@ import InfiniteLoading from '~~/src/components/InfiniteLoading.vue'
|
||||
import type { InfiniteLoaderState } from '~~/src/helpers/global/components'
|
||||
import LayoutPanel from '~~/src/components/layout/Panel.vue'
|
||||
import LayoutSidebar from '~~/src/components/layout/sidebar/Sidebar.vue'
|
||||
import LayoutSidebarPromo from '~~/src/components/layout/sidebar/Promo.vue'
|
||||
import LayoutSidebarMenu from '~~/src/components/layout/sidebar/menu/Menu.vue'
|
||||
import LayoutSidebarMenuGroup from '~~/src/components/layout/sidebar/menu/group/Group.vue'
|
||||
import LayoutSidebarMenuGroupItem from '~~/src/components/layout/sidebar/menu/group/Item.vue'
|
||||
@@ -153,6 +154,7 @@ export {
|
||||
LayoutTabsVertical,
|
||||
LayoutTable,
|
||||
LayoutSidebar,
|
||||
LayoutSidebarPromo,
|
||||
LayoutSidebarMenu,
|
||||
LayoutSidebarMenuGroup,
|
||||
LayoutSidebarMenuGroupItem,
|
||||
|
||||
Reference in New Issue
Block a user