Skip to content

Commit 47bdde8

Browse files
authored
Merge branch 'main' into 55-feat/판매페이지-및-판매확인페이지-UI-구현
2 parents 8aaff9d + 00338d0 commit 47bdde8

11 files changed

Lines changed: 220 additions & 1 deletion

File tree

src/pages/main/ui/MainPage.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,61 @@ export default function MainPage() {
3131
<main className="relative flex min-h-screen flex-col items-center justify-center">
3232
<h1 className="typo-title-1 text-gray-900">LOOPIT</h1>
3333
<p className="typo-body-1 mt-4 text-gray-500">중고거래 플랫폼</p>
34+
import { GearIcon, SellIcon, ShoppingIcon } from '@shared/assets/icons';
35+
import { ROUTES } from '@shared/constants';
36+
import { BannerCard } from '@shared/ui/BannerCard';
37+
import { ChatbotFloatingButton } from '@shared/ui/ChatbotFloatingButton';
38+
import { useNavigate } from 'react-router';
39+
40+
export default function MainPage() {
41+
const navigate = useNavigate();
42+
43+
return (
44+
<main className="flex w-full flex-col items-center gap-14 px-4 pt-8 pb-24">
45+
<section className="h-79.25 w-full max-w-300" aria-label="메인 슬로건 영역" />
46+
47+
<section className="grid w-full max-w-300 grid-cols-1 gap-6 xl:grid-cols-3">
48+
<BannerCard
49+
title={
50+
<>
51+
중고 전자기기
52+
<br />
53+
구매하기
54+
</>
55+
}
56+
description="합리적인 매물을 찾아보세요."
57+
imageSrc={ShoppingIcon}
58+
onClick={() => navigate(ROUTES.BUY, { viewTransition: true })}
59+
/>
60+
<BannerCard
61+
title={
62+
<>
63+
중고 전자기기
64+
<br />
65+
판매하기
66+
</>
67+
}
68+
description="사용하지 않는 기기를 판매해보세요."
69+
imageSrc={SellIcon}
70+
onClick={() => navigate(ROUTES.SELL, { viewTransition: true })}
71+
/>
72+
<BannerCard
73+
title={
74+
<>
75+
근처
76+
<br />
77+
수리점 찾기
78+
</>
79+
}
80+
description="가까운 수리점을 찾아드려요."
81+
imageSrc={GearIcon}
82+
onClick={() => navigate(ROUTES.REPAIR, { viewTransition: true })}
83+
/>
84+
</section>
85+
<ChatbotFloatingButton
86+
className="fixed right-4 bottom-4"
87+
onClick={() => navigate(ROUTES.CHATBOT, { viewTransition: true })}
88+
/>
3489
</main>
3590
);
3691
}

src/pages/playground.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { BannerCard } from '@shared/ui/BannerCard/BannerCard';
22
import { Button } from '@shared/ui/Button/Button';
33
import { Card } from '@shared/ui/Card/Card';
4+
import { ChatbotFloatingButton } from '@shared/ui/ChatbotFloatingButton';
5+
import { ChatBubble } from '@shared/ui/ChatBubble';
46
import { ChatInput } from '@shared/ui/ChatInput';
57
import { ChatPromptList } from '@shared/ui/ChatPromptList';
68
import { Checkbox } from '@shared/ui/Checkbox/Checkbox';
@@ -136,6 +138,20 @@ export default function Playground() {
136138
onPromptClick={(prompt) => console.log('prompt click:', prompt)}
137139
/>
138140
</section>
141+
{/* Chat Bubble */}
142+
<section className="flex flex-col gap-4">
143+
<h2 className="typo-body-1">ChatBubble</h2>
144+
<div className="flex flex-col gap-6" role="log" aria-live="polite">
145+
<ChatBubble variant="receiver" message="대답" meta="오전 00:00" metaDateTime="2024-01-01T00:00:00+09:00" />
146+
<ChatBubble variant="sender" message="직접질문" meta="오후 00:00" metaDateTime="2024-01-01T12:00:00+09:00" />
147+
<ChatBubble
148+
variant="sender"
149+
message="메시지가 길어질 경우에는 이렇게 줄바꿈이 됩니다아아아아아아아아아아 얼마나 늘려야 줄바꿈이 될까요"
150+
meta="오후 00:00"
151+
metaDateTime="2024-01-01T12:00:00+09:00"
152+
/>
153+
</div>
154+
</section>
139155
{/* Chat Input */}
140156
<section className="flex flex-col gap-4">
141157
<h2 className="typo-body-1">ChatInput</h2>
@@ -146,6 +162,11 @@ export default function Playground() {
146162
onSend={() => {}}
147163
/>
148164
</section>
165+
{/* Chatbot Floating Button */}
166+
<section className="flex flex-col gap-4">
167+
<h2 className="typo-body-1">ChatbotFloatingButton</h2>
168+
<ChatbotFloatingButton />
169+
</section>
149170
{/* Favorite Button */}
150171
<section className="flex flex-col gap-4">
151172
<h2 className="typo-body-1">FavoriteButton</h2>

src/shared/assets/icons/common/chatbot.svg

Lines changed: 9 additions & 0 deletions
Loading

src/shared/assets/icons/common/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export { default as CloseIcon } from './close.svg?react';
1010
export { default as HamburgerIcon } from './hamburger-md.svg?react';
1111
export { default as PictureIcon } from './picture.svg?react';
1212
export { default as kakaoLoginImg } from './kakao.png';
13+
export { default as ChatbotIcon } from './chatbot.svg?react';

src/shared/assets/icons/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ export {
22
AlertDotIcon,
33
ChatCircleCloseIcon,
44
CloseIcon,
5+
HamburgerIcon,
6+
kakaoLoginImg,
57
CaretDownMdIcon,
68
CheckIcon,
79
DoneIcon,
8-
HamburgerIcon,
910
HeartIcon,
1011
kakaoLoginImg,
1112
PictureIcon,
1213
SearchMagnifyingGlassIcon,
1314
SendIcon,
15+
SearchMagnifyingGlassIcon,
16+
ChatbotIcon,
1417
} from './common';
1518
export { GearIcon, ShoppingIcon, SellIcon } from './banner';
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { chatBubbleVariants } from './ChatBubble.variants';
2+
import type { ComponentPropsWithoutRef } from 'react';
3+
import type { VariantProps } from 'tailwind-variants';
4+
5+
export type ChatBubbleProps = ComponentPropsWithoutRef<'div'> &
6+
VariantProps<typeof chatBubbleVariants> & {
7+
message: string;
8+
meta?: string;
9+
metaDateTime?: string;
10+
};
11+
12+
export const ChatBubble = ({ message, meta, metaDateTime, variant, className, ...props }: ChatBubbleProps) => {
13+
const styles = chatBubbleVariants({ variant });
14+
15+
return (
16+
<div className={styles.root({ className })} role="listitem" {...props}>
17+
<div className={styles.bubble()}>
18+
<span className={styles.text()}>{message}</span>
19+
</div>
20+
{meta && (
21+
<time className={styles.meta()} dateTime={metaDateTime}>
22+
{meta}
23+
</time>
24+
)}
25+
</div>
26+
);
27+
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { tv } from 'tailwind-variants';
2+
3+
export const chatBubbleVariants = tv({
4+
slots: {
5+
root: ['flex w-full items-end gap-[10px]'],
6+
bubble: [
7+
'inline-flex',
8+
'items-center',
9+
'justify-center',
10+
'min-h-[60px]',
11+
'px-[31px]',
12+
'py-[18px]',
13+
'rounded-[var(--radius-l)]',
14+
'max-w-[559px]',
15+
],
16+
text: ['typo-body-1', 'whitespace-pre-wrap', 'break-words'],
17+
meta: ['typo-caption-2', 'text-[var(--color-gray-400)]'],
18+
},
19+
20+
variants: {
21+
variant: {
22+
receiver: {
23+
root: ['justify-start'],
24+
bubble: ['bg-[var(--color-gray-100)]'],
25+
text: ['text-[var(--color-gray-900)]'],
26+
meta: ['order-last'],
27+
},
28+
sender: {
29+
root: ['justify-end', 'flex-row-reverse'],
30+
bubble: ['bg-[var(--color-brand-primary)]'],
31+
text: ['text-[var(--color-gray-900)]'],
32+
meta: ['order-last'],
33+
},
34+
},
35+
},
36+
37+
defaultVariants: {
38+
variant: 'receiver',
39+
},
40+
});

src/shared/ui/ChatBubble/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { ChatBubble } from './ChatBubble';
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { ChatbotIcon } from '@shared/assets/icons';
2+
import { chatbotFloatingButtonVariants } from './ChatbotFloatingButton.variants';
3+
import type { ComponentPropsWithoutRef } from 'react';
4+
5+
export type ChatbotFloatingButtonProps = ComponentPropsWithoutRef<'button'> & {
6+
label?: string;
7+
};
8+
9+
export const ChatbotFloatingButton = ({
10+
label = '챗봇상담',
11+
className,
12+
type = 'button',
13+
...props
14+
}: ChatbotFloatingButtonProps) => {
15+
const styles = chatbotFloatingButtonVariants();
16+
17+
return (
18+
<button type={type} className={styles.root({ className })} aria-label={label} {...props}>
19+
<span className={styles.content()}>
20+
<ChatbotIcon className={styles.icon()} aria-hidden="true" />
21+
<span className={styles.label()}>{label}</span>
22+
</span>
23+
</button>
24+
);
25+
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { tv } from 'tailwind-variants';
2+
3+
export const chatbotFloatingButtonVariants = tv({
4+
slots: {
5+
root: [
6+
'group flex items-center',
7+
'w-[80px] h-[80px]',
8+
'rounded-full',
9+
'bg-[var(--color-gray-50)]',
10+
'shadow-[0_4px_4px_0_var(--color-gray-100)]',
11+
'p-0',
12+
'overflow-hidden',
13+
'transition-[width,background-color,box-shadow] duration-200 ease-out',
14+
'hover:py-[9px] hover:pl-[8px] hover:pr-[19px]',
15+
'focus-visible:py-[9px] focus-visible:pl-[8px] focus-visible:pr-[19px]',
16+
'hover:w-[182px] hover:bg-[var(--color-gray-200)]',
17+
'focus-visible:w-[182px] focus-visible:bg-[var(--color-gray-200)]',
18+
],
19+
content: [
20+
'flex items-center justify-center w-full h-full',
21+
'gap-0 transition-[gap] duration-200 ease-out',
22+
'group-hover:justify-start group-focus-visible:justify-start',
23+
'group-hover:gap-0 group-focus-visible:gap-0',
24+
'group-hover:w-[144px] group-hover:h-[67px] group-focus-visible:w-[144px] group-focus-visible:h-[67px]',
25+
'group-hover:mx-auto group-focus-visible:mx-auto',
26+
],
27+
icon: ['w-[74px] h-[67px] shrink-0'],
28+
label: [
29+
'typo-title-3 text-[var(--color-black)] whitespace-nowrap',
30+
'opacity-0 max-w-0 translate-x-2',
31+
'transition-[opacity,transform,max-width] duration-200 ease-out',
32+
'group-hover:opacity-100 group-hover:max-w-[120px] group-hover:translate-x-0',
33+
'group-focus-visible:opacity-100 group-focus-visible:max-w-[120px] group-focus-visible:translate-x-0',
34+
],
35+
},
36+
});

0 commit comments

Comments
 (0)