Skip to content

Commit 17f0848

Browse files
authored
Merge pull request #34 from sw-security-web-app/fetchRecentChat
SCRUM-70 로그인 여부에 따라 route 설정 완료, 최근 대화기록 불러오기 완료
2 parents 44f737f + 3779cb8 commit 17f0848

File tree

11 files changed

+185
-129
lines changed

11 files changed

+185
-129
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { useEffect } from "react";
2+
import { useStore } from "../store/store"; // Zustand store import
3+
import { useNavigate } from "@remix-run/react";
4+
5+
export const useAuthRedirect = () => {
6+
const isLogin = useStore((state) => state.isLogin()); // 로그인 상태 가져오기
7+
const navigate = useNavigate();
8+
9+
useEffect(() => {
10+
if (!isLogin) {
11+
navigate("/login", { replace: true });
12+
}
13+
}, [isLogin, navigate]);
14+
};

sw-security/app/components/modal.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ type Props = {
44
setIsOpen: (isOpen: boolean) => void;
55
text: string;
66
title: string;
7+
btnColor: string;
78
};
89

9-
export default function Modal({ setIsOpen, text, title }: Props) {
10+
export default function Modal({ setIsOpen, text, title, btnColor }: Props) {
1011
return (
1112
<div className={modalStyle.modalContainer}>
1213
<div className={modalStyle.modalHeader}>
@@ -28,6 +29,7 @@ export default function Modal({ setIsOpen, text, title }: Props) {
2829
<div className={modalStyle.modalBtnDiv}>
2930
<button
3031
className={modalStyle.modalBtn}
32+
style={{ backgroundColor: btnColor }}
3133
onClick={() => setIsOpen(false)}
3234
>
3335
확인
Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,30 @@
1+
import { useState, useEffect } from "react";
12
import { Link } from "@remix-run/react";
23
import nickNameHeader from "../css/nickNameHeader.module.css";
4+
import { GoPerson } from "react-icons/go";
35

46
type Props = { nickNameColor: string; profileColor: string };
7+
58
export default function Profile({ nickNameColor, profileColor }: Props) {
6-
const isBrowser = typeof window !== "undefined"; // 브라우저에서만 실행되도록 체크
7-
const userName = isBrowser ? localStorage.getItem("userName") : null; // 브라우저에서만 localStorage 접근
8-
return (
9-
// <Link to="/main/profile" style={{ textDecoration: "none" }}>
10-
// <div className={nickNameHeader.container}>
11-
// <div
12-
// className={nickNameHeader.nickname}
13-
// style={{ color: nickNameColor }}
14-
// >
15-
// 닉네임
16-
// </div>
17-
// <div
18-
// className={nickNameHeader.profile}
19-
// style={{ backgroundColor: profileColor }}
20-
// ></div>
21-
// </div>
22-
// </Link>
9+
const [userName, setUserName] = useState<string | null>(null);
10+
11+
useEffect(() => {
12+
// 클라이언트에서만 localStorage 접근
13+
const storedUserName = localStorage.getItem("userName");
14+
setUserName(storedUserName);
15+
}, []); // 컴포넌트가 마운트될 때만 실행
2316

17+
return (
2418
<div className={nickNameHeader.container}>
2519
<div className={nickNameHeader.nickname} style={{ color: nickNameColor }}>
2620
{userName}
2721
</div>
2822
<div
2923
className={nickNameHeader.profile}
3024
style={{ backgroundColor: profileColor }}
31-
></div>
25+
>
26+
<GoPerson style={{ width: "2.8rem", height: "2.8rem" }} />
27+
</div>
3228
</div>
3329
);
3430
}

sw-security/app/css/modal.module.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
}
6868
.modalBtn {
6969
border: none;
70-
background-color: #2a2c2f;
70+
/* background-color: #2a2c2f; */
7171
width: 6.67rem;
7272
height: 2.8rem;
7373
border-radius: 0.7rem;

sw-security/app/routes/_auth.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ export default function Join() {
1515
<>
1616
{isOpen && (
1717
<div className={modalStyle.overlay}>
18-
<Modal setIsOpen={setIsOpen} text={modalText} title={modalTitle} />
18+
<Modal
19+
setIsOpen={setIsOpen}
20+
text={modalText}
21+
title={modalTitle}
22+
btnColor="#2a2c2f"
23+
/>
1924
</div>
2025
)}
2126
<div className={authStyle.container}>

sw-security/app/routes/main._index.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import { useEffect } from "react";
77

88
export default function Join() {
99
const navigate = useNavigate();
10-
const isLogin = useOutletContext;
10+
const login = useOutletContext;
1111

12-
useEffect(() => {
13-
if (!isLogin) {
14-
navigate("/login");
15-
return;
16-
}
17-
}, [isLogin, navigate]);
12+
// useEffect(() => {
13+
// if (login) {
14+
// navigate("/login");
15+
// return;
16+
// }
17+
// }, [login, navigate]);
1818
return (
1919
<div className={defaultStyle.inner}>
2020
<div className={mainStyle.textContainer}>

sw-security/app/routes/main.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,12 @@ import modalStyle from "../css/modal.module.css";
77
import { useStore } from "../store/store";
88
import { useEffect, useState } from "react";
99
import CautionModal from "~/components/cautionModal";
10+
import { useAuthRedirect } from "~/Hooks/useAuthRedirect";
1011
export default function MainLayout() {
1112
const [isOpen, setIsOpen] = useState(false);
1213
const [modalText, setModalText] = useState("");
1314
const [modalTitle, setModalTitle] = useState("");
14-
const { isLogin } = useStore();
15-
const navigate = useNavigate();
16-
17-
useEffect(() => {
18-
console.log(isLogin);
19-
if (!isLogin) {
20-
navigate("/login");
21-
return;
22-
}
23-
}, [isLogin, navigate]);
15+
useAuthRedirect();
2416

2517
return (
2618
<>
@@ -41,7 +33,6 @@ export default function MainLayout() {
4133
setIsOpen,
4234
setModalText,
4335
setModalTitle,
44-
isLogin,
4536
}}
4637
/>
4738
</div>

sw-security/app/routes/main_.chatMain._index.tsx

Lines changed: 42 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
1-
import { useSearchParams, Link, useNavigate } from "@remix-run/react";
1+
import {
2+
useSearchParams,
3+
Link,
4+
useNavigate,
5+
useOutletContext,
6+
} from "@remix-run/react";
27
import chatMainStyle from "../css/chatMain.module.css";
38
import api from "~/api/api";
49
import { FaRegTrashAlt } from "react-icons/fa";
5-
import { useEffect } from "react";
10+
import { useEffect, useState } from "react";
611

712
export default function ChatMain() {
813
const [searchParams] = useSearchParams();
914
const ai = searchParams.get("ai") || "AI"; // 기본값 설정
1015
const navigate = useNavigate();
16+
const [chatDiv, setChatDiv] = useState<
17+
{ chatRoomId: number; latestAnswer: string; latestCreatedAt: string }[]
18+
>([]);
1119

1220
const createChatRoom = async () => {
1321
try {
@@ -22,18 +30,25 @@ export default function ChatMain() {
2230
} catch (error) {
2331
console.error("채팅방 생성 실패:", error);
2432
alert("새 대화 생성에 실패했습니다."); // 에러 처리
33+
} finally {
2534
}
2635
};
27-
28-
const fetchChatList = async () => {
36+
const fetchChatDiv = async () => {
2937
try {
30-
const response = await api.get("api/chat-room/latest");
38+
const response = await api.get(`/api/chat-room/latest?aiModelType=${ai}`);
39+
if (response.status === 200) {
40+
setChatDiv(response.data);
41+
} else {
42+
const error = await response.data;
43+
alert(error.message);
44+
}
3145
} catch (error: any) {
32-
console.error(error.message);
46+
alert(error.message);
3347
}
3448
};
49+
3550
useEffect(() => {
36-
fetchChatList;
51+
fetchChatDiv();
3752
}, []);
3853

3954
return (
@@ -50,66 +65,26 @@ export default function ChatMain() {
5065
</div>
5166
</div>
5267
<div className={chatMainStyle.chatSelectGrid}>
53-
<div className={chatMainStyle.child}>
54-
<div className={chatMainStyle.dateDiv}>
55-
<span>2024.11.11</span>
56-
<FaRegTrashAlt
57-
style={{ color: "white" }}
58-
className={chatMainStyle.trashIcon}
59-
/>
60-
</div>
61-
<div className={chatMainStyle.contentDiv}>
62-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Nesciunt
63-
minima quibusdam, nobis, illum omnis natus hic odit laudantium
64-
laboriosam animi aut aspernatur fuga est. Totam delectus
65-
perferendis est iusto exercitationem?
66-
</div>
67-
</div>
68-
<div className={chatMainStyle.child}>
69-
<div className={chatMainStyle.dateDiv}>
70-
<span>2024.11.11</span>
71-
<FaRegTrashAlt
72-
style={{ color: "white" }}
73-
className={chatMainStyle.trashIcon}
74-
/>
75-
</div>
76-
<div className={chatMainStyle.contentDiv}>
77-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Nesciunt
78-
minima quibusdam, nobis, illum omnis natus hic odit laudantium
79-
laboriosam animi aut aspernatur fuga est. Totam delectus
80-
perferendis est iusto exercitationem?
81-
</div>
82-
</div>
83-
<div className={chatMainStyle.child}>
84-
<div className={chatMainStyle.dateDiv}>
85-
<span>2024.11.11</span>
86-
<FaRegTrashAlt
87-
style={{ color: "white" }}
88-
className={chatMainStyle.trashIcon}
89-
/>
90-
</div>
91-
<div className={chatMainStyle.contentDiv}>
92-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Nesciunt
93-
minima quibusdam, nobis, illum omnis natus hic odit laudantium
94-
laboriosam animi aut aspernatur fuga est. Totam delectus
95-
perferendis est iusto exercitationem?
96-
</div>
97-
</div>
98-
<div className={chatMainStyle.child}>
99-
<div className={chatMainStyle.dateDiv}>
100-
<span>2024.11.11</span>
101-
<FaRegTrashAlt
102-
style={{ color: "white" }}
103-
className={chatMainStyle.trashIcon}
104-
/>
105-
</div>
106-
<div className={chatMainStyle.contentDiv}>
107-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Nesciunt
108-
minima quibusdam, nobis, illum omnis natus hic odit laudantium
109-
laboriosam animi aut aspernatur fuga est. Totam delectus
110-
perferendis est iusto exercitationem?
111-
</div>
112-
</div>
68+
{chatDiv.map((chat) => (
69+
<Link
70+
style={{ textDecoration: "none" }}
71+
to={`/main/chatMain/${ai}/${chat.chatRoomId}`}
72+
className={chatMainStyle.child}
73+
>
74+
<div key={chat.chatRoomId}>
75+
<div className={chatMainStyle.dateDiv}>
76+
<span>{chat.latestCreatedAt}</span>
77+
{/* <FaRegTrashAlt
78+
style={{ color: "white" }}
79+
className={chatMainStyle.trashIcon}
80+
/> */}
81+
</div>
82+
<div className={chatMainStyle.contentDiv}>
83+
{chat.latestAnswer}
84+
</div>
85+
</div>
86+
</Link>
87+
))}
11388
</div>
11489
</div>
11590
</div>

sw-security/app/routes/main_.chatMain.tsx

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,30 @@ import logoStyle from "../css/logo.module.css";
1010
import { useStore } from "../store/store";
1111
import { useEffect, useState } from "react";
1212
import Logo from "~/components/logo";
13+
import api from "~/api/api";
14+
import { create } from "zustand";
15+
import { useAuthRedirect } from "~/Hooks/useAuthRedirect";
1316

1417
export default function ChatMainLayout() {
1518
const [searchParams] = useSearchParams();
1619
const { ai2 } = useParams();
1720
const [ai, setAi] = useState<string>("AI");
21+
const [chatList, setChatList] = useState<number[]>([]);
22+
useAuthRedirect();
23+
24+
const fetchChatList = async () => {
25+
try {
26+
const response = await api.get(`/api/chat-room/get?aiModelType=${ai}`);
27+
if (response.status === 200) {
28+
setChatList(response.data.map((chat: any) => chat.chatRoomId));
29+
} else {
30+
const error = await response.data;
31+
alert(error.message);
32+
}
33+
} catch (error: any) {
34+
alert(error.message);
35+
}
36+
};
1837

1938
useEffect(() => {
2039
if (ai2) {
@@ -27,6 +46,12 @@ export default function ChatMainLayout() {
2746
}
2847
}, [ai2, searchParams]);
2948

49+
useEffect(() => {
50+
if (ai != "AI") {
51+
fetchChatList();
52+
}
53+
}, [ai]);
54+
3055
const logout = useStore((state) => state.logout);
3156
const navigate = useNavigate();
3257
const handleLogOut = () => {
@@ -61,9 +86,16 @@ export default function ChatMainLayout() {
6186
</div>
6287
</div>
6388
<div className={chatMainStyle.chatHistoryConatainer}>
64-
<span className={chatMainStyle.chatTitle}>대화 내용 1</span>
65-
<span className={chatMainStyle.chatTitle}>대화 내용 2</span>
66-
<span className={chatMainStyle.chatTitle}>대화 내용 3</span>
89+
{chatList.map((id, index) => (
90+
<Link
91+
style={{ textDecoration: "none" }}
92+
to={`/main/chatMain/${ai}/${id}`}
93+
className={chatMainStyle.chatTitle}
94+
key={id}
95+
>
96+
<span>대화 내용 {id}</span>
97+
</Link>
98+
))}
6799
</div>
68100
<div className={chatMainStyle.bottomItemContainer}>
69101
<button
@@ -75,16 +107,7 @@ export default function ChatMainLayout() {
75107
<img src="/img/myProfile.svg" alt="myProfile" />
76108
<span className={chatMainStyle.itemText2}>내 프로필</span>
77109
</button>
78-
<button
79-
className={chatMainStyle.itemDiv2}
80-
onClick={handleLogOut}
81-
// style={{
82-
// border: "none",
83-
// backgroundColor: "transparent",
84-
// padding: "0",
85-
// cursor: "pointer",
86-
// }}
87-
>
110+
<button className={chatMainStyle.itemDiv2} onClick={handleLogOut}>
88111
<img src="/img/logout.svg" alt="logout" />
89112
<span className={chatMainStyle.itemText2}>로그아웃</span>
90113
</button>

0 commit comments

Comments
 (0)