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

Gmmq 690 style: 메인 페이지 UI 리터칭 #242

Merged
merged 7 commits into from
Nov 29, 2023
Binary file added src/assets/images/thumbnail/thumb-back.webp
Binary file not shown.
Binary file added src/assets/images/thumbnail/thumb-devops.webp
Binary file not shown.
Binary file added src/assets/images/thumbnail/thumb-front.webp
Binary file not shown.
Binary file not shown.
Binary file added src/assets/images/thumbnail/thumb-ml_ai.webp
Binary file not shown.
Binary file added src/assets/images/thumbnail/thumb-mobile.webp
Binary file not shown.
234 changes: 174 additions & 60 deletions src/components/molecules/EventGridItem/EventGridItem.tsx
Original file line number Diff line number Diff line change
@@ -1,114 +1,228 @@
import { Text, Box, Image, HStack, Flex } from '@chakra-ui/react';
import {
Text,
Box,
Image,
HStack,
Flex,
Card,
CardBody,
Divider,
CardFooter,
Tooltip,
} from '@chakra-ui/react';
import { Link } from 'react-router-dom';
import { v4 as uuidv4 } from 'uuid';
import { Avatar } from '~/components/atoms/Avatar';
import { BorderBox } from '~/components/atoms/BorderBox';
import { Badge } from '~/components/atoms/Badge';
import { Label } from '~/components/atoms/Label';
import { assets } from '~/config/assets';
import { appPaths } from '~/config/paths';
import CONSTANTS from '~/constants';
import { EventListItem } from '~/types/event/eventList';
import { EventStatus } from '~/types/eventStatus';
import { Position } from '~/types/position';

const THUMBNAILS = assets.thumbnail;

const EventGridItem = ({ event: { info, mentorInfo } }: { event: EventListItem }) => {
const openDate = new Date(info.timeInfo.openDateTime).toLocaleDateString();
const closeDate = new Date(info.timeInfo.closeDateTime).toLocaleDateString();
const isFullCount = info.currentApplicantCount === info.maximumCount;
const srcKey = info.positions[0] || 'FRONT';
const thumbnailSrc = THUMBNAILS[srcKey as keyof typeof THUMBNAILS];

return (
<Link to={appPaths.eventDetail(info.id)}>
<Box
w={'full'}
aspectRatio={3 / 4.5}
<Card
overflow={'hidden'}
role="group"
position={'relative'}
>
<Image
src="https://i.pinimg.com/564x/97/7c/ad/977cad5f391fe80dc7aa4a8194c30e9e.jpg"
alt="썸네일 이미지"
borderTopRadius={'1rem'}
height={'45%'}
w={'full'}
/>
<BorderBox
borderTop={'none'}
borderTopRadius={0}
h={'55%'}
<Box
overflow={'hidden'}
borderTopRadius={'lg'}
h={'35%'}
>
<Image
src={thumbnailSrc}
aspectRatio={3 / 2}
objectFit={'cover'}
_groupHover={{
transform: 'scale(120%)',
transition: '.5s',
}}
/>
</Box>
<CardBody>
<Flex
direction={'column'}
justifyContent={'space-between'}
h={'full'}
gap={'0.5rem'}
flex={1}
gap={2}
>
<AvatarAndStatus
status={info.status}
<Tooltip
label={info.title}
placement="top-start"
fontSize={'xs'}
fontWeight={'normal'}
openDelay={500}
bg={'white'}
color={'gray.700'}
hasArrow
>
<Text
size="md"
noOfLines={1}
fontWeight={'bold'}
color={'gray.700'}
>
{info.title}
</Text>
</Tooltip>
<MentorInfo
imageUrl={mentorInfo.imageUrl}
nickname={mentorInfo.nickname}
/>
<Text
// flex={1}
h={'30%'}
noOfLines={2}
color={'gray.800'}
>
{info.title}
</Text>
</Flex>
</CardBody>
<Divider
borderColor={'gray.300'}
mx={'auto'}
w={'95%'}
/>
<CardFooter>
<Flex
justify={'space-between'}
w={'full'}
>
{/* FIXME 이벤트 대표 직무 1개로 변경하기 */}
<PositionLabels positions={info.positions} />
<Flex
justifyContent={'space-between'}
flexWrap={'wrap'}
<Label
py={0}
px={1.5}
maxH={'1.3rem'}
fontSize={'xs'}
fontWeight={'medium'}
bg={'gray.200'}
color={'gray.500'}
textDecoration={isFullCount ? 'line-through' : 'none'}
>
<Text fontSize={'0.875rem'}>
{openDate} ~ {closeDate}
</Text>
<Text
fontSize={'0.875rem'}
>{`인원 ${info.currentApplicantCount}/${info.maximumCount}`}</Text>
</Flex>
{info.currentApplicantCount} / {info.maximumCount}
</Label>
</Flex>
</BorderBox>
</Box>
</CardFooter>

<Tooltip
label={`${openDate} ~ ${closeDate}`}
placement="top"
fontSize={'sm'}
fontWeight={'normal'}
bg={'white'}
color={'gray.700'}
hasArrow
>
<Box
position={'absolute'}
top={'3%'}
right={'3%'}
zIndex={'docked'}
>
<StatusBadge status={info.status} />
</Box>
</Tooltip>
</Card>
</Link>
);
};

export default EventGridItem;

const AvatarAndStatus = ({
status,
imageUrl,
nickname,
}: {
status: EventStatus;
imageUrl: string;
nickname: string;
}) => {
const StatusBadge = ({ status }: { status: EventStatus }) => {
const isActive = status === 'OPEN' || status === 'REOPEN';

return (
<Label
bg={isActive ? 'green.500' : 'gray.300'}
py={'0.1rem'}
h={'fit-content'}
color={isActive ? 'white' : 'gray.600'}
fontWeight={'semibold'}
>
{CONSTANTS.EVENT_STATUS[status]}
</Label>
);
};

const MentorInfo = ({ imageUrl, nickname }: { imageUrl: string; nickname: string }) => {
return (
<Flex justifyContent={'space-between'}>
<HStack>
<Avatar
src={imageUrl}
w={'2rem'}
h={'2rem'}
w={'1.5rem'}
h={'1.5rem'}
/>
<Text color={'gray.900'}>{nickname}</Text>
<Text
color={'gray.700'}
fontSize={'sm'}
fontWeight={'semibold'}
noOfLines={1}
>
{nickname}
</Text>
<Badge
type="mentee"
fontSize={'xs'}
fontWeight={'medium'}
py={0}
>
멘토
</Badge>
</HStack>
<Label bg={isActive ? 'primary.900' : 'gray.400'}>{CONSTANTS.EVENT_STATUS[status]}</Label>
</Flex>
);
};

const PositionLabels = ({ positions }: { positions: Position[] }) => {
const MAX_LABELS_TO_DISPLAY = 1;

return (
<Flex
flexWrap={'wrap'}
gap={'0.2rem'}
>
{positions.map((position) => (
<Flex gap={'0.2rem'}>
{positions.slice(0, MAX_LABELS_TO_DISPLAY).map((position) => (
<Label
key={uuidv4()}
type={position}
py={0}
/>
))}
{positions.length > MAX_LABELS_TO_DISPLAY && (
<Tooltip
placement="top"
bg={'white'}
rounded={'xl'}
hasArrow
label={
<Flex
direction={'column'}
align={'start'}
>
{positions.slice(MAX_LABELS_TO_DISPLAY).map((position) => (
<Label
Comment on lines +207 to +208
Copy link
Contributor

Choose a reason for hiding this comment

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

positions를 직접 자르는것 보다 splice를 사용하는 것도 좋아보여요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

splice를 사용하면 원본 배열을 건드는 것으로 알고 있어서 slice를 사용했습니다.
혹시나 같은 컴포넌트에서 positions를 사용할 일이 있을 수도 있으니 허허... :)

Copy link
Contributor

Choose a reason for hiding this comment

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

반대로 알고 있었네요 ㅜㅜ

key={uuidv4()}
w={'fit-content'}
type={position}
py={0}
/>
))}
</Flex>
}
>
<Text
ml={1}
fontSize={'xs'}
color={'gray.500'}
fontWeight={'semibold'}
>{`+${positions.length - MAX_LABELS_TO_DISPLAY}`}</Text>
</Tooltip>
)}
</Flex>
);
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Text } from '@chakra-ui/react';
import { Heading, Text } from '@chakra-ui/react';
import { useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Pagination } from '~/components/molecules/Pagination';
Expand Down Expand Up @@ -37,14 +37,13 @@ const EventGridTemplate = () => {

return (
<>
<Text
color={'gray.800'}
<Heading
fontSize={'1.5rem'}
fontWeight={'semibold'}
color={'gray.800'}
mb={'2rem'}
>
진행 중인 피드백
</Text>
</Heading>
{data.events.length ? (
<>
<EventGrid events={data.events} />
Expand Down
14 changes: 14 additions & 0 deletions src/config/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import logoSvg from '~/assets/images/logo.svg';
import menteeSvg from '~/assets/images/mentee-outlined.svg';
import mentorSvg from '~/assets/images/mentor-outlined.svg';
import noDataSvg from '~/assets/images/no-data.svg';
import backend from '~/assets/images/thumbnail/thumb-back.webp';
import devops from '~/assets/images/thumbnail/thumb-devops.webp';
import frontend from '~/assets/images/thumbnail/thumb-front.webp';
import fullstack from '~/assets/images/thumbnail/thumb-fullstack.webp';
import ml_ai from '~/assets/images/thumbnail/thumb-ml_ai.webp';
import mobile from '~/assets/images/thumbnail/thumb-mobile.webp';

export const assets = {
logoSvg,
Expand All @@ -14,4 +20,12 @@ export const assets = {
menteeSvg,
mentorSvg,
noDataSvg,
thumbnail: {
FRONT: frontend,
MOBILE: mobile,
FULLSTACK: fullstack,
BACK: backend,
ML_AI: ml_ai,
DEVOPS: devops,
},
};
Loading