Skip to content

Commit 9089366

Browse files
authored
Merge pull request YJU-OKURA#127 from dorimu0/refact/folder-structure
プロジェクトフォルダ構造の改善
2 parents b7dbe15 + 1e5b9a3 commit 9089366

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+252
-195
lines changed

public/gif/eclipse.gif

-20.7 KB
Loading

public/svgs/login/logo.svg

+8-8
Loading

src/api/apiUtils.ts

+59-51
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,88 @@
1-
import axios from 'axios';
21
import BASE_URLS from './baseUrl';
32
import HTTP_STATUS from './httpStatus';
43

5-
const api = axios.create();
6-
7-
api.interceptors.request.use(async config => {
4+
async function fetchWithInterceptors(url: string, options: RequestInit) {
85
const token = localStorage.getItem('access_token');
96

107
if (token) {
11-
config.headers.Authorization = `Bearer ${token}`;
8+
options.headers = {
9+
...options.headers,
10+
Authorization: `Bearer ${token}`,
11+
};
1212
}
1313

14-
return config;
15-
});
14+
let response = await fetch(url, options);
1615

17-
api.interceptors.response.use(
18-
response => {
19-
return response;
20-
},
21-
async error => {
22-
const originalRequest = error.config;
23-
// トークンの有効期限が切れていて、トークン更新要求がない場合
24-
if (error.response.status === 401 && !originalRequest._retry) {
25-
originalRequest._retry = true;
26-
const body = {
27-
refresh_token: localStorage.getItem('refresh_token'),
28-
};
29-
try {
30-
const res = await axios.post(
31-
`${BASE_URLS.gin}/auth/google/refresh-token`,
32-
body
33-
);
34-
if (res.status === 200) {
35-
const newToken = res.data.data.access_token;
36-
localStorage.setItem('access_token', newToken);
37-
api.defaults.headers.common['Authorization'] = `Bearer ${newToken}`;
38-
// トークンが更新された後、元のリクエストを再実行
39-
return api(originalRequest);
40-
}
41-
} catch (error) {
42-
console.error('トークンの有効期限が切れました。');
43-
window.location.href = '/intro';
16+
if (response.status === 401) {
17+
const refreshToken = localStorage.getItem('refresh_token');
18+
const refreshResponse = await fetch(
19+
`${BASE_URLS.gin}/auth/google/refresh-token`,
20+
{
21+
method: 'POST',
22+
headers: {
23+
'Content-Type': 'application/json',
24+
},
25+
body: JSON.stringify({refresh_token: refreshToken}),
4426
}
27+
);
28+
29+
if (refreshResponse.ok) {
30+
const data = await refreshResponse.json();
31+
const newToken = data.data.access_token;
32+
localStorage.setItem('access_token', newToken);
33+
options.headers = {
34+
...options.headers,
35+
Authorization: `Bearer ${newToken}`,
36+
};
37+
38+
response = await fetch(url, options);
39+
} else {
40+
console.error('トークンの有効期限が切れました。');
41+
window.location.href = '/intro';
4542
}
46-
return Promise.reject(error);
4743
}
48-
);
44+
45+
if (!response.ok) {
46+
throw new Error(`HTTP error! status: ${response.status}`);
47+
}
48+
49+
return response;
50+
}
4951

5052
const req = async (
5153
url: string,
52-
method: string,
54+
method: RequestInit['method'],
5355
server: 'gin' | 'nest',
54-
body: object | undefined = undefined
56+
body: BodyInit | null | undefined = undefined
5557
) => {
56-
api.defaults.baseURL = BASE_URLS[server];
58+
const headers = new Headers();
59+
const token = localStorage.getItem('access_token');
60+
if (token) {
61+
headers.append('Authorization', `Bearer ${token}`);
62+
}
5763

58-
if (typeof body !== 'undefined') {
59-
if (body instanceof FormData) {
60-
api.defaults.headers.common['Content-Type'] = 'multipart/form-data';
61-
} else if (typeof body !== 'undefined') {
62-
api.defaults.headers.common['Content-Type'] = 'application/json';
63-
}
64+
if (body instanceof FormData) {
65+
headers.append('Content-Type', 'multipart/form-data');
66+
} else if (body) {
67+
headers.append('Content-Type', 'application/json');
68+
body = JSON.stringify(body);
6469
}
70+
6571
try {
66-
const response = await api.request({
67-
url,
72+
const response = await fetchWithInterceptors(BASE_URLS[server] + url, {
6873
method,
69-
data: body,
70-
withCredentials: true,
74+
headers,
75+
body,
76+
credentials: 'include',
7177
});
78+
7279
if (response.status === HTTP_STATUS.NO_CONTENT) {
7380
return {};
7481
}
75-
return response.data;
82+
83+
return response.json();
7684
} catch (error) {
77-
console.error(`${method} リクエスト中にエラーが発生しました.`, error);
85+
console.error(`${method} リクエスト中にエラーが発生しました`, error);
7886
throw error;
7987
}
8088
};

src/api/auth/getGoogleLogin.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import req from '../apiUtils';
22

33
const getGoogleLogin = async () => {
4-
const response = await req('auth/google/login', 'get', 'gin');
4+
const response = await req('/auth/google/login', 'get', 'gin');
55

66
return response.data;
77
};

src/api/feedback/getCheckRefer.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import req from '../apiUtils';
2+
3+
const getCheckRefer = async (cId: number, mId: number): Promise<boolean> => {
4+
const response = await req(
5+
`/class/${cId}/feedback/materials/${mId}/check-refer`,
6+
'get',
7+
'nest'
8+
);
9+
10+
console.log(response);
11+
12+
return response;
13+
};
14+
15+
export default getCheckRefer;

src/app/Loading.tsx

-5
This file was deleted.

src/app/[className]/[materialName]/components/FeedbackContainer.tsx src/app/classes/[cId]/[mId]/components/FeedbackContainer.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,23 @@ import getFeedbacks from '@/src/api/feedback/getFeedbacks';
77
import materialState from '@/src/recoil/atoms/materialState';
88
import {feedback} from '@/src/interfaces/feedback';
99
import '@/src/styles/variable.css';
10+
import getCheckRefer from '@/src/api/feedback/getCheckRefer';
1011

1112
const FeedbackContainer = () => {
1213
const material = useRecoilValue(materialState);
1314
const [reload, setReload] = useState<boolean>(false);
15+
const [references, setReferences] = useState<boolean>(false);
1416
const [feedbacks, setFeedbacks] = useState<feedback[]>([]);
1517
useEffect(() => {
1618
if (!material) return;
1719
getFeedbacks(4, parseInt(material.id), 1, 5).then(res => {
1820
console.log(res);
1921
setFeedbacks(res);
2022
});
23+
getCheckRefer(4, parseInt(material.id)).then(res => {
24+
console.log(res);
25+
setReferences(res);
26+
});
2127
}, [reload]);
2228

2329
return (
@@ -55,7 +61,9 @@ const FeedbackContainer = () => {
5561
<div className="text-2xl font-semibold p-5">
5662
✅ 사용자가 질문한 내용과 가장 관련성이 높은 페이지
5763
</div>
58-
{material ? <FeedbackKeywordList mId={material?.id} /> : null}
64+
{material ? (
65+
<FeedbackKeywordList mId={material?.id} references={references} />
66+
) : null}
5967
</div>
6068
</div>
6169
</div>

src/app/[className]/[materialName]/components/FeedbackForm.tsx src/app/classes/[cId]/[mId]/components/FeedbackForm.tsx

+24-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
'use client';
22
import {useState} from 'react';
3+
import Image from 'next/image';
34
import getFeedback from '@/src/api/feedback/getFeedback';
45
import postFeedback from '@/src/api/feedback/postFeedback';
6+
import gifs from '@/public/gif';
57

68
const FeedbackForm = ({
79
mId,
@@ -11,6 +13,7 @@ const FeedbackForm = ({
1113
setReload: (value: boolean) => void;
1214
}) => {
1315
const [isOpen, setIsOpen] = useState(false);
16+
const [isLoading, setIsLoading] = useState(false);
1417
const [feedback, setFeedback] = useState('');
1518
const [type, setType] = useState('all');
1619

@@ -24,6 +27,7 @@ const FeedbackForm = ({
2427
console.log('feedback:', feedback);
2528
setFeedback('');
2629
}
30+
setIsLoading(true);
2731
getFeedback(4, mId, type, chat);
2832
};
2933

@@ -37,6 +41,7 @@ const FeedbackForm = ({
3741
};
3842

3943
const chat = async (reader: ReadableStreamDefaultReader) => {
44+
setIsLoading(false);
4045
let feedbackData = '';
4146
try {
4247
while (reader) {
@@ -46,12 +51,10 @@ const FeedbackForm = ({
4651
setFeedback(feedback => feedback + decodedValue);
4752

4853
if (feedback.includes(' ')) {
49-
console.log(feedbackData);
5054
feedbackData = '';
5155
}
5256
if (done) {
53-
// console.log('스트림이 완료되었습니다.');
54-
// console.log(feedback);
57+
console.log(feedbackData);
5558
break;
5659
}
5760
}
@@ -78,19 +81,19 @@ const FeedbackForm = ({
7881
>
7982
<div className="w-[550px] h-full text-center box-border">
8083
<div className="text-4xl font-semibold pt-4">
81-
어시스턴트의 분석 결과
84+
The Assistant’s Analysis
8285
</div>
8386
<div className="text-sm text-neutral-400 my-2">
84-
팀과 협업하여 미노리를 최대한 활용하세요.
87+
Collaborate with your team to get the most out of monday.com
8588
</div>
8689
<div className="w-full m-auto flex text-left py-2">
8790
<div className="w-1/2 flex items-center">
8891
<select
8992
className="border p-2 rounded-lg"
9093
onChange={handleChangeType}
9194
>
92-
<option value="all">모든 피드백</option>
93-
<option value="part">일부 피드백</option>
95+
<option value="all">All Feedback</option>
96+
<option value="part">Partial feedback</option>
9497
</select>
9598
<div className="px-2">
9699
<button
@@ -104,22 +107,32 @@ const FeedbackForm = ({
104107
</div>
105108
<div className="py-3">
106109
<div className="w-full h-[260px] rounded-md p-4 bg-gray-100 overflow-scroll">
107-
{feedback.replace(/AI|:|"/g, '')}
108-
{/* {feedback} */}
110+
{isLoading ? (
111+
<div className="h-[260px] flex justify-center items-center">
112+
<Image
113+
src={gifs.eclipse}
114+
width={100}
115+
height={100}
116+
alt="gif"
117+
/>
118+
</div>
119+
) : (
120+
feedback.replace(/AI|:|"/g, '')
121+
)}
109122
</div>
110123
</div>
111124
<div className="w-2/3 m-auto flex justify-between">
112125
<button
113126
className="border border-gray-500 rounded-full px-6 py-2"
114127
onClick={() => setIsOpen(false)}
115128
>
116-
{'< '}뒤로가기
129+
{'< '}Close
117130
</button>
118131
<button
119132
className="bg-blue-500 text-white rounded-full px-6 py-2"
120133
onClick={handleClickSave}
121134
>
122-
저장
135+
Save
123136
</button>
124137
</div>
125138
</div>

src/app/[className]/[materialName]/components/FeedbackKeywordList.tsx src/app/classes/[cId]/[mId]/components/FeedbackKeywordList.tsx

+13-9
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,29 @@ import Image from 'next/image';
44
import getKeywords from '@/src/api/feedback/getKeywords';
55
import {keyword} from '@/src/interfaces/feedback';
66

7-
const FeedbackKeywordList = ({mId}: {mId: string}) => {
7+
const FeedbackKeywordList = ({
8+
mId,
9+
references,
10+
}: {
11+
mId: string;
12+
references: boolean;
13+
}) => {
814
const [keywords, setKeywords] = useState<keyword[]>([]);
915
const [loading, setLoading] = useState(true);
1016
useEffect(() => {
11-
console.log(mId);
12-
getKeywords(4, parseInt(mId))
13-
.then(res => {
17+
if (references) {
18+
getKeywords(4, parseInt(mId)).then(res => {
1419
console.log(res);
1520
setKeywords(res);
1621
setLoading(false);
17-
})
18-
.catch(err => {
19-
console.log(err);
20-
setLoading(false);
2122
});
23+
}
2224
}, []);
2325
return (
2426
<div className="flex justify-center">
25-
{loading ? (
27+
{!references ? (
28+
<div>not found</div>
29+
) : loading ? (
2630
<div className="text-3xl p-3 font-semibold">
2731
<Image
2832
src="/gif/loading.gif"

src/app/[className]/[materialName]/components/ManageContainer.tsx src/app/classes/[cId]/[mId]/components/ManageContainer.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import FeedbackContainer from './FeedbackContainer';
88
import '@/src/styles/variable.css';
99

1010
const ManageContainer = () => {
11-
const tabs = ['Material', 'Quiz', 'Feedback', 'Attendance'];
11+
const tabs = ['자료', '퀴즈', '피드백', '출석'];
1212
const [activeTab, setActiveTab] = useState(tabs[0]);
1313
const tabMapping = {
14-
Material: <ManageMaterialContainer />,
14+
자료: <ManageMaterialContainer />,
1515
// Quiz: <QuizContainer />,
16-
Feedback: <FeedbackContainer />,
16+
피드백: <FeedbackContainer />,
1717
// Attendance: <AttendanceContainer />,
1818
};
1919
return (

src/app/[className]/[materialName]/components/subComponents/PromptChat.tsx src/app/classes/[cId]/[mId]/components/subComponents/PromptChat.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const PromptChat = ({pId}: {pId: number}) => {
2020
res.messages.reverse();
2121
setMsg(res.messages);
2222
});
23-
}, [reload]);
23+
}, []);
2424

2525
const handleClickIcon = (mId: number) => {
2626
patchMessage(4, pId, mId, true).then(res => {

src/app/[className]/[materialName]/components/subComponents/SubContainer.tsx src/app/classes/[cId]/[mId]/components/subComponents/SubContainer.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import materialState from '@/src/recoil/atoms/materialState';
88
import '@/src/styles/variable.css';
99

1010
const SubContainer = () => {
11-
const TABS = ['PromptChat', 'Storage'];
11+
const TABS = ['프롬프트창', '저장목록'];
1212
const [activeTab, setActiveTab] = useState(TABS[0]);
1313
const material = useRecoilValue(materialState);
1414
const tabMapping = {
15-
PromptChat: <PromptChat pId={material ? material.prompts[0]?.id : 0} />,
16-
Storage: <Storage pId={material ? material.prompts[0]?.id : 0} />,
15+
프롬프트창: <PromptChat pId={material ? material.prompts[0]?.id : 0} />,
16+
저장목록: <Storage pId={material ? material.prompts[0]?.id : 0} />,
1717
};
1818

1919
return (

0 commit comments

Comments
 (0)