Skip to content

Commit

Permalink
feat: 챗봇 페이지 화면 구현 (#7)
Browse files Browse the repository at this point in the history
* feat: 챗봇 페이지 제목 및 레벨 선택 카드 구현

* feat: 질문 추천 스와이퍼 구현

* feat: default color와 background 지정
  • Loading branch information
Najeong-Kim authored Jan 30, 2025
1 parent 1d10452 commit 50bf161
Show file tree
Hide file tree
Showing 16 changed files with 217 additions and 2 deletions.
21 changes: 20 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"next-themes": "^0.4.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"ua-parser-js": "^2.0.0"
"ua-parser-js": "^2.0.0",
"swiper": "^11.2.1"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
Expand Down
Binary file added public/images/chat/advanced.png
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 public/images/chat/beginner.png
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 public/images/chat/intermediate.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions src/app/chat/components/LevelCard/LevelCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import clsx from "clsx";
import { Level } from "../../data";
import Image from "next/image";

const LevelCard = ({
level,
isSelected,
onClick,
image,
}: {
level: Level;
isSelected: boolean;
onClick: () => void;
image: string;
}) => {
return (
<button
className={clsx(
"flex h-[6rem] w-[5.2rem] flex-col items-center justify-center gap-[0.2rem] rounded-md border",
isSelected
? "border-blue-400 text-blue-500 shadow-normal"
: "border-gray-300 text-gray-600",
)}
onClick={onClick}
>
<Image src={image} alt={level} width={28} height={28} />
<span className="text-body-2-semibold">{level}</span>
</button>
);
};

export default LevelCard;
1 change: 1 addition & 0 deletions src/app/chat/components/LevelCard/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./LevelCard";
33 changes: 33 additions & 0 deletions src/app/chat/components/Questions/Question.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import clsx from "clsx";
import { QuestionType } from "../../data";

interface QuestionProps {
id: number;
type: QuestionType;
question: string;
isActive: boolean;
isPrev: boolean;
}

const Question = ({ id, type, question, isActive, isPrev }: QuestionProps) => {
return (
<div
key={id}
className={clsx("flex h-full items-center", isPrev && "justify-end")}
>
<button
className={clsx(
"flex h-full flex-col gap-[0.6rem] rounded-lg px-[1.6rem] py-[2rem] text-start transition-all",
isActive
? "h-full w-[27rem] bg-blue-100"
: "h-[11.4rem] w-[23rem] bg-gray-200",
)}
>
<p className="text-body-3-semibold text-blue-500">{type}</p>
<p className="text-body-1-medium">{question}</p>
</button>
</div>
);
};

export default Question;
32 changes: 32 additions & 0 deletions src/app/chat/components/Questions/Questions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Navigation } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/react";
import { questions } from "../../data";
import Question from "./Question";
import "swiper/css";
import "swiper/css/navigation";

const Questions = () => {
return (
<>
<Swiper
className="h-[14.1rem] w-[84rem]"
modules={[Navigation]}
slidesPerView={3}
centeredSlides={true}
navigation={true}
spaceBetween={12}
loop
>
{questions.map((question) => (
<SwiperSlide key={question.id}>
{({ isActive, isPrev }) => (
<Question {...question} isActive={isActive} isPrev={isPrev} />
)}
</SwiperSlide>
))}
</Swiper>
</>
);
};

export default Questions;
1 change: 1 addition & 0 deletions src/app/chat/components/Questions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./Questions";
12 changes: 12 additions & 0 deletions src/app/chat/components/Title/Title.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const Title = () => {
return (
<div className="flex flex-col items-center gap-[0.8rem]">
<h1 className="text-title-1-semibold">오늘은 어떤 정보를 쌓아볼까요?</h1>
<p className="text-body-2-regular text-gray-700">
블록체인에 얼마나 관심을 가지고 계신가요? 적합한 질문을 추천드릴게요!
</p>
</div>
);
};

export default Title;
1 change: 1 addition & 0 deletions src/app/chat/components/Title/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./Title";
39 changes: 39 additions & 0 deletions src/app/chat/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export enum Level {
BEGINNER = "초급",
INTERMEDIATE = "중급",
ADVANCED = "고급",
}

export enum QuestionType {
CONCEPT = "개념",
EXAMPLE = "예제",
QUESTION = "질문",
}

export const questions = [
{
id: 1,
type: QuestionType.CONCEPT,
question: "블록체인이란 무엇인가요?",
},
{
id: 2,
type: QuestionType.EXAMPLE,
question: "이더리움의 블록체인 구조 예시를 알려줘.",
},
{
id: 3,
type: QuestionType.QUESTION,
question: "블록체인은 어떤 문제를 해결하려고 만들어졌어?",
},
{
id: 4,
type: QuestionType.CONCEPT,
question: "블록체인이 무엇인가요?",
},
{
id: 5,
type: QuestionType.QUESTION,
question: "블록체인은 어떤 문제를 해결하려고 만들어졌어?",
},
];
35 changes: 35 additions & 0 deletions src/app/chat/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"use client";

import ThemeSwitch from "@/components/ThemeSwitch";
import { Level } from "./data";
import LevelCard from "./components/LevelCard/LevelCard";
import { useState } from "react";
import Title from "./components/Title";
import Questions from "./components/Questions";

export default function ChatPage() {
const [selectedLevel, setSelectedLevel] = useState<Level>(Level.BEGINNER);

return (
<main className="h-screen w-screen bg-gray-100">
<div className="flex size-full flex-col items-center justify-center gap-[2.2rem]">
<Title />
<div className="flex flex-col items-center gap-[3rem]">
<div className="flex gap-[1.8rem]">
{Object.entries(Level).map(([key, value]) => (
<LevelCard
key={key}
level={value}
isSelected={selectedLevel === value}
onClick={() => setSelectedLevel(value)}
image={`/images/chat/${key.toLowerCase()}.png`}
/>
))}
</div>
<Questions />
</div>
<ThemeSwitch />
</div>
</main>
);
}
8 changes: 8 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@tailwind utilities;

:root {
--color-background: #ffffff;
--color-gray-100: #f8fafb;
--color-gray-200: #f2f4f5;
--color-gray-300: #eaeced;
Expand Down Expand Up @@ -40,6 +41,7 @@
}

.dark {
--color-background: #0e0e0e;
--color-gray-100: #1d1d1d;
--color-gray-200: #28292a;
--color-gray-300: #464848;
Expand Down Expand Up @@ -75,3 +77,9 @@
0px 1px 4px 0px rgba(152, 154, 155, 0.3),
0px 0px 2px 0px rgba(152, 154, 155, 0.2);
}

html,
body {
font-size: 10px;
color: var(--color-gray-900);
}
1 change: 1 addition & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ module.exports = {
"system-green": "var(--color-system-green)",
white: "#FFFFFF",
dark: "#0E0E0E",
background: "var(--color-background)",
},
fontSize: {
// headline
Expand Down

0 comments on commit 50bf161

Please sign in to comment.