✨ feat(people): add employee details
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import { Dialog, DialogProps, DialogTitle, DialogContent, IconButton, Typography, Stack, Divider } from '@mui/material'
|
||||
import CloseIcon from '@mui/icons-material/Close'
|
||||
|
||||
export type EmployeeDescription = {
|
||||
title: string
|
||||
position?: string
|
||||
image?: string
|
||||
body?: string[]
|
||||
captions?: string[]
|
||||
}
|
||||
|
||||
export type EmployeeDescriptionDialogProps = DialogProps & {
|
||||
description: EmployeeDescription
|
||||
}
|
||||
|
||||
const EmployeeDescriptionDialog: React.FC<EmployeeDescriptionDialogProps> = ({ description, ...props }) => (
|
||||
<Dialog fullWidth {...props}>
|
||||
<DialogTitle>
|
||||
<Typography variant="h4" component="div">
|
||||
{description.title}
|
||||
</Typography>
|
||||
{description.position && (
|
||||
<Typography variant="h6" color="text.secondary">
|
||||
{description.position}
|
||||
</Typography>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<IconButton
|
||||
aria-label="close"
|
||||
onClick={props.onClose as React.MouseEventHandler<HTMLButtonElement>}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0,
|
||||
}}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<DialogContent sx={{ pt: 0 }}>
|
||||
<img
|
||||
srcSet={description.image}
|
||||
src={description.image}
|
||||
alt={description.title}
|
||||
loading="lazy"
|
||||
style={{ width: '100%', objectFit: 'contain' }}
|
||||
/>
|
||||
{description.body &&
|
||||
description.body.map((paragraph, idx) => (
|
||||
<Typography key={idx} pb={1}>
|
||||
{paragraph}
|
||||
</Typography>
|
||||
))}
|
||||
{description.captions && (
|
||||
<>
|
||||
<Divider sx={{ my: 2 }} />
|
||||
<Stack color="text.secondary" mt={1} gap={1}>
|
||||
{description.captions.map((caption, idx) => (
|
||||
<Typography key={idx} variant="caption">
|
||||
{caption}
|
||||
</Typography>
|
||||
))}
|
||||
</Stack>
|
||||
</>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
|
||||
export default EmployeeDescriptionDialog
|
||||
@@ -1,16 +1,28 @@
|
||||
import { Card, CardContent, CardMedia, CardProps, Typography } from '@mui/material'
|
||||
import { FC } from 'react'
|
||||
import { FC, useMemo } from 'react'
|
||||
import { Card, CardActionArea, CardContent, CardMedia, CardProps, Typography } from '@mui/material'
|
||||
|
||||
export interface EmplyeeCardProps extends CardProps {
|
||||
import { EmployeeDescription } from 'components/molecules/EmployeeDescription/index.js'
|
||||
|
||||
export type EmployeeCardProps = Omit<CardProps, 'onClick'> & {
|
||||
name: string
|
||||
image: string
|
||||
position?: string
|
||||
description?: Partial<EmployeeDescription>
|
||||
onClick?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>, description: EmployeeDescription) => void
|
||||
}
|
||||
|
||||
export const EmplyeeCard: FC<EmplyeeCardProps> = ({ name, position, image, sx, ...props }) => {
|
||||
return (
|
||||
<Card sx={{ flexShrink: 0, width: 270, height: 360, ...sx }} {...props}>
|
||||
<CardMedia component="img" sx={{ minHeight: '140' }} image={image} alt={`${name} image`} />
|
||||
export const EmplyeeCard: FC<EmployeeCardProps> = ({ name, position, image, description = {}, sx, onClick, ...props }) => {
|
||||
const handleClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
onClick(event, {
|
||||
title: name,
|
||||
image: image,
|
||||
position: position,
|
||||
...description,
|
||||
})
|
||||
}
|
||||
const content = useMemo(
|
||||
() => [
|
||||
<CardMedia component="img" sx={{ minHeight: '140' }} image={image} alt={`${name} image`} />,
|
||||
<CardContent>
|
||||
<Typography gutterBottom variant="h5" component="div">
|
||||
{name}
|
||||
@@ -20,7 +32,13 @@ export const EmplyeeCard: FC<EmplyeeCardProps> = ({ name, position, image, sx, .
|
||||
{position}
|
||||
</Typography>
|
||||
)}
|
||||
</CardContent>
|
||||
</CardContent>,
|
||||
],
|
||||
[name, position, image]
|
||||
)
|
||||
return (
|
||||
<Card sx={{ flexShrink: 0, width: 270, height: 360, ...sx }} {...props} onClick={handleClick}>
|
||||
<CardActionArea>{content}</CardActionArea>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { Box, IconButton } from '@mui/material'
|
||||
import { EmplyeeCard, EmplyeeCardProps } from 'components/molecules/EmplyeeCard/index.js'
|
||||
import ShuffleIcon from '@mui/icons-material/NextPlan'
|
||||
|
||||
import { EmplyeeCard, EmployeeCardProps } from 'components/molecules/EmplyeeCard/index.js'
|
||||
import { EmployeeDescription } from 'components/molecules/EmployeeDescription/index.js'
|
||||
|
||||
interface Props {
|
||||
items: EmplyeeCardProps[]
|
||||
items: EmployeeCardProps[]
|
||||
onClick?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>, description: EmployeeDescription) => void
|
||||
|
||||
// Visual offsets
|
||||
offsetStep?: number // px shift per card
|
||||
@@ -15,6 +18,7 @@ const EmplyeeStack: FC<Props> = ({
|
||||
offsetStep = 10, // px shift per card
|
||||
maxVisible = 6, // how many cards we actually render (performance)
|
||||
items: initialItems,
|
||||
onClick,
|
||||
}) => {
|
||||
const [items, setItems] = useState(initialItems)
|
||||
|
||||
@@ -29,7 +33,7 @@ const EmplyeeStack: FC<Props> = ({
|
||||
}
|
||||
|
||||
if (items.length === 0) return null
|
||||
if (items.length === 1) return <EmplyeeCard {...items[0]} />
|
||||
if (items.length === 1) return <EmplyeeCard {...items[0]} onClick={onClick} />
|
||||
|
||||
return (
|
||||
<Box
|
||||
@@ -41,6 +45,7 @@ const EmplyeeStack: FC<Props> = ({
|
||||
{items.slice(0, maxVisible).map((props, idx) => (
|
||||
<EmplyeeCard
|
||||
{...props}
|
||||
onClick={onClick}
|
||||
key={idx}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
|
||||
Reference in New Issue
Block a user