diff --git a/package-lock.json b/package-lock.json index dca895d..169b401 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25021,6 +25021,18 @@ "@babel/runtime": "^7.4.4" } }, + "@material-ui/lab": { + "version": "4.0.0-alpha.60", + "resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.60.tgz", + "integrity": "sha512-fadlYsPJF+0fx2lRuyqAuJj7hAS1tLDdIEEdov5jlrpb5pp4b+mRDUqQTUxi4inRZHS1bEXpU8QWUhO6xX88aA==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.2", + "clsx": "^1.0.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + } + }, "@material-ui/styles": { "version": "4.11.4", "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.4.tgz", diff --git a/package.json b/package.json index 9a52464..eb6bafb 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@material-ui/core": "^4.12.3", "@material-ui/data-grid": "^4.0.0-alpha.35", "@material-ui/icons": "^4.11.2", + "@material-ui/lab": "^4.0.0-alpha.60", "@mui/x-data-grid": "^4.0.0", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^11.2.7", diff --git a/src/App.js b/src/App.js index 0a4d68a..ea37fa7 100644 --- a/src/App.js +++ b/src/App.js @@ -15,10 +15,10 @@ import Register from './components/Register' import Home from './components/content/home' import Profile from './components/profile' import Courses from './components/course' -import StyledApp from './style' +import StyledApp from './styles/AppStyle' import { clearMessage } from './actions/message' -import { history } from './helpers/history' +import { history } from './utils/history' import { Button } from 'antd' function App() { diff --git a/src/actions/attendance.js b/src/actions/attendance.js new file mode 100644 index 0000000..4b14641 --- /dev/null +++ b/src/actions/attendance.js @@ -0,0 +1,58 @@ +import { GET_COURSEDATES, GET_ATTENDANCEBOOK, PUT_COURSEDATES, PUT_ATTENDANCEBOOK } from './types' + +import AttendanceService from '../services/AttendanceService' + +export const getCourseDates = courseId => async dispatch => { + try { + const res = await AttendanceService.getCourseDates(courseId) + console.log(res.data.data) + dispatch({ + type: GET_COURSEDATES, + payload: res.data.data, + }) + return Promise.resolve(res.data.data) + } catch (err) { + return Promise.reject(err) + } +} + +export const getAttendanceBook = courseId => async dispatch => { + try { + const res = await AttendanceService.getAttendanceBook(courseId) + console.log(res.data.data) + dispatch({ + type: GET_ATTENDANCEBOOK, + payload: res.data.data, + }) + return Promise.resolve(res.data.data) + } catch (err) { + return Promise.reject(err) + } +} + +export const putCourseDates = courseDateId => async dispatch => { + try { + const res = await AttendanceService.putCourseDates(courseDateId) + console.log(res.data.data) + dispatch({ + type: PUT_COURSEDATES, + payload: res.data.data, + }) + return Promise.resolve(res.data.data) + } catch (err) { + return Promise.reject(err) + } +} + +export const putAttendanceBook = courseDateId => async dispatch => { + try { + const res = await AttendanceService.put(courseDateId) + dispatch({ + type: PUT_ATTENDANCEBOOK, + payload: res.data.data, + }) + return Promise.resolve(res.data.data) + } catch (err) { + return Promise.reject(err) + } +} diff --git a/src/actions/types.js b/src/actions/types.js index 876db2c..8a55d33 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -20,3 +20,8 @@ export const DELETE_MEMBER = 'DELETE_MEMBER' export const DELETE_MEMBERLIST = 'DELETE_MEMBERLIST' export const RETRIEVE_USERS = 'RETRIEVE_USERS' + +export const GET_COURSEDATES = 'GET_COURSEDATES' +export const GET_ATTENDANCEBOOK = 'GET_ATTENDANCEBOOK' +export const PUT_COURSEDATES = 'PUT_COURSEDATES' +export const PUT_ATTENDANCEBOOK = 'PUT_ATTENDANCEBOOK' diff --git a/src/components/Attendance/getColumns.js b/src/components/Attendance/getColumns.js new file mode 100644 index 0000000..15ae019 --- /dev/null +++ b/src/components/Attendance/getColumns.js @@ -0,0 +1,38 @@ +import courseDate from '../../utils/courseDateIdtoString' + +export default function getColumns(courseDates) { + const columnsState = [ + { + headerAlign: 'center', + field: '학번', + headerName: '학번', + width: 120, + editable: false, + sortable: false, + align: 'center', + headAlign: 'center', + }, + { + headerAlign: 'center', + field: '이름', + headerName: '이름', + width: 120, + editable: false, + align: 'center', + }, + ] + + for (let i = 0; i < courseDates.length; i += 1) { + columnsState.push({ + field: `${courseDate(courseDates, i)}`, + headerName: `${courseDate(courseDates, i)}`, + width: 180, + editable: true, + sortable: false, + type: 'boolean', + align: 'center', + }) + } + console.log(columnsState) + return columnsState +} diff --git a/src/components/Attendance/getRows.js b/src/components/Attendance/getRows.js new file mode 100644 index 0000000..0cbbee8 --- /dev/null +++ b/src/components/Attendance/getRows.js @@ -0,0 +1,19 @@ +import courseDate from '../../utils/courseDateIdtoString' + +export default function getRows(courseDates, attendanceBook) { + const rowsState = [] + const studentsState = [] + const courseDateState = {} + + for (let i = 0; i < attendanceBook[0].length; i += 1) { + studentsState.push({ id: i + 1, 이름: `${attendanceBook[0][i].name}`, 학번: `${attendanceBook[0][i].studentId}` }) + } + for (let i = 0; i < studentsState.length; i += 1) { + for (let j = 1; j < attendanceBook.length; j += 1) { + courseDateState[courseDate(courseDates, j - 1)] = attendanceBook[j][i] + } + rowsState.push({ ...studentsState[i], ...courseDateState }) + } + console.log(rowsState) + return rowsState +} diff --git a/src/components/Attendance/index.jsx b/src/components/Attendance/index.jsx index 0a30da8..f322616 100644 --- a/src/components/Attendance/index.jsx +++ b/src/components/Attendance/index.jsx @@ -4,9 +4,20 @@ import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react' import { withRouter, Redirect } from 'react-router-dom' import StyledAttendance from './style' import PropTypes from 'prop-types' -import { DataGrid } from '@mui/x-data-grid' +import { + DataGrid, + GridToolbarContainer, + GridToolbarColumnsButton, + GridToolbarExport, + useGridSlotComponentProps, +} from '@mui/x-data-grid' +import Pagination from '@material-ui/lab/Pagination/Pagination' import CourseDataService from '../../services/CourseService' +import { getCourseDates, getAttendanceBook } from '../../actions/attendance' + +import getColumns from './getColumns' +import getRows from './getRows' function Attendance(props) { Attendance.propTypes = { @@ -30,8 +41,36 @@ function Attendance(props) { } const [currentCourse, setCurrentCourse] = useState(initialCourseState) // 현재 강좌 정보 저장 + const [columns, setColumns] = useState([]) + const [rows, setRows] = useState([]) + const [page, setPage] = useState(0) + const courseId = props.match.params.id const dispatch = useDispatch() + const courseDates = useSelector(state => state.courseDates) + const attendanceBook = useSelector(state => state.attendanceBook) + + function CustomToolbar() { + // 툴바 커스텀 + return ( + + + + + ) + } + + function CustomPagination() { + // 출석부 페이지네이션 + const { state, apiRef } = useGridSlotComponentProps() + return ( + apiRef.current.setPage(value - 1)} + /> + ) + } const getCourse = id => { // 현재 강좌를 찾는 함수 @@ -44,66 +83,76 @@ function Attendance(props) { }) } + const getAttendanceInfo = id => { + dispatch(getCourseDates(id)) + .then(data => { + console.log('2', data) + dispatch(getAttendanceBook(id)) + .then(data => console.log('2', data)) + .catch(e => { + console.log(e) + }) + }) + .catch(e => { + console.log(e) + }) + } + useEffect(() => { // router의 params가 바뀌면 실행 - getCourse(props.match.params.id) - }, [props.match.params.id]) - - const columns = [ - { - field: '학번', - headerName: '학번', - width: 100, - editable: true, - }, - { - field: '이름', - headerName: '학번', - width: 100, - editable: true, - }, - { - field: '2021090613', - headerName: '2021090613', - type: 'number', - width: 200, - editable: true, - }, - ] - - const rows = [ - { id: 1, 이름: 'Snow', 학번: 'Jon', 2021090613: 35 }, - { id: 2, 이름: 'Lannister', 학번: 'Cersei', 2021090613: 42 }, - { id: 3, 이름: 'Lannister', 학번: 'Jaime', 2021090613: 45 }, - { id: 4, 이름: 'Stark', 학번: 'Arya', 2021090613: 16 }, - { id: 5, 이름: 'Targaryen', 학번: 'Daenerys', 2021090613: null }, - { id: 6, 이름: 'Melisandre', 학번: null, 2021090613: 150 }, - { id: 7, 이름: 'Clifford', 학번: 'Ferrara', 2021090613: 44 }, - { id: 8, 이름: 'Frances', 학번: 'Rossini', 2021090613: 36 }, - { id: 9, 이름: 'Roxie', 학번: 'Harvey', 2021090613: 65 }, - ] + getCourse(courseId) + getAttendanceInfo(courseId) + }, [courseId]) + + useEffect(() => { + // 쓸데없이 두 번 렌더링됨 + console.log(1) + if (courseDates.length !== 0 && attendanceBook.length !== 0) { + console.log(3) + setColumns(getColumns(courseDates)) // columns 상태 저장 + setRows(getRows(courseDates, attendanceBook)) // rows 상태 저장 + } + }, [attendanceBook]) return (
{userId && userId !== 'undefined' ? ( - {currentCourse.courseId !== null ? ( + {courseId !== null && attendanceBook[0] ? (

강좌명: {currentCourse.name}

설명: {currentCourse.description}

-

총 학생수: 35명

+

학생수: {attendanceBook[0].length}명

setPage(newPage)} + localeText={{ + toolbarColumns: '열', + columnsPanelTextFieldLabel: '열 찾기', + columnsPanelTextFieldPlaceholder: '열 이름을 입력해주세요.', + columnsPanelShowAllButton: '모든 열 보이기', + columnsPanelHideAllButton: '모든 열 감추기', + toolbarExport: '추출', + toolbarExportCSV: 'CSV로 다운로드', + }} + components={{ + Toolbar: CustomToolbar, + Pagination: CustomPagination, + }} + columnBuffer={10} + pagenation autoHeight rows={rows} columns={columns} pageSize={10} rowsPerPageOptions={[10]} disableSelectionOnClick + disableColumnMenu />
diff --git a/src/components/Attendance/style.jsx b/src/components/Attendance/style.jsx index 1fc1050..a9a0223 100644 --- a/src/components/Attendance/style.jsx +++ b/src/components/Attendance/style.jsx @@ -1,20 +1,15 @@ import styled from 'styled-components' const Attendance = styled.div` - div { - .datagrid { - items-align: center; - display: flex; - height: 100%; - width: 100%; - max-width: 30rem; - @media screen and (max-width: 30rem) { - flex-direction: column; - .gridparent { - flexgrow: 1; - width: 30rem; - } - } + .datagrid { + items-align: center; + display: flex; + height: 100%; + width: 100%; + max-width: 30rem; + .gridparent { + flexgrow: 1; + width: 40rem; } } ` diff --git a/src/components/addmemberList/addmember/index.jsx b/src/components/addmemberList/addmember/index.jsx index edd2b9c..f4388d5 100644 --- a/src/components/addmemberList/addmember/index.jsx +++ b/src/components/addmemberList/addmember/index.jsx @@ -7,7 +7,7 @@ import PropTypes from 'prop-types' const Member = ({ member, removeMember }) => { Member.propTypes = { - member: PropTypes.func.isRequired, + member: PropTypes.objectOf(PropTypes.shape).isRequired, removeMember: PropTypes.func.isRequired, } diff --git a/src/components/addmemberList/modal/index.jsx b/src/components/addmemberList/modal/index.jsx index 33f4038..31d428f 100644 --- a/src/components/addmemberList/modal/index.jsx +++ b/src/components/addmemberList/modal/index.jsx @@ -12,9 +12,8 @@ import StyledAddmemberModal from './style' import { createMemberlist, retrieveMemberlist, findMemberByName } from '../../../actions/memberlist' import { retrieveUsers, findUserByName } from '../../../actions/userlist' -function getModalStyle() { - // 여기서부터 modal style - return { +const useStyles = makeStyles(theme => ({ + paper: { top: `50%`, left: `50%`, WebkitTransform: `translate(-50%, -50%)`, @@ -22,18 +21,11 @@ function getModalStyle() { MozTransform: `translate(-50%, -50%)`, OTransform: `translate(-50%, -50%)`, transform: `translate(-50%, -50%)`, - } -} - -const useStyles = makeStyles(theme => ({ - paper: { position: 'absolute', - width: '80vw', + width: '60vw', maxWidth: '100%', - height: '42vh', - backgroundColor: theme.palette.background.paper, - border: '2px solid #000', - boxShadow: theme.shadows[5], + height: '45vh', + backgroundColor: 'white', padding: theme.spacing(2, 4, 3), }, })) @@ -43,17 +35,26 @@ const columns = [ { field: 'studentId', headerName: '학번', - // width: '20rem', + align: 'center', + headAlign: 'center', + editable: false, + width: 150, }, { field: 'name', headerName: '이름', - // width: '20rem', + align: 'center', + headAlign: 'center', + editable: false, + width: 150, }, { field: 'userId', headerName: '학생 ID', - // width: '20rem', + align: 'center', + headAlign: 'center', + editable: false, + width: 150, }, ] @@ -70,7 +71,6 @@ export default function AddmemberModal({ courseId, submitted, cookies }) { const [submitable, setSubmitable] = useState(false) // 멤버리스트 생성 준비 상태를 나타냄 const classes = useStyles() - const [modalStyle] = useState(getModalStyle) const [open, setOpen] = useState(false) const dispatch = useDispatch() @@ -89,8 +89,6 @@ export default function AddmemberModal({ courseId, submitted, cookies }) { console.log(rows) }, [rows]) - // const userlist = useSelector(state => state.userlist[0]) // userlist 목록 가져온다. - const userlistReducer = users => users.reduce((acc, cur, index) => [...acc, { id: index, ...cur }], []) // id 넘버를 포함한 객체 배열 반환 const getUserlist = () => { @@ -208,7 +206,7 @@ export default function AddmemberModal({ courseId, submitted, cookies }) { ) const body = ( // 모달에 들어갈 내용 -
+
{rows.length !== 0 ? ( - ({ - ...column, - sortable: false, - }))} - pageSize={pageSize} - onPageSizeChange={newPageSize => setPageSize(newPageSize)} - rowsPerPageOptions={[5, 10, 20]} - pagination - checkboxSelection - selectionModel={selectionModel} - onSelectionModelChange={e => { - console.log(reduxmemberList) - console.log(selectionModel) - onChangeCheck(e) - }} - disableSelectionOnClick - /> +
+ ({ + ...column, + sortable: false, + }))} + pageSize={pageSize} + onPageSizeChange={newPageSize => setPageSize(newPageSize)} + pagination + autoHeight + disableColumnMenu + checkboxSelection + disableSelectionOnClick + selectionModel={selectionModel} + onSelectionModelChange={e => { + console.log(reduxmemberList) + console.log(selectionModel) + onChangeCheck(e) + }} + /> + {submitable === true ? ( + + ) : ( +
수강생을 추가해주세요.
+ )} +
) : (
로딩 중..
)}
- {submitable === true ? ( - - ) : ( -
수강생을 추가해주세요.
- )}
) return ( -
-
- -

학생명단을 등록 및 수정하려면 버튼을 눌러주세요.

-
- - {body} - +
+ +

학생명단을 등록 및 수정하려면 버튼을 눌러주세요.

+ + {body} + ) } diff --git a/src/components/addmemberList/modal/style.jsx b/src/components/addmemberList/modal/style.jsx index 68f0f8e..3137388 100644 --- a/src/components/addmemberList/modal/style.jsx +++ b/src/components/addmemberList/modal/style.jsx @@ -1,9 +1,20 @@ import styled from 'styled-components' const Addmembermodal = styled.div` - .grid{ - height: 30vh; width: 20vw; - align-content: center; + .modal{ + top: 50%, + left: 50%, + -webkit-transform: translate(-50%, -50%), + -ms-transform: translate(-50%, -50%), + -moz-transform: translate(-50%, -50%), + -o-transform: translate(-50%, -50%), + transform: translate(-50%, -50%), + position: absolute, + width: 60vw, + maxWidth: 100%, + height: 45vh, + backgroundColor: white, + padding: spacing(2, 4, 3), } ` diff --git a/src/components/course/index.jsx b/src/components/course/index.jsx index e8ba42b..685c988 100644 --- a/src/components/course/index.jsx +++ b/src/components/course/index.jsx @@ -12,7 +12,7 @@ import MemberList from '../addmemberList' import QrScanner from '../QrScanner' import Attendance from '../Attendance' import StyledCourse from './style' -import { history } from '../../helpers/history' +import { history } from '../../utils/history' function Courses({ cookies }) { Courses.propTypes = { diff --git a/src/modules/attendance.js b/src/modules/attendance.js new file mode 100644 index 0000000..e0026c0 --- /dev/null +++ b/src/modules/attendance.js @@ -0,0 +1,34 @@ +import { GET_COURSEDATES, GET_ATTENDANCEBOOK, PUT_COURSEDATES, PUT_ATTENDANCEBOOK } from '../actions/types' + +const courseState = [] +const attendanceState = [] + +export function courseDates(courseDates = courseState, action) { + const { type, payload } = action + + switch (type) { + case GET_COURSEDATES: + return [...payload] + + case PUT_COURSEDATES: + return [...payload] + + default: + return courseDates + } +} + +export function attendanceBook(attendanceBook = attendanceState, action) { + const { type, payload } = action + + switch (type) { + case GET_ATTENDANCEBOOK: + return [...payload] + + case PUT_ATTENDANCEBOOK: + return [...payload] + + default: + return attendanceBook + } +} diff --git a/src/modules/index.js b/src/modules/index.js index 0e2be8e..a9bffa2 100644 --- a/src/modules/index.js +++ b/src/modules/index.js @@ -3,6 +3,7 @@ import auth from './auth' import message from './message' import memberlist from './memberlist' import userlist from './userlist' +import { courseDates, attendanceBook } from './attendance' import { combineReducers } from 'redux' const rootReducer = combineReducers({ @@ -11,6 +12,8 @@ const rootReducer = combineReducers({ courses, memberlist, userlist, + courseDates, + attendanceBook, }) export default rootReducer diff --git a/src/services/AttendanceService.js b/src/services/AttendanceService.js new file mode 100644 index 0000000..c707e87 --- /dev/null +++ b/src/services/AttendanceService.js @@ -0,0 +1,15 @@ +import http from '../common/http' + +const getCourseDates = courseId => http.get(`v1/courseDates/getCourseDates/${courseId}`) + +const putCourseDates = courseDateId => http.put(`v1/courseDates/${courseDateId}`) + +const getAttendanceBook = courseId => http.get(`v1/attendance/getAttendanceBook/${courseId}`) + +const AttendanceService = { + putCourseDates, + getCourseDates, + getAttendanceBook, +} + +export default AttendanceService diff --git a/src/style.js b/src/styles/AppStyle.js similarity index 100% rename from src/style.js rename to src/styles/AppStyle.js diff --git a/src/utils/courseDateIdtoString.js b/src/utils/courseDateIdtoString.js new file mode 100644 index 0000000..014138b --- /dev/null +++ b/src/utils/courseDateIdtoString.js @@ -0,0 +1,5 @@ +export default function courseDate(courseDates, idx) { + return `${courseDates[idx].courseDateId.slice(0, 4)}년 ${courseDates[idx].courseDateId.slice(-6, -4)}월 ${courseDates[ + idx + ].courseDateId.slice(-4, -2)}일 ${courseDates[idx].courseDateId.slice(-2)}시` +} diff --git a/src/helpers/history.js b/src/utils/history.js similarity index 100% rename from src/helpers/history.js rename to src/utils/history.js