Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/app/design-system/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { useState } from 'react';
import { customToast } from '@/shared/components/toast/CustomToastUtils';
import ModalLayout from '@/shared/components/modalPop/ModalLayout';
import ConfirmPop from '@/shared/components/modalPop/ConfirmPop';
import ChatInput from '@/shared/components/InputBox/ChatInput';
import SelectBox from '@/shared/components/InputBox/SelectBox';
import LikeBtn from '@/shared/components/like/LikeBtn';
import Share from '@/shared/components/share/Share';
Expand All @@ -34,7 +33,6 @@ function Page() {
<Input placeholder="내용을 입력해주세요." id="test" variant="search" />
<Input placeholder="칵테일을 검색해 보세요" id="test" variant="comment" />
<Input placeholder="내용을 입력해주세요." id="test" size="lg" />
<ChatInput placeholder="내용을 입력해주세요" id="test" />
</div>

{/* select */}
Expand Down
8 changes: 4 additions & 4 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Metadata } from 'next';
import '@/shared/styles/global.css';
import { Toaster } from 'react-hot-toast';
import ScrollTopBtn from '@/shared/components/scrollTop/ScrollTopBtn';
import Header from '@/shared/components/header/Header';
import Footer from '@/shared/components/footer/Footer';
import FooterWrapper from '@/shared/components/footer/FooterWrapper';
import ScrollTopBtnWrapper from '@/shared/components/scrollTop/ScrollTopBtnWrapper';
export const metadata: Metadata = {
title: 'SSOUL',
description: '칵테일을 좋아하는 사람들을 위한 서비스',
Expand All @@ -22,7 +22,7 @@ export default function RootLayout({
<div id="observer-target" className="h-[0.5px]"></div>
{children}
</main>
<Footer />
<FooterWrapper />

<div id="modal-root"></div>
<Toaster
Expand All @@ -36,7 +36,7 @@ export default function RootLayout({
}}
/>

<ScrollTopBtn />
<ScrollTopBtnWrapper />
</body>
</html>
);
Expand Down
23 changes: 23 additions & 0 deletions src/app/recommend/components/ChatCocktailCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Image from 'next/image';
import Dummy from '@/shared/assets/images/dummy/exampleCocktail.png';
import Link from 'next/link';
import Keep from '@/shared/components/keep/Keep';

function ChatCocktailCard() {
return (
<div className="relative flex flex-col w-full min-w-[200px] rounded-2xl overflow-hidden bg-white shadow-[0_0_12px_rgba(255,255,255,0.4)]">
<Link href="/" className="block relative">
<div className="relative w-full h-[200px]">
<Image src={Dummy} fill className="object-cover" alt="칵테일 이름" />
</div>

<div className="p-3 flex flex-col gap-1 text-center">
<strong className="text-black text-lg">{'진피즈'}</strong>
<span className="text-gray-500 text-sm">+ 상세보기</span>
</div>
</Link>
<Keep className="absolute top-2 right-2 z-50" />
</div>
);
}
export default ChatCocktailCard;
44 changes: 44 additions & 0 deletions src/app/recommend/components/ChatForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use client';

import Send from '@/shared/assets/icons/send_36.svg';
import { keyDown } from '@/shared/utills/keyDown';
import { useState } from 'react';

function ChatForm() {
const handleInput = (e: React.FormEvent<HTMLTextAreaElement>) => {
const target = e.currentTarget;

if (target.value == '') {
target.style.height = '';
}
target.style.height = `${target.scrollHeight}px`;
};

return (
<div className="fixed left-0 bottom-0 w-full px-3 py-4 bg-primary">
<form onSubmit={(e) => e.preventDefault()}>
<div className="flex items-end w-full gap-2">
<label htmlFor="chatInput" className="sr-only">
질문 입력창
</label>
<textarea
id="chatInput"
name="chatInput"
onKeyDown={(e) => keyDown(e)}
onInput={(e) => handleInput(e)}
placeholder="칵테일 추천 질문을 입력해주세요."
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"
/>
<button
type="button"
aria-label="보내기"
className="flex-center w-10 md:w-13 h-10 md:h-13 rounded-xl border-1 border-white bg-secondary/20"
>
<Send className="text-secondary" />
</button>
</div>
</form>
</div>
);
}
export default ChatForm;
42 changes: 42 additions & 0 deletions src/app/recommend/components/ChatRadio.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
interface Option {
label: string;
value: string;
}

interface RadioGroupProps {
options: Option[];
value: string;
onChange: (value: string) => void;
}

function ChatRadio({ options, value, onChange }: RadioGroupProps) {
return (
<div role="radiogroup" className="flex flex-col gap-3 mt-5">
{options.map((opt) => (
<label
htmlFor={`radio-${opt.value}`}
key={opt.value}
className="flex items-center gap-2 cursor-pointer"
>
<input
id={`radio-${opt.value}`}
type="radio"
name="radio-group"
value={opt.value}
checked={value === opt.value}
onChange={() => onChange(opt.value)}
className="sr-only"
/>
<span
className={`w-full rounded-3xl px-2 py-1 text-center transition-colors duration-150
${value === opt.value ? 'bg-secondary shadow-[inset_0_0_6px_rgba(255,196,1,1)]' : 'bg-gray-dark/30'}
hover:bg-secondary/100 `}
>
<span>{opt.label}</span>
</span>
</label>
))}
</div>
);
}
export default ChatRadio;
39 changes: 39 additions & 0 deletions src/app/recommend/components/MyChat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
interface Message {
id: string;
sender: 'ssury' | 'user';
text: string;
type?: 'radio' | 'text';
}

// 메시지 (연속 메시지)
const messages: Message[] = [
{
id: '1',
sender: 'user',
text: '냥냥냥글자가길어지면 어케될까요 너무너무너무 궁금해요 하하하하하하하하하하하하하하하하하하ㅏ',
},
{ id: '2', sender: 'user', text: '배고파요' },
];

function MyChat() {
return (
<article aria-label="내 메시지" className="flex flex-col items-end">
<header className="w-fit">
<strong>나</strong>
</header>

{/* 메시지 그룹 */}
<div className="flex flex-col items-end gap-3 mt-3 pr-3 max-w-[80%]">
{messages.map((msg) => (
<div
key={msg.id}
className="w-fit min-w-[120px] p-3 rounded-2xl rounded-tr-none bg-tertiary text-white"
>
<p className="whitespace-pre-line">{msg.text}</p>
</div>
))}
</div>
</article>
);
}
export default MyChat;
89 changes: 89 additions & 0 deletions src/app/recommend/components/SsuryChat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use client';

import Ssury from '@/shared/assets/ssury/ssury_shaker.webp';
import Image from 'next/image';
import { useState } from 'react';
import ChatRadio from './ChatRadio';
import ChatCocktailCard from './ChatCocktailCard';

interface Message {
id: string;
text?: string;
type?: 'radio' | 'text' | 'recommend';
}

function SsuryChat() {
const [selected, setSelected] = useState('option1');

// radio 옵션
const options = [
{ label: '옵션 1', value: 'option1' },
{ label: '옵션 2', value: 'option2' },
{ label: '옵션 3', value: 'option3' },
];

// 메시지 (연속 메시지)
const messages: Message[] = [
{
id: '1',
text: '안녕하세요, 바텐더 쑤리에요. \n 취향에 맞는 칵테일을 추천해드릴게요!',
},
{
id: '2',
text: '어떤 유형으로 찾아드릴까요?',
type: 'radio',
},
{
id: '3',
type: 'recommend',
},
];

return (
<article aria-label="취향추천 챗봇 메시지" className="">
<header className="flex items-end">
<div className="relative w-15 md:w-20 h-15 md:h-20">
<Image
src={Ssury}
alt="쑤리아바타"
width={80}
height={80}
className="object-cover w-15 h-15"
/>
</div>
<strong>쑤리</strong>
</header>

{/* 메시지 그룹 */}
<div className="flex flex-col gap-3 mt-3 pl-3">
{messages.map((msg) => (
<div key={msg.id}>
{msg.type === 'recommend' ? (
<ul className="inline-grid grid-cols-1 sm:grid-cols-3 gap-2 justify-start">
<li>
<ChatCocktailCard />
</li>
<li>
<ChatCocktailCard />
</li>
<li>
<ChatCocktailCard />
</li>
</ul>
) : (
<div className="flex flex-col w-fit max-w-[80%] min-w-[120px] p-3 rounded-2xl rounded-tl-none bg-white text-black">
{msg.text && <p className="whitespace-pre-line">{msg.text}</p>}

{/* radio */}
{msg.type === 'radio' && (
<ChatRadio options={options} value={selected} onChange={setSelected} />
)}
</div>
)}
</div>
))}
</div>
</article>
);
}
export default SsuryChat;
21 changes: 20 additions & 1 deletion src/app/recommend/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,23 @@
import Bg from '@/shared/assets/images/recommend_bg.webp';
import ChatForm from './components/ChatForm';
import SsuryChat from './components/SsuryChat';
import MyChat from './components/MyChat';

function Page() {
return <div>recommend</div>;
return (
<div
className="relative bg-repeat-y bg-top bg-auto w-full flex"
style={{ backgroundImage: `url(${Bg.src})` }}
>
<h1 className="sr-only">취향추천하기</h1>
<div className="page-layout max-w-1024 py-12 ">
<div className="flex flex-col gap-10 pb-20">
<SsuryChat />
<MyChat></MyChat>
</div>
</div>
<ChatForm />
</div>
);
}
export default Page;
2 changes: 1 addition & 1 deletion src/shared/assets/icons/send_36.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/shared/assets/images/recommend_bg.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 0 additions & 38 deletions src/shared/components/InputBox/ChatInput.tsx

This file was deleted.

11 changes: 11 additions & 0 deletions src/shared/components/footer/FooterWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use client';

import { usePathname } from 'next/navigation';
import Footer from './Footer';

export default function FooterWrapper() {
const pathname = usePathname();
const showFooter = pathname !== '/recommend';

return showFooter ? <Footer /> : null;
}
3 changes: 2 additions & 1 deletion src/shared/components/keep/Keep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ interface Props {

function Keep({ className }: Props) {
const [isClick, setIsClick] = useState(false);
const handleClick = () => {
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
setIsClick(!isClick);
};

Expand Down
12 changes: 12 additions & 0 deletions src/shared/components/scrollTop/ScrollTopBtnWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use client';

import { usePathname } from 'next/navigation';
import ScrollTopBtn from './ScrollTopBtn';

function ScrollTopBtnWrapper() {
const pathname = usePathname();
const showScroll = pathname !== '/recommend';

return showScroll ? <ScrollTopBtn /> : null;
}
export default ScrollTopBtnWrapper;
Loading