Showing
7 changed files
with
114 additions
and
40 deletions
... | @@ -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(){ | ... | ... |
-
Please register or login to post a comment