송용우

Merge commit '7a39581f' into feature/rest_api

...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
13 "moment": "^2.27.0", 13 "moment": "^2.27.0",
14 "open-color": "^1.7.0", 14 "open-color": "^1.7.0",
15 "react": "^16.13.1", 15 "react": "^16.13.1",
16 + "react-calendar-heatmap": "^1.8.1",
16 "react-dom": "^16.13.1", 17 "react-dom": "^16.13.1",
17 "react-redux": "^7.2.0", 18 "react-redux": "^7.2.0",
18 "react-router-dom": "^5.2.0", 19 "react-router-dom": "^5.2.0",
......
1 +import React, { use } from 'react';
2 +import CalendarHeatmap from 'react-calendar-heatmap';
3 +import 'react-calendar-heatmap/dist/styles.css';
4 +/*
5 +TODO: 날짜 범위 지정, 날짜별 검색 추가
6 +*/
7 +/*
8 +solvedBJbyDATE:
9 +solvedBJbyDATE:{
10 + 20190304 : [Object]
11 + ...
12 +}
13 +*/
14 +const HeatMap = (HMArr) => {
15 + return (
16 + <div>
17 + <CalendarHeatmap
18 + onClick={() => {
19 + console.log(HMArr);
20 + }}
21 + startDate={new Date('2020-01-01')}
22 + endDate={new Date('2020-12-01')}
23 + values={HMArr.HMArr}
24 + classForValue={(value) => {
25 + if (!value) {
26 + return 'color-empty';
27 + }
28 + return `color-github-${value.count}`;
29 + }}
30 + tooltipDataAttrs={(value) => {
31 + return {
32 + 'data-tooltip': `${value.date} has count: ${value.count}`,
33 + };
34 + }}
35 + showWeekdayLabels={true}
36 + />
37 + </div>
38 + );
39 +};
40 +
41 +export default HeatMap;
...@@ -3,7 +3,7 @@ import { makeStyles } from '@material-ui/core/styles'; ...@@ -3,7 +3,7 @@ import { makeStyles } from '@material-ui/core/styles';
3 import Paper from '@material-ui/core/Paper'; 3 import Paper from '@material-ui/core/Paper';
4 import Grid from '@material-ui/core/Grid'; 4 import Grid from '@material-ui/core/Grid';
5 import palette from '../../lib/styles/palette'; 5 import palette from '../../lib/styles/palette';
6 -import AuthForm from '../auth/AuthForm'; 6 +import HeatMap from './HeatMap';
7 const useStyles = makeStyles((theme) => ({ 7 const useStyles = makeStyles((theme) => ({
8 root: { 8 root: {
9 flexGrow: 1, 9 flexGrow: 1,
...@@ -17,7 +17,7 @@ const useStyles = makeStyles((theme) => ({ ...@@ -17,7 +17,7 @@ const useStyles = makeStyles((theme) => ({
17 color: theme.palette.text.secondary, 17 color: theme.palette.text.secondary,
18 }, 18 },
19 })); 19 }));
20 -const HomeForm = ({ PSdata, goalNum }) => { 20 +const HomeForm = ({ PSdata, HMArr, goalNum }) => {
21 const classes = useStyles(); 21 const classes = useStyles();
22 return PSdata ? ( 22 return PSdata ? (
23 <div className={classes.root}> 23 <div className={classes.root}>
...@@ -48,7 +48,11 @@ const HomeForm = ({ PSdata, goalNum }) => { ...@@ -48,7 +48,11 @@ const HomeForm = ({ PSdata, goalNum }) => {
48 <h3>마지막으로 문제</h3> 48 <h3>마지막으로 문제</h3>
49 </Paper> 49 </Paper>
50 </Grid> 50 </Grid>
51 - 51 + <Grid item xs={12}>
52 + <Paper className={classes.paper}>
53 + <HeatMap HMArr={HMArr} />
54 + </Paper>
55 + </Grid>
52 <Grid item xs={4}> 56 <Grid item xs={4}>
53 <Paper className={classes.paper}> 57 <Paper className={classes.paper}>
54 <h1>{PSdata.weekNum}</h1> 58 <h1>{PSdata.weekNum}</h1>
......
1 -import React, { useEffect } from 'react'; 1 +import React, { useEffect, useState } from 'react';
2 import { useDispatch, useSelector } from 'react-redux'; 2 import { useDispatch, useSelector } from 'react-redux';
3 import { withRouter } from 'react-router-dom'; 3 import { withRouter } from 'react-router-dom';
4 import HomeForm from '../../components/home/HomeForm'; 4 import HomeForm from '../../components/home/HomeForm';
5 import { getPROFILE, initializeProfile } from '../../modules/profile'; 5 import { getPROFILE, initializeProfile } from '../../modules/profile';
6 const HomeContainer = ({ history }) => { 6 const HomeContainer = ({ history }) => {
7 const dispatch = useDispatch(); 7 const dispatch = useDispatch();
8 + const [HMArr, setHMArr] = useState([]);
8 const { user, profile } = useSelector(({ user, profile }) => ({ 9 const { user, profile } = useSelector(({ user, profile }) => ({
9 user: user.user, 10 user: user.user,
10 profile: profile, 11 profile: profile,
11 })); 12 }));
12 13
14 + const makeHeatmapValues = (PSdata) => {
15 + let obj_keys = Object.keys(PSdata);
16 + let result = [];
17 + for (let i = 0; i < obj_keys.length; i++) {
18 + result.push({
19 + date:
20 + //2019-10-15
21 + obj_keys[i].slice(0, 4) +
22 + '-' +
23 + obj_keys[i].slice(4, 6) +
24 + '-' +
25 + obj_keys[i].slice(6, 8),
26 + count: PSdata[obj_keys[i]].length,
27 + });
28 + }
29 + return result;
30 + };
31 +
13 useEffect(() => { 32 useEffect(() => {
14 if (!user) { 33 if (!user) {
15 alert('로그인이 필요합니다 '); 34 alert('로그인이 필요합니다 ');
...@@ -23,7 +42,9 @@ const HomeContainer = ({ history }) => { ...@@ -23,7 +42,9 @@ const HomeContainer = ({ history }) => {
23 } 42 }
24 }, [dispatch, user, history]); 43 }, [dispatch, user, history]);
25 useEffect(() => { 44 useEffect(() => {
26 - console.log(profile); 45 + if (profile.solvedBJ_date) {
46 + setHMArr(makeHeatmapValues(profile.solvedBJ_date.solvedBJbyDATE));
47 + }
27 }, [profile]); 48 }, [profile]);
28 useEffect(() => { 49 useEffect(() => {
29 if (user) { 50 if (user) {
...@@ -31,6 +52,12 @@ const HomeContainer = ({ history }) => { ...@@ -31,6 +52,12 @@ const HomeContainer = ({ history }) => {
31 dispatch(getPROFILE({ username })); 52 dispatch(getPROFILE({ username }));
32 } 53 }
33 }, [dispatch, user]); 54 }, [dispatch, user]);
34 - return <HomeForm PSdata={profile.solvedBJ_date} goalNum={profile.goalNum} />; 55 + return (
56 + <HomeForm
57 + PSdata={profile.solvedBJ_date}
58 + HMArr={HMArr}
59 + goalNum={profile.goalNum}
60 + />
61 + );
35 }; 62 };
36 export default withRouter(HomeContainer); 63 export default withRouter(HomeContainer);
......
This diff is collapsed. Click to expand it.
...@@ -2,47 +2,48 @@ const mongoose = require("mongoose"); ...@@ -2,47 +2,48 @@ const mongoose = require("mongoose");
2 2
3 const { Schema } = mongoose; 3 const { Schema } = mongoose;
4 4
5 -const ChallengeSchema = new Schema( 5 +const ChallengeSchema=new Schema({
6 - { 6 + challengeName: {type: String, required: true},
7 - challengeName: { type: String, required: true }, 7 + startDate: {type: Object, required: true},
8 - startDate: { type: Object, required: true }, 8 + endDate: {type: Object, required: true},
9 - endDate: { type: Object, required: true }, 9 + durationPerSession: {type: String, required: true}, // '1d' means one day per session, '2w' means 2 weeks per session, '3m' means 3 months per session.
10 - durationPerSession: { type: String, required: true }, // '1d' means one day per session, '2w' means 2 weeks per session, '3m' means 3 months per session. 10 + goalPerSession: {type: Number, required:true}, // number of problems for one session
11 - goalPerSession: { type: Number, required: true }, // number of problems for one session 11 + status: { type: String }
12 - isOpen: { type: Boolean }, 12 +},{
13 - }, 13 + collection: 'challenge'
14 - { 14 +});
15 - collection: "challenge", 15 +
16 - } 16 +ChallengeSchema.statics.findByChallengeName=function(challengeName){
17 -); 17 + return this.findOne({challengeName:challengeName});
18 - 18 +}
19 -ChallengeSchema.statics.findByChallengeName = function (challengeName) { 19 +
20 - return this.findOne({ challengeName: challengeName }); 20 +ChallengeSchema.methods.getChallengeName=function(){
21 -};
22 -
23 -ChallengeSchema.methods.getChallengeName = function () {
24 return this.challengeName; 21 return this.challengeName;
25 -}; 22 +}
26 23
27 -ChallengeSchema.methods.getStartDate = function () { 24 +ChallengeSchema.methods.getStartDate=function(){
28 return this.startDate; 25 return this.startDate;
29 -}; 26 +}
30 27
31 -ChallengeSchema.methods.getEndDate = function () { 28 +ChallengeSchema.methods.getEndDate=function(){
32 return this.endDate; 29 return this.endDate;
33 -}; 30 +}
34 31
35 -ChallengeSchema.methods.getDurationPerSession = function () { 32 +ChallengeSchema.method.getDurationPerSession=function(){
36 return this.durationPerSession; 33 return this.durationPerSession;
37 -}; 34 +}
38 35
39 -ChallengeSchema.methods.getGoalPerSession = function () { 36 +ChallengeSchema.methods.getGoalPerSession=function(){
40 return this.goalPerSession; 37 return this.goalPerSession;
41 -}; 38 +}
42 39
43 -ChallengeSchema.methods.serialize = function () { 40 +ChallengeSchema.methods.getStatus=function(){
41 + return this.status;
42 +}
43 +
44 +ChallengeSchema.methods.serialize=function(){
44 return this.toJSON(); 45 return this.toJSON();
45 -}; 46 +}
46 47
47 -const Challenge = mongoose.model("Challenge", ChallengeSchema); 48 +const Challenge = mongoose.model('Challenge', ChallengeSchema);
48 module.exports = Challenge; 49 module.exports = Challenge;
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -6,7 +6,7 @@ const SessionSchema = new Schema({ ...@@ -6,7 +6,7 @@ const SessionSchema = new Schema({
6 challengeId: { type: Schema.Types.ObjectId, ref: 'Challenge' }, 6 challengeId: { type: Schema.Types.ObjectId, ref: 'Challenge' },
7 sessionStartDate: { type: Object }, 7 sessionStartDate: { type: Object },
8 sessionEndDate: { type: Object }, 8 sessionEndDate: { type: Object },
9 - isOpen: { type: Boolean } 9 + status: { type: String }
10 },{ 10 },{
11 collection: 'session' 11 collection: 'session'
12 }); 12 });
...@@ -23,8 +23,8 @@ SessionSchema.methods.getSessionEndDate=function(){ ...@@ -23,8 +23,8 @@ SessionSchema.methods.getSessionEndDate=function(){
23 return this.sessionEndDate; 23 return this.sessionEndDate;
24 } 24 }
25 25
26 -SessionSchema.methods.getIsOpen=function(){ 26 +SessionSchema.methods.getStatus=function(){
27 - return this.isOpen; 27 + return this.status;
28 } 28 }
29 29
30 SessionSchema.methods.serialize=function(){ 30 SessionSchema.methods.serialize=function(){
......