Skip to content

Commit

Permalink
feat: Add team sections to the Custom category in activity library (#…
Browse files Browse the repository at this point in the history
…9511)

* feat: Add team sections to the Custom category in activity library

* Clean up create custom activity card
  • Loading branch information
Dschoordsch committed Mar 7, 2024
1 parent e7539d1 commit 2338414
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 63 deletions.
145 changes: 86 additions & 59 deletions packages/client/components/ActivityLibrary/ActivityLibrary.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as ScrollArea from '@radix-ui/react-scroll-area'
import graphql from 'babel-plugin-relay/macro'
import clsx from 'clsx'
import React, {useEffect, useMemo} from 'react'
import React, {Fragment, useEffect, useMemo} from 'react'
import {PreloadedQuery, commitLocalUpdate, usePreloadedQuery} from 'react-relay'
import {Redirect} from 'react-router'
import {Link} from 'react-router-dom'
Expand Down Expand Up @@ -134,17 +134,63 @@ const CategoryIDToColorClass = Object.fromEntries(

export type Template = Omit<ActivityLibrary_template$data, ' $fragmentType'>

type SubCategory = 'popular' | 'recentlyUsed' | 'recentlyUsedInOrg' | 'neverTried' | 'getStarted'

const subCategoryMapping: Record<SubCategory, string> = {
const MAX_PER_SUBCATEGORY = 6
const subCategoryMapping = {
popular: 'Popular templates',
recentlyUsed: 'You used these recently',
recentlyUsedInOrg: 'Others in your organization are using',
neverTried: 'Try these activities',
getStarted: 'Activities to get you started'
getStarted: 'Activities to get you started',
other: 'Other activities'
} as const

const mapRetroSubCategories = (templates: readonly Template[]) => {
const subCategoryTemplates = Object.fromEntries(
Object.keys(subCategoryMapping).map((subCategory) => {
return [
subCategory,
templates
.filter((template) => template.subCategories?.includes(subCategory))
.slice(0, MAX_PER_SUBCATEGORY) as Template[]
]
})
)
//just in case there were values already
subCategoryTemplates.other = []
subCategoryTemplates.other = templates.filter(
(template) =>
!Object.values(subCategoryTemplates)
.flat()
.find((catTemplate) => catTemplate.id === template.id)
)

return Object.fromEntries(
Object.entries(subCategoryTemplates).map(([key, value]) => {
return [subCategoryMapping[key as keyof typeof subCategoryMapping]!, value]
})
)
}

const MAX_PER_SUBCATEGORY = 6
const mapTeamCategories = (templates: readonly Template[]) => {
// list public templates last
const publicTemplates = [] as Template[]
const mapped = templates.reduce((acc, template) => {
const {team, scope} = template
if (scope === 'PUBLIC') {
publicTemplates.push(template)
} else {
const {name} = team
if (!acc[name]) {
acc[name] = []
}
acc[name]!.push(template)
}
return acc
}, {} as Record<string, Template[]>)

mapped['Parabol'] = publicTemplates
return mapped
}

export const ActivityLibrary = (props: Props) => {
const atmosphere = useAtmosphere()
Expand Down Expand Up @@ -209,23 +255,24 @@ export const ActivityLibrary = (props: Props) => {
)
}, [searchQuery, filteredTemplates, categoryId])

const subCategoryTemplates = Object.fromEntries(
Object.keys(subCategoryMapping).map((subCategory) => {
return [
subCategory,
templatesToRender
.filter((template) => template.subCategories?.includes(subCategory))
.slice(0, MAX_PER_SUBCATEGORY) as Template[]
]
})
) as Record<SubCategory, Template[]>
const sectionedTemplates = useMemo(() => {
// Show the teams on search as well, because you can search by team name
if (categoryId === CUSTOM_CATEGORY_ID || searchQuery.length > 0) {
return mapTeamCategories(templatesToRender)
}

const otherTemplates = templatesToRender.filter(
(template) =>
!Object.values(subCategoryTemplates)
.flat()
.find((catTemplate) => catTemplate.id === template.id)
) as Template[]
if (searchQuery.length > 0) {
return undefined
}

if (categoryId === 'retrospective') {
return mapRetroSubCategories(templatesToRender)
}
if (categoryId === 'recommended') {
return {[subCategoryMapping.getStarted]: [...templatesToRender]}
}
return undefined
}, [categoryId, templatesToRender])

if (!featureFlags.retrosInDisguise) {
return <Redirect to='/404' />
Expand All @@ -235,10 +282,6 @@ export const ActivityLibrary = (props: Props) => {
return <Redirect to={`/activity-library/category/${QUICK_START_CATEGORY_ID}`} />
}

const selectedCategory = categoryId as CategoryID | typeof QUICK_START_CATEGORY_ID
const quickStartTitle =
selectedCategory === 'recommended' ? subCategoryMapping['getStarted'] : undefined

return (
<div className='flex h-full w-full flex-col bg-white'>
<div className='mx-2 flex'>
Expand Down Expand Up @@ -272,7 +315,7 @@ export const ActivityLibrary = (props: Props) => {
)}
<Link
className='rounded-full bg-sky-500 px-4 py-2 text-sm font-medium text-white hover:bg-sky-600'
to={`/activity-library/new-activity/${selectedCategory}`}
to={`/activity-library/new-activity/${categoryId}`}
>
Create custom activity
</Link>
Expand All @@ -297,7 +340,7 @@ export const ActivityLibrary = (props: Props) => {
<Link
className={clsx(
'flex-shrink-0 cursor-pointer rounded-full py-2 px-4 text-sm text-slate-800',
category === selectedCategory && searchQuery.length === 0
category === categoryId && searchQuery.length === 0
? [
`${CategoryIDToColorClass[category]}`,
'font-semibold text-white focus:text-white'
Expand Down Expand Up @@ -326,8 +369,8 @@ export const ActivityLibrary = (props: Props) => {
)}
{templatesToRender.length === 0 ? (
<div className='mx-auto flex p-2 text-slate-700'>
<img className='w-32' src={halloweenRetrospectiveTemplate} />
<div className='ml-10'>
<img className='w-32' src={halloweenRetrospectiveTemplate} />
<div className='mb-4 text-xl font-semibold'>No results found!</div>
<div className='mb-6 max-w-[360px]'>
Try tapping a category above, using a different search, or creating exactly what
Expand All @@ -340,49 +383,33 @@ export const ActivityLibrary = (props: Props) => {
</div>
) : (
<>
{categoryId === 'retrospective' && searchQuery.length === 0 ? (
{sectionedTemplates ? (
<>
{(Object.keys(subCategoryMapping) as SubCategory[]).map(
(subCategory) =>
subCategoryTemplates[subCategory].length > 0 && (
<>
<div className='ml-4 mt-8 text-xl font-bold text-slate-700'>
{subCategoryMapping[subCategory]}
</div>
{Object.entries(sectionedTemplates).map(
([subCategory, subCategoryTemplates]) =>
subCategoryTemplates.length > 0 && (
<Fragment key={subCategory}>
{subCategory && (
<div className='ml-4 mt-8 text-xl font-bold text-slate-700'>
{subCategory}
</div>
)}
<div className='mt-1 grid auto-rows-fr grid-cols-[repeat(auto-fill,minmax(min(40%,256px),1fr))] gap-4 px-4 md:mt-4'>
<ActivityGrid
templates={subCategoryTemplates[subCategory]}
selectedCategory={selectedCategory}
templates={subCategoryTemplates}
selectedCategory={categoryId}
/>
</div>
</>
</Fragment>
)
)}
{otherTemplates.length > 0 && (
<>
<div className='ml-4 mt-8 text-xl font-bold text-slate-700'>
Other activities
</div>
<div className='mt-1 grid auto-rows-fr grid-cols-[repeat(auto-fill,minmax(min(40%,256px),1fr))] gap-4 px-4 md:mt-4'>
<ActivityGrid
templates={otherTemplates}
selectedCategory={selectedCategory}
/>
</div>
</>
)}
</>
) : (
<>
{quickStartTitle && (
<div className='ml-4 mt-8 text-xl font-bold text-slate-700'>
{quickStartTitle}
</div>
)}
<div className='grid auto-rows-fr grid-cols-[repeat(auto-fill,minmax(min(40%,256px),1fr))] gap-4 p-4'>
<ActivityGrid
templates={templatesToRender as Template[]}
selectedCategory={selectedCategory}
selectedCategory={categoryId}
/>
</div>
</>
Expand Down
4 changes: 2 additions & 2 deletions packages/client/components/ActivityLibrary/Categories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ export const CATEGORY_THEMES: Record<AllCategoryID, CardTheme> = {

export const CATEGORY_ID_TO_NAME: Record<AllCategoryID, string> = {
[QUICK_START_CATEGORY_ID]: 'Quick Start',
[CUSTOM_CATEGORY_ID]: 'Custom',
retrospective: 'Retrospective',
estimation: 'Estimation',
standup: 'Standup',
feedback: 'Feedback',
strategy: 'Strategy',
premortem: 'Pre-Mortem',
postmortem: 'Post-Mortem',
[CUSTOM_CATEGORY_ID]: 'Custom'
postmortem: 'Post-Mortem'
}

export const MEETING_TYPE_TO_CATEGORY: Record<MeetingTypeEnum, CategoryID> = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ const CreateActivityCard = (props: Props) => {
<ActivityLibraryCard
className={'flex-1 cursor-pointer'}
theme={CATEGORY_THEMES[category]}
badge={<ActivityBadge className='mx-2 bg-gold-300 text-grape-700'>Premium</ActivityBadge>}
badge={<ActivityBadge className='m-2 bg-gold-300 text-grape-700'>Premium</ActivityBadge>}
>
<div className='flex flex-1 flex-col items-center justify-center text-center font-semibold md:mx-10'>
<div className='flex h-full w-full flex-col items-center justify-center pb-2 font-semibold'>
<div className='h-12 w-12'>
<AddIcon className='h-full w-full text-slate-700' />
</div>
Expand Down

0 comments on commit 2338414

Please sign in to comment.