From b1881994c667ae72858be056b5d068c583b238ad Mon Sep 17 00:00:00 2001 From: rlaisqls Date: Sat, 3 Jun 2023 11:12:55 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=95=99=EC=83=9D=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/admin/src/apis/files/index.ts | 20 +++ services/admin/src/apis/managers/index.ts | 41 ++++++ .../components/modals/StudentEditGrade.tsx | 128 ++++++++++++++++++ .../src/components/modals/StudentEditRoom.tsx | 127 +++++++++++++++++ services/admin/src/context/modal.tsx | 2 + services/admin/src/hooks/useMangersApis.tsx | 54 ++++++++ services/admin/src/pages/myPage/index.tsx | 60 +++++++- 7 files changed, 426 insertions(+), 6 deletions(-) create mode 100644 services/admin/src/components/modals/StudentEditGrade.tsx create mode 100644 services/admin/src/components/modals/StudentEditRoom.tsx diff --git a/services/admin/src/apis/files/index.ts b/services/admin/src/apis/files/index.ts index ed2f2fb5..7c47756c 100644 --- a/services/admin/src/apis/files/index.ts +++ b/services/admin/src/apis/files/index.ts @@ -21,3 +21,23 @@ export const studentAccountIssuance = async (file: FileList[0]) => { ); return data; }; + +export const studentEditRoom = async (file: FileList[0]) => { + const reqeustFile = new FormData(); + reqeustFile.append('file', file); + const { data } = await instance.post( + `${router}/students/file/room`, + reqeustFile, + ); + return data; +}; + +export const studentEditGrade = async (file: FileList[0]) => { + const reqeustFile = new FormData(); + reqeustFile.append('file', file); + const { data } = await instance.post( + `${router}/students/file/gcn`, + reqeustFile, + ); + return data; +}; \ No newline at end of file diff --git a/services/admin/src/apis/managers/index.ts b/services/admin/src/apis/managers/index.ts index 49d7ba99..52fc0a89 100644 --- a/services/admin/src/apis/managers/index.ts +++ b/services/admin/src/apis/managers/index.ts @@ -8,6 +8,9 @@ import { import { ResetPasswordRequest } from './request'; import { PointType } from '../points'; import { TagType } from '../tags/response'; +import { useMutation } from '@tanstack/react-query'; +import fileSaver from 'file-saver'; +import { getFileNameFromContentDisposition } from '@/utils/decoder'; const router = '/managers'; @@ -69,3 +72,41 @@ export const getMyProfile = async () => { ); return data; }; + +export const getStudentInfoExcel = () => + useMutation( + () => + instance.get(`${router}/students/file`, { + responseType: 'blob', + }), + { + onSuccess: (res) => { + const blob = new Blob([res.data], { + type: res.headers['content-type'], + }); + const fileName = res.headers['content-disposition']; + + fileSaver.saveAs(blob, getFileNameFromContentDisposition(fileName)); + }, + }, + ); + +export const uploadStudentInfoFile = async (file: FileList[0]) => { + const reqeustFile = new FormData(); + reqeustFile.append('file', file); + const { data } = await instance.post( + `${router}/students/file/gcn`, + reqeustFile, + ); + return data; +}; + +export const uploadRoomInfoFile = async (file: FileList[0]) => { + const reqeustFile = new FormData(); + reqeustFile.append('file', file); + const { data } = await instance.post( + `${router}/students/file/room`, + reqeustFile, + ); + return data; +}; \ No newline at end of file diff --git a/services/admin/src/components/modals/StudentEditGrade.tsx b/services/admin/src/components/modals/StudentEditGrade.tsx new file mode 100644 index 00000000..6ffc925d --- /dev/null +++ b/services/admin/src/components/modals/StudentEditGrade.tsx @@ -0,0 +1,128 @@ +import { Button, Modal, Text } from '@team-aliens/design-system'; +import styled from 'styled-components'; +import { useState } from 'react'; +import { useUploadStudentInfoFile } from '@/hooks/useMangersApis'; +import { getStudentInfoExcel } from '@/apis/managers/index'; + +interface PropsType { + closeModal: () => void; +} + +export const StudentEditGrade = ({ closeModal }: PropsType) => { + const [uploadedFile, setUplodaedFile] = useState(null); + const studentAccount = useUploadStudentInfoFile(uploadedFile, closeModal); + const { mutate: getStudentInfo } = getStudentInfoExcel(); + + const onFileUpload = (e: React.ChangeEvent) => { + setUplodaedFile(e.target.files[0]); + }; + + return ( + + 취소 + , + , + ]} + > + + <_Text margin={[15, 0, 14, 0]}> + 학생 정보 엑셀을 다운로드 받은 후
학번을 수정하여 업로드 해주세요. + + {uploadedFile ? ( + <> + <_UploadedFile htmlFor="input-file"> + + + +
+ + {uploadedFile.name} + + + {uploadedFile.size / 1000}KB + +
+ + <_Upload + id="input-file" + type="file" + accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + onChange={onFileUpload} + /> + + ) : ( + <> + <_Load htmlFor="input-file"> + + + + 여기에 파일을 끌어다 놓아주세요. + + <_Upload + id="input-file" + type="file" + accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + onChange={onFileUpload} + /> + + )} +
+ ); +}; + +const _UploadedFile = styled.label` + background: #eeeeee; + border-radius: 4px; + height: 58px; + display: flex; + align-items: center; + gap: 14px; + padding-left: 13px; + margin-bottom: 158px; +`; + +const _Load = styled.label` + padding: 6px 25px; + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + border-radius: 4px; + color: white; + cursor: pointer; + margin-bottom: 62px; +`; + +const _Upload = styled.input` + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + display: none; +`; + +const _Text = styled(Text)` + width: 324px; +`; \ No newline at end of file diff --git a/services/admin/src/components/modals/StudentEditRoom.tsx b/services/admin/src/components/modals/StudentEditRoom.tsx new file mode 100644 index 00000000..eb4f3760 --- /dev/null +++ b/services/admin/src/components/modals/StudentEditRoom.tsx @@ -0,0 +1,127 @@ +import { Button, Modal, Text } from '@team-aliens/design-system'; +import styled from 'styled-components'; +import { useState } from 'react'; +import { useUploadRoomInfoFile } from "@/hooks/useMangersApis"; +import { getStudentInfoExcel } from '@/apis/managers/index'; + +interface PropsType { + closeModal: () => void; +} + +export const StudentEditRoom = ({ closeModal }: PropsType) => { + const [uploadedFile, setUplodaedFile] = useState(null); + const studentAccount = useUploadRoomInfoFile(uploadedFile, closeModal); + const { mutate: getStudentInfo } = getStudentInfoExcel(); + + const onFileUpload = (e: React.ChangeEvent) => { + setUplodaedFile(e.target.files[0]); + }; + + return ( + + 취소 + , + , + ]} + > + + <_Text margin={[15, 0, 14, 0]}> + 학생 정보 엑셀을 다운로드 받은 후
호실, 자리 위치를 + 수정하여 업로드 해주세요. + + {uploadedFile ? ( + <> + <_UploadedFile htmlFor="input-file"> + + + +
+ + {uploadedFile.name} + + + {uploadedFile.size / 1000}KB + +
+ + <_Upload + id="input-file" + type="file" + accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + onChange={onFileUpload} + /> + + ) : ( + <> + <_Load htmlFor="input-file"> + + + + 여기에 파일을 끌어다 놓아주세요. + + <_Upload + id="input-file" + type="file" + accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + onChange={onFileUpload} + /> + + )} +
+ ); +}; + +const _UploadedFile = styled.label` + background: #eeeeee; + border-radius: 4px; + height: 58px; + display: flex; + align-items: center; + gap: 14px; + padding-left: 13px; + margin-bottom: 158px; +`; + +const _Load = styled.label` + padding: 6px 25px; + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + border-radius: 4px; + color: white; + cursor: pointer; + margin-bottom: 62px; +`; + +const _Upload = styled.input` + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + display: none; +`; + +const _Text = styled(Text)` + width: 324px; +`; \ No newline at end of file diff --git a/services/admin/src/context/modal.tsx b/services/admin/src/context/modal.tsx index c458e532..5e7a632a 100644 --- a/services/admin/src/context/modal.tsx +++ b/services/admin/src/context/modal.tsx @@ -29,6 +29,8 @@ export type SelectedModalType = | 'GIVE_TAG_OPTIONS' | 'VIEW_TAG_OPTIONS' | 'DELETE_TAG' + | 'STUDENT_EDIT_ROOM_EXCEL' + | 'STUDENT_EDIT_GRADE_EXCEL' | ''; interface ModalState { diff --git a/services/admin/src/hooks/useMangersApis.tsx b/services/admin/src/hooks/useMangersApis.tsx index 95da38e2..a86698ea 100644 --- a/services/admin/src/hooks/useMangersApis.tsx +++ b/services/admin/src/hooks/useMangersApis.tsx @@ -15,6 +15,9 @@ import { pagePath } from '@/utils/pagePath'; import { TagType } from '@/apis/tags/response'; import { useSelectedStudentIdStore } from '@/store/useSelectedStudentIdStore'; import { useQueryClient } from '@tanstack/react-query'; +import { uploadRoomInfoFile, uploadStudentInfoFile } from '@/apis/managers/index'; +import { AxiosError } from "axios"; + interface PropsType { selectedId: string; @@ -91,3 +94,54 @@ export const useDeleteStudent = (student_id: string) => { }, }); }; + + +export const useUploadStudentInfoFile = ( + file: FileList[0], + closeModal: () => void, +) => { + const { toastDispatch } = useToast(); + + return useMutation(() => uploadStudentInfoFile(file), { + onSuccess: () => { + toastDispatch({ + actionType: 'APPEND_TOAST', + toastType: 'SUCCESS', + message: '엑셀이 업로드 되었습니다.', + }); + closeModal(); + }, + onError: (e: AxiosError<{ message: string }>) => { + toastDispatch({ + actionType: 'APPEND_TOAST', + toastType: 'ERROR', + message: e.response.data.message, + }); + }, + }); +}; + +export const useUploadRoomInfoFile = ( + file: FileList[0], + closeModal: () => void, +) => { + const { toastDispatch } = useToast(); + + return useMutation(() => uploadRoomInfoFile(file), { + onSuccess: () => { + toastDispatch({ + actionType: 'APPEND_TOAST', + toastType: 'SUCCESS', + message: '엑셀이 업로드 되었습니다.', + }); + closeModal(); + }, + onError: (e: AxiosError<{ message: string }>) => { + toastDispatch({ + actionType: 'APPEND_TOAST', + toastType: 'ERROR', + message: e.response.data.message, + }); + }, + }); +}; diff --git a/services/admin/src/pages/myPage/index.tsx b/services/admin/src/pages/myPage/index.tsx index 695d34bf..1fa5e7e8 100644 --- a/services/admin/src/pages/myPage/index.tsx +++ b/services/admin/src/pages/myPage/index.tsx @@ -15,12 +15,16 @@ import { useMyProfileInfo } from '@/hooks/useMangersApis'; import { StudentRegistrationExcel } from '@/components/modals/StudentRegistrationExcel'; import { pagePath } from '@/utils/pagePath'; import { SchoolCheckingCodeModal } from '@/components/modals/SchoolCheckingCode'; +import { StudentEditRoom } from '@/components/modals/StudentEditRoom'; +import { StudentEditGrade } from '@/components/modals/StudentEditGrade'; export function MyPage() { const { modalState, selectModal, closeModal } = useModal(); const openNewQuestionModal = () => selectModal('NEW_QNA'); const openLogoutModal = () => selectModal('LOGOUT'); const openStudentExelModal = () => selectModal('STUDENT_EXEL'); + const openStudentEditRoomExcel = () => selectModal('STUDENT_EDIT_ROOM_EXCEL'); + const openStudentEditGradeExcel = () => selectModal('STUDENT_EDIT_GRADE_EXCEL'); const { onHandleChange: onChange, state: qnaState } = useForm({ @@ -79,12 +83,26 @@ export function MyPage() { answer={myProfileData?.answer} /> - <_StudentIssuance onClick={openStudentExelModal}> - - 학생 등록 - - - + <_StudentExcelWrapper> + <_StudentIssuance onClick={openStudentExelModal}> + + 학생 등록 + + + + <_StudentEditWrapper> + <_StudentEdit onClick={openStudentEditRoomExcel}> + + 호실 정보 변경 + + + <_StudentEdit onClick={openStudentEditGradeExcel}> + + 학번 정보 변경 + + + + {modalState.selectedModal === 'NEW_QNA' && ( @@ -107,6 +125,12 @@ export function MyPage() { {modalState.selectedModal === 'STUDENT_EXEL' && ( )} + {modalState.selectedModal === 'STUDENT_EDIT_ROOM_EXCEL' && ( + + )} + {modalState.selectedModal === 'STUDENT_EDIT_GRADE_EXCEL' && ( + + )} ); } @@ -137,6 +161,11 @@ const _PasswordChange = styled(Link)` } `; +const _StudentExcelWrapper = styled.div` + display: flex; + justify-content: space-between; +`; + const _StudentIssuance = styled.div` width: 500px; height: 70px; @@ -150,6 +179,25 @@ const _StudentIssuance = styled.div` border-radius: 4px; `; +const _StudentEditWrapper = styled.div` + display: flex; + justify-content: space-between; + width: 500px; +`; + +const _StudentEdit = styled.div` + width: 233px; + height: 70px; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + padding: 0 24px; + margin-top: 25px; + box-shadow: 0 1px 20px rgba(204, 204, 204, 0.24); + border-radius: 4px; +`; + const _Logout = styled(Text)` width: 250px; box-shadow: 0 1px 20px rgba(204, 204, 204, 0.24);