From e5d8f24d5f07a7986e96d10225b2175c813a18c6 Mon Sep 17 00:00:00 2001 From: jisoung Date: Fri, 2 Jun 2023 18:56:02 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat=20:=20=EC=84=A0=ED=83=9D=20=ED=95=99?= =?UTF-8?q?=EC=83=9D=20=EB=AA=A8=EB=8B=AC=20=ED=8D=BC=EB=B8=94=EB=A6=AC?= =?UTF-8?q?=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/src/components/main/Divider.tsx | 2 + .../admin/src/components/main/StudentList.tsx | 45 +++++- .../components/modals/StudentSelectModal.tsx | 147 ++++++++++++++++++ 3 files changed, 186 insertions(+), 8 deletions(-) create mode 100644 services/admin/src/components/modals/StudentSelectModal.tsx diff --git a/services/admin/src/components/main/Divider.tsx b/services/admin/src/components/main/Divider.tsx index 0cd8fd8..54f4b7e 100644 --- a/services/admin/src/components/main/Divider.tsx +++ b/services/admin/src/components/main/Divider.tsx @@ -4,11 +4,13 @@ interface DividerProps { width?: number; height?: number; margin?: string; + maxWidth?: number; } export function Divider({ height = 500, margin = '0 20px 0 40px', width = 1, + maxWidth = 1300, }: DividerProps) { return <_Divider height={height} width={width} margin={margin} />; } diff --git a/services/admin/src/components/main/StudentList.tsx b/services/admin/src/components/main/StudentList.tsx index c4498b8..8860a2e 100644 --- a/services/admin/src/components/main/StudentList.tsx +++ b/services/admin/src/components/main/StudentList.tsx @@ -49,6 +49,10 @@ import { useSelectedStudentIdStore } from '@/store/useSelectedStudentIdStore'; import { usePointHistoryId } from '@/store/usePointHistoryId'; import { useQueryClient } from '@tanstack/react-query'; import { useDeleteTagIdStore } from '@/store/useDeleteTagId'; +import SideBarPortal from '../sidebar/SideBarPortal'; +import { PointList } from './PointList'; +import { SideBar } from '../sidebar'; +import StudentSelectModal from '../modals/StudentSelectModal'; interface Props extends FilterState { mode: ModeType; @@ -84,13 +88,16 @@ export function StudentList({ refetchSearchStudents, availableFeature, }: Props) { - const [selectedStudentId, resetStudentId, appendStudentId, deleteStudentId] = - useSelectedStudentIdStore((state) => [ - state.selectedStudentId, - state.resetStudentId, - state.appendStudentId, - state.deleteStudentId, - ]); + const [selectedStudentId] = useSelectedStudentIdStore((state) => [ + state.selectedStudentId, + state.resetStudentId, + state.appendStudentId, + state.deleteStudentId, + ]); + + const [clickedStudentId, setClickedStudentId] = useClickedStudentIdStore( + (state) => [state.clickedStudentId, state.setClickedStudentId], + ); const [pointHistoryId] = usePointHistoryId((state) => [state.pointHistoryId]); const [tagId] = useDeleteTagIdStore((state) => [state.deleteTagId]); const { modalState, selectModal, closeModal } = useModal(); @@ -198,6 +205,14 @@ export function StudentList({ const deleteStudentTag = useDeleteStudentTag(selectedStudentId[0], tagId); + const [selectedStudentId, resetStudentId, appendStudentId, deleteStudentId] = + useSelectedStudentIdStore((state) => [ + state.selectedStudentId, + state.resetStudentId, + state.appendStudentId, + state.deleteStudentId, + ]); + return ( <_Wrapper> <_Filter className="filter"> @@ -412,10 +427,24 @@ export function StudentList({ tagModal={tagModal} /> )} + + {openAllPointHistorySideBar && ( + { + setOpenAllPointHistorySideBar(false); + }} + > + + 상/벌점 내역 + + + + )} + + {Boolean(selectedStudentId.length) && } ); } - const _Wrapper = styled.div` width: 1030px; transition: width 0.7s ease-in-out; diff --git a/services/admin/src/components/modals/StudentSelectModal.tsx b/services/admin/src/components/modals/StudentSelectModal.tsx new file mode 100644 index 0000000..490f66f --- /dev/null +++ b/services/admin/src/components/modals/StudentSelectModal.tsx @@ -0,0 +1,147 @@ +import styled from 'styled-components'; +import { useState, useEffect } from 'react'; +import { useSelectedStudentIdStore } from '@/store/useSelectedStudentIdStore'; +import { Text, Button } from '@team-aliens/design-system'; +import { fadeInRight } from '../../components/animation/fade'; +import { Divider } from '../main/Divider'; +import { useModal } from '@/hooks/useModal'; +import { useStudentPointHistory } from '@/hooks/usePointsApi'; +import OutsideClickHandler from 'react-outside-click-handler'; + +export default function StudentSelectModal() { + const [selectedStudentId] = useSelectedStudentIdStore((state) => [ + state.selectedStudentId, + state.resetStudentId, + state.appendStudentId, + state.deleteStudentId, + ]); + + const [click, setClick] = useState(false); + const { selectModal } = useModal(); + + return ( + setClick(false)}> + <_Wrapper> + <_Header> + 기본정보 + + 최근 부여 항목 + + + <_StudentWrapper> + {selectedStudentId.map((student) => ( + <_Student> + <> + 박준수 + 1111 + + + <> + 타호실 출입 + +4 + + + ))} + + <_UnderWrapper> + + {selectedStudentId.length}명이 선택되었습니다. + + <_ButtonWrapper> + {click && ( + <_Items> + <_Item onClick={() => selectModal('GIVE_POINT')}> + + 상/벌점 + + + <_Item onClick={() => selectModal('GIVE_TAG_OPTIONS')}> + + 학생 태그 + + + + )} + + + + + + ); +} + +const _Wrapper = styled.div` + display: flex; + flex-direction: column; + position: fixed; + bottom: 32px; + right: 28px; + background-color: white; + width: 418px; + height: 448px; + box-shadow: 0px 3px 20px rgba(0, 0, 0, 0.19); + border-radius: 8px; + z-index: 2; + padding: 36px 40px 23px; + /* animation: ${fadeInRight} 0.3s; */ +`; + +const _Header = styled.div` + display: flex; + justify-content: space-between; +`; + +const _StudentWrapper = styled.div` + width: 338px; + height: 260px; + position: relative; + overflow-y: scroll; + margin-top: 10px; +`; + +const _Student = styled.div` + display: flex; + position: relative; + align-items: center; + width: 100%; + height: 57px; + background-color: #f9f9f9; + margin-bottom: 8px; + border: 1px solid #eeeeee; + border-radius: 5px; + padding: 0 28px; +`; + +const _UnderWrapper = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +const _ButtonWrapper = styled.div` + display: flex; + position: relative; +`; + +const _Items = styled.div` + position: absolute; + width: 132px; + max-height: 138px; + left: -137px; + top: -46px; + background-color: ${({ theme }) => theme.color.gray1}; + box-shadow: 0px 2px 12px rgba(0, 0, 0, 0.1); + border-radius: 4px; + z-index: 3; + overflow-y: scroll; +`; + +const _Item = styled.div` + display: flex; + justify-content: center; + align-items: center; + gap: 12px; + margin: 0 8px; + height: 46px; + border-bottom: 1px solid ${({ theme }) => theme.color.gray3}; +`; From 1051d3c2cc7dd5450fc3e2852e083a629c7fbc8b Mon Sep 17 00:00:00 2001 From: jisoung Date: Fri, 2 Jun 2023 20:00:13 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix=20:=20=EC=9E=98=EB=AA=BB=EB=90=9C=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EC=B0=B8=EC=A1=B0=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/src/components/main/StudentList.tsx | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/services/admin/src/components/main/StudentList.tsx b/services/admin/src/components/main/StudentList.tsx index 8860a2e..70ca3cd 100644 --- a/services/admin/src/components/main/StudentList.tsx +++ b/services/admin/src/components/main/StudentList.tsx @@ -49,9 +49,6 @@ import { useSelectedStudentIdStore } from '@/store/useSelectedStudentIdStore'; import { usePointHistoryId } from '@/store/usePointHistoryId'; import { useQueryClient } from '@tanstack/react-query'; import { useDeleteTagIdStore } from '@/store/useDeleteTagId'; -import SideBarPortal from '../sidebar/SideBarPortal'; -import { PointList } from './PointList'; -import { SideBar } from '../sidebar'; import StudentSelectModal from '../modals/StudentSelectModal'; interface Props extends FilterState { @@ -205,6 +202,14 @@ export function StudentList({ const deleteStudentTag = useDeleteStudentTag(selectedStudentId[0], tagId); + const [selectedStudentId, resetStudentId, appendStudentId, deleteStudentId] = + useSelectedStudentIdStore((state) => [ + state.selectedStudentId, + state.resetStudentId, + state.appendStudentId, + state.deleteStudentId, + ]); + const [selectedStudentId, resetStudentId, appendStudentId, deleteStudentId] = useSelectedStudentIdStore((state) => [ state.selectedStudentId, @@ -427,20 +432,6 @@ export function StudentList({ tagModal={tagModal} /> )} - - {openAllPointHistorySideBar && ( - { - setOpenAllPointHistorySideBar(false); - }} - > - - 상/벌점 내역 - - - - )} - {Boolean(selectedStudentId.length) && } ); From 3e29887283dc13886ed61718a27d9bbaf0969a23 Mon Sep 17 00:00:00 2001 From: jikwan Date: Sun, 4 Jun 2023 03:32:24 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=EC=B5=9C=EA=B7=BC=20=ED=95=99?= =?UTF-8?q?=EC=83=9D=20=EC=83=81/=EB=B2=8C=EC=A0=90=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/admin/src/apis/points/index.ts | 17 ++++++ services/admin/src/apis/points/response.ts | 8 +++ .../components/main/DetailBox/PointItem.tsx | 59 +++++++++++++++++++ .../admin/src/components/main/StudentList.tsx | 32 +++------- .../components/modals/StudentSelectModal.tsx | 40 ++++--------- services/admin/src/hooks/usePointsApi.tsx | 18 ++++++ services/admin/src/pages/Home.tsx | 6 -- 7 files changed, 119 insertions(+), 61 deletions(-) diff --git a/services/admin/src/apis/points/index.ts b/services/admin/src/apis/points/index.ts index 48513cd..a7814c0 100644 --- a/services/admin/src/apis/points/index.ts +++ b/services/admin/src/apis/points/index.ts @@ -10,6 +10,7 @@ import { instance } from '..'; import { AllPointListResponse, AllPointsOptionResponse, + RecentStudentPointResponse, StudentPointHistoryResponse, } from './response'; import { useToast } from '@/hooks/useToast'; @@ -40,6 +41,22 @@ export const getStudentPointHistory = async ( } }; +/** 학생 상/벌점 최근 내역 조회 */ +export const getRecentStudentPointHistory = async ( + student_id: string, + page?: number, + size?: number, +) => { + if (student_id) { + const { data } = await instance.get>( + `${router}/history/students/${student_id}/recent${ + page || size ? `?page=${page}&size=${size}` : '' + }`, + ); + return data; + } +}; + /** 상/벌점 전체 조회 */ export const getAllPoints = async () => { const { data } = await instance.get>( diff --git a/services/admin/src/apis/points/response.ts b/services/admin/src/apis/points/response.ts index 6a762ec..c8bbb96 100644 --- a/services/admin/src/apis/points/response.ts +++ b/services/admin/src/apis/points/response.ts @@ -18,6 +18,14 @@ export interface StudentPointHistoryResponse { point_histories: StudentPointHistoryType[]; } +export interface RecentStudentPointResponse { + student_name: string; + student_gcn: string; + point_type: PointType; + point_score: number; + point_name: string; +} + export interface StudentPointHistoryType { point_history_id: string; type: PointType; diff --git a/services/admin/src/components/main/DetailBox/PointItem.tsx b/services/admin/src/components/main/DetailBox/PointItem.tsx index 726a6f7..3d4414f 100644 --- a/services/admin/src/components/main/DetailBox/PointItem.tsx +++ b/services/admin/src/components/main/DetailBox/PointItem.tsx @@ -7,6 +7,7 @@ import { } from '@/apis/points/response'; import { PointEnum, PointType } from '@/apis/points'; import { usePointHistoryId } from '@/store/usePointHistoryId'; +import { useRecentStudentPointHistory } from '@/hooks/usePointsApi'; interface PropsType extends StudentPointHistoryType { isDeleteListOption?: boolean; @@ -100,6 +101,31 @@ export function PointItem({ ); } +export function RecentPointItem({ studentId }: { studentId: string }) { + const { data: recentStudentPointHistory } = + useRecentStudentPointHistory(studentId); + + return ( + <_Student> + <> + {recentStudentPointHistory?.student_name} + {recentStudentPointHistory?.student_gcn} + + <_Divider /> + <> + <_HollowBox width={80}> + + {recentStudentPointHistory?.point_name || '내역 없음'} + + + {recentStudentPointHistory?.point_score} + + + ); +} + // 전체내역 확인할 때 사용되는 컴포넌트 export function AllPointItem({ point_history_id, @@ -196,3 +222,36 @@ const _Delete = styled.div` margin: 0 12px; cursor: pointer; `; + +const _Student = styled.div` + display: flex; + position: relative; + justify-content: space-between; + align-items: center; + width: 100%; + height: 57px; + background-color: #f9f9f9; + margin-bottom: 8px; + border: 1px solid #eeeeee; + border-radius: 5px; + padding: 0 28px; +`; + +const _HollowBox = styled.div<{ width: number }>` + div { + width: ${({ width }) => width}px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } +`; + +const _Divider = styled.div` + position: absolute; + top: 50%; + left: 47%; + transform: translate(-50%, -50%); + width: 1px; + height: 28px; + background-color: ${({ theme }) => theme.color.gray3}; +`; diff --git a/services/admin/src/components/main/StudentList.tsx b/services/admin/src/components/main/StudentList.tsx index 70ca3cd..94ffa25 100644 --- a/services/admin/src/components/main/StudentList.tsx +++ b/services/admin/src/components/main/StudentList.tsx @@ -85,16 +85,14 @@ export function StudentList({ refetchSearchStudents, availableFeature, }: Props) { - const [selectedStudentId] = useSelectedStudentIdStore((state) => [ - state.selectedStudentId, - state.resetStudentId, - state.appendStudentId, - state.deleteStudentId, - ]); + const [selectedStudentId, resetStudentId, appendStudentId, deleteStudentId] = + useSelectedStudentIdStore((state) => [ + state.selectedStudentId, + state.resetStudentId, + state.appendStudentId, + state.deleteStudentId, + ]); - const [clickedStudentId, setClickedStudentId] = useClickedStudentIdStore( - (state) => [state.clickedStudentId, state.setClickedStudentId], - ); const [pointHistoryId] = usePointHistoryId((state) => [state.pointHistoryId]); const [tagId] = useDeleteTagIdStore((state) => [state.deleteTagId]); const { modalState, selectModal, closeModal } = useModal(); @@ -202,22 +200,6 @@ export function StudentList({ const deleteStudentTag = useDeleteStudentTag(selectedStudentId[0], tagId); - const [selectedStudentId, resetStudentId, appendStudentId, deleteStudentId] = - useSelectedStudentIdStore((state) => [ - state.selectedStudentId, - state.resetStudentId, - state.appendStudentId, - state.deleteStudentId, - ]); - - const [selectedStudentId, resetStudentId, appendStudentId, deleteStudentId] = - useSelectedStudentIdStore((state) => [ - state.selectedStudentId, - state.resetStudentId, - state.appendStudentId, - state.deleteStudentId, - ]); - return ( <_Wrapper> <_Filter className="filter"> diff --git a/services/admin/src/components/modals/StudentSelectModal.tsx b/services/admin/src/components/modals/StudentSelectModal.tsx index 490f66f..8ecaa49 100644 --- a/services/admin/src/components/modals/StudentSelectModal.tsx +++ b/services/admin/src/components/modals/StudentSelectModal.tsx @@ -5,8 +5,8 @@ import { Text, Button } from '@team-aliens/design-system'; import { fadeInRight } from '../../components/animation/fade'; import { Divider } from '../main/Divider'; import { useModal } from '@/hooks/useModal'; -import { useStudentPointHistory } from '@/hooks/usePointsApi'; import OutsideClickHandler from 'react-outside-click-handler'; +import { RecentPointItem } from '../main/DetailBox/PointItem'; export default function StudentSelectModal() { const [selectedStudentId] = useSelectedStudentIdStore((state) => [ @@ -23,25 +23,17 @@ export default function StudentSelectModal() { setClick(false)}> <_Wrapper> <_Header> - 기본정보 - + + 기본정보 + + 최근 부여 항목 <_StudentWrapper> - {selectedStudentId.map((student) => ( - <_Student> - <> - 박준수 - 1111 - - - <> - 타호실 출입 - +4 - - - ))} + {selectedStudentId.map((student) => { + return ; + })} <_UnderWrapper> @@ -93,25 +85,13 @@ const _Header = styled.div` const _StudentWrapper = styled.div` width: 338px; - height: 260px; + height: 285px; + margin-bottom: 30px; position: relative; overflow-y: scroll; margin-top: 10px; `; -const _Student = styled.div` - display: flex; - position: relative; - align-items: center; - width: 100%; - height: 57px; - background-color: #f9f9f9; - margin-bottom: 8px; - border: 1px solid #eeeeee; - border-radius: 5px; - padding: 0 28px; -`; - const _UnderWrapper = styled.div` display: flex; justify-content: space-between; diff --git a/services/admin/src/hooks/usePointsApi.tsx b/services/admin/src/hooks/usePointsApi.tsx index 6781754..64ce181 100644 --- a/services/admin/src/hooks/usePointsApi.tsx +++ b/services/admin/src/hooks/usePointsApi.tsx @@ -8,12 +8,14 @@ import { cancelPointHistory, getAllPointHistory, getAllPoints, + getRecentStudentPointHistory, getStudentPointHistory, PointType, } from '@/apis/points'; import { usePointHistoryList } from './usePointHistoryList'; import { useToast } from './useToast'; import { useModal } from './useModal'; +import { RecentStudentPointResponse } from '@/apis/points/response'; export const useAllPointHistory = (pointType: PointType) => useQuery( @@ -44,6 +46,22 @@ export const useStudentPointHistory = ( ); }; +export const useRecentStudentPointHistory = ( + student_id: string, + isActive?: boolean, + page?: number, + size?: number, +) => { + return useQuery( + [`getStudentPointHistory`, student_id], + () => getRecentStudentPointHistory(student_id, page, size), + { + refetchOnWindowFocus: true, + enabled: isActive && Boolean(student_id), + }, + ); +}; + export const usePointOptionList = () => useQuery(['usePointList'], () => getAllPoints(), { refetchOnWindowFocus: true, diff --git a/services/admin/src/pages/Home.tsx b/services/admin/src/pages/Home.tsx index 62fdd27..a7a6d73 100644 --- a/services/admin/src/pages/Home.tsx +++ b/services/admin/src/pages/Home.tsx @@ -81,12 +81,6 @@ export function Home() { const { data: availableFeature } = useAvailAbleFeatures(); - const { data: studentPointHistory, refetch: refetchStudentPointHistory } = - useStudentPointHistory( - selectedStudentId[selectedStudentId.length - 1], - availableFeature?.point_service, - ); - const onChangeSortType = () => { const value: SortType = filter.sort === 'GCN' ? 'NAME' : 'GCN'; changeObjectValue('sort', value); From 538d6f2d097feffd910a74a4685957be608e9a8f Mon Sep 17 00:00:00 2001 From: jikwan Date: Sun, 4 Jun 2023 10:56:13 +0900 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20=ED=95=99=EC=83=9D=20=EC=B5=9C?= =?UTF-8?q?=EA=B7=BC=20=EC=83=81/=EB=B2=8C=EC=A0=90=20=EB=82=B4=EC=97=AD?= =?UTF-8?q?=20queryKey=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/admin/src/hooks/usePointsApi.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/admin/src/hooks/usePointsApi.tsx b/services/admin/src/hooks/usePointsApi.tsx index 6cdc444..0fac655 100644 --- a/services/admin/src/hooks/usePointsApi.tsx +++ b/services/admin/src/hooks/usePointsApi.tsx @@ -53,7 +53,7 @@ export const useRecentStudentPointHistory = ( size?: number, ) => { return useQuery( - [`getStudentPointHistory`, student_id], + [`getRecentStudentPointHistory`, student_id], () => getRecentStudentPointHistory(student_id, page, size), { refetchOnWindowFocus: true, From fe022544970181118f69d351f97880cde983ba20 Mon Sep 17 00:00:00 2001 From: jikwan Date: Sun, 4 Jun 2023 11:10:15 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/admin/src/components/main/DetailBox/PointItem.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/admin/src/components/main/DetailBox/PointItem.tsx b/services/admin/src/components/main/DetailBox/PointItem.tsx index ebeb2ed..17c7c8a 100644 --- a/services/admin/src/components/main/DetailBox/PointItem.tsx +++ b/services/admin/src/components/main/DetailBox/PointItem.tsx @@ -124,7 +124,9 @@ export function RecentPointItem({ studentId }: { studentId: string }) { {recentStudentPointHistory?.point_score} - + ); +} + export function StudentPointItem({ isDeleteListOption = false, canDelete = false,