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

[구글 캘린더] 구글 캘린더 연동 #176

Merged
merged 6 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions src/config/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export const API_PATH = import.meta.env.VITE_REACT_APP_API_PATH;
export const GOOGLE_API_KEY = import.meta.env.VITE_REACT_APP_GOOGLE_API_KEY;
export const GOOGLE_CLIENT_ID = import.meta.env.VITE_REACT_APP_GOOGLE_CLIENT_ID;
export const CALENDAR_ID = import.meta.env.VITE_REACT_APP_CALENDAR_ID;
// env명 변경 필요
26 changes: 24 additions & 2 deletions src/page/Reservation/view/Month.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { ArrowBackIosNewOutlined, ArrowForwardIosOutlined } from '@mui/icons-mat
import { useGetReservations } from 'query/reservations';
import { Reservation } from 'model/reservations';
import { useEffect, useState } from 'react';
import {
convertEventToReservation, initClient, listUpcomingEvents, signIn,
} from 'ts/googleapi';
import * as S from './style';
// eslint-disable-next-line import/no-cycle
import MonthModal from './Month/MonthModal';
Expand All @@ -25,6 +28,13 @@ export type CalendarContent = {
currentMonth: number,
};

const filterEventsByMonth = (events: gapi.client.calendar.Event[], currentMonth: number, currentYear: number) => {
return events.filter((event) => {
const eventDate = new Date(event.start?.dateTime || event.start?.date || '');
return eventDate.getMonth() === currentMonth && eventDate.getFullYear() === currentYear;
});
};

function CalendarCell({
date, today, data, currentMonth,
}: CalendarContent) {
Expand All @@ -45,7 +55,7 @@ function CalendarCell({
<div css={S.scheduleContent}>
{date && filteredData.map((item, index) => (
<p css={S.schedule(index)}>
{item.startDateTime.slice(10, 20)}
{item.startDateTime.slice(11, 16)}
{' '}
{item.reason}
</p>
Expand Down Expand Up @@ -86,6 +96,7 @@ export default function Month() {
const [currentMonth, setCurrentMonth] = useState(new Date().getMonth());
const [currentCalendar, setCurrentCalendar] = useState<Calendars[]>([]);
const [open, setOpen] = useState<boolean>(false);
const [events, setEvents] = useState<Reservation[]>([]);

const handleClose = () => setOpen(false);
const nextYear = () => setCurrentYear((prev) => prev + 1);
Expand Down Expand Up @@ -121,6 +132,16 @@ export default function Month() {
setCurrentCalendar(weeklyCalendar);
}, [currentMonth, currentYear]);

useEffect(() => {
initClient().then(() => {
listUpcomingEvents().then((res) => {
const filteredEvents = filterEventsByMonth(res, currentMonth, currentYear);
const reservations = filteredEvents.map((event) => convertEventToReservation(event));
setEvents(reservations);
dooohun marked this conversation as resolved.
Show resolved Hide resolved
});
});
}, [currentMonth, currentYear]);

return (
<TableContainer>
<div css={S.AlignItems}>
Expand All @@ -141,6 +162,7 @@ export default function Month() {
<Button variant="outlined" onClick={() => setOpen(true)}>
예약 확인
</Button>
<Button onClick={signIn}>구글 로그인</Button>
dooohun marked this conversation as resolved.
Show resolved Hide resolved
</div>
<Table>
<TableHead>
Expand All @@ -158,7 +180,7 @@ export default function Month() {
{currentCalendar.map((week) => (
<TableRow sx={{ width: '100%' }}>
{week.map((day) => (
<CalendarCell today={day.today} date={day.date} data={data} currentMonth={currentMonth} />
<CalendarCell today={day.today} date={day.date} data={[...(data || []), ...events]} currentMonth={currentMonth} />
))}
</TableRow>
))}
Expand Down
5 changes: 3 additions & 2 deletions src/page/Reservation/view/Month/DetailInfomation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ export default function DetailInfomation({
</div>
<div>
시간 :
{startDateTime.slice(10, 20)}
{' '}
{startDateTime.slice(11, 16)}
{' '}
~
{' '}
{endDateTime.slice(10, 20)}
{endDateTime.slice(11, 16)}
</div>
<div>
정보 :
Expand Down
89 changes: 89 additions & 0 deletions src/ts/googleapi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { GOOGLE_API_KEY, GOOGLE_CLIENT_ID } from 'config/constants';
import { Reservation } from 'model/reservations';
import { gapi } from 'gapi-script';
import { useSnackBar } from './useSnackBar';

const DISCOVERY_DOCS = ['https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest'];
const SCOPES = 'https://www.googleapis.com/auth/calendar.events';

export const initClient = () => {
return new Promise<void>((resolve, reject) => {
gapi.load('client:auth2', () => {
gapi.client.init({
apiKey: GOOGLE_API_KEY,
clientId: GOOGLE_CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES,
}).then(() => {
resolve();
}).catch((error) => {
reject(error);
});
});
});
};

export const signIn = () => {
return gapi.auth2.getAuthInstance().signIn();
};

export const signOut = () => {
return gapi.auth2.getAuthInstance().signOut();
};
const CALENDAR_ID = 'bcsdlab@gmail.com';
export const listUpcomingEvents = async (): Promise<gapi.client.calendar.Event[]> => {
try {
const response = await gapi.client.calendar.events.list({
calendarId: CALENDAR_ID,
timeMin: (new Date()).toISOString(),
showDeleted: false,
singleEvents: true,
maxResults: 100,
orderBy: 'startTime',
});
return response.result.items || [];
} catch (error) {
return [];
}
};

export const createEvent = async (event: gapi.client.calendar.Event) => {
try {
const response = await gapi.client.calendar.events.insert({
calendarId: CALENDAR_ID,
resource: event,
});
return response.result;
} catch (error) {
return null;
}
};

export const convertEventToReservation = (event: gapi.client.calendar.Event): Reservation => {
return {
memberCount: event.attendees?.length || 0,
reason: event.summary || '-',
detailedReason: event.description || '-',
startDateTime: event.start?.dateTime || '',
endDateTime: event.end?.dateTime || '',
memberName: event.organizer?.displayName || event.organizer?.email || '-',
};
};

export const convertReservationToEvent = (reservation: Reservation): gapi.client.calendar.Event => {
const startDate = new Date(reservation.startDateTime);
const endDate = new Date(reservation.endDateTime);
return {
summary: reservation.reason,
description: reservation.detailedReason,
start: {
dateTime: startDate.toISOString(),
timeZone: 'Asia/Seoul',
},
end: {
dateTime: endDate.toISOString(),
timeZone: 'Asia/Seoul',
},
attendees: reservation.memberCount ? [{ email: reservation.memberName }] : [],
};
};
Loading