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: update activity library card UI #9168

Merged
merged 12 commits into from
Dec 4, 2023
77 changes: 43 additions & 34 deletions packages/client/components/ActivityLibrary/ActivityCard.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,77 @@
import clsx from 'clsx'
import React, {ComponentPropsWithoutRef, PropsWithChildren} from 'react'
import React, {PropsWithChildren} from 'react'
import {upperFirst} from '../../utils/upperFirst'
import {MeetingTypeEnum} from '../../__generated__/NewMeetingQuery.graphql'
import {backgroundImgMap, CategoryID, MEETING_TYPE_TO_CATEGORY} from './Categories'
import {twMerge} from 'tailwind-merge'

export interface CardTheme {
primary: string
secondary: string
}

export const ActivityCardImage = (
props: PropsWithChildren<React.ImgHTMLAttributes<HTMLImageElement>>
) => {
const {className, src} = props

return (
<div
className={clsx(
'my-1 flex flex-1 items-center justify-center overflow-hidden px-4',
className
)}
>
<img className={'h-full w-full object-contain'} src={src} />
</div>
)
type ActivityCardImageProps = {
className?: string
src: string
category: CategoryID
}

const ActivityCardTitle = (props: ComponentPropsWithoutRef<'div'>) => {
const {children, className, ...rest} = props
export const ActivityCardImage = (props: PropsWithChildren<ActivityCardImageProps>) => {
const {className, src, category} = props
const backgroundSrc = backgroundImgMap[category]

return (
<div
className={clsx(
'px-2 py-1 text-sm font-semibold leading-5 text-slate-800 sm:text-base',
className={twMerge(
'relative flex h-full w-full items-center justify-center overflow-hidden',
className
)}
{...rest}
style={{backgroundImage: `url(${backgroundSrc})`, backgroundSize: 'cover'}}
>
{children}
<img
className='absolute top-0 left-0 z-10 h-full w-full object-contain p-10'
src={src}
alt='Card Illustration'
/>
</div>
)
}

export interface ActivityCardProps {
className?: string
theme: CardTheme
titleAs?: React.ElementType
title?: string
badge?: React.ReactNode
children?: React.ReactNode
type?: MeetingTypeEnum
}

export const ActivityCard = (props: ActivityCardProps) => {
const {className, theme, title, titleAs, badge, children} = props
const Title = titleAs ?? ActivityCardTitle
const {className, theme, title, children, type, badge} = props
const category = type && MEETING_TYPE_TO_CATEGORY[type]

return (
<div className={clsx('flex flex-col overflow-hidden rounded-lg', theme.secondary, className)}>
<div className='flex flex-shrink-0'>
<Title>{title}</Title>
<div className={clsx('ml-auto h-8 w-8 flex-shrink-0 rounded-bl-full', theme.primary)} />
</div>
{children}
<div className='flex flex-shrink-0 group-hover/card:hidden'>
<div className={clsx('mt-auto h-8 w-8 flex-shrink-0 rounded-tr-full', theme.primary)} />
<div className='ml-auto'>{badge}</div>
<div className='flex w-full flex-col'>
<div
className={twMerge(
'relative flex h-full min-w-0 flex-col overflow-hidden rounded-lg',
`bg-${theme.secondary}`,
className
)}
>
<div className='flex-1'>
{children}
<div className='absolute bottom-0 right-0'>{badge}</div>
</div>
</div>
{title && category && (
<div className='mt-2 px-2 pb-2'>
<div className='truncate pb-1 text-lg leading-5 text-slate-800'>{title}</div>
<div className={clsx('font-semibold italic', `text-${theme.primary}`)}>
{upperFirst(category)}
</div>
</div>
)}
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const ActivityDetails = (props: Props) => {
if (!activity) {
return <Redirect to='/activity-library' />
}
const {category, illustrationUrl, viewerLowestScope} = activity
const {category, illustrationUrl, viewerLowestScope, type} = activity
const prevCategory = history.location.state?.prevCategory
const categoryLink = `/activity-library/category/${
prevCategory ?? category ?? QUICK_START_CATEGORY_ID
Expand Down Expand Up @@ -99,11 +99,12 @@ const ActivityDetails = (props: Props) => {
)}
>
<ActivityCard
className='ml-14 mb-8 h-[200px] w-80 xl:ml-0 xl:mb-0'
className='ml-14 mb-8 max-h-[200px] w-80 xl:ml-0 xl:mb-0'
theme={CATEGORY_THEMES[category as CategoryID]}
badge={null}
type={type}
>
<ActivityCardImage src={illustrationUrl} />
<ActivityCardImage src={illustrationUrl} category={category as CategoryID} />
</ActivityCard>
<div className='pb-20'>
<div className='mb-10 space-y-2 pl-14'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const ActivityDetailsCategoryBadge = (props: Props) => {
<DropdownMenu.Trigger asChild disabled={!isEditing}>
<PlainButton className={clsx(!isEditing && 'cursor-default', 'flex')} disabled={false}>
<ActivityDetailsBadge
className={clsx(CATEGORY_THEMES[category].primary, 'select-none text-white')}
className={clsx(`bg-${CATEGORY_THEMES[category].primary}`, 'select-none text-white')}
>
{CATEGORY_ID_TO_NAME[category]}
</ActivityDetailsBadge>
Expand All @@ -59,7 +59,10 @@ const ActivityDetailsCategoryBadge = (props: Props) => {
value={categoryId}
>
<span
className={clsx(CATEGORY_THEMES[categoryId].primary, 'h-5 w-5 rounded-full')}
className={clsx(
`bg-${CATEGORY_THEMES[categoryId].primary}`,
'h-5 w-5 rounded-full'
)}
></span>
<span className='pl-5 pr-10 text-xs font-semibold'>
{CATEGORY_ID_TO_NAME[categoryId]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ const getTemplateDocumentValue = (
.join('-')

const CategoryIDToColorClass = {
[QUICK_START_CATEGORY_ID]: 'bg-grape-700',
[QUICK_START_CATEGORY_ID]: 'grape-700',
...Object.fromEntries(
Object.entries(CATEGORY_THEMES).map(([key, value]) => {
return [key, value.primary]
Expand Down Expand Up @@ -164,6 +164,7 @@ const ActivityGrid = ({templates, selectedCategory}: ActivityGridProps) => {
key={template.id}
theme={CATEGORY_THEMES[template.category as CategoryID]}
title={template.name}
type={template.type}
badge={
!template.isFree ? (
<ActivityBadge className='m-2 bg-gold-300 text-grape-700'>Premium</ActivityBadge>
Expand All @@ -173,6 +174,7 @@ const ActivityGrid = ({templates, selectedCategory}: ActivityGridProps) => {
<ActivityCardImage
className='group-hover/card:hidden'
src={template.illustrationUrl}
category={template.category as CategoryID}
/>
<ActivityLibraryCardDescription
className='hidden group-hover/card:flex'
Expand Down Expand Up @@ -304,7 +306,7 @@ export const ActivityLibrary = (props: Props) => {
'flex-shrink-0 cursor-pointer rounded-full py-2 px-4 text-sm text-slate-800',
category === selectedCategory && searchQuery.length === 0
? [
CategoryIDToColorClass[category],
`bg-${CategoryIDToColorClass[category]}`,
'font-semibold text-white focus:text-white'
]
: 'border border-slate-300 bg-white'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ export const ActivityLibraryCard = (props: ActivityCardProps) => {

return (
<ActivityCard
className={clsx(
'group/card transition-shadow focus-within:ring-2 focus-within:ring-primary hover:shadow-md motion-reduce:transition-none',
className
)}
className={clsx('focus-within:ring-2 focus-within:ring-primary hover:shadow-md', className)}
{...rest}
/>
)
Expand Down
39 changes: 32 additions & 7 deletions packages/client/components/ActivityLibrary/Categories.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
import {MeetingTypeEnum} from '../../__generated__/NewMeetingQuery.graphql'
import retroBackgroundSrc from '../../../../static/images/illustrations/retro-background.png'
import standupBackgroundSrc from '../../../../static/images/illustrations/standup-background.png'
import feedbackBackgroundSrc from '../../../../static/images/illustrations/feedback-background.png'
import estimationBackgroundSrc from '../../../../static/images/illustrations/estimation-background.png'
import strategyBackgroundSrc from '../../../../static/images/illustrations/strategy-background.png'
import premortemBackgroundSrc from '../../../../static/images/illustrations/premortem-background.png'
import postmortemBackgroundSrc from '../../../../static/images/illustrations/postmortem-background.png'
import {CardTheme} from './ActivityCard'

export const MAIN_CATEGORIES = [
Expand All @@ -14,13 +22,13 @@ export type CategoryID = typeof MAIN_CATEGORIES[number]
export const DEFAULT_CARD_THEME: CardTheme = {primary: 'bg-slate-500', secondary: 'bg-slate-200'}

export const CATEGORY_THEMES: Record<CategoryID, CardTheme> = {
standup: {primary: 'bg-aqua-400', secondary: 'bg-aqua-100'},
estimation: {primary: 'bg-tomato-500', secondary: 'bg-tomato-100'},
retrospective: {primary: 'bg-grape-500', secondary: 'bg-[#F2E1F7]'},
feedback: {primary: 'bg-jade-400', secondary: 'bg-jade-100'},
strategy: {primary: 'bg-rose-500', secondary: 'bg-rose-100'},
premortem: {primary: 'bg-gold-500', secondary: 'bg-gold-100'},
postmortem: {primary: 'bg-grass-500', secondary: 'bg-grass-100'}
standup: {primary: 'aqua-400', secondary: 'aqua-100'},
estimation: {primary: 'tomato-500', secondary: 'tomato-100'},
retrospective: {primary: 'grape-500', secondary: '[#F2E1F7]'},
feedback: {primary: 'jade-400', secondary: 'jade-100'},
strategy: {primary: 'rose-500', secondary: 'rose-100'},
premortem: {primary: 'gold-500', secondary: 'gold-100'},
postmortem: {primary: 'grass-500', secondary: 'grass-100'}
}

export const QUICK_START_CATEGORY_ID = 'recommended'
Expand All @@ -35,3 +43,20 @@ export const CATEGORY_ID_TO_NAME: Record<CategoryID | typeof QUICK_START_CATEGOR
premortem: 'Pre-Mortem',
postmortem: 'Post-Mortem'
}

export const MEETING_TYPE_TO_CATEGORY: Record<MeetingTypeEnum, CategoryID> = {
retrospective: 'retrospective',
action: 'feedback',
poker: 'estimation',
teamPrompt: 'standup'
}

export const backgroundImgMap = {
retrospective: retroBackgroundSrc,
standup: standupBackgroundSrc,
feedback: feedbackBackgroundSrc,
estimation: estimationBackgroundSrc,
strategy: strategyBackgroundSrc,
premortem: premortemBackgroundSrc,
postmortem: postmortemBackgroundSrc
} as const
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,6 @@ const Bold = (props: ComponentPropsWithoutRef<'span'>) => {
)
}

const CategoryTitle = (props: ComponentPropsWithoutRef<'div'>) => {
const {children, className, ...rest} = props

return (
<div
className={clsx('p-4 text-lg font-semibold leading-5 text-slate-700', className)}
{...rest}
>
{children}
</div>
)
}

type ActivityType = 'retrospective' | 'poker'

type SupportedActivity = {
Expand Down Expand Up @@ -294,15 +281,18 @@ export const CreateNewActivity = (props: Props) => {
className='aspect-[320/190] w-80 group-data-[state=checked]:ring-4 group-data-[state=checked]:ring-sky-500 group-data-[state=checked]:ring-offset-4'
theme={DEFAULT_CARD_THEME}
title={activity.title}
titleAs={CategoryTitle}
type={activity.type}
>
<ActivityCardImage src={activity.image} />
<ActivityCardImage
src={activity.image}
category={activity.type === 'retrospective' ? 'retrospective' : 'estimation'}
/>
</ActivityCard>
<div className='flex gap-x-3 p-3'>
{activity.includedCategories.map((badge) => (
<ActivityBadge
key={badge}
className={clsx('text-white', CATEGORY_THEMES[badge].primary)}
className={clsx('text-white', `bg-${CATEGORY_THEMES[badge].primary}`)}
>
{CATEGORY_ID_TO_NAME[badge]}
</ActivityBadge>
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 I think the images would be more versatile if these wouldn't have rounded corners

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.
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.
Binary file added static/images/illustrations/retro-background.png
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.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading