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: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?

.env
18 changes: 18 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
},
"devDependencies": {
"@eslint/js": "^9.25.0",
"@types/node": "^22.15.24",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"@types/react-router-dom": "^5.3.3",
Expand Down
Binary file added public/images/characters/앙글이.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/characters/웅이.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/characters/티바노.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import SettingPage from "./pages/chatting/SettingPage";
import ChatPage from "./pages/chatting/ChatPage";
import styled from "styled-components";
import WritingPage from "./pages/writing/WritingPage";
import Testpage from "./pages/Testpage";

function App() {
return (
Expand All @@ -19,13 +20,14 @@ function App() {
<Route path="/" element={<Home />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/oauth/kakao/callback" element={<KakaoCallback />} />
<Route path="/diary/:id" element={<DiaryDetail />} />
<Route path="/diary/:diaryId" element={<DiaryDetail />} />
<Route path="/setChatting" element={<SettingPage />} />
<Route path="/chat/:chatId/:character" element={<ChatPage />} />
<Route path="/writing" element={<WritingPage />} />

<Route path="/comments" element={<Comments />} />
<Route path="/hashtags" element={<Hashtags />} />
<Route path="/test" element={<Testpage />} />
</Routes>
</Wrapper>
</BrowserRouter>
Expand Down
71 changes: 35 additions & 36 deletions src/components/home/DiaryList.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,48 @@
import { useEffect, useState } from "react";
import styled from "styled-components";
import { getAllDiary } from "../../services/apis/diary/diary";
import { useNavigate } from "react-router-dom";

const diaries = [
{
date: "2025.05.01.",
title: "오늘은 영화 보러 간 날",
tags: ["#취미", "#휴식"],
content: "재밌었다!".repeat(30),
},
{
date: "2025.05.01.",
title: "오늘은 영화 보러 간 날",
tags: ["#취미", "#휴식"],
content: "재밌었다!".repeat(30),
},
{
date: "2025.05.01.",
title: "오늘은 영화 보러 간 날",
tags: ["#취미", "#휴식"],
content: "재밌었다!".repeat(30),
},
{
date: "2025.05.01.",
title: "오늘은 영화 보러 간 날",
tags: ["#취미", "#휴식"],
content: "재밌었다!".repeat(30),
},
{
date: "2025.05.01.",
title: "오늘은 영화 보러 간 날",
tags: ["#취미", "#휴식"],
content: "재밌었다!".repeat(30),
},
];
interface Diary {
id: number;
date: string;
title: string;
hashTags: string[];
content: string;
}

const DiaryList = () => {
const [diaries, setDiaries] = useState<Diary[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const navigate = useNavigate();

useEffect(() => {
getAllDiary()
.then((data) => {
setDiaries(data);
setLoading(false);
})
.catch((err) => {
console.error("일기 불러오기 실패:", err);
setError("일기를 불러오지 못했습니다.");
setLoading(false);
});
}, []);

if (loading) return <div>로딩 중...</div>;
if (error) return <div>{error}</div>;

return (
<Wrapper>
<CardList>
{diaries.map((diary, index) => (
<Card key={index}>
{diaries.map((diary) => (
<Card key={diary.id} onClick={() => navigate(`/diary/${diary.id}`)}>
<DateText>{diary.date}</DateText>
<DiaryTitle>{diary.title}</DiaryTitle>
<TagWrapper>
{diary.tags.map((tag, i) => (
<Tag key={i}>{tag}</Tag>
{diary.hashTags.map((tag, i) => (
<Tag key={i}>#{tag}</Tag>
))}
</TagWrapper>
<Content>{diary.content}</Content>
Expand Down
88 changes: 59 additions & 29 deletions src/pages/DiaryDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,82 @@
import styled from "styled-components";
import { IoHomeOutline } from "react-icons/io5";
import { BsPencil } from "react-icons/bs";
import { useNavigate } from "react-router-dom";
import { useNavigate, useParams } from "react-router-dom";
import { useEffect, useState } from "react";
import { getDiary } from "../services/apis/diary/diary";

interface DiaryResponse {
id: number;
date: string;
title: string;
content: string;
comment: string;
character: string;
hashTags: string[];
}

const DiaryDetail = () => {
const navigate = useNavigate();
const { diaryId } = useParams();

const [diary, setDiary] = useState<DiaryResponse | null>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState<boolean>(true);

useEffect(() => {
if (diaryId) {
getDiary(diaryId)
.then((data) => {
setDiary(data);
setLoading(false);
})
.catch((err) => {
console.error("Error fetching diary:", err);
setError("일기를 불러오는 데 실패했습니다.");
setLoading(false);
});
}
}, [diaryId]);

const formatDate = (rawDate: string) => {
const date = new Date(rawDate);
return `${date.getFullYear()}.${String(date.getMonth() + 1).padStart(2, "0")}.${String(date.getDate()).padStart(2, "0")}.`;
};

if (loading) return <div>로딩 중...</div>;
if (error) return <div>{error}</div>;
if (!diary) return <div>일기 데이터가 없습니다.</div>;

return (
<Container>
<Header>
<HomeIcon onClick={() => navigate("/")} />
<DateText>2025.05.01.</DateText>
<EditIcon />
<DateText>{formatDate(diary.date)}</DateText>
<EditIcon onClick={() => navigate(`/edit/${diary.id}`)} />
</Header>

<Body>
<Title>오늘은 영화 보러 간 날</Title>
<Title>{diary.title}</Title>
<TagBox>
<Tag>#취미</Tag>
<Tag>#휴식</Tag>
{diary.hashTags && diary.hashTags.length > 0 ? (
diary.hashTags.map((tag, idx) => <Tag key={idx}>#{tag}</Tag>)
) : (
<Tag>#태그없음</Tag>
)}
</TagBox>

<Content>
오늘은 오랜만에 영화관에 가서 영화를 보고 왔다. 요즘 바빠서 제대로
쉬지도 못했는데, 이렇게 여유롭게 시간을 보내니 기분이 참 좋았다.
친구와 약속을 잡고 미리 예매까지 해두었는데, 다행히 좋은 자리에서
관람할 수 있었다. <br />
<br />
보고 싶었던 영화라 기대가 컸는데, 그 기대를 저버리지 않고 정말
재미있었다. 배우들의 연기도 훌륭했고, 스토리도 탄탄해서 중간에 지루할
틈이 없었다. 영화관 특유의 분위기, 어두운 조명과 커다란 스크린, 웅장한
사운드까지 모든 게 몰입감을 더해줬다. <br />
<br />
팝콘과 콜라도 빠질 수 없어서, 먹으면서 영화 보는 재미도 쏠쏠했다. 엔딩
크레딧이 올라갈 때는 아쉬운 마음도 들었지만, 오랜만에 힐링되는 시간을
보낸 것 같아 만족스럽다. 다음엔 다른 장르의 영화도 보러 가고 싶다.
</Content>
<Content>{diary.content}</Content>

<CommentTitle>AI 친구의 코멘트</CommentTitle>
<CommentCard>
<CharacterRow>
<CharacterImg src="/ai_character.png" alt="웅이" />
<CharacterName>웅이</CharacterName>
</CharacterRow>
<CommentText>
오랜만에 영화관에서 좋은 시간 보냈다니 내가 다 기쁘다! 너의 여유로운
하루가 참 따뜻하게 느껴져 :)
</CommentText>
<CharacterRow>
<CharacterImg
src={`/images/characters/${diary.character}.png`}
alt={diary.character}
/>
<CharacterName>{diary.character}</CharacterName>
</CharacterRow>
<CommentText>{diary.comment}</CommentText>
</CommentCard>
</Body>
</Container>
Expand Down
30 changes: 30 additions & 0 deletions src/pages/Testpage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useState } from "react";
import { sendMessageToGPT } from "../services/gpt/openai";

const Testpage = () => {
const [input, setInput] = useState("");
const [response, setResponse] = useState("");

const handleSubmit = async () => {
const result = await sendMessageToGPT(input);
setResponse(result);
};

return (
<div style={{ padding: 20 }}>
<h1>GPT와 대화하기</h1>
<textarea
rows={4}
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="메시지를 입력하세요"
/>
<br />
<button onClick={handleSubmit}>전송</button>
<h3>GPT 응답:</h3>
<p>{response}</p>
</div>
);
};

export default Testpage;
32 changes: 25 additions & 7 deletions src/pages/chatting/ChatPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ const EndChatButton = styled.button`

const ChatPage = () => {
const { chatId = "", character = "" } = useParams();
const [messages, setMessages] = useState<{ role: string; content: string }[]>([]);
const [messages, setMessages] = useState<{ role: string; content: string }[]>(
[],
);
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
const { selectedDate } = useSettingStore();
Expand All @@ -99,13 +101,28 @@ const ChatPage = () => {
const reply = await postComment(chatId, character, userMessage.content);

if (reply?.content) {
setMessages((prev) => [...prev, { role: "bot", content: reply.content }]);
setMessages((prev) => [
...prev,
{ role: "bot", content: reply.content },
]);
} else {
setMessages((prev) => [...prev, { role: "bot", content: "응답을 받지 못했습니다. 다시 시도해 주세요." }]);
setMessages((prev) => [
...prev,
{
role: "bot",
content: "응답을 받지 못했습니다. 다시 시도해 주세요.",
},
]);
}
} catch (error) {
console.error("Failed to send message:", error);
setMessages((prev) => [...prev, { role: "bot", content: "에러가 발생했습니다. 나중에 다시 시도해 주세요." }]);
setMessages((prev) => [
...prev,
{
role: "bot",
content: "에러가 발생했습니다. 나중에 다시 시도해 주세요.",
},
]);
} finally {
setLoading(false);
}
Expand All @@ -125,9 +142,10 @@ const ChatPage = () => {
}

try {
const formattedDate = selectedDate instanceof Date
? selectedDate.toISOString().split("T")[0]
: selectedDate;
const formattedDate =
selectedDate instanceof Date
? selectedDate.toISOString().split("T")[0]
: selectedDate;

const response = await postDiary(chatId, character, formattedDate);
alert("대화가 저장되었습니다.");
Expand Down
6 changes: 3 additions & 3 deletions src/pages/writing/WritingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const TextInput = styled.input`
outline: none;

&::placeholder {
color: #B0BCD2;
color: #b0bcd2;
}
`;

Expand All @@ -110,7 +110,7 @@ const TagInput = styled.input`
border-radius: 12px;
outline: none;
&::placeholder {
color: #B0BCD2;
color: #b0bcd2;
}
`;

Expand All @@ -127,6 +127,6 @@ const ContentArea = styled.textarea`
outline: none;

&::placeholder {
color: #6D7EA0;
color: #6d7ea0;
}
`;
Loading