diff --git a/packages/common/src/assets/svg/DefaultProfile.svg b/packages/common/src/assets/svg/DefaultProfile.svg new file mode 100644 index 00000000..37dc9765 --- /dev/null +++ b/packages/common/src/assets/svg/DefaultProfile.svg @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/packages/common/src/components/RankingCard/index.stories.tsx b/packages/common/src/components/RankingCard/index.stories.tsx new file mode 100644 index 00000000..716ba8a2 --- /dev/null +++ b/packages/common/src/components/RankingCard/index.stories.tsx @@ -0,0 +1,91 @@ +'use client'; + +import RankingCard from '.'; + +import type { Meta, StoryObj } from '@storybook/react'; + +export default { + title: 'common/RankingCard', + component: RankingCard, + parameters: { + backgrounds: { + default: 'dark', + }, + }, +} as Meta; + +type Story = StoryObj; + +export const First: Story = { + args: { + rank: 1, + id: 'userId', + cumulatePoint: 1000, + user: { + id: 'userId', + email: 'moondgod@gmail.com', + name: '이정우', + profileImage: + 'https://s3.ap-northeast-2.amazonaws.com/st.dangidata/billing/course/image/133823_20230516174511852.png', + }, + }, +}; + +export const Second: Story = { + args: { + rank: 2, + id: 'userId', + cumulatePoint: 999, + user: { + id: 'userId', + email: 'moondgod@gmail.com', + name: '전지환님', + profileImage: + 'https://image.rocketpunch.com/user/352358/352358_1606388335.jpg?s=200x200&t=cover', + }, + }, +}; + +export const Third: Story = { + args: { + rank: 3, + id: 'userId', + cumulatePoint: 700, + user: { + id: 'userId', + email: 'moondgod@gmail.com', + name: '형록이형', + profileImage: + 'https://publy.imgix.net/user-uploaded/463804/2023.04/d07558f49fda2c53d9c02a038337c88e84fff56ad0d04eed8f37c774c0eca49c.jpeg?w=400&h=400&auto=format&fm=jpeg', + }, + }, +}; + +export const Fourth: Story = { + args: { + rank: 4, + id: 'userId', + cumulatePoint: 600, + user: { + id: 'userId', + email: 'moondgod@gmail.com', + name: '이정우교수', + profileImage: + 'https://file.newswire.co.kr/data/datafile2/thumb_480/2021/08/1889381261_20210805181428_7137680622.jpg', + }, + }, +}; + +export const NoIMG: Story = { + args: { + rank: 4, + id: 'userId', + cumulatePoint: 600, + user: { + id: 'userId', + email: 'moondgod@gmail.com', + name: '이정우교수', + profileImage: null, + }, + }, +}; diff --git a/packages/common/src/components/RankingCard/index.tsx b/packages/common/src/components/RankingCard/index.tsx new file mode 100644 index 00000000..3a951318 --- /dev/null +++ b/packages/common/src/components/RankingCard/index.tsx @@ -0,0 +1,35 @@ +'use client'; + +import * as S from './style'; + +import { RankingPropsType } from 'types'; + +import DefaultProfile from '../../assets/svg/DefaultProfile.svg'; + +import Image from 'next/image'; + +interface RankingCardType extends RankingPropsType { + rank: number; +} + +const RankingCard: React.FC = ({ + rank, + cumulatePoint, + user: { profileImage, name }, +}) => { + return ( + + {rank <= 3 && {rank}} + + profile + + {name} + + {cumulatePoint} + M + + + ); +}; + +export default RankingCard; diff --git a/packages/common/src/components/RankingCard/style.ts b/packages/common/src/components/RankingCard/style.ts new file mode 100644 index 00000000..f01886dc --- /dev/null +++ b/packages/common/src/components/RankingCard/style.ts @@ -0,0 +1,77 @@ +import styled from '@emotion/styled'; + +export const CardWrapper = styled.div` + width: 14rem; + height: 19rem; + border-radius: 1.25rem; + background: ${({ theme }) => theme.color.white}; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + position: relative; + + // 후에 리스트 래퍼에 이 코드를 작성합니다. + /* .medal:nth-child(1n) { + background-color: #ffd79b; + } + .medal:nth-child(2n) { + background-color: ${({ theme }) => theme.color.gray['040']}; + } + .medal:nth-child(3n) { + background-color: #ce865d; + } */ +`; + +export const ProfileWrapper = styled.div` + width: 5.625rem; + height: 5.625rem; + border-radius: 50%; + + overflow: hidden; + position: relative; + img { + object-fit: cover; + } +`; + +export const UserName = styled.span` + ${({ theme }) => theme.typo.h5}; + color: ${({ theme }) => theme.color.black}; + font-weight: 700; + margin: 0.75rem 0 5.1875rem; +`; + +export const FlexWrapper = styled.div` + display: flex; + align-items: center; +`; + +export const Point = styled.span` + ${({ theme }) => theme.typo.button}; + color: ${({ theme }) => theme.color.black}; + font-weight: 500; + margin-right: 0.25rem; +`; + +export const PointUnit = styled.span` + ${({ theme }) => theme.typo.body2}; + color: ${({ theme }) => theme.color.black}; + font-weight: 500; +`; + +export const Medal = styled.div` + ${({ theme }) => theme.typo.body2} + color: ${({ theme }) => theme.color.black}; + font-weight: 500; + + width: 2.5rem; + height: 2.5rem; + border-radius: 50%; + position: absolute; + top: -0.75rem; + left: -0.75rem; + display: flex; + align-items: center; + justify-content: center; +`; diff --git a/packages/common/src/components/RankingItem/index.stories.tsx b/packages/common/src/components/RankingItem/index.stories.tsx index 1f130fec..3c2ee68d 100644 --- a/packages/common/src/components/RankingItem/index.stories.tsx +++ b/packages/common/src/components/RankingItem/index.stories.tsx @@ -7,6 +7,11 @@ import type { Meta, StoryObj } from '@storybook/react'; export default { title: 'common/RankingItem', component: RankingItem, + parameters: { + backgrounds: { + default: 'dark', + }, + }, } as Meta; type Story = StoryObj; @@ -70,3 +75,17 @@ export const Hund: Story = { }, }, }; + +export const NoIMG: Story = { + args: { + ranking: 100, + id: '100등', + cumulatePoint: 0, + user: { + id: '100등', + email: 'sample@gmail.com', + name: '100등', + profileImage: null, + }, + }, +}; diff --git a/packages/common/src/components/RankingItem/index.tsx b/packages/common/src/components/RankingItem/index.tsx index 5d03d539..76d752c2 100644 --- a/packages/common/src/components/RankingItem/index.tsx +++ b/packages/common/src/components/RankingItem/index.tsx @@ -3,6 +3,8 @@ import * as S from './style'; import { slicePoint } from '../../utils'; +import DefaultProfile from '../../assets/svg/DefaultProfile.svg'; + import { RankingPropsType } from 'types'; interface RankingItemProps extends RankingPropsType { @@ -21,7 +23,7 @@ const RankingItem: React.FC = ({ alt='profile image' width={40} height={40} - src={profileImage} + src={profileImage ?? DefaultProfile} /> {name} diff --git a/packages/common/src/components/RankingItem/style.ts b/packages/common/src/components/RankingItem/style.ts index 85e9b52d..310fd238 100644 --- a/packages/common/src/components/RankingItem/style.ts +++ b/packages/common/src/components/RankingItem/style.ts @@ -12,6 +12,7 @@ export const ItemWrapper = styled.div` align-items: center; transition: ease-in-out 0.2s; cursor: pointer; + background-color: ${({ theme }) => theme.color.white}; &:hover { background: #ffd79b; diff --git a/packages/common/src/image.d.ts b/packages/common/src/image.d.ts index 03f7e4ab..ccb76282 100644 --- a/packages/common/src/image.d.ts +++ b/packages/common/src/image.d.ts @@ -2,3 +2,4 @@ declare module '*.jpg'; declare module '*.png'; declare module '*.jpeg'; declare module '*.gif'; +declare module '*.svg'; diff --git a/packages/common/tsconfig.json b/packages/common/tsconfig.json index 6754fd45..32a82c6c 100644 --- a/packages/common/tsconfig.json +++ b/packages/common/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "tsconfig/react-components.json", - "include": ["**/*.ts", "**/*.tsx"], + "include": ["**/*.ts", "**/*.tsx", "src", "src/custom.d.ts"], "exclude": ["node_modules"], "compilerOptions": { "paths": { diff --git a/packages/types/src/ranking.ts b/packages/types/src/ranking.ts index 4d07dad7..e9e174ce 100644 --- a/packages/types/src/ranking.ts +++ b/packages/types/src/ranking.ts @@ -5,6 +5,6 @@ export interface RankingPropsType { id: string; email: string; name: string; - profileImage: string; + profileImage: string | null; }; }