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(fe2): Add 2024 specklecon banners #2765

Merged
merged 1 commit into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 21 additions & 2 deletions packages/frontend-2/components/projects/DashboardHeader.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<template>
<div>
<PromoBannersWrapper v-if="promoBanners.length" :banners="promoBanners" />
<div class="bg-foundation divide-y divide-outline-3 mb-8 empty:mb-0">
<ProjectsInviteBanners
v-if="projectsInvites?.projectInvites?.length"
Expand All @@ -13,7 +14,6 @@
:invites="workspacesInvites"
/>
</div>
<PromoBannersWrapper v-if="promoBanners.length" :banners="promoBanners" />
</div>
</template>
<script setup lang="ts">
Expand All @@ -23,6 +23,8 @@ import type {
ProjectsDashboardHeaderWorkspaces_UserFragment
} from '~/lib/common/generated/gql/graphql'
import type { PromoBanner } from '~/lib/promo-banners/types'
import submitImage from '~/assets/images/banners/submit.gif'
import earlybirdImage from '~/assets/images/banners/earlybird.gif'

graphql(`
fragment ProjectsDashboardHeaderProjects_User on User {
Expand All @@ -41,5 +43,22 @@ defineProps<{
workspacesInvites?: ProjectsDashboardHeaderWorkspaces_UserFragment
}>()

const promoBanners = ref<PromoBanner[]>([])
const promoBanners = ref<PromoBanner[]>([
{
primaryText: 'Specklecon - Submit your proposal',
url: 'https://conf.speckle.systems/',
priority: 1,
expiryDate: '2024-09-02',
image: submitImage,
isBackgroundImage: true
},
{
primaryText: 'Specklecon - Early Bird Tickets',
url: 'https://conf.speckle.systems/',
priority: 2,
expiryDate: '2024-09-15',
image: earlybirdImage,
isBackgroundImage: true
}
])
</script>
60 changes: 19 additions & 41 deletions packages/frontend-2/components/promo-banners/Banner.vue
Original file line number Diff line number Diff line change
@@ -1,55 +1,33 @@
<template>
<!--
Banner Component has been built for the hackathon, but as new events
are added and the banners designed, this can be modified to allow new styles
-->
<div
class="p-6 lg:p-8 bg-[#27272a] text-white rounded-xl w-full flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4 sm:gap-6 mb-2 select-none"
<NuxtLink
:to="url"
target="_blank"
class="rounded-xl w-full flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4 sm:gap-6 mb-4 select-none overflow-hidden"
:class="isBackgroundImage ? '' : 'bg-[#27272a] text-white p-6 lg:p-8'"
>
<div class="flex flex-col lg:flex-row gap-2 lg:gap-6 items-start lg:items-center">
<!-- Hardcoded image to image for first banner - Need CMS setup to handle this for future banners -->
<img
src="~~/assets/images/banners/speckleverse.svg"
class="h-10 sm:h-11"
:alt="primaryText"
/>
<img
v-if="isBackgroundImage"
:src="image"
class="w-full h-full"
:alt="primaryText"
/>
<div
v-else
class="flex flex-col lg:flex-row gap-2 lg:gap-6 items-start lg:items-center"
>
<div class="flex flex-col sm:flex-row sm:gap-1">
<span class="font-medium">{{ primaryText }}</span>
<span v-if="secondaryText">{{ secondaryText }}</span>
</div>
</div>

<div class="flex gap-2">
<a :href="url" target="_blank">
<button
class="bg-white/90 hover:bg-white border border-transparent rounded py-1 px-2.5 text-[#27272a] font-medium text-sm sm:text-base"
>
Learn more
</button>
</a>

<button
class="bg-transparent hover:bg-white/10 border border-white rounded py-1 px-2.5 text-white text-sm sm:text-base"
@click="dismissBanner"
>
Close
</button>
</div>
</div>
</NuxtLink>
</template>
<script setup lang="ts">
const props = defineProps<{
id: string
defineProps<{
primaryText: string
secondaryText?: string
url: string
image: string
isBackgroundImage?: boolean
}>()

const emit = defineEmits<{
(e: 'banner-dismissed', id: string): void
}>()

function dismissBanner() {
emit('banner-dismissed', props.id)
}
</script>
61 changes: 13 additions & 48 deletions packages/frontend-2/components/promo-banners/Wrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,33 @@
<div>
<PromoBannersBanner
v-if="activeBanner"
:id="activeBanner.id"
:primary-text="activeBanner.primaryText"
:secondary-text="activeBanner.secondaryText"
:url="activeBanner.url"
@banner-dismissed="activeBanner && handleDismissed(activeBanner.id)"
:image="activeBanner.image"
:is-background-image="activeBanner.isBackgroundImage"
></PromoBannersBanner>
</div>
</template>

<script setup lang="ts">
import { useSynchronizedCookie } from '~/lib/common/composables/reactiveCookie'
import dayjs from 'dayjs'
import type { PromoBanner } from '~/lib/promo-banners/types'

const props = defineProps<{
banners: PromoBanner[]
}>()

const hideAllPromoBanners = useSynchronizedCookie<boolean>('hide-all-promo-banners', {
expires: dayjs().add(1, 'day').toDate()
// Filter and sort the banners based on expiry date and priority
const activeBanner = computed(() => {
return (
props.banners
.filter((banner) => {
// Check if the banner is not expired
const expiryDate = dayjs(banner.expiryDate, 'YYYY-MM-DD')
return dayjs().isBefore(expiryDate)
})
.sort((a, b) => a.priority - b.priority)[0] || null
) // Return the highest priority banner or null if all are expired
})

const bannerCookies = ref<Map<string, Ref<boolean>>>(new Map())
const activeBannerId = ref<string | null>(null)

props.banners.forEach((banner) => {
bannerCookies.value.set(
banner.id,
useSynchronizedCookie<boolean>(`banner-dismissed-${banner.id}`)
)
})

const sortedBanners = computed(() => {
return props.banners
.filter((banner) => {
const expiryDate = dayjs(banner.expiryDate, 'YYYY-MM-DD')
return dayjs().isBefore(expiryDate)
})
.sort((a, b) => a.priority - b.priority)
})

// Determine the active banner based on the sorted list, expiry date and cookie status
sortedBanners.value.forEach((banner) => {
const cookie = bannerCookies.value.get(banner.id)
if (cookie && !cookie.value && !hideAllPromoBanners.value && !activeBannerId.value) {
activeBannerId.value = banner.id
}
})

const activeBanner = computed(() =>
activeBannerId.value
? props.banners.find((banner) => banner.id === activeBannerId.value)
: null
)

function handleDismissed(id: string) {
hideAllPromoBanners.value = true
activeBannerId.value = null

const dismissedCookie = bannerCookies.value.get(id)
if (dismissedCookie) {
dismissedCookie.value = true
}
}
</script>
3 changes: 2 additions & 1 deletion packages/frontend-2/lib/promo-banners/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
export type PromoBanner = {
id: string
primaryText: string
secondaryText?: string
url: string
priority: number
expiryDate: string // ISO date string, e.g. "YYYY-MM-DD"
image: string
isBackgroundImage?: boolean
}