mirror of
https://github.com/bakhirev/assayo.git
synced 2024-11-16 16:21:41 +00:00
JIRA-1234 feat(lang): test test test
This commit is contained in:
parent
3d4cb821ac
commit
9f77159b31
|
@ -1,5 +1,6 @@
|
||||||
import React, { ReactNode } from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
|
|
||||||
|
import localization from 'ts/helpers/Localization';
|
||||||
import style from '../styles/index.module.scss';
|
import style from '../styles/index.module.scss';
|
||||||
|
|
||||||
export interface IUiKitWrapperProps {
|
export interface IUiKitWrapperProps {
|
||||||
|
@ -25,22 +26,22 @@ function UiKitWrapper({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`${style.wrapper} ${className || ''}`}
|
className={`${style.wrapper} ${className || ''}`}
|
||||||
title={help}
|
title={localization.get(help)}
|
||||||
>
|
>
|
||||||
{title && (
|
{title && (
|
||||||
<h6 className={style.title}>
|
<h6 className={style.title}>
|
||||||
{title}
|
{localization.get(title)}
|
||||||
</h6>
|
</h6>
|
||||||
)}
|
)}
|
||||||
{description && (
|
{description && (
|
||||||
<p className={style.description}>
|
<p className={style.description}>
|
||||||
{description}
|
{localization.get(description)}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{children}
|
{children}
|
||||||
{help && (
|
{help && (
|
||||||
<p className={style.help}>
|
<p className={style.help}>
|
||||||
{example}
|
{localization.get(example)}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{error && (
|
{error && (
|
||||||
|
|
|
@ -28,6 +28,16 @@ localization.parse('ru', `
|
||||||
§ sidebar.person.changes: Все изменения
|
§ sidebar.person.changes: Все изменения
|
||||||
§ sidebar.person.words: Популярные слова
|
§ sidebar.person.words: Популярные слова
|
||||||
§ sidebar.person.settings: Настройки
|
§ sidebar.person.settings: Настройки
|
||||||
|
§ page.welcome.step1: Выполните команду в корне вашего проекта
|
||||||
|
§ page.welcome.step2: Перетащите файл log.txt на эту страницу
|
||||||
|
§ page.welcome.description1: Git создаст файл log.txt. Он содержит данные для построения отчёта. Или git shortlog -s -n -e если отчёт вам не нужен. Создайте файл
|
||||||
|
§ page.welcome.description2: в корне проекта, чтобы обьединить статистику по сотрудникам.
|
||||||
|
§ page.welcome.description: Git создаст файл log.txt. Он содержит данные для построения отчёта. Или git shortlog -s -n -e если отчёт вам не нужен. Создайте файл [.mailmap|https://git-scm.com/docs/gitmailmap] в корне проекта, чтобы обьединить статистику по сотрудникам.
|
||||||
|
§ page.welcome.warning1: Сервис *НЕ ХРАНИТ* и *НЕ ПЕРЕДАЁТ* ваши данные. Все расчёты выполняются локально в вашем браузере прямо на вашей машине.
|
||||||
|
§ page.welcome.warning2: Сервис *НЕ СОБИРАЕТ СТАТИСТИКУ* по проектам. Вы можете отключить интернет, проверить трафик и даже собрать локальный билд из [исходников|https://github.com/bakhirev/assayo].
|
||||||
|
§ page.team.author.title: Статистика по сотрудникам
|
||||||
|
§ page.team.author.description1: *Часть статитики* (скорость работы, затраченные деньги и т.п.) *по сотрудникам с типом «Помошник» не считается*, т.к. это эпизодическая роль в проекте. Предпологаем, что они не влияют на проект, а их правками можно пренебречь на фоне общего объема работы.
|
||||||
|
§ page.team.author.description2: *Сортировка по умолчанию* — это сортировка по количеству задач и группам (текущие, уволенные, помогающие сотрудники).
|
||||||
§ page.team.author.types: Тип работ
|
§ page.team.author.types: Тип работ
|
||||||
§ page.team.author.commits: Коммитов
|
§ page.team.author.commits: Коммитов
|
||||||
§ page.team.author.commitsSmall: коммитов
|
§ page.team.author.commitsSmall: коммитов
|
||||||
|
@ -41,6 +51,9 @@ localization.parse('ru', `
|
||||||
§ page.team.author.moneyAll: Получил
|
§ page.team.author.moneyAll: Получил
|
||||||
§ page.team.author.moneyWorked: Отработал
|
§ page.team.author.moneyWorked: Отработал
|
||||||
§ page.team.author.moneyLosses: Переплата
|
§ page.team.author.moneyLosses: Переплата
|
||||||
|
§ page.team.hours.title: Распределение коммитов в течении каждого дня недели
|
||||||
|
§ page.team.month.title: Календарь работы по проекту
|
||||||
|
§ page.team.scope.title: Статистика по фичам
|
||||||
§ page.team.scope.scope: Фича
|
§ page.team.scope.scope: Фича
|
||||||
§ page.team.scope.days: Раб. дней
|
§ page.team.scope.days: Раб. дней
|
||||||
§ page.team.scope.authorsDays: Человеко-дней
|
§ page.team.scope.authorsDays: Человеко-дней
|
||||||
|
@ -50,13 +63,18 @@ localization.parse('ru', `
|
||||||
§ page.team.scope.types: Тип работ
|
§ page.team.scope.types: Тип работ
|
||||||
§ page.team.scope.authors: Персональный вклад
|
§ page.team.scope.authors: Персональный вклад
|
||||||
§ page.team.scope.cost: Стоимость
|
§ page.team.scope.cost: Стоимость
|
||||||
|
§ page.team.type.title: Статистика по типам задач
|
||||||
|
§ page.team.type.description: *Персональный вклад* считается по количеству коммитов, а не объему измененных строк или файлов. Поэтому следует так же смотреть раздел «Анализ файлов», чтобы оценить масштаб изменений.
|
||||||
§ page.team.type.type: Тип работы
|
§ page.team.type.type: Тип работы
|
||||||
§ page.team.type.tasks: Задач
|
§ page.team.type.tasks: Задач
|
||||||
|
§ page.team.type.tasksSmall: задач
|
||||||
§ page.team.type.days: Дней
|
§ page.team.type.days: Дней
|
||||||
|
§ page.team.type.daysSmall: дней
|
||||||
§ page.team.type.authorsDays: Человеко-дней
|
§ page.team.type.authorsDays: Человеко-дней
|
||||||
§ page.team.type.commits: Коммитов
|
§ page.team.type.commits: Коммитов
|
||||||
§ page.team.type.commitsSmall: коммитов
|
|
||||||
§ page.team.type.authors: Персональный вклад
|
§ page.team.type.authors: Персональный вклад
|
||||||
|
§ page.team.total.titleA: Объём работ
|
||||||
|
§ page.team.total.titleB: Стоимость
|
||||||
§ page.team.total.daysWorked.title: человеко-дней
|
§ page.team.total.daysWorked.title: человеко-дней
|
||||||
§ page.team.total.daysWorked.description: Учтены только дни, в которые делались коммиты
|
§ page.team.total.daysWorked.description: Учтены только дни, в которые делались коммиты
|
||||||
§ page.team.total.commits.title: коммитов
|
§ page.team.total.commits.title: коммитов
|
||||||
|
@ -77,12 +95,16 @@ localization.parse('ru', `
|
||||||
§ page.team.total.workSpeed.description: Средняя скорость работы команды при текущем составе сотрудников
|
§ page.team.total.workSpeed.description: Средняя скорость работы команды при текущем составе сотрудников
|
||||||
§ page.team.total.moneySpeed.title: в месяц
|
§ page.team.total.moneySpeed.title: в месяц
|
||||||
§ page.team.total.moneySpeed.description: Прогнозируемая сумма выплаты на зп при текущем составе сотрудников без учета налогов и сопутствующих затрат
|
§ page.team.total.moneySpeed.description: Прогнозируемая сумма выплаты на зп при текущем составе сотрудников без учета налогов и сопутствующих затрат
|
||||||
§ page.team.total.titleA: Объём работ
|
§ page.team.total.description1: *Человеко-дни* — это работа одного сотрудника в течение одного рабочего дня. Например, за один календарный день, команда из трех сотрудников выдает объем работы в три человеко-дня.
|
||||||
§ page.team.total.titleB: Стоимость
|
§ page.team.total.description2: *Днями прогулов* считаются только рабочие дни, когда коммиты могли бы быть сделаны. Выходные, государственные праздники и отпуска в расчёте не участвуют.
|
||||||
§ page.team.tree.filters1: Пользователь
|
§ page.team.total.description3: Карточка *работает и уволилось* показывает фактический состав сотрудников, которые постоянно участвуют в работе. Кроме этого, есть «помощники» — это сотрудники, как правило другой специализации, которые могут иногда делать коммиты в проект.
|
||||||
§ page.team.tree.filters2: и более
|
§ page.team.total.description4: *Переплатой* считаются только рабочие дни, когда коммиты могли бы быть сделаны. Выходные, государственные праздники и отпуска в расчёте не участвуют. Именно поэтому переплата + фактическая стоимость != общей. В общей стоимости заложена оплата выходных, государственных праздников и отпусков.
|
||||||
§ page.team.tree.filters3: коммитов в файле или папке
|
§ page.team.total.description5: *Работой на выходных* считается по коэфициенту х2 от оплаты обычного дня. Выше отображена именно переплата (х1), т.к. сам факт переработки в данном контексте не интересен. Мы не смотрим скорость сжигания бюджета. Мы смотрим переплату при увеличении скорости работы.
|
||||||
§ page.team.tree.percent: Процент перезаписи
|
§ page.team.tree.title: Дерево проекта с учётом выбранных фильтров
|
||||||
|
§ page.team.tree.filters.author: Сотрудник
|
||||||
|
§ page.team.tree.filters.commits: Количество коммитов
|
||||||
|
§ page.team.tree.filters.help: Минимальное количество коммитов, которое сделал сотрудник в файле
|
||||||
|
§ page.team.tree.filters.all: Все сотрудники
|
||||||
§ page.team.tree.add: Кто добавлял
|
§ page.team.tree.add: Кто добавлял
|
||||||
§ page.team.tree.change: Кто менял
|
§ page.team.tree.change: Кто менял
|
||||||
§ page.team.tree.remove: Кто удалял
|
§ page.team.tree.remove: Кто удалял
|
||||||
|
|
|
@ -8,7 +8,6 @@ import SplashScreen from 'ts/components/SplashScreen';
|
||||||
import Confirm from 'ts/components/ModalWindow/Confirm';
|
import Confirm from 'ts/components/ModalWindow/Confirm';
|
||||||
|
|
||||||
import PageWrapper from '../../PageWrapper';
|
import PageWrapper from '../../PageWrapper';
|
||||||
// import Main from '../../Main/index';
|
|
||||||
import Team from '../../Team/index';
|
import Team from '../../Team/index';
|
||||||
import Person from '../../Person/index';
|
import Person from '../../Person/index';
|
||||||
import Welcome from '../../Welcome/index';
|
import Welcome from '../../Welcome/index';
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
|
|
||||||
import UiKitButton from 'ts/components/UiKit/components/Button';
|
|
||||||
import localization from 'ts/helpers/Localization';
|
|
||||||
|
|
||||||
import style from '../styles/card.module.scss';
|
|
||||||
|
|
||||||
interface ICardProps {
|
|
||||||
icon: string;
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
link: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Card({
|
|
||||||
icon,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
link,
|
|
||||||
}: ICardProps): React.ReactElement | null {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<figure className={style.card}>
|
|
||||||
<h4 className={style.card_title}>
|
|
||||||
{localization.get(title)}
|
|
||||||
</h4>
|
|
||||||
<img
|
|
||||||
className={style.card_icon}
|
|
||||||
src={icon}
|
|
||||||
/>
|
|
||||||
<figcaption className={style.card_description}>
|
|
||||||
{localization.get(description)}
|
|
||||||
</figcaption>
|
|
||||||
<UiKitButton
|
|
||||||
className={style.card_button}
|
|
||||||
onClick={() => {
|
|
||||||
navigate(link);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Перейти в отчёт
|
|
||||||
</UiKitButton>
|
|
||||||
</figure>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Card;
|
|
|
@ -1,31 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import Card from './components/Card';
|
|
||||||
|
|
||||||
import style from './styles/index.module.scss';
|
|
||||||
|
|
||||||
function Main() {
|
|
||||||
return (
|
|
||||||
<section className={style.main}>
|
|
||||||
<h2 className={style.main_title}>
|
|
||||||
Выберите раздел аналити
|
|
||||||
</h2>
|
|
||||||
<div className={style.main_cards}>
|
|
||||||
<Card
|
|
||||||
icon="./assets/cards/money_lazy.png"
|
|
||||||
title="Команда"
|
|
||||||
description="Собраны метрики работы команды в целом, сумарные финансовые показатели, рекомендации для менеджера проекта."
|
|
||||||
link="/team/total"
|
|
||||||
/>
|
|
||||||
<Card
|
|
||||||
icon="./assets/cards/money_lazy.png"
|
|
||||||
title="Сотрудник"
|
|
||||||
description="Данные по каждому сотруднику отдельно. Личные достижения, характеристики, показатели работоспособности."
|
|
||||||
link="/person/total/0"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Main;
|
|
|
@ -1,86 +0,0 @@
|
||||||
@import '../../../../styles/variables';
|
|
||||||
|
|
||||||
.card {
|
|
||||||
display: inline-block;
|
|
||||||
width: 300px;
|
|
||||||
min-height: 270px;
|
|
||||||
margin: 0 24px 24px 0;
|
|
||||||
padding: 16px;
|
|
||||||
vertical-align: top;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
background-color: #FFFFFF;
|
|
||||||
|
|
||||||
&_icon {
|
|
||||||
display: block;
|
|
||||||
width: auto;
|
|
||||||
height: 90px;
|
|
||||||
margin: 16px auto;
|
|
||||||
box-sizing: border-box;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
&_title,
|
|
||||||
&_description {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0;
|
|
||||||
line-height: 1.3;
|
|
||||||
text-align: center;
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--color-black);
|
|
||||||
}
|
|
||||||
|
|
||||||
&_title {
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: var(--color-11);
|
|
||||||
}
|
|
||||||
|
|
||||||
&_description {
|
|
||||||
font-size: var(--font-xs);
|
|
||||||
font-weight: 100;
|
|
||||||
line-height: 16px;
|
|
||||||
color: var(--color-grey);
|
|
||||||
}
|
|
||||||
|
|
||||||
&_button {
|
|
||||||
padding: 0 16px;
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
|
||||||
.card {
|
|
||||||
min-height: 220px;
|
|
||||||
padding: 16px 0;
|
|
||||||
|
|
||||||
&_title {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&_description {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 650px) {
|
|
||||||
.card {
|
|
||||||
min-height: auto;
|
|
||||||
padding: 32px 0;
|
|
||||||
|
|
||||||
&_value {
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&_title {
|
|
||||||
font-size: var(--font-s);
|
|
||||||
}
|
|
||||||
|
|
||||||
&_icon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
@import '../../../../styles/variables';
|
|
||||||
|
|
||||||
.main {
|
|
||||||
display: grid;
|
|
||||||
grid-template-areas: 'header' 'main';
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
grid-template-rows: 66px 1fr;
|
|
||||||
min-height: 100vh;
|
|
||||||
background-color: #F5F7F9;
|
|
||||||
|
|
||||||
&_title {
|
|
||||||
grid-area: header;
|
|
||||||
font-size: var(--font-l);
|
|
||||||
line-height: var(--font-l);
|
|
||||||
|
|
||||||
font-weight: 100;
|
|
||||||
display: block;
|
|
||||||
padding: 24px;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
|
||||||
text-align: center;
|
|
||||||
color: #84858D;
|
|
||||||
background-color: #252735;
|
|
||||||
}
|
|
||||||
|
|
||||||
&_cards {
|
|
||||||
grid-area: main;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
min-height: calc(100vh - 60px);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 800px) {
|
|
||||||
}
|
|
|
@ -162,7 +162,7 @@ const Author = observer(({
|
||||||
{mode !== 'print' && (
|
{mode !== 'print' && (
|
||||||
<RecommendationsWrapper recommendations={recommendations} />
|
<RecommendationsWrapper recommendations={recommendations} />
|
||||||
)}
|
)}
|
||||||
<Title title="Статистика по сотрудникам"/>
|
<Title title="page.team.author.title"/>
|
||||||
<PageWrapper template="table">
|
<PageWrapper template="table">
|
||||||
<DataLoader
|
<DataLoader
|
||||||
to="response"
|
to="response"
|
||||||
|
@ -177,12 +177,12 @@ const Author = observer(({
|
||||||
<PageWrapper>
|
<PageWrapper>
|
||||||
<PageColumn>
|
<PageColumn>
|
||||||
<Description
|
<Description
|
||||||
text="*Часть статитики* (скорость работы, затраченные деньги и т.п.) *по сотрудникам с типом «Помошник» не считается*, т.к. это эпизодическая роль в проекте. Предпологаем, что они не влияют на проект, а их правками можно пренебречь на фоне общего объема работы."
|
text={localization.get('page.team.author.description1')}
|
||||||
/>
|
/>
|
||||||
</PageColumn>
|
</PageColumn>
|
||||||
<PageColumn>
|
<PageColumn>
|
||||||
<Description
|
<Description
|
||||||
text="*Сортировка по умолчанию* — это сортировка по количеству задач и группам (текущие, уволенные, помогающие сотрудники)."
|
text={localization.get('page.team.author.description2')}
|
||||||
/>
|
/>
|
||||||
</PageColumn>
|
</PageColumn>
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
|
|
|
@ -16,7 +16,7 @@ const Hours = observer((): React.ReactElement => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RecommendationsWrapper recommendations={recommendations} />
|
<RecommendationsWrapper recommendations={recommendations} />
|
||||||
<Title title="Распределение коммитов в течении каждого дня недели"/>
|
<Title title="page.team.hours.title"/>
|
||||||
<PageWrapper template="table">
|
<PageWrapper template="table">
|
||||||
<HoursChart statistic={statistic} />
|
<HoursChart statistic={statistic} />
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { observer } from 'mobx-react-lite';
|
||||||
|
|
||||||
import localization from 'ts/helpers/Localization';
|
|
||||||
import dataGripStore from 'ts/store/DataGrip';
|
import dataGripStore from 'ts/store/DataGrip';
|
||||||
|
|
||||||
import RecommendationsWrapper from 'ts/components/Recommendations/wrapper';
|
import RecommendationsWrapper from 'ts/components/Recommendations/wrapper';
|
||||||
|
@ -24,7 +23,7 @@ const Month = observer(({
|
||||||
{mode !== 'print' && (
|
{mode !== 'print' && (
|
||||||
<RecommendationsWrapper recommendations={recommendations}/>
|
<RecommendationsWrapper recommendations={recommendations}/>
|
||||||
)}
|
)}
|
||||||
<Title title={localization.get('Календарь работы по проекту')}/>
|
<Title title="page.team.month.title"/>
|
||||||
<PageWrapper template="table">
|
<PageWrapper template="table">
|
||||||
<YearChart
|
<YearChart
|
||||||
maxCommits={max}
|
maxCommits={max}
|
||||||
|
|
|
@ -116,7 +116,7 @@ const Scope = observer(({
|
||||||
{mode !== 'print' && (
|
{mode !== 'print' && (
|
||||||
<RecommendationsWrapper recommendations={recommendations} />
|
<RecommendationsWrapper recommendations={recommendations} />
|
||||||
)}
|
)}
|
||||||
<Title title="Статистика по фичам"/>
|
<Title title="page.team.scope.title"/>
|
||||||
<PageWrapper template="table">
|
<PageWrapper template="table">
|
||||||
<DataLoader
|
<DataLoader
|
||||||
to="response"
|
to="response"
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { observer } from 'mobx-react-lite';
|
||||||
import { IPagination } from 'ts/interfaces/Pagination';
|
import { IPagination } from 'ts/interfaces/Pagination';
|
||||||
import dataGripStore from 'ts/store/DataGrip';
|
import dataGripStore from 'ts/store/DataGrip';
|
||||||
import { getShortDateRange } from 'ts/helpers/formatter';
|
import { getShortDateRange } from 'ts/helpers/formatter';
|
||||||
import localization from 'ts/helpers/Localization';
|
|
||||||
|
|
||||||
import UiKitButton from 'ts/components/UiKit/components/Button';
|
import UiKitButton from 'ts/components/UiKit/components/Button';
|
||||||
import UiKitSelect from 'ts/components/UiKit/components/Select';
|
import UiKitSelect from 'ts/components/UiKit/components/Select';
|
||||||
|
@ -60,7 +59,7 @@ const Tempo = observer((): React.ReactElement => {
|
||||||
if (!partOfData?.length) return (<NothingFound />);
|
if (!partOfData?.length) return (<NothingFound />);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title title={localization.get('common.filters')} />
|
<Title title="common.filters" />
|
||||||
<PageWrapper>
|
<PageWrapper>
|
||||||
<div className={style.tempo_page_filters}>
|
<div className={style.tempo_page_filters}>
|
||||||
<UiKitButton
|
<UiKitButton
|
||||||
|
|
|
@ -26,7 +26,7 @@ const Total = observer((): React.ReactElement => {
|
||||||
return (
|
return (
|
||||||
<PageWrapper>
|
<PageWrapper>
|
||||||
<PageColumn>
|
<PageColumn>
|
||||||
<Title title={localization.get('page.team.total.titleA')}/>
|
<Title title="page.team.total.titleA"/>
|
||||||
<div>
|
<div>
|
||||||
<CardWithIcon
|
<CardWithIcon
|
||||||
value={statistic.daysWorked}
|
value={statistic.daysWorked}
|
||||||
|
@ -61,17 +61,17 @@ const Total = observer((): React.ReactElement => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Description
|
<Description
|
||||||
text="*Человеко-дни* — это работа одного сотрудника в течение одного рабочего дня. Например, за один календарный день, команда из трех сотрудников выдает объем работы в три человеко-дня."
|
text={localization.get('page.team.total.description1')}
|
||||||
/>
|
/>
|
||||||
<Description
|
<Description
|
||||||
text="*Днями прогулов* считаются только рабочие дни, когда коммиты могли бы быть сделаны. Выходные, государственные праздники и отпуска в расчёте не участвуют."
|
text={localization.get('page.team.total.description2')}
|
||||||
/>
|
/>
|
||||||
<Description
|
<Description
|
||||||
text="Карточка *работает и уволилось* показывает фактический состав сотрудников, которые постоянно участвуют в работе. Кроме этого, есть «помощники» — это сотрудники, как правило другой специализации, которые могут иногда делать коммиты в проект."
|
text={localization.get('page.team.total.description3')}
|
||||||
/>
|
/>
|
||||||
</PageColumn>
|
</PageColumn>
|
||||||
<PageColumn>
|
<PageColumn>
|
||||||
<Title title={localization.get('page.team.total.titleB')}/>
|
<Title title="page.team.total.titleB"/>
|
||||||
<div>
|
<div>
|
||||||
<CardWithIcon
|
<CardWithIcon
|
||||||
value={getShortMoney(statistic.moneyAll)}
|
value={getShortMoney(statistic.moneyAll)}
|
||||||
|
@ -106,10 +106,10 @@ const Total = observer((): React.ReactElement => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Description
|
<Description
|
||||||
text="*Переплатой* считаются только рабочие дни, когда коммиты могли бы быть сделаны. Выходные, государственные праздники и отпуска в расчёте не участвуют. Именно поэтому переплата + фактическая стоимость != общей. В общей стоимости заложена оплата выходных, государственных праздников и отпусков."
|
text={localization.get('page.team.total.description4')}
|
||||||
/>
|
/>
|
||||||
<Description
|
<Description
|
||||||
text="*Работой на выходных* считается по коэфициенту х2 от оплаты обычного дня. Выше отображена именно переплата (х1), т.к. сам факт переработки в данном контексте не интересен. Мы не смотрим скорость сжигания бюджета. Мы смотрим переплату при увеличении скорости работы."
|
text={localization.get('page.team.total.description5')}
|
||||||
/>
|
/>
|
||||||
</PageColumn>
|
</PageColumn>
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
|
|
|
@ -37,7 +37,10 @@ function TreeView({ response }: ITreeViewProps) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fileChart = getOptions({ order: dataGripStore.dataGrip.author.list, suffix: 'строк' });
|
const fileChart = getOptions({ order: dataGripStore.dataGrip.author.list, suffix: 'строк' });
|
||||||
const rewriteChart = getOptions({ order: ['добавили', 'изменили'], suffix: 'строк' });
|
const rewriteChart = getOptions({ order: [
|
||||||
|
'page.team.tree.lineAdd',
|
||||||
|
'page.team.tree.lineRemove',
|
||||||
|
], suffix: 'page.team.tree.line' });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
|
@ -69,14 +72,14 @@ function TreeView({ response }: ITreeViewProps) {
|
||||||
value={file ? 100 : 0}
|
value={file ? 100 : 0}
|
||||||
options={rewriteChart}
|
options={rewriteChart}
|
||||||
details={{
|
details={{
|
||||||
'добавили': file?.lines || 0,
|
'page.team.tree.lineAdd': file?.lines || 0,
|
||||||
'изменили': (file?.total?.changes || 0) + (file?.total?.removed || 0),
|
'page.team.tree.lineRemove': (file?.total?.changes || 0) + (file?.total?.removed || 0),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
title="Кто добавлял"
|
title="page.team.tree.add"
|
||||||
properties="file"
|
properties="file"
|
||||||
minWidth={200}
|
minWidth={200}
|
||||||
template={(file: any) => (
|
template={(file: any) => (
|
||||||
|
@ -88,7 +91,7 @@ function TreeView({ response }: ITreeViewProps) {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
title="Кто менял"
|
title="page.team.tree.change"
|
||||||
properties="file"
|
properties="file"
|
||||||
minWidth={200}
|
minWidth={200}
|
||||||
template={(file: any) => (
|
template={(file: any) => (
|
||||||
|
@ -100,7 +103,7 @@ function TreeView({ response }: ITreeViewProps) {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
title="Кто удалял"
|
title="page.team.tree.remove"
|
||||||
properties="file"
|
properties="file"
|
||||||
minWidth={200}
|
minWidth={200}
|
||||||
template={(file: any) => (
|
template={(file: any) => (
|
||||||
|
@ -128,7 +131,7 @@ const Tree = observer((): React.ReactElement => {
|
||||||
<>
|
<>
|
||||||
<Title title={localization.get('common.filters')} />
|
<Title title={localization.get('common.filters')} />
|
||||||
<TreeFilters/>
|
<TreeFilters/>
|
||||||
<Title title="Дерево проекта с учётом выбранных фильтров"/>
|
<Title title="page.team.tree.title"/>
|
||||||
<PageWrapper template="table">
|
<PageWrapper template="table">
|
||||||
<DataLoader
|
<DataLoader
|
||||||
to="response"
|
to="response"
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { observer } from 'mobx-react-lite';
|
||||||
import dataGripStore from 'ts/store/DataGrip';
|
import dataGripStore from 'ts/store/DataGrip';
|
||||||
import UiKitSelect from 'ts/components/UiKit/components/SelectWithButtons';
|
import UiKitSelect from 'ts/components/UiKit/components/SelectWithButtons';
|
||||||
import UiKitInputNumber from 'ts/components/UiKit/components/InputNumber';
|
import UiKitInputNumber from 'ts/components/UiKit/components/InputNumber';
|
||||||
|
import localization from 'ts/helpers/Localization';
|
||||||
|
|
||||||
import treeStore from '../store/Tree';
|
import treeStore from '../store/Tree';
|
||||||
import style from '../styles/filters.module.scss';
|
import style from '../styles/filters.module.scss';
|
||||||
|
@ -11,12 +12,12 @@ import style from '../styles/filters.module.scss';
|
||||||
const TreeFilters = observer((): React.ReactElement => {
|
const TreeFilters = observer((): React.ReactElement => {
|
||||||
const authors = dataGripStore.dataGrip.author.list;
|
const authors = dataGripStore.dataGrip.author.list;
|
||||||
const options = authors.map((title: string, id: number) => ({ id: id + 1, title }));
|
const options = authors.map((title: string, id: number) => ({ id: id + 1, title }));
|
||||||
options.unshift({ id: 0, title: 'Все сотрудники' });
|
options.unshift({ id: 0, title: localization.get('page.team.tree.filters.all') });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<UiKitSelect
|
<UiKitSelect
|
||||||
title="Сотрудник"
|
title="page.team.tree.filters.author"
|
||||||
value={treeStore.authorId}
|
value={treeStore.authorId}
|
||||||
options={options}
|
options={options}
|
||||||
className={style.filter}
|
className={style.filter}
|
||||||
|
@ -25,8 +26,8 @@ const TreeFilters = observer((): React.ReactElement => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<UiKitInputNumber
|
<UiKitInputNumber
|
||||||
title="Количество коммитов"
|
title="page.team.tree.filters.commits"
|
||||||
help="Минимальное количество коммитов, которое сделал сотрудник в файле"
|
help="page.team.tree.filters.help"
|
||||||
value={treeStore.minCommits}
|
value={treeStore.minCommits}
|
||||||
className={style.filter}
|
className={style.filter}
|
||||||
onChange={(minCommits: number) => {
|
onChange={(minCommits: number) => {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import RecommendationsWrapper from 'ts/components/Recommendations/wrapper';
|
||||||
import Description from 'ts/components/Description';
|
import Description from 'ts/components/Description';
|
||||||
|
|
||||||
import { getMax } from 'ts/pages/Common/helpers/getMax';
|
import { getMax } from 'ts/pages/Common/helpers/getMax';
|
||||||
|
import localization from 'ts/helpers/Localization';
|
||||||
|
|
||||||
interface ITypeViewProps {
|
interface ITypeViewProps {
|
||||||
response?: IPagination<any>;
|
response?: IPagination<any>;
|
||||||
|
@ -31,8 +32,8 @@ interface ITypeViewProps {
|
||||||
function TypeView({ response, updateSort }: ITypeViewProps) {
|
function TypeView({ response, updateSort }: ITypeViewProps) {
|
||||||
if (!response) return null;
|
if (!response) return null;
|
||||||
|
|
||||||
const taskChart = getOptions({ max: getMax(response, 'tasks'), suffix: 'задач' });
|
const taskChart = getOptions({ max: getMax(response, 'tasks'), suffix: 'page.team.type.tasksSmall' });
|
||||||
const daysByAuthorsChart = getOptions({ max: getMax(response, 'daysByAuthorsTotal'), suffix: 'дней' });
|
const daysByAuthorsChart = getOptions({ max: getMax(response, 'daysByAuthorsTotal'), suffix: 'page.team.type.daysSmall' });
|
||||||
const authorChart = getOptions({ order: dataGripStore.dataGrip.author.list });
|
const authorChart = getOptions({ order: dataGripStore.dataGrip.author.list });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -121,7 +122,7 @@ const Type = observer(({
|
||||||
{mode !== 'print' && (
|
{mode !== 'print' && (
|
||||||
<RecommendationsWrapper recommendations={recommendations} />
|
<RecommendationsWrapper recommendations={recommendations} />
|
||||||
)}
|
)}
|
||||||
<Title title="Статистика по типам задач"/>
|
<Title title="page.team.type.title"/>
|
||||||
<PageWrapper template="table">
|
<PageWrapper template="table">
|
||||||
<DataLoader
|
<DataLoader
|
||||||
to="response"
|
to="response"
|
||||||
|
@ -135,7 +136,7 @@ const Type = observer(({
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
<PageWrapper>
|
<PageWrapper>
|
||||||
<Description
|
<Description
|
||||||
text="*Персональный вклад* считается по количеству коммитов, а не объему измененных строк или файлов. Поэтому следует так же смотреть раздел «Анализ файлов», чтобы оценить масштаб изменений."
|
text={localization.get('page.team.type.description')}
|
||||||
/>
|
/>
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -2,6 +2,8 @@ import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import Console from 'ts/components/Console';
|
import Console from 'ts/components/Console';
|
||||||
|
import localization from 'ts/helpers/Localization';
|
||||||
|
|
||||||
import style from './styles/index.module.scss';
|
import style from './styles/index.module.scss';
|
||||||
|
|
||||||
function WarningInfo() {
|
function WarningInfo() {
|
||||||
|
@ -33,34 +35,32 @@ function WarningInfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function Welcome() {
|
function Welcome() {
|
||||||
|
const canShowWarning = true;
|
||||||
const command = 'git --no-pager log --numstat --oneline --all --reverse --date=iso-strict --pretty=format:"%ad>%cN>%cE>%s" > log.txt\n';
|
const command = 'git --no-pager log --numstat --oneline --all --reverse --date=iso-strict --pretty=format:"%ad>%cN>%cE>%s" > log.txt\n';
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{true && (<WarningInfo />)}
|
{canShowWarning && (<WarningInfo />)}
|
||||||
<section className={style.welcome}>
|
<section className={style.welcome}>
|
||||||
<div className={style.welcome_row}>
|
<div className={style.welcome_row}>
|
||||||
<h2 className={style.welcome_first_title}>
|
<h2 className={style.welcome_first_title}>
|
||||||
Выполните команду в корне вашего проекта
|
{localization.get('page.welcome.step1')}
|
||||||
</h2>
|
</h2>
|
||||||
<Console
|
<Console
|
||||||
className={style.welcome_console}
|
className={style.welcome_console}
|
||||||
textForCopy={command}
|
textForCopy={command}
|
||||||
/>
|
/>
|
||||||
<p className={style.welcome_description}>
|
<p className={style.welcome_description}>
|
||||||
Git создаст файл log.txt.
|
{localization.get('page.welcome.description1')}
|
||||||
Он содержит данные для построения отчёта.
|
|
||||||
Или git shortlog -s -n -e если отчёт вам не нужен.
|
|
||||||
Создайте файл
|
|
||||||
<Link
|
<Link
|
||||||
className={`${style.welcome_link}`}
|
className={`${style.welcome_link}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
to="https://git-scm.com/docs/gitmailmap">
|
to="https://git-scm.com/docs/gitmailmap">
|
||||||
.mailmap
|
.mailmap
|
||||||
</Link>
|
</Link>
|
||||||
{' в корне проекта, чтобы обьединить статистику по сотрудникам.'}
|
{localization.get('page.welcome.description2')}
|
||||||
</p>
|
</p>
|
||||||
<h2 className={style.welcome_last_title}>
|
<h2 className={style.welcome_last_title}>
|
||||||
Перетащите файл log.txt на эту страницу
|
{localization.get('page.welcome.step2')}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -84,7 +84,7 @@
|
||||||
|
|
||||||
&_link {
|
&_link {
|
||||||
display: inline;
|
display: inline;
|
||||||
margin: 16px 0 0 4px;
|
margin: 16px 4px 0 4px;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue