Skip to content

Commit

Permalink
Merge pull request #80 from TEAM-Hearus/develop
Browse files Browse the repository at this point in the history
배포
  • Loading branch information
koeunbeee authored Oct 1, 2024
2 parents 016fda2 + a9e9fd8 commit 8bfec69
Show file tree
Hide file tree
Showing 21 changed files with 730 additions and 112 deletions.
9 changes: 7 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { Reset } from 'styled-reset';
import Router from './pages';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { AlertProvider } from './contexts/AlertContext';
import AlertComponent from './components/organisms/Alerts/globalAlert/GlobalAlert';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

const queryClient = new QueryClient();

function App() {
return (
<QueryClientProvider client={queryClient}>
<Reset />
<Router />
<AlertProvider>
<Reset />
<Router />
<AlertComponent />
</AlertProvider>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
Expand Down
4 changes: 3 additions & 1 deletion src/apis/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ export const OAuthLogin = async ({
code,
}: ISocialLoginParams) => {
try {
const encodedState = encodeURIComponent(state);
const encodedCode = encodeURIComponent(code);
const res = await fetch(
`${API_URL}/login/oauth2/code/${social}?state=${state}&code=${code}`,
`${API_URL}/login/oauth2/code/${social}?state=${encodedState}&code=${encodedCode}`,
{
credentials: 'include',
},
Expand Down
17 changes: 13 additions & 4 deletions src/components/molecules/ScriptToolTip/ScriptToolTip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
deleteScheduleElement,
getLectureByScheduleElement,
} from '../../../apis/schedule';
import { useAlert } from '../../../contexts/AlertContext';
import ScriptIcon from '../../../assets/images/nav/my-script-inactive.svg?react';
import TrashCan from '../../../assets/images/orange-trash-can.svg?react';
import styles from './ScriptToolTip.module.scss';
Expand All @@ -18,7 +19,7 @@ interface IProps {
const ScriptToolTip = ({ id, scheduleName }: IProps) => {
const [selectedScriptId, setSelectedScriptId] = useState<string | null>(null);
const queryClient = useQueryClient();

const { addAlert, showConfirm } = useAlert();
const { userInfo } = useUserInfoStore();

const { data } = useQuery({
Expand All @@ -32,9 +33,10 @@ const ScriptToolTip = ({ id, scheduleName }: IProps) => {
queryClient.invalidateQueries({
queryKey: ['schedule', userInfo.userName],
});
addAlert(`'${scheduleName}' 수업이 삭제되었습니다.`, 'success');
},
onError: () => {
alert('시간표 삭제를 실패했습니다.');
addAlert('시간표 삭제를 실패했습니다.', 'error');
},
});

Expand All @@ -46,8 +48,14 @@ const ScriptToolTip = ({ id, scheduleName }: IProps) => {
setSelectedScriptId(null);
};

const handleDeleteBtnClick = () => {
if (window.confirm(`'${scheduleName}' 수업을 삭제하시겠습니까?`)) {
const handleDeleteBtnClick = async () => {
const confirmed = await showConfirm(
`'${scheduleName}'`,
'이 수업을 시간표에서 삭제하시겠습니까?',
'삭제',
);

if (confirmed) {
deleteMutation.mutate(id);
}
};
Expand All @@ -62,6 +70,7 @@ const ScriptToolTip = ({ id, scheduleName }: IProps) => {
</div>
{data?.map((lecture) => (
<div
key={lecture.id}
className={styles.tooltipItem}
onClick={() => handleToolTipClick(lecture.id)}
>
Expand Down
1 change: 0 additions & 1 deletion src/components/molecules/TimeTableItem/TimeTableItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import styles from './TimeTableItem.module.scss';

const TimeTableItem = ({
id,
//scheduleId,
name,
location,
dayOfWeek,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
@import '../../../styles/variables/fonts.scss';

.modalContainer {
position: absolute;
top: 90px;
left: 85%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
width: 450px;
background-color: white;
padding: 25px;
padding: 40px;
border-radius: 12px;
box-shadow: 0px 2px 20px 0px #0000001a;
}
Expand All @@ -17,6 +16,7 @@
flex-direction: column;
gap: 10px;
padding: 5px;
width: 370px;
}
.inputTitle {
@include SemiBold_Body_16;
Expand Down Expand Up @@ -62,6 +62,7 @@
flex-direction: column;
padding: 30px 5px;
color: $dark-font_5A;
width: 370px;
}

.dayOfWeek {
Expand Down Expand Up @@ -126,7 +127,6 @@
.submitButton {
width: 368px;
height: 48px;
margin-left: 6px;
background-color: $brand-point;
border: 0;
border-radius: 6px;
Expand Down
16 changes: 10 additions & 6 deletions src/components/organisms/AddScheduleForm/AddScheduleForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState, useEffect, useRef } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUserInfoStore } from '../../../store/useUserInfoStore';
import { useAlert } from '../../../contexts/AlertContext';
import Warning from '../../../assets/images/warning.svg?react';
import {
COLORS,
Expand Down Expand Up @@ -35,6 +36,7 @@ const AddScheduleForm = ({ onClose }: IProps) => {
const startMinuteRef = useRef<HTMLInputElement | null>(null);
const endHourRef = useRef<HTMLInputElement | null>(null);
const endMinuteRef = useRef<HTMLInputElement | null>(null);
const { addAlert } = useAlert();

const { userInfo } = useUserInfoStore();

Expand Down Expand Up @@ -100,14 +102,14 @@ const AddScheduleForm = ({ onClose }: IProps) => {
mutationFn: (data: IScheduleElementDTO) =>
addScheduleElement(data, userInfo.userName),
onSuccess: () => {
alert('강의 추가에 성공했습니다.');
addAlert('강의 추가에 성공했습니다.', 'success');
onClose();
queryClient.invalidateQueries({
queryKey: ['schedule', userInfo.userName],
});
},
onError: () => {
alert('강의 추가를 실패했습니다.');
addAlert('강의 추가를 실패했습니다.', 'error');
},
});

Expand All @@ -120,16 +122,18 @@ const AddScheduleForm = ({ onClose }: IProps) => {
const formattedData = transformToScheduleElementDTO(lectureInfo);
if (existingSchedule) {
if (hasNewElementConflict(formattedData, existingSchedule)) {
alert(
'새로운 강의 시간이 기존 스케줄과 겹칩니다. 다른 시간을 선택해주세요.',
addAlert(
'새로운 강의 시간이 기존 스케줄과 겹칩니다.\n 다른 시간을 선택해주세요.',
'error',
);
return;
} else {
postMutation.mutate(formattedData);
}
} else {
alert(
'스케줄 데이터를 불러올 수 없습니다. 페이지를 새로고침한 후 다시 시도해주세요.',
addAlert(
'스케줄 데이터를 불러올 수 없습니다.\n 페이지를 새로고침한 후 다시 시도해주세요.',
'error',
);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
@import '../../../../styles/variables/colors.scss';
@import '../../../../styles/variables/fonts.scss';

.overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.1);
z-index: 10;
}
.container {
width: 380px;
background-color: white;
border: 0;
border-radius: 12px;
padding: 30px 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 30px;
z-index: 20;
}
.title {
padding-top: 10px;
width: 300px;
color: $dark-font_33;
@include SemiBold_Title_28;
text-align: center;
}
.message {
color: $dark-font_33;
@include SemiBold_Body_16;
text-align: center;
white-space: pre-wrap;
}
.btnBox {
display: flex;
gap: 10px;
}
.cancleBtn {
width: 160px;
height: 48px;
border: 0;
border-radius: 8px;
background-color: $light-bg_F2;
color: $dark-font_5A;
@include SemiBold_Body_16;
cursor: pointer;
}
.selectBtn {
width: 160px;
height: 48px;
border: 0;
border-radius: 8px;
background-color: $brand-point;
color: $white_FF;
@include SemiBold_Body_16;
cursor: pointer;
}
34 changes: 34 additions & 0 deletions src/components/organisms/Alerts/confirmAlert/ConfirmAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import styles from './ConfirmAlert.module.scss';

interface ConfirmAlertProps {
title: string;
message: string;
buttonText: string;
onConfirm: (result: boolean) => void;
}

const ConfirmAlert = ({
message,
title,
buttonText,
onConfirm,
}: ConfirmAlertProps) => {
return (
<div className={styles.overlay}>
<div className={styles.container}>
<h2 className={styles.title}>{title}</h2>
<p className={styles.message}>{message}</p>
<div className={styles.btnBox}>
<button className={styles.cancleBtn} onClick={() => onConfirm(false)}>
취소
</button>
<button className={styles.selectBtn} onClick={() => onConfirm(true)}>
{buttonText}
</button>
</div>
</div>
</div>
);
};

export default ConfirmAlert;
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
@import '../../../../styles/variables/colors.scss';
@import '../../../../styles/variables/fonts.scss';

@keyframes slide-up {
0% {
transform: translateY(50%);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}

@keyframes slide-down {
0% {
transform: translateY(0);
opacity: 1;
}
100% {
transform: translateY(-30%);
opacity: 0;
}
}

.alertContainer {
position: fixed;
top: 10px;
left: 50%;
transform: translateX(-50%);
z-index: 40;
display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
}

.alert {
animation: slide-up 0.5s ease-out forwards;
display: flex;
align-items: center;
justify-content: center;
color: $brand-point;
background-color: $white_FF;
border: 1px solid $brand-point;
border-radius: 8px;
width: 300px;
height: 44px;
cursor: pointer;
word-break: break-all;
white-space: pre-wrap;
padding: 5px 15px;
box-shadow: 0 3px 2px rgba(0, 0, 0, 0.2);
}

.alert.closing {
animation: slide-down 0.5s ease;
}

.success {
color: $dark-font_33;
background-color: $white_FF;
border: 1px solid $dark-font_33;
}
32 changes: 32 additions & 0 deletions src/components/organisms/Alerts/globalAlert/GlobalAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useAlert } from '../../../../contexts/AlertContext';
import { useState } from 'react';
import styles from './GlobalAlert.module.scss';

const AlertComponent = () => {
const { alerts, removeAlert } = useAlert();
const [closingAlertIds, setClosingAlertIds] = useState<number[]>([]);

const handleClose = (id: number) => {
setClosingAlertIds((prev) => [...prev, id]);

setTimeout(() => {
removeAlert(id);
}, 300);
};

return (
<div className={styles.alertContainer}>
{alerts.map((alert) => (
<button
key={alert.id}
className={`${styles.alert} ${styles[alert.type]} ${alert.closing || closingAlertIds.includes(alert.id) ? styles.closing : ''}`}
onClick={() => handleClose(alert.id)}
>
{alert.message}
</button>
))}
</div>
);
};

export default AlertComponent;
Loading

0 comments on commit 8bfec69

Please sign in to comment.