Skip to content

Commit 8044daf

Browse files
authored
[feat] 챗봇 추천페이지 입력 폼 기능 (#77)
* [style] 추천 배경 이미지 변경 , repeat 넣기 * [style] 하트 z-index 제거 * [style] 배경 변경 * [style] input 최대 width 제한 * [style] 스켈레톤 ui 추가(spinner 오류로 임시) * [feat, fix] textarea 유틸함수 추가 및 수정 (resize. submit) * [fix] 채팅 목록 및 입력창 컴포넌트 분리 * [feat] textarea 입력 시 mychat이랑 연결 * [fix] husky 오류 해결
1 parent e8363ac commit 8044daf

File tree

8 files changed

+105
-52
lines changed

8 files changed

+105
-52
lines changed

src/app/recommend/page.tsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import ChatForm from '@/domains/recommend/components/ChatForm';
2-
import MyChat from '@/domains/recommend/components/MyChat';
3-
import SsuryChat from '@/domains/recommend/components/SsuryChat';
1+
import ChatSection from '@/domains/recommend/components/ChatSection';
42
import Bg from '@/shared/assets/images/recommend_bg.webp';
53

64
function Page() {
@@ -10,13 +8,7 @@ function Page() {
108
style={{ backgroundImage: `url(${Bg.src})` }}
119
>
1210
<h1 className="sr-only">취향추천하기</h1>
13-
<div className="page-layout max-w-1024 py-12 ">
14-
<div className="flex flex-col gap-10 pb-20">
15-
<SsuryChat />
16-
<MyChat></MyChat>
17-
</div>
18-
</div>
19-
<ChatForm />
11+
<ChatSection />
2012
</div>
2113
);
2214
}

src/domains/recipe/skeleton/SkeletonRecipe.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ function SkeletonRecipe() {
3838
</li>
3939
))}
4040
</ul>
41-
4241
</div>
4342
);
4443
}

src/domains/recommend/components/ChatForm.tsx

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
'use client';
22

33
import Send from '@/shared/assets/icons/send_36.svg';
4-
import { keyDown } from '@/shared/utills/keyDown';
4+
import { handleTextareaSubmit } from '@/shared/utills/handleTextareaSubmit';
5+
import { resizeTextarea } from '@/shared/utills/textareaResize';
6+
import { useRef, useState } from 'react';
57

6-
function ChatForm() {
7-
const handleInput = (e: React.FormEvent<HTMLTextAreaElement>) => {
8-
const target = e.currentTarget;
8+
interface Props {
9+
onSubmit: (message: string) => void;
10+
}
11+
12+
function ChatForm({ onSubmit }: Props) {
13+
const [value, setValue] = useState('');
14+
const textareaRef = useRef<HTMLTextAreaElement>(null);
915

10-
if (target.value == '') {
11-
target.style.height = '';
12-
}
13-
target.style.height = `${target.scrollHeight}px`;
16+
const handleSubmit = (value: string) => {
17+
const text = value.trim();
18+
if (!text) return;
19+
onSubmit(text);
20+
setValue('');
1421
};
1522

1623
return (
@@ -21,15 +28,19 @@ function ChatForm() {
2128
질문 입력창
2229
</label>
2330
<textarea
31+
ref={textareaRef}
32+
value={value}
33+
onChange={(e) => setValue(e.target.value)}
34+
onKeyDown={(e) => handleTextareaSubmit(e, textareaRef.current, handleSubmit)}
2435
id="chatInput"
2536
name="chatInput"
26-
onKeyDown={(e) => keyDown(e)}
27-
onInput={(e) => handleInput(e)}
37+
onInput={(e) => resizeTextarea(e.currentTarget)}
2838
placeholder="칵테일 추천 질문을 입력해주세요."
2939
className="w-[calc(100%-3rem)] md:w-[calc(100%-3.75rem)] px-4 py-2 md:py-3.5 rounded-lg h-[40px] md:h-[52px] max-h-[160px] md:max-h-[280px] bg-white text-primary placeholder:text-gray-dark resize-none outline-none"
3040
/>
3141
<button
3242
type="button"
43+
onClick={() => handleTextareaSubmit(null, textareaRef.current, handleSubmit)}
3344
aria-label="보내기"
3445
className="flex-center w-10 md:w-13 h-10 md:h-13 rounded-xl border-1 border-white bg-secondary/20"
3546
>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use client';
2+
3+
import { useState } from 'react';
4+
import ChatForm from './ChatForm';
5+
import MyChat from './MyChat';
6+
import SsuryChat from './SsuryChat';
7+
8+
function ChatSection() {
9+
const [messages, setMessages] = useState<string[]>([]);
10+
11+
const handleSubmit = (message: string) => {
12+
setMessages((prev) => [...prev, message]);
13+
};
14+
15+
return (
16+
<section className="page-layout max-w-1024 py-12 ">
17+
<h2 className="sr-only">대화 목록 및 입력 창</h2>
18+
<div className="flex flex-col gap-10 pb-20">
19+
<SsuryChat />
20+
{messages.map((msg, i) => (
21+
<MyChat key={i} message={msg} />
22+
))}
23+
</div>
24+
<ChatForm onSubmit={handleSubmit} />
25+
</section>
26+
);
27+
}
28+
export default ChatSection;

src/domains/recommend/components/MyChat.tsx

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
1-
interface Message {
2-
id: string;
3-
sender: 'ssury' | 'user';
4-
text: string;
5-
type?: 'radio' | 'text';
1+
interface Props {
2+
message: string;
63
}
74

8-
// 메시지 (연속 메시지)
9-
const messages: Message[] = [
10-
{
11-
id: '1',
12-
sender: 'user',
13-
text: '냥냥냥글자가길어지면 어케될까요 너무너무너무 궁금해요 하하하하하하하하하하하하하하하하하하ㅏ',
14-
},
15-
{ id: '2', sender: 'user', text: '배고파요' },
16-
];
5+
// 메시지 (연속 메시지) 예시..
6+
// const messages: Message[] = [
7+
// {
8+
// id: '1',
9+
// sender: 'user',
10+
// text: '냥냥냥글자가길어지면 어케될까요 너무너무너무 궁금해요 하하하하하하하하하하하하하하하하하하ㅏ',
11+
// },
12+
// { id: '2', sender: 'user', text: '배고파요' },
13+
// ];
1714

18-
function MyChat() {
15+
function MyChat({ message }: Props) {
1916
return (
2017
<article aria-label="내 메시지" className="flex flex-col items-end">
2118
<header className="w-fit">
@@ -24,14 +21,9 @@ function MyChat() {
2421

2522
{/* 메시지 그룹 */}
2623
<div className="flex flex-col items-end gap-3 mt-3 pr-3 max-w-[80%]">
27-
{messages.map((msg) => (
28-
<div
29-
key={msg.id}
30-
className="w-fit min-w-[120px] p-3 rounded-2xl rounded-tr-none bg-tertiary text-white"
31-
>
32-
<p className="whitespace-pre-line">{msg.text}</p>
33-
</div>
34-
))}
24+
<div className="w-fit min-w-[120px] p-3 rounded-2xl rounded-tr-none bg-tertiary text-white">
25+
<p className="whitespace-pre-line">{message}</p>
26+
</div>
3527
</div>
3628
</article>
3729
);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
export const handleTextareaSubmit = (
2+
e: React.KeyboardEvent<HTMLTextAreaElement> | null,
3+
textarea: HTMLTextAreaElement | null,
4+
onSubmit: (value: string) => void
5+
) => {
6+
if (!textarea) return;
7+
8+
if (!e) {
9+
const value = textarea.value.trim();
10+
if (!value) return;
11+
12+
onSubmit(value);
13+
textarea.value = '';
14+
textarea.style.height = '';
15+
return;
16+
}
17+
18+
// 엔터 처리
19+
if (e.key === 'Enter') {
20+
if (e.shiftKey) return; // Shift+Enter
21+
22+
e.preventDefault();
23+
24+
// mac OS 일때는 Composing 방지
25+
if (e.nativeEvent.isComposing) return;
26+
27+
const value = textarea.value.trim();
28+
if (!value) return;
29+
30+
onSubmit(value);
31+
textarea.value = '';
32+
textarea.style.height = '';
33+
}
34+
};

src/shared/utills/keyDown.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const resizeTextarea = (textarea: HTMLTextAreaElement) => {
2+
if (!textarea) return;
3+
textarea.style.height = '';
4+
textarea.style.height = `${textarea.scrollHeight}px`;
5+
};

0 commit comments

Comments
 (0)