Commit 56411e77 authored by BsmhDev's avatar BsmhDev

charts to manager and player history. tags and quizes to server side. toast to...

charts to manager and player history. tags and quizes to server side. toast to main (need to add toasts to search)
parent 24cbf0f4
......@@ -18,6 +18,8 @@
@import "{}/imports/ui/stylesheets/game-card.less";
@import "{}/imports/ui/stylesheets/loading.less";
@import "{}/imports/ui/stylesheets/history-player.less";
@import "{}/imports/ui/stylesheets/snackbar.less";
// @import "{}/imports/ui/stylesheets/answer-count-font.ttf";
......
......@@ -587,6 +587,12 @@ export default Class.create({
})); // [{questionOrder: o, time: t}, ...]
return questionAndScore;
},
getMaxQuizOrder() {
const questionStartEvents = this.gameLog.filter(
e => e.nameType === eventTypes.QuestionStart,
);
return questionStartEvents.length;
},
getPlayerScoreAndAvarageScore(pId) {
const playerQuestionAndScore = this.getPlayerQuestionAndScore(pId);
const playerAndAvarageScore = playerQuestionAndScore.map(o => ({
......
import { Meteor } from 'meteor/meteor';
import Game from '../games.js';
import Game, { eventTypes } from '../games.js';
Meteor.publish('games.all', function() {
return Game.find();
......@@ -18,6 +18,15 @@ Meteor.publish('games.games-managed', function() {
});
Meteor.publish('games.games-played', function() {
const games = Game.find({ gameLog: { $elemMatch: { playerId: this.userId } } });
const games = Game.find({
gameLog: { $elemMatch: { playerId: this.userId } },
});
return games;
});
Meteor.publish('games.open', function() {
const games = Game.find({
gameLog: { $not: { $elemMatch: { nameType: eventTypes.GameClose } } },
});
return games;
});
......@@ -164,19 +164,7 @@ export default Class.create({
},
},
meteorMethods: {
create() {
return this.save();
},
update(fields) {
this.set(fields);
this.lastUpdated = new Date();
return this.save();
},
delete() {
return this.remove();
},
},
meteorMethods: {},
helpers: {
getTags() {
......
import Quiz from '../quizes';
Quiz.extend({
meteorMethods: {
create() {
return this.save();
},
update(fields) {
this.set(fields);
this.lastUpdated = new Date();
return this.save();
},
delete() {
return this.remove();
},
},
});
import Tag from '../tags';
Tag.extend({
meteorMethods: {
create() {
return this.save();
},
update(fields) {
this.set(fields);
return this.save();
},
delete() {
return this.remove();
},
},
});
......@@ -22,16 +22,5 @@ export default Class.create({
},
},
meteorMethods: {
create() {
return this.save();
},
update(fields) {
this.set(fields);
return this.save();
},
delete() {
return this.remove();
},
},
meteorMethods: {},
});
import '../../api/quizes/server/publication';
import '../../api/quizes/server/methods';
import '../../api/tags/server/publication';
import '../../api/tags/server/methods';
import '../../api/games/server/publication';
import '../../api/games/server/methods';
import '../../api/users/server/publications';
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { FlowRouter } from 'meteor/kadira:flow-router';
const GameCardManaged = ({ game }) => {
const showStatistics = () => {};
const showStatistics = () => {
FlowRouter.go('Manage.History', { code: game.code });
};
return (
<div className="panel panel-default game-card">
......
import React from 'react';
import { Meteor } from 'meteor/meteor';
import {
LineChart,
XAxis,
......@@ -11,7 +10,7 @@ import {
Line,
} from 'recharts';
const QuestionScoreLineChart = ({ game }) => {
const OneLinesChart = ({ data, dataKeyX, dataKeyY }) => {
const CustomizedXAxisTick = ({ x, y, stroke, payload }) => (
<g transform={`translate(${x},${y})`}>
<text textAnchor="middle" fill="#666" dy={16}>
......@@ -29,23 +28,17 @@ const QuestionScoreLineChart = ({ game }) => {
return (
<ResponsiveContainer width="100%" aspect={5.0 / 3.0}>
<LineChart
data={game.getPlayerScoreAndAvarageScore(Meteor.userId())}
data={data}
margin={{ top: 5, right: 35, left: 0, bottom: 5 }}
>
<XAxis dataKey="questionOrder" tick={<CustomizedXAxisTick />} />
<XAxis dataKey={dataKeyX} tick={<CustomizedXAxisTick />} />
<YAxis tick={<CustomizedYAxisTick />} />
<CartesianGrid strokeDasharray="3 3" />
<Legend verticalAlign="bottom" />
<Tooltip />
<Line
type="monotone"
dataKey="playerScore"
stroke="#8884d8"
name="אני"
/>
<Line
type="monotone"
dataKey="avarageScore"
dataKey={dataKeyY}
stroke="#82ca9d"
name="ממוצע"
/>
......@@ -54,4 +47,4 @@ const QuestionScoreLineChart = ({ game }) => {
);
};
export default QuestionScoreLineChart;
export default OneLinesChart;
......@@ -4,19 +4,9 @@ import { FlowRouter } from 'meteor/kadira:flow-router';
import Game from '/imports/api/games/games';
import Quiz from '/imports/api/quizes/quizes';
export default({ quiz }) => {
const forkQuiz = () => {
const newQuiz = new Quiz({
questions: quiz.questions,
title: quiz.title,
tags: quiz.tags,
owner: Meteor.userId(),
});
newQuiz.create();
};
export default ({ quiz, actions }) => {
const deleteQuiz = () => {
quiz.delete();
actions.deleteQuiz(quiz);
};
const initGame = () => {
......@@ -83,7 +73,7 @@ export default({ quiz }) => {
<a
href="javascript:void(0)"
className="delete quiz-card-link"
onClick={forkQuiz}
onClick={actions.forkQuiz}
>
<span className="glyphicon glyphicon-duplicate quiz-card-link-text-icon" />
<span className="quiz-card-link-text quiz-card-link-text">
......@@ -115,9 +105,10 @@ export default({ quiz }) => {
);
};
const TagTemplate = ({ tag }) =>
const TagTemplate = ({ tag }) => (
<h4 className="pull-right tag">
<span className="label label-info" aria-hidden="true">
{tag.name}
</span>
</h4>;
</h4>
);
import React from 'react';
import { FlowRouter } from 'meteor/kadira:flow-router';
import Quiz from '/imports/api/quizes/quizes.js';
import QuestionForm from './question-form.js';
......@@ -17,10 +16,6 @@ const validateTitle = (title) => {
};
const QuizForm = ({ quiz, validate, actions }) => {
const createQuiz = (e) => {
actions.saveQuiz(e);
FlowRouter.go('Manage.Home');
};
const titleValidation = validate && validateTitle(quiz.title);
return (
<div id="quiz-form">
......@@ -92,7 +87,7 @@ const QuizForm = ({ quiz, validate, actions }) => {
<hr />
<div className="row">
<div className="col-sm-12">
<button className="btn btn-success btn-lg col-sm-2 pull-left" onClick={createQuiz}>
<button className="btn btn-success btn-lg col-sm-2 pull-left" onClick={actions.saveQuiz}>
<span className="glyphicon glyphicon-ok" aria-hidden="true" />
</button>
</div>
......
import React from 'react';
import { Meteor } from 'meteor/meteor';
import {
LineChart,
XAxis,
......@@ -11,41 +10,43 @@ import {
Line,
} from 'recharts';
const QuestionTimeLineChart = ({ game }) => {
const CustomizedXAxisTick = ({ x, y, stroke, payload }) => (
const TwoLinesChart = ({ data, dataKeyA, dataKeyB, dataKeyX }) => {
const CustomizedXAxisTick = ({ x, y, payload }) => (
<g transform={`translate(${x},${y})`}>
<text textAnchor="middle" fill="#666" dy={16}>
שאלה {payload.value}#
</text>
</g>
);
const CustomizedYAxisTick = ({ x, y, stroke, payload }) => (
<g transform={`translate(${x},${y})`}>
<text textAnchor="middle" fill="#666" dx={-16} dy={3}>
{payload.value}
</text>
</g>
);
const CustomizedYAxisTick = ({ x, y, payload }) => {
return (
<g transform={`translate(${x},${y})`}>
<text textAnchor="middle" fill="#666" dx={-16} dy={3}>
{payload.value}
</text>
</g>
);
};
return (
<ResponsiveContainer width="100%" aspect={5.0 / 3.0}>
<LineChart
data={game.getPlayerTimeAndAvarageTime(Meteor.userId())}
data={data}
margin={{ top: 5, right: 35, left: 0, bottom: 5 }}
>
<XAxis dataKey="questionOrder" tick={<CustomizedXAxisTick />} />
<XAxis dataKey={dataKeyX} tick={<CustomizedXAxisTick />} />
<YAxis tick={<CustomizedYAxisTick />} />
<CartesianGrid strokeDasharray="3 3" />
<Legend verticalAlign="bottom" />
<Tooltip />
<Line
type="monotone"
dataKey="playerTime"
dataKey={dataKeyA}
stroke="#8884d8"
name="אני"
/>
<Line
type="monotone"
dataKey="avarageTime"
dataKey={dataKeyB}
stroke="#82ca9d"
name="ממוצע"
/>
......@@ -54,4 +55,4 @@ const QuestionTimeLineChart = ({ game }) => {
);
};
export default QuestionTimeLineChart;
export default TwoLinesChart;
import React from 'react';
import ManageNavbar from '../../components/manage-navbar.js';
import OneLinesChart from '../../components/one-line-chart';
const HistoryManager = ({ game }) => (
<div id="history-player">
<div className="row">
<ManageNavbar />
</div>
<div className="row">
<div className="col-xs-2 col-sm-2 col-md-2 col-lg-2 col-xl-2" />
<div className="col-xs-8 col-sm-8 col-md-8 col-lg-8 col-xl-8 chart">
<div className="row">
<OneLinesChart
data={game.getAvarageQuestionAndScore()}
dataKeyX="questionOrder"
dataKeyY="score"
/>
</div>
</div>
<div className="col-xs-2 col-sm-2 col-md-2 col-lg-2 col-xl-2" />
</div>
<div className="row">
<div className="col-xs-2 col-sm-2 col-md-2 col-lg-2 col-xl-2" />
<div className="col-xs-8 col-sm-8 col-md-8 col-lg-8 col-xl-8 chart">
<div className="row">
<OneLinesChart
data={game.getAvarageQuestionAndTime()}
dataKeyX="questionOrder"
dataKeyY="time"
/>
</div>
</div>
<div className="col-xs-2 col-sm-2 col-md-2 col-lg-2 col-xl-2" />
</div>
</div>
);
export default HistoryManager;
import React from 'react';
import { Meteor } from 'meteor/meteor';
import ManageNavbar from '../../components/manage-navbar.js';
import QuestionScoreLineChart from '../../components/question-score-line-chart';
import QuestionTimeLineChart from '../../components/question-time-line-chart';
import TwoLinesChart from '../../components/two-lines-chart';
const HistoryPlayer = ({ game }) => (
<div id="history-player">
......@@ -14,7 +13,12 @@ const HistoryPlayer = ({ game }) => (
<div className="col-xs-8 col-sm-8 col-md-8 col-lg-8 col-xl-8 chart">
<div className="row">
<QuestionScoreLineChart game={game} />
<TwoLinesChart
data={game.getPlayerScoreAndAvarageScore(Meteor.userId())}
dataKeyA="playerScore"
dataKeyB="avarageScore"
dataKeyX="questionOrder"
/>
</div>
</div>
......@@ -26,7 +30,12 @@ const HistoryPlayer = ({ game }) => (
<div className="col-xs-8 col-sm-8 col-md-8 col-lg-8 col-xl-8 chart">
<div className="row">
<QuestionTimeLineChart game={game} />
<TwoLinesChart
data={game.getPlayerTimeAndAvarageTime(Meteor.userId())}
dataKeyA="playerTime"
dataKeyB="avarageTime"
dataKeyX="questionOrder"
/>
</div>
</div>
......
......@@ -14,6 +14,8 @@ class Home extends React.Component {
}
render() {
console.log('badGameCode:');
console.log(this.state.badGameCode);
const enterGame = (e) => {
e.preventDefault();
const form = e.target;
......@@ -21,7 +23,9 @@ class Home extends React.Component {
form.gameCode.value = '';
const maybeGame = Game.find({ code: gameCode }).fetch();
const maybeGameUserManage = maybeGame.filter(g => g.quiz.owner === Meteor.userId());
const maybeGameUserManage = maybeGame.filter(
g => g.quiz.owner === Meteor.userId(),
);
const maybeRedirectToGameAsManager = maybeGameUserManage.map((g) => {
FlowRouter.go('Game.Main', { code: g.code });
return g;
......@@ -44,11 +48,10 @@ class Home extends React.Component {
this.setState({ badGameCode: true });
setTimeout(() => this.setState({ badGameCode: false }), 3000);
};
return (
maybeRedirectToGameAsManager.length === 0 &&
maybeRedirectToGameAsManager.length === 0 &&
maybeRedirectToGameAsPlayer.length === 0 &&
notifyUser()
);
notifyUser();
};
return (
......@@ -73,7 +76,11 @@ class Home extends React.Component {
/>
</div>
<div className="row">
<button className="btn btn-primary" type="submit" id="start-game-btn">
<button
className="btn btn-primary"
type="submit"
id="start-game-btn"
>
התחל משחק!
</button>
</div>
......@@ -84,7 +91,9 @@ class Home extends React.Component {
<p id="center-home-massage">
<a href="/Manage">נהל או צור משחק חדש</a>
</p>
<div id="snackbar" className={this.state.badGameCode ? 'show' : ''}>לא נמצא משחק</div>
<div id="snackbar" className={this.state.badGameCode ? 'show' : ''}>
לא נמצא משחק
</div>
</div>
);
}
......@@ -96,7 +105,7 @@ const HomeContainer = ({ loading }) => {
};
export default createContainer(() => {
const gameHandle = Meteor.subscribe('games.all');
const gameHandle = Meteor.subscribe('games.open');
const loading = !gameHandle.ready();
return {
loading,
......
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { FlowRouter } from 'meteor/kadira:flow-router';
import uuidV4 from 'uuid/v4';
import Quiz from '../../../api/quizes/quizes.js';
import Tag from '../../../api/tags/tags.js';
......@@ -141,11 +142,15 @@ class CreateQuiz extends React.Component {
const quiz = this.state.quiz;
const tags = quiz.tags.map((t) => {
const tag = Tag.findOne({ name: t.name });
return tag ? tag._id : new Tag(t).create();
return tag ? tag._id : new Tag(t).applyMethod('create', []);
});
const questions = quiz.questions.map((q, i) => ({ ...q, order: i + 1 }));
const quiz$ = new Quiz({ ...quiz, questions, tags, owner: Meteor.userId() }, { cast: true });
quiz$.create();
quiz$.applyMethod('create', [], (err, result) => {
console.log(err);
console.log(result);
result && FlowRouter.go('Manage.Home');
});
};
const actions = {
......
......@@ -137,11 +137,11 @@ class EditQuiz extends React.Component {
const quiz = this.state.quiz;
const tags = quiz.tags.map((t) => {
const tag = Tag.findOne({ name: t.name });
return tag ? tag._id : new Tag(t).create();
return tag ? tag._id : new Tag(t).applyMethod('create', []);
});
const questions = quiz.questions.map((q, i) => ({ ...q, order: i + 1 }));
const quiz$ = Quiz.findOne();
quiz$.update({ ...quiz, questions, tags }, { cast: true });
quiz$.applyMethod('update', [{ ...quiz, questions, tags }, { cast: true }]);
};
const actions = {
......
......@@ -19,6 +19,8 @@ class Main extends React.Component {
super(props);
this.state = {
activeTab: tabNames.myQuizes,
quizDeleted: false,
quizForked: false,
};
}
......@@ -27,6 +29,32 @@ class Main extends React.Component {
const activeTab = this.state.activeTab;
const changeTab = tabName => () => this.setState({ activeTab: tabName });
const deleteQuiz = (quiz) => {
quiz.applyMethod('delete', []);
const notifyUser = () => {
this.setState({ quizDeleted: true });
setTimeout(() => this.setState({ quizDeleted: false }), 3000);
};
notifyUser();
};
const forkQuiz = (quiz) => {
const newQuiz = new Quiz({
questions: quiz.questions,
title: quiz.title,
tags: quiz.tags,
owner: Meteor.userId(),
});
newQuiz.applyMethod('create', []);
const notifyUser = () => {
this.setState({ quizForked: true });
setTimeout(() => this.setState({ quizForked: false }), 3000);
};
notifyUser();
};
const actions = {
deleteQuiz,
forkQuiz,
};
return (
<div id="quiz-management-main">
<div className="tab-btns btn-pref btn-group btn-group-justified btn-group-lg" role="group">
......@@ -73,8 +101,8 @@ class Main extends React.Component {
</div>
</a>
{quizes.length
? quizes.map(quiz => <QuizCard key={quiz._id} quiz={quiz} />)
: <div>לא יצרת אפילו שאלון אחד, למה אתה מחכה?</div>}
? quizes.map(quiz => <QuizCard key={quiz._id} quiz={quiz} actions={actions} />)
: <h3>לא יצרת אפילו שאלון אחד, למה אתה מחכה?</h3>}
</div>
<div
......@@ -94,6 +122,9 @@ class Main extends React.Component {
</div>
</div>
</div>
<div id="snackbar" className={this.state.quizDeleted || this.state.quizForked ? 'show' : ''}>
{this.state.quizDeleted ? 'השאלון נמחק בהצלחה' : 'השאלון הועתק בהצלחה'}
</div>
</div>
);
}
......@@ -101,10 +132,6 @@ class Main extends React.Component {
const ManagementContainer = ({ loading, quizes, gamesPlayed, gamesManaged }) => {
if (loading) return <Loading />;
console.log('gamesManaged:');
console.log(gamesManaged);
console.log('gamesPlayed:');
console.log(gamesPlayed);
return <Main quizes={quizes} gamesPlayed={gamesPlayed} gamesManaged={gamesManaged} />;
};
......
......@@ -54,51 +54,4 @@
color: #fff;
font-size: 22px;
}
/* The snackbar - position it at the bottom and in the middle of the screen */
#snackbar {
visibility: hidden; /* Hidden by default. Visible on click */
min-width: 250px; /* Set a default minimum width */
margin-left: -125px; /* Divide value of min-width by 2 */
background-color: #333; /* Black background color */
color: #fff; /* White text color */
text-align: center; /* Centered text */
border-radius: 2px; /* Rounded borders */
padding: 16px; /* Padding */
position: fixed; /* Sit on top of the screen */
z-index: 1; /* Add a z-index if needed */
left: 50%; /* Center the snackbar */
bottom: 30px; /* 30px from the bottom */
}
/* Show the snackbar when clicking on a button (class added with JavaScript) */
#snackbar.show {
visibility: visible; /* Show the snackbar */
/* Add animation: Take 0.5 seconds to fade in and out the snackbar.
However, delay the fade out process for 2.5 seconds */
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
animation: fadein 0.5s, fadeout 0.5s 2.5s;
}
/* Animations to fade the snackbar in and out */
@-webkit-keyframes fadein {
from {bottom: 0; opacity: 0;}
to {bottom: 30px; opacity: 1;}
}
@keyframes fadein {
from {bottom: 0; opacity: 0;}
to {bottom: 30px; opacity: 1;}
}
@-webkit-keyframes fadeout {
from {bottom: 30px; opacity: 1;}
to {bottom: 0; opacity: 0;}
}
@keyframes fadeout {
from {bottom: 30px; opacity: 1;}
to {bottom: 0; opacity: 0;}
}
}
/* The snackbar - position it at the bottom and in the middle of the screen */
#snackbar {
visibility: hidden; /* Hidden by default. Visible on click */
min-width: 250px; /* Set a default minimum width */
margin-left: -125px; /* Divide value of min-width by 2 */
background-color: #333; /* Black background color */
color: #fff; /* White text color */
text-align: center; /* Centered text */
border-radius: 2px; /* Rounded borders */
padding: 16px; /* Padding */
position: fixed; /* Sit on top of the screen */
z-index: 1; /* Add a z-index if needed */
left: 50%; /* Center the snackbar */
bottom: 30px; /* 30px from the bottom */
}
/* Show the snackbar when clicking on a button (class added with JavaScript) */
#snackbar.show {
visibility: visible; /* Show the snackbar */
/* Add animation: Take 0.5 seconds to fade in and out the snackbar.
However, delay the fade out process for 2.5 seconds */
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
animation: fadein 0.5s, fadeout 0.5s 2.5s;
}
/* Animations to fade the snackbar in and out */
@-webkit-keyframes fadein {
from {bottom: 0; opacity: 0;}
to {bottom: 30px; opacity: 1;}
}
@keyframes fadein {
from {bottom: 0; opacity: 0;}
to {bottom: 30px; opacity: 1;}
}
@-webkit-keyframes fadeout {
from {bottom: 30px; opacity: 1;}
to {bottom: 0; opacity: 0;}
}
@keyframes fadeout {
from {bottom: 30px; opacity: 1;}
to {bottom: 0; opacity: 0;}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment