This commit is contained in:
bakhirev 2024-11-30 00:33:41 +03:00
parent 787812ef26
commit daa2181770
33 changed files with 578 additions and 36 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

160
public/themes/dark.css Normal file
View file

@ -0,0 +1,160 @@
body {
--theme-sidebar: #12131B;
--theme-main: #12131B;
--theme-border: #666666;
--theme-progress: #008000;
--theme-font: #CCCCCC;
--theme-accent: #008000;
}
.sidebar,
.sidebar_item,
.sidebar_item:hover,
.sidebar_item.selected,
.switch,
.switch_item,
.switch_item.selected {
background-color: var(--theme-sidebar);
}
body,
.header,
.header_filters_input,
.recommendations_card,
.page_wrapper,
.splash_screen,
.progress_bar,
.card_with_icon,
.table_cell,
.table_header_cell,
.main_wrapper_white,
.ui_kit_common,
.ui_kit_button,
.ui_kit_button:hover,
.ui_kit_tags_item,
.ui_kit_select_value,
.modal_window,
.pie_chart,
.tempo_task_commits,
.tempo_task_tag,
.tempo_task_hours,
.achievement,
.get_list_container,
.get_list,
.get_list_title {
background-color: var(--theme-main);
}
.progress_bar_line {
background-color: var(--theme-progress);
}
.header_title,
.header_filters_input,
.switch_item_title,
.sidebar_item_title,
.paginator_text,
.title,
.table_cell,
.table_header_cell,
.ui_kit_common,
.ui_kit_button,
.ui_kit_tags_item,
.ui_kit_select_value,
.ui_kit_checkbox_title,
.pie_chart_percent,
.pie_chart_text,
.tempo_author,
.tempo_task_tag,
.tempo_task_value,
.card_with_icon_value,
.description_title,
.description_text,
.description_text > span,
.description_list,
.recommendations_modal_title,
.recommendations_modal_sub_title,
.achievement_title,
.achievement_description {
color: var(--theme-font);
}
.page_wrapper,
.main_wrapper_white,
.pie_chart,
.table_row {
border-color: var(--theme-border);
}
.logo,
.sidebar_title,
.header_setting {
display: none;
}
/* Header */
.header_print {
margin-right: 0;
}
/* Sidebar */
.sidebar {
padding-top: 8px;
padding-left: 0;
border-right: 1px solid var(--theme-border);
}
.sidebar_item:hover > .sidebar_item_title,
.sidebar_item.selected > .sidebar_item_title {
text-decoration: underline;
color: var(--theme-accent);
}
/* Switch */
.switch {
width: 100%;
padding: 0;
}
.switch_item,
.switch_item.selected {
border-radius: 0;
border-bottom: 2px solid var(--theme-border);
}
.switch_item.selected {
border-color: var(--theme-accent);
}
.switch_item.selected > .switch_item_title {
font-weight: bold;
color: var(--theme-accent);
}
/* Main */
.page_wrapper_main {
padding-top: 0;
}
.card_with_icon_icon {
filter: grayscale(1);
}
.pie_chart_icon,
.pie_chart_color,
.line_chart_item {
filter: grayscale(0.3);
}
.main_wrapper_white,
.pie_chart {
border-radius: 0;
}
.ui_kit_button,
.ui_kit_select_value,
.tempo_task_tag,
.tempo_task_commits,
.tempo_task_hours {
border: 1px solid var(--theme-border);
}

139
public/themes/demo.html Normal file
View file

@ -0,0 +1,139 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport"
content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, maximum-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="cleartype" content="on">
<meta name="HandheldFriendly" content="True">
<meta name="format-detection" content="telephone=no">
<meta name="format-detection" content="address=no">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="theme-color" content="white"/>
<meta name="defaultLanguage" content="ru">
<link rel="icon" href="../favicon.svg"/>
<link rel="apple-touch-icon" href="../logo192.png"/>
<title>Code Scoring</title>
<style>
body {
width: 100vw;
height: 100vh;
padding: 0;
margin: 0;
border: none;
overflow: hidden;
}
header {
width: 100vw;
height: 40px;
background-color: #001529;
background-image: url(./jira/header.png);
background-position: top left;
background-size: auto 100%;
background-repeat: no-repeat;
}
main {
width: 100vw;
height: calc(100vh - 40px);
overflow: hidden;
white-space: nowrap;
}
aside {
display: inline-block;
width: 220px;
height: 100%;
vertical-align: top;
background-color: #001529;
background-image: url(./jira/sidebar.png);
background-position: top left;
background-size: 100% auto;
background-repeat: no-repeat;
}
iframe {
display: inline-block;
width: calc(100vw - 220px);
height: 100%;
padding: 0;
margin: 0;
border: none;
overflow: hidden;
vertical-align: top;
}
</style>
</head>
<body>
<header id="header"></header>
<main id="main">
<aside id="sidebar"></aside><iframe
id="frame"
width="100%"
height="100%"
src="/demo/?dump=./test.txt&theme=./themes/white.css"
></iframe>
</main>
<script>
function getParametersFromString(text) {
return Object.fromEntries((text || '')
.substring(1, Infinity)
.split('&')
.map((token) => token.split('=')));
}
function getParametersFromURL() {
const parameters = {
...getParametersFromString(location.search),
...getParametersFromString(location.hash),
};
delete parameters[''];
return parameters;
}
function getStyleById(id) {
return document.getElementById(id).style;
}
function init() {
const parameters = getParametersFromURL();
const sidebar = getStyleById('sidebar');
const header = getStyleById('header');
if (parameters.sidebarImage) {
sidebar.backgroundImage = `url(${parameters.sidebarImage})`;
}
if (parameters.sidebarColor) {
sidebar.backgroundColor = `#${parameters.sidebarColor}`;
}
if (parameters.width) {
sidebar.width = `${parameters.width}px`;
getStyleById('frame').width = `calc(100vw - ${parameters.width}px)`;
}
if (parameters.headerImage) {
header.backgroundImage = `url(${parameters.headerImage})`;
}
if (parameters.headerColor) {
header.backgroundColor = `#${parameters.headerColor}`;
}
if (parameters.height) {
header.height = `${parameters.height}px`;
getStyleById('main').height = `calc(100vh - ${parameters.height}px)`;
}
if (parameters.title) {
document.title = parameters.title;
}
}
init();
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

93
public/themes/index.html Normal file
View file

@ -0,0 +1,93 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport"
content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, maximum-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="cleartype" content="on">
<meta name="HandheldFriendly" content="True">
<meta name="format-detection" content="telephone=no">
<meta name="format-detection" content="address=no">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="theme-color" content="white"/>
<meta name="defaultLanguage" content="ru">
<link rel="icon" href="../favicon.svg"/>
<link rel="apple-touch-icon" href="../logo192.png"/>
<title>IFrame demo</title>
<style>
body {
font-family: Verdana;
width: 100vw;
height: 100vh;
padding: 0;
margin: 0;
border: none;
overflow: hidden;
}
main {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
text-align: center;
}
nav {
display: block;
text-align: center;
}
a {
font-weight: 100;
font-size: 14px;
display: block;
height: 42px;
width: 297px;
padding: 0 18px;
margin: 0 auto 12px;
cursor: pointer;
line-height: 42px;
text-align: center;
box-sizing: border-box;
white-space: nowrap;
vertical-align: top;
text-decoration: none;
border: 1px solid #E2E9F0;
border-radius: 4px;
color: #FFFFFF;
background-color: #1a73e8;
}
</style>
</head>
<body>
<main>
<nav>
<a
target="_blank"
href="./demo.html?sidebarImage=./jira/sidebar.png&sidebarColor=F4F5F7&width=220&headerImage=./jira/header.png&headerColor=0747A6&height=40&title=JIRA">
JIRA
</a>
<a
target="_blank"
href="./demo.html?sidebarImage=./gitlab/sidebar.png&sidebarColor=FBFAFD&width=266&height=100&headerImage=./gitlab/header.png&headerColor=171321&title=GitLab">
GitLab
</a>
<a
target="_blank"
href="./demo.html?sidebarImage=./cs/sidebar.png&sidebarColor=001529&width=200&height=0&title=CodeScoring">
CodeScoring
</a>
</nav>
</main>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

85
public/themes/white.css Normal file
View file

@ -0,0 +1,85 @@
body {
--theme-sidebar: #FFFFFF;
--theme-main: #F5F7F9;
--theme-border: #E2E9F0;
--theme-progress: #DDDDDD;
--theme-font: #12131B;
--theme-accent: #0B59CC;
}
.sidebar,
.sidebar_item,
.sidebar_item:hover,
.sidebar_item.selected,
.switch,
.switch_item,
.switch_item.selected {
background-color: var(--theme-sidebar);
}
body,
.header,
.page_wrapper,
.splash_screen,
.progress_bar {
background-color: var(--theme-main);
}
.progress_bar_line {
background-color: var(--theme-progress);
}
.switch_item_title,
.sidebar_item_title {
color: var(--theme-font);
}
.logo,
.sidebar_title,
.header_setting {
display: none;
}
/* Header */
.header_print {
margin-right: 0;
}
/* Sidebar */
.sidebar {
padding-top: 8px;
padding-left: 0;
border-right: 1px solid var(--theme-border);
}
.sidebar_item:hover > .sidebar_item_title,
.sidebar_item.selected > .sidebar_item_title {
text-decoration: underline;
color: var(--theme-accent);
}
/* Switch */
.switch {
width: 100%;
padding: 0;
}
.switch_item,
.switch_item.selected {
border-radius: 0;
border-bottom: 2px solid var(--theme-border);
}
.switch_item.selected {
border-color: var(--theme-accent);
}
.switch_item.selected > .switch_item_title {
font-weight: bold;
color: var(--theme-accent);
}
/* Main */
.page_wrapper_main {
padding-top: 0;
}

View file

@ -27,6 +27,7 @@ function Modal({
children,
}: IModalProps) {
const [canClose, setCanClose] = useState<boolean>(!delay);
const formattedId = id || 'modal_window';
useEffect(globalScroll.useOnOff, []);
@ -42,16 +43,16 @@ function Modal({
return ReactDOM.createPortal((
<div
id={`${id}-wrapper`}
id={`${formattedId}-wrapper`}
className={`${style.modal_window_wrapper || ''}`}
onClick={(event: any) => {
event.stopPropagation();
if (event.target?.id !== `${id}-wrapper`) return;
if (event.target?.id !== `${formattedId}-wrapper`) return;
if (onClose && canClose) onClose();
}}
>
<div
id={id}
id={formattedId}
className={`${customClass} ${className || ''}`}
onClick={(event: any) => {
event.stopPropagation();

View file

@ -42,8 +42,6 @@
align-items: center;
justify-content: center;
overflow: hidden;
background-color: rgba(90, 90, 90, 0.2);
}
&_halo {

View file

@ -5,8 +5,8 @@ import { observer } from 'mobx-react-lite';
import Message from './components/Message';
import IMessage from './interfaces/Message';
import notificationsStore from './store/index';
import style from './styles/index.module.scss';
import style from './styles/index.module.scss';
const Notifications = observer(() => {
const items = notificationsStore.messages.map((message: IMessage) => (

View file

@ -1,4 +1,5 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { getRandom } from 'ts/helpers/random';
@ -16,6 +17,7 @@ function Answer({
mode,
onClick,
}: IAnswerProps): React.ReactElement | null {
const { t } = useTranslation();
const [iconIndex] = useState(getRandom(5));
const className = [style.quiz_answer_wrapper];
const textClasName = [style.quiz_answer_text];
@ -38,7 +40,7 @@ function Answer({
src={`./assets/games/quize/balloon_${iconIndex}.png`}
/>
<figcaption className={textClasName.join(' ')}>
{answer.title}
{t(answer.title)}
</figcaption>
</figure>
</div>

View file

@ -1,4 +1,5 @@
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import UiKitButton from 'ts/components/UiKit/components/Button';
@ -26,6 +27,7 @@ function Question({
question,
onClick,
}: IQuestionProps): React.ReactElement | null {
const { t } = useTranslation();
const [selected, setSelected] = useState<IAnswer | null>(null);
const [disabled, setDisabled] = useState<boolean>(false);
const [mode, setMode] = useState<string[]>([]);
@ -76,7 +78,7 @@ function Question({
className={`${style.quiz_title} ${hideClassName}`}
style={{ backgroundImage: 'url(./assets/games/quize/cloud_big.png)' }}
>
{question.title}
{t(question.title)}
</div>
<div className={style.quiz_question_answer}>
{answers}

View file

@ -1,5 +1,6 @@
import dataGripStore from 'ts/store/DataGrip';
import { getRandom, shuffle } from 'ts/helpers/random';
import localization from 'ts/helpers/Localization';
import IQuiz from '../interfaces/Quiz';
import getQuestion from './getQuestion';
@ -35,56 +36,95 @@ function getQuestionByNumber(question: string, rightAnswer: number) {
function getHowTaskInDay(user: any) {
if (!user) return null;
const question = `Сколько максимум задач в день делал ${user.author}?`;
const question = localization.get('page.team.building.quiz.question13', user.author);
const byTimestamp = dataGripStore.dataGrip.timestamp.statisticByAuthor[user.author];
const rightAnswer = byTimestamp.tasksByTimestampCounter.max;
return getQuestionByNumber(question, rightAnswer);
}
function getHowTypes() {
const notFirst = dataGripStore.dataGrip.type.list
.slice(2)
.filter((v: string) => v);
if (notFirst.length < 3) return null;
const answers = shuffle(notFirst).slice(0, 3);
const [a, b, c] = answers.map((answer) => notFirst.indexOf(answer));
let rightAnswer = 0;
if (b < a && b < c) rightAnswer = 1;
if (c < a && c < b) rightAnswer = 2;
return getQuestion('page.team.building.quiz.question14', answers, rightAnswer);
}
function getHowDaysInProject(authors: any) {
const days = authors.map((author: any) => author.allDaysInProject);
const skip = Math.floor(authors.length * 0.2);
const middle = days.slice(skip, authors.length - skip);
if (middle.length < 3) return null;
const rightAnswer = Math.ceil(days.reduce((a: number, b: number) => (a + b)) / days.length);
return getQuestionByNumber('page.team.building.quiz.question15', 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),
getQuestionByList(authorsWithStaff, 'page.team.building.quiz.question01', (s: any) => s.firstCommit.milliseconds),
getQuestionByList(authors, 'page.team.building.quiz.question02', (s: any) => s.tasks.length),
getQuestionByList(authors, 'page.team.building.quiz.question03', (s: any) => s.taskInDay),
getQuestionByList(authors, 'page.team.building.quiz.question04', (s: any) => s.daysAll),
getQuestionByList(authors, 'page.team.building.quiz.question05', (s: any) => s.daysAll, 2),
getQuestionByList(authors, 'page.team.building.quiz.question06', (s: any) => s.commits / s.daysWorked),
getQuestionByList(authors, 'page.team.building.quiz.question07', (s: any) => s.commits / s.daysWorked, 2),
getQuestionByList(authors, 'page.team.building.quiz.question08', (s: any) => s.middleMessageLength),
getQuestionByList(authors, 'page.team.building.quiz.question09', (s: any) => s.middleMessageLength, 2),
getQuestionByList(authors, 'page.team.building.quiz.question10', (s: any) => s.daysLosses / s.daysWorked, 2),
getQuestionByNumber('page.team.building.quiz.question11', dismissed),
getQuestionByNumber('page.team.building.quiz.question12', staff),
getHowTaskInDay(randomUsers[0]),
getHowTaskInDay(randomUsers[1]),
getHowTaskInDay(randomUsers[2]),
]
getHowTypes(),
getHowDaysInProject(authors),
];
const formattedQuestions = questions
.filter((question) => question)
.map((question, i: number) => ({ ...question, index: i + 1 }));
return {
title: '',
description: 'Насколько хорошо ты знаешь команду?',
questions: shuffle(questions),
questions: shuffle(formattedQuestions),
results: [
{
title: 'Поздравляем, пытка окончена',
description: 'Вы протестировали этот квиз и готовы написать на него отзыв длинной два или три предложения.',
title: 'Недостаточно',
description: 'Правильных ответов меньше 40%. Ознакомьтесь с данными о вашей команде в соседних разделах и попробуйте снова!',
min: 0,
max: 60,
max: 7,
},
{
title: 'Хорошо',
description: 'Правильных ответов от 40% до 70%. Вы имеете хорошее представление о вашей команде, но можете узнать её лучше. Ознакомьтесь с данными в соседних разделах и попробуйте снова!',
min: 8,
max: 13,
},
{
title: 'Отлично',
description: 'Правильных ответов больше 70%. Вы отлично знаете статистику по вашей команде. !',
min: 14,
max: 25,
},
],
};

View file

@ -32,7 +32,7 @@ function UiKitInputString({
type="text"
value={value}
placeholder={placeholder}
className={`${className} ${style.ui_kit_common} }`}
className={`${className} ${style.ui_kit_common}`}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
if (onChange) onChange(event.target.value);
}}

View file

@ -35,7 +35,7 @@
.ui_kit_button {
font-weight: bold;
font-size: var(--font-xs);
font-size: 13px;
display: inline-block;
height: var(--temp-size);
min-width: var(--temp-size);

View file

@ -1,5 +1,10 @@
let overflow = document.body.style.overflow;
function setBlur(value?: number) {
const element = document.getElementById('root');
if (element) element.style.filter = value ? `blur(${value}px)` : 'none';
}
function on() {
document.body.style.overflow = overflow;
}
@ -12,8 +17,10 @@ function off(delay?: number) {
function useOnOff() {
off();
setBlur(1);
return () => {
on();
setBlur();
};
}

View file

@ -242,6 +242,21 @@ export default `
§ page.team.building.races.title: Скорость закрытия задач
§ page.team.building.races.go: Поехали!
§ page.team.building.swimmingPool.title: Максимальная длинна подписи коммита
§ page.team.building.quiz.question01: Кто сделал первый коммит?
§ page.team.building.quiz.question02: Кто закрыл больше задач?
§ page.team.building.quiz.question03: Кто быстрее всех делает задачи?
§ page.team.building.quiz.question04: Кто дольше всех работал на проекте?
§ page.team.building.quiz.question05: Кто меньше всех работал на проекте?
§ page.team.building.quiz.question06: Кто чаще коммитит?
§ page.team.building.quiz.question07: Кто реже коммитит?
§ page.team.building.quiz.question08: У кого саммые длинные подписи коммитов?
§ page.team.building.quiz.question09: У кого саммые короткие подписи коммитов?
§ page.team.building.quiz.question10: У кого больше всего дней без коммитов?
§ page.team.building.quiz.question11: Сколько человек уволилось?
§ page.team.building.quiz.question12: Сколько человек помогало проекту?
§ page.team.building.quiz.question13: Сколько максимум задач в день делал $1?
§ page.team.building.quiz.question14: Задач какого типа больше влили?
§ page.team.building.quiz.question15: Сколько в среднем дней работают на проекте?
§ page.person.print.photo.title: Фотография
§ page.person.print.photo.description: место для фотографии
§ page.person.total.title: Основные характеристики