update
BIN
build/assets/games/billboard1.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
build/assets/games/cloud.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
build/assets/games/lawn.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
build/assets/games/stands.png
Normal file
After Width: | Height: | Size: 277 KiB |
1
build/assets/menu/building.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#84858D"><path d="M3 14c0 1.3.84 2.4 2 2.82V20H3v2h6v-2H7v-3.18C8.16 16.4 9 15.3 9 14V6H3zm2-6h2v3H5zm0 5h2v1c0 .55-.45 1-1 1s-1-.45-1-1zm15.64-4.46-.96-.32c-.41-.14-.68-.52-.68-.95V3c0-.55-.45-1-1-1h-3c-.55 0-1 .45-1 1v4.28c0 .43-.27.81-.68.95l-.96.32c-.81.28-1.36 1.04-1.36 1.9V20c0 1.1.9 2 2 2h7c1.1 0 2-.9 2-2v-9.56c0-.86-.55-1.62-1.36-1.9M16 4h1v1h-1zm4 16h-7v-2h7zm0-4h-7v-2h7zm0-4h-7v-1.56l.95-.32C15.18 9.72 16 8.57 16 7.28V7h1v.28c0 1.29.82 2.44 2.05 2.85l.95.31z"></path></svg>
|
After Width: | Height: | Size: 553 B |
1
build/assets/menu/department.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#84858D"><path d="M6.32 13.01c.96.02 1.85.5 2.45 1.34C9.5 15.38 10.71 16 12 16s2.5-.62 3.23-1.66c.6-.84 1.49-1.32 2.45-1.34-.72-1.22-3.6-2-5.68-2-2.07 0-4.96.78-5.68 2.01M4 13c1.66 0 3-1.34 3-3S5.66 7 4 7s-3 1.34-3 3 1.34 3 3 3m16 0c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3m-8-3c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3"></path><path d="M21 14h-3.27c-.77 0-1.35.45-1.68.92-.04.06-1.36 2.08-4.05 2.08-1.43 0-3.03-.64-4.05-2.08-.39-.55-1-.92-1.68-.92H3c-1.1 0-2 .9-2 2v4h7v-2.26c1.15.8 2.54 1.26 4 1.26s2.85-.46 4-1.26V20h7v-4c0-1.1-.9-2-2-2"></path></svg>
|
After Width: | Height: | Size: 644 B |
1
build/assets/menu/release.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#84858D"><path d="M5 5h2v3h10V5h2v6h2V5c0-1.1-.9-2-2-2h-4.18C14.4 1.84 13.3 1 12 1s-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h5v-2H5zm7-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1"></path><path d="m18.01 13-1.42 1.41 1.58 1.58H12v2h6.17l-1.58 1.59 1.42 1.41 3.99-4z"></path></svg>
|
After Width: | Height: | Size: 360 B |
BIN
public/assets/games/billboard1.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
public/assets/games/cloud.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
public/assets/games/lawn.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
public/assets/games/stands.png
Normal file
After Width: | Height: | Size: 277 KiB |
1
public/assets/menu/building.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#84858D"><path d="M3 14c0 1.3.84 2.4 2 2.82V20H3v2h6v-2H7v-3.18C8.16 16.4 9 15.3 9 14V6H3zm2-6h2v3H5zm0 5h2v1c0 .55-.45 1-1 1s-1-.45-1-1zm15.64-4.46-.96-.32c-.41-.14-.68-.52-.68-.95V3c0-.55-.45-1-1-1h-3c-.55 0-1 .45-1 1v4.28c0 .43-.27.81-.68.95l-.96.32c-.81.28-1.36 1.04-1.36 1.9V20c0 1.1.9 2 2 2h7c1.1 0 2-.9 2-2v-9.56c0-.86-.55-1.62-1.36-1.9M16 4h1v1h-1zm4 16h-7v-2h7zm0-4h-7v-2h7zm0-4h-7v-1.56l.95-.32C15.18 9.72 16 8.57 16 7.28V7h1v.28c0 1.29.82 2.44 2.05 2.85l.95.31z"></path></svg>
|
After Width: | Height: | Size: 553 B |
1
public/assets/menu/release.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#84858D"><path d="M5 5h2v3h10V5h2v6h2V5c0-1.1-.9-2-2-2h-4.18C14.4 1.84 13.3 1 12 1s-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h5v-2H5zm7-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1"></path><path d="m18.01 13-1.42 1.41 1.58 1.58H12v2h6.17l-1.58 1.59 1.42 1.41 3.99-4z"></path></svg>
|
After Width: | Height: | Size: 360 B |
57
src/ts/components/BillBoard/index.module.scss
Normal file
|
@ -0,0 +1,57 @@
|
|||
@import 'src/styles/variables';
|
||||
|
||||
.billboard {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0 auto -44px;
|
||||
text-align: center;
|
||||
|
||||
background-repeat: repeat-x;
|
||||
background-size: auto 100%;
|
||||
background-position: bottom left;
|
||||
|
||||
&_box {
|
||||
position: relative;
|
||||
|
||||
display: inline-block;
|
||||
width: 400px;
|
||||
height: 160px;
|
||||
margin: 0 auto;
|
||||
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% auto;
|
||||
background-position: top left;
|
||||
}
|
||||
|
||||
&_title {
|
||||
font-family: monospace;
|
||||
font-size: 22px;
|
||||
font-weight: 100;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 92px;
|
||||
padding: var(--space-m);
|
||||
margin: 0 auto;
|
||||
|
||||
line-height: 1.3;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
text-shadow: 1px 1px 1px #FEFEFE;
|
||||
color: #4E3C3B;
|
||||
}
|
||||
|
||||
&_cloud {
|
||||
padding-top: 50px;
|
||||
background-color: #F0AE7A;
|
||||
background-size: auto 56%;
|
||||
}
|
||||
|
||||
&_green {
|
||||
padding-top: 12px;
|
||||
background-color: #557D4B;
|
||||
}
|
||||
}
|
||||
|
47
src/ts/components/BillBoard/index.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import style from './index.module.scss';
|
||||
|
||||
interface BillBoardProps {
|
||||
title: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
function BillBoard({
|
||||
title,
|
||||
type,
|
||||
}: BillBoardProps): React.ReactElement | null {
|
||||
const { t } = useTranslation();
|
||||
if (!title) return null;
|
||||
|
||||
const className = type === 'cloud'
|
||||
? style.billboard_cloud
|
||||
: style.billboard_green;
|
||||
|
||||
const icon = type === 'cloud'
|
||||
? './assets/games/cloud.png'
|
||||
: './assets/games/lawn.png';
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${style.billboard} ${className}`}
|
||||
style={{
|
||||
backgroundImage: `url(${icon})`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={style.billboard_box}
|
||||
style={{
|
||||
backgroundImage: 'url(./assets/games/billboard1.png)',
|
||||
}}
|
||||
>
|
||||
<div className={style.billboard_title}>
|
||||
{t(title || '')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default BillBoard;
|
14
src/ts/components/Quiz/helpers/getQuestion.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
export default function getQuestion(
|
||||
question: string,
|
||||
answers: string[],
|
||||
rightAnswer: number,
|
||||
) {
|
||||
return {
|
||||
title: question,
|
||||
index: 0,
|
||||
answers: answers.map((title: string, index: number) => ({
|
||||
title,
|
||||
score: rightAnswer === index ? 1 : 0,
|
||||
})),
|
||||
};
|
||||
}
|
91
src/ts/components/Quiz/helpers/getQuestions.ts
Normal file
|
@ -0,0 +1,91 @@
|
|||
import dataGripStore from 'ts/store/DataGrip';
|
||||
import { getRandom, shuffle } from 'ts/helpers/random';
|
||||
|
||||
import IQuiz from '../interfaces/Quiz';
|
||||
import getQuestion from './getQuestion';
|
||||
|
||||
function getQuestionByList(
|
||||
authors: any[],
|
||||
question: string,
|
||||
getValue: Function,
|
||||
rightIndex?: number,
|
||||
) {
|
||||
const answers = authors
|
||||
.sort((a, b) => getValue(b) - getValue(a))
|
||||
.slice(0, 3)
|
||||
.map((data) => data.author);
|
||||
const rightAnswer = answers[rightIndex || 0];
|
||||
const formattedAnswers = shuffle(answers);
|
||||
return getQuestion(question, formattedAnswers, formattedAnswers.indexOf(rightAnswer));
|
||||
}
|
||||
|
||||
function getQuestionByNumber(question: string, rightAnswer: number) {
|
||||
let a, b;
|
||||
if (rightAnswer < 3) {
|
||||
a = rightAnswer + 1;
|
||||
b = rightAnswer + 2;
|
||||
} else {
|
||||
a = rightAnswer + (getRandom(rightAnswer) * (Math.random() > 0.5 ? 1 : -1));
|
||||
b = rightAnswer + (getRandom(rightAnswer) * (Math.random() > 0.5 ? 1 : -1));
|
||||
if (a === b) return null;
|
||||
}
|
||||
const answers = shuffle([rightAnswer, a, b]);
|
||||
return getQuestion(question, answers, answers.indexOf(rightAnswer));
|
||||
}
|
||||
|
||||
function getHowTaskInDay(user: any) {
|
||||
if (!user) return null;
|
||||
const question = `Сколько максимум задач в день делал ${user.author}?`;
|
||||
const byTimestamp = dataGripStore.dataGrip.timestamp.statisticByAuthor[user.author];
|
||||
const rightAnswer = byTimestamp.tasksByTimestampCounter.max;
|
||||
return getQuestionByNumber(question, rightAnswer);
|
||||
}
|
||||
|
||||
export default function getQuizQuestions(): IQuiz {
|
||||
const authorsWithStaff = [...dataGripStore.dataGrip.author.statistic];
|
||||
const authors = authorsWithStaff.filter((data) => !data.isStaff);
|
||||
const dismissed = dataGripStore.dataGrip.author.employment.dismissed.length;
|
||||
const staff = dataGripStore.dataGrip.author.employment.staff.length;
|
||||
// const types = shuffle(dataGripStore.dataGrip.type.list.slice(2)).slice(0, 3);
|
||||
const randomUsers = shuffle([...authors]).slice(0, 3);
|
||||
|
||||
// сколько в среднем работают на проекте
|
||||
// во сколько чаще всего комитят
|
||||
// Кто устроился на работу в __Янаваре
|
||||
// Кто первый стал коммитить ночью
|
||||
// Задач какого типа больше
|
||||
|
||||
const questions = [
|
||||
getQuestionByList(authorsWithStaff, 'Кто сделал первый коммит?', (s: any) => s.firstCommit.milliseconds),
|
||||
getQuestionByList(authors, 'Кто закрыл больше задач?', (s: any) => s.tasks.length),
|
||||
getQuestionByList(authors, 'Кто быстрее всех делает задачи?', (s: any) => s.taskInDay),
|
||||
getQuestionByList(authors, 'Кто дольше всех работал на проекте?', (s: any) => s.daysAll),
|
||||
getQuestionByList(authors, 'Кто меньше всех работал на проекте?', (s: any) => s.daysAll, 2),
|
||||
getQuestionByList(authors, 'Кто чаще коммитит?', (s: any) => s.commits / s.daysWorked),
|
||||
getQuestionByList(authors, 'Кто реже коммитит?', (s: any) => s.commits / s.daysWorked, 2),
|
||||
getQuestionByList(authors, 'У кого саммые длинные подписи коммитов?', (s: any) => s.middleMessageLength),
|
||||
getQuestionByList(authors, 'У кого саммые короткие подписи коммитов?', (s: any) => s.middleMessageLength, 2),
|
||||
getQuestionByList(authors, 'У кого больше всего дней без коммитов?', (s: any) => s.daysLosses / s.daysWorked, 2),
|
||||
getQuestionByNumber('Сколько человек уволилось?', dismissed),
|
||||
getQuestionByNumber('Сколько человек помогало проекту?', staff),
|
||||
getHowTaskInDay(randomUsers[0]),
|
||||
getHowTaskInDay(randomUsers[1]),
|
||||
getHowTaskInDay(randomUsers[2]),
|
||||
]
|
||||
.filter((question) => question)
|
||||
.map((question, i: number) => ({ ...question, index: i + 1 }));
|
||||
|
||||
return {
|
||||
title: '',
|
||||
description: 'Насколько хорошо ты знаешь команду?',
|
||||
questions: shuffle(questions),
|
||||
results: [
|
||||
{
|
||||
title: 'Поздравляем, пытка окончена',
|
||||
description: 'Вы протестировали этот квиз и готовы написать на него отзыв длинной два или три предложения.',
|
||||
min: 0,
|
||||
max: 60,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|