Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FE] 비회원 로그인 페이지와 약속 조회/수정 페이지를 연결 #105

Merged
merged 4 commits into from
Jul 31, 2024
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
6 changes: 5 additions & 1 deletion frontend/src/apis/getHeaders.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { getCookie } from '@utils/cookies';

import { COOKIE_KEYS } from '@constants/cookies';

export default function getHeaders(): Record<string, string> {
const headers = { 'Content-type': 'application/json' };
const token = localStorage.getItem('momoToken');
const token = getCookie(COOKIE_KEYS.token);

if (token) {
return { ...headers, Authorization: `Bearer ${token}` };
Expand Down
19 changes: 9 additions & 10 deletions frontend/src/apis/getMeetingFrame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ export interface Schedules {

interface MeetingFrameSeverResponse {
data: {
meeting: {
name: string;
firstTime: string;
lastTime: string;
availableDates: string[];
};
attendees: string[];
meetingName: string;
firstTime: string;
lastTime: string;
availableDates: string[];
attendeeNames: string[];
};
}

Expand All @@ -37,13 +35,14 @@ const getMeetingFrame = async (uuid: string): Promise<MeetingFrame | undefined>
});

if (response) {
const { name, firstTime, lastTime, availableDates } = response.data.meeting;
const { meetingName, firstTime, lastTime, availableDates } = response.data;

return {
meetingName: name,
meetingName,
firstTime,
lastTime,
availableDates,
attendees: response.data.attendees,
attendees: response.data.attendeeNames,
};
}
};
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/apis/postSchedule.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { getCookie } from '@utils/cookies';

import { API_URL } from '@constants/api';

import { fetchClient } from './fetchClient';
Expand All @@ -11,15 +13,15 @@ export const postSchedule = async ({
requestData: Schedules[];
}) => {
const url = `${API_URL}/api/v1/schedule/${uuid}`;
const attendeeName = localStorage.getItem('meetingAttendee');
const attendeeName = getCookie('attendeeName');

await fetchClient({
url,
method: 'POST',
errorMessage: '약속 참여 시간을 등록하는 중 문제가 발생했어요 :(',
body: {
attendeeName,
schedules: requestData,
dateTimes: requestData,
},
});
};
17 changes: 7 additions & 10 deletions frontend/src/components/Time/Picker/TimePickerContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { useQuery } from '@tanstack/react-query';
import React from 'react';
import { useParams } from 'react-router-dom';

import { handleGetMeetingSchedules } from '@apis/getMeetingSchedules';
import { getMeetingMySchedule } from '@apis/getMeetingSchedules';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Comment]

오늘 약속 참여자 자신의 스케쥴을 조회하는 API 엔드포인트가 me로 변경되었는데, getMeetingMySchedule함수 이름도 getMeetingMeSchedule로 변경하는 것이 좋을까요? 🤔
함수명 자체는 어색한 것 같은데 백엔드 엔드포이트와 맞추려면 어쩔수 없나 싶기도 하네요~
빙봉은 어떻게 생각하시나요

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hwinkr
해리가 제안 주신 의견대로라면 API path가 변경되면 또 함수명을 그에 맞게 수정해야 하지 않을까요?
API 경로가 변경될 때마다 함수명도 수정해야 한다면, 프론트엔드 코드가 백엔드 API 구조에 너무 의존하게 되는 것 같습니다🤔
API 호출 함수명은 API 경로와 독립적으로, 해당 기능의 의미를 명확히 전달할 수 있도록 프론트엔드 관점에서 정의하는 것은 어떨까요?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋아요~ 그렇게 하는게 좋을 것 같아요 프론트엔드에서는 my로 하시죠!


import { QUERY_KEY } from '@constants/queryKeys';

import TimePicker from '.';

const TEST_UUID = '550e8400';

interface TimePickerContainerProps {
firstTime: string;
lastTime: string;
Expand All @@ -20,16 +18,15 @@ export default function TimePickerContainer({
lastTime,
availableDates,
}: TimePickerContainerProps) {
const attendeeName = localStorage.getItem('meetingAttendee');
const params = useParams<{ uuid: string }>();
const uuid = params.uuid!;
Comment on lines +21 to +22
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2]

현재 useParams를 많은 컴포넌트들에서 호출하고 있고, 호출한 후의 역할이 서버에서 전달해주는 uuid를 찾는 역할이니 커스텀 훅 useUUID를 만들어서 중복을 줄여보는 것은 어떨까요?

const { data: meetingSchedules } = useQuery({
queryKey: [QUERY_KEY.meetingSchedules, attendeeName],
queryFn: () =>
handleGetMeetingSchedules({ uuid: TEST_UUID, attendeeName: attendeeName as string }),
queryKey: [QUERY_KEY.meetingMySchedule],
queryFn: () => getMeetingMySchedule(uuid),
staleTime: 0,
});

if (!meetingSchedules) return <></>;
if (!('attendeeName' in meetingSchedules)) return <></>;
if (!meetingSchedules) return null;

return (
<TimePicker
Expand Down
21 changes: 19 additions & 2 deletions frontend/src/components/Time/Viewer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { css } from '@emotion/react';
import { useQuery } from '@tanstack/react-query';
import { useContext } from 'react';
import { useParams } from 'react-router-dom';
import { useNavigate, useParams } from 'react-router-dom';

import { TimePickerUpdateStateContext } from '@contexts/TimePickerUpdateStateProvider';

import Button from '@components/_common/Button';

import { handleGetMeetingSchedules } from '@apis/getMeetingSchedules';

import { getCookie } from '@utils/cookies';

import { COOKIE_KEYS } from '@constants/cookies';
import { QUERY_KEY } from '@constants/queryKeys';

import { generateScheduleMatrix } from '../Picker/TimePicker.util';
Expand Down Expand Up @@ -50,6 +53,8 @@ export default function TimeViewer({
const params = useParams<{ uuid: string }>();
const uuid = params.uuid!;

const navigate = useNavigate();

const { data: meetingSchedules } = useQuery({
queryKey: [QUERY_KEY.meetingSchedules, selectedAttendee],
queryFn: () => handleGetMeetingSchedules({ uuid, attendeeName: selectedAttendee }),
Expand All @@ -63,6 +68,18 @@ export default function TimeViewer({
meetingSchedules,
});

const handleScheduleUpdate = () => {
// TODO : 쿠키에 토큰 있는지 확인 해야 함.
const savedToken = getCookie(COOKIE_KEYS.token);

if (!savedToken) {
alert('로그인 해주세요');
navigate(`/meeting/${uuid}/login`);
return;
}
handleToggleIsTimePickerUpdate();
};

return (
<div>
<table css={s_table} aria-label="약속 시간 조회 테이블">
Expand Down Expand Up @@ -103,7 +120,7 @@ export default function TimeViewer({
</table>

<div css={s_buttonContainer}>
<Button text="수정하기" onClick={handleToggleIsTimePickerUpdate} />
<Button text="수정하기" onClick={handleScheduleUpdate} />
</div>
</div>
);
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/constants/cookies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const COOKIE_KEYS = {
token: 'token',
attendeeName: 'attendeeName',
};
1 change: 1 addition & 0 deletions frontend/src/constants/queryKeys.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const QUERY_KEY = {
meeting: 'meeting',
meetingSchedules: 'meetingSchedules',
meetingMySchedule: 'meetingMySchedule',
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export const s_inputContainer = css`
gap: 1rem;

width: 90%;
height: 16rem;
padding: 1.6rem;

background-color: #f7dacb;
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/pages/AttendeeLoginPage/AttendeeLoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import postAttendeeLogin from '@apis/attendee';

import { setCookie } from '@utils/cookies';

import { COOKIE_KEYS } from '@constants/cookies';

import { s_button, s_container, s_inputContainer } from './AttendeeLoginPage.styles';

export default function AttendeeLoginPage() {
Expand All @@ -30,9 +32,10 @@ export default function AttendeeLoginPage() {
request: { name, password },
});

setCookie('token', response.data.token, { path: '/', maxAge: 604800 });
setCookie(COOKIE_KEYS.token, response.data.token, { path: '/', maxAge: 604800 });
setCookie(COOKIE_KEYS.attendeeName, name); // TODO: name을 cookie에 저장하지 않고, navigate 두 번째 인자로 넘기는 방향 (@Yoonkyoungme)

navigate('/meeting-time-pick'); // TODO: meeting 조회/수정 페이지로 이동
navigate(`/meeting/${uuid}`);
} catch (error) {
console.error('Login failed:', error);
}
Expand Down
14 changes: 10 additions & 4 deletions frontend/src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import GlobalLayout from '@layouts/GlobalLayout';

import { TimePickerUpdateStateProvider } from '@contexts/TimePickerUpdateStateProvider';

import AttendeeLoginPage from '@pages/AttendeeLoginPage/AttendeeLoginPage';
import CreateMeetingPage from '@pages/CreateMeetingPage';
import MeetingLinkSharePage from '@pages/MeetingLinkSharePage';
import MeetingTimePickPage from '@pages/MeetingTimePickPage';
Expand All @@ -14,6 +15,10 @@ const router = createBrowserRouter([
path: '/',
element: <GlobalLayout />,
children: [
{
index: true,
element: <CreateMeetingPage />,
},
{
path: 'meeting',
children: [
Expand All @@ -25,14 +30,15 @@ const router = createBrowserRouter([
</TimePickerUpdateStateProvider>
),
},
{
path: 'create',
element: <CreateMeetingPage />,
},

{
path: 'complete',
element: <MeetingLinkSharePage />,
},
{
path: ':uuid/login',
element: <AttendeeLoginPage />,
},
],
},
],
Expand Down
1 change: 1 addition & 0 deletions frontend/src/stores/servers/schedule/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const usePostScheduleMutation = (onSettledCallback: () => void) => {
onSettled: () => {
onSettledCallback();
queryClient.invalidateQueries({ queryKey: [QUERY_KEY.meeting, ''] });
queryClient.invalidateQueries({ queryKey: [QUERY_KEY.meetingMySchedule] });
``;
},
});
Expand Down