mirror of
https://github.com/bakhirev/assayo.git
synced 2024-11-16 08:11:40 +00:00
update
This commit is contained in:
parent
81cb8a1b40
commit
320e4b0485
156562
public/test.txt
156562
public/test.txt
File diff suppressed because it is too large
Load diff
|
@ -39,7 +39,12 @@ function CommitInfo({ commits }: { commits: ICommit[] }): React.ReactElement {
|
||||||
function TaskInfo({ tasks }: { tasks: ITask }): React.ReactElement {
|
function TaskInfo({ tasks }: { tasks: ITask }): React.ReactElement {
|
||||||
const items = Object.entries(tasks)
|
const items = Object.entries(tasks)
|
||||||
.map(([task, commits]: [string, any]) => {
|
.map(([task, commits]: [string, any]) => {
|
||||||
const prId = dataGrip.pr.prByTask.get(task);
|
const taskInfo = dataGrip.tasks.statisticByName.get(task);
|
||||||
|
const milliseconds = commits[0].milliseconds;
|
||||||
|
const prId = taskInfo?.prIds?.find((id: string) => {
|
||||||
|
const pr = dataGrip.pr.pr.get(id);
|
||||||
|
return pr.dateMerge >= milliseconds;
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={style.day_info_link}>
|
<div className={style.day_info_link}>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
border-radius: var(--border-radius-s);
|
border-radius: var(--border-radius-s);
|
||||||
background-color: var(--color-white);
|
background-color: var(--color-white);
|
||||||
|
|
||||||
animation: notification_item 3s linear 0.5s forwards;
|
animation: notification_item 0.5s linear 3s forwards;
|
||||||
|
|
||||||
&_icon {
|
&_icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
color: var(--color-white);
|
color: var(--color-white);
|
||||||
animation: notification_title 3s linear 0.5s forwards;
|
animation: notification_title 0.5s linear 3s forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
&_title {
|
&_title {
|
||||||
|
@ -72,12 +72,10 @@
|
||||||
@keyframes notification_item {
|
@keyframes notification_item {
|
||||||
80% {
|
80% {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: auto;
|
|
||||||
padding: var(--space-xxl) var(--space-xxl) var(--space-xxl) 56px;
|
padding: var(--space-xxl) var(--space-xxl) var(--space-xxl) 56px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
height: 0;
|
|
||||||
padding: 0 var(--space-xxl) 0 64px;
|
padding: 0 var(--space-xxl) 0 64px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,16 +19,17 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
vertical-align: middle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&_icon {
|
&_icon {
|
||||||
width: 160px;
|
width: 160px;
|
||||||
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
&_legend {
|
&_legend {
|
||||||
min-width: 160px;
|
min-width: 160px;
|
||||||
padding-left: var(--space-xxl);
|
padding-left: var(--space-xxl);
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
&_line {
|
&_line {
|
||||||
|
@ -55,6 +56,7 @@
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
vertical-align: top;
|
||||||
color: var(--color-black);
|
color: var(--color-black);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,10 +31,11 @@ function DetailsCell({
|
||||||
|
|
||||||
const localClassName = getClassName(style.table_cell, column, ['body', row], className);
|
const localClassName = getClassName(style.table_cell, column, ['body', row], className);
|
||||||
|
|
||||||
const hasIcon = ((column.properties && row[column.properties])
|
const value = row?.[column?.properties || ''];
|
||||||
|| !column.properties
|
const notNull = (Array.isArray(value) || value instanceof Set) // @ts-ignore
|
||||||
|| !column.properties?.length)
|
? (value?.length || value?.size)
|
||||||
&& column.formatter;
|
: !!value;
|
||||||
|
const hasIcon = notNull || !column?.properties;
|
||||||
|
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
if (!hasIcon || !updateRowsConfig) return;
|
if (!hasIcon || !updateRowsConfig) return;
|
||||||
|
|
|
@ -33,5 +33,5 @@ export default function getAdaptiveColumnWidth(
|
||||||
adaptiveColumnsWidth = adaptiveTableWidth / adaptiveColumnsCount;
|
adaptiveColumnsWidth = adaptiveTableWidth / adaptiveColumnsCount;
|
||||||
});
|
});
|
||||||
|
|
||||||
return Math.max(adaptiveColumnsWidth, 40);
|
return Math.max(adaptiveColumnsWidth || 40, 40);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,14 @@ export default function getDefaultProps(children: React.ReactNode) {
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const defaultWidth = child?.props?.width || {
|
const defaultWidth = child?.props?.width || {
|
||||||
[ColumnTypesEnum.STRING]: 200,
|
|
||||||
[ColumnTypesEnum.NUMBER]: 110,
|
|
||||||
[ColumnTypesEnum.SHORT_NUMBER]: 70,
|
[ColumnTypesEnum.SHORT_NUMBER]: 70,
|
||||||
}[template || ''] || 0;
|
}[template || ''] || 0;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const minWidth = child?.props?.minWidth || 40;
|
const minWidth = child?.props?.minWidth || {
|
||||||
|
[ColumnTypesEnum.STRING]: 200,
|
||||||
|
[ColumnTypesEnum.NUMBER]: 110,
|
||||||
|
}[template || ''] || 40;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const isSortable = child?.props?.isSortable // @ts-ignore
|
const isSortable = child?.props?.isSortable // @ts-ignore
|
||||||
|
@ -42,4 +43,4 @@ export default function getDefaultProps(children: React.ReactNode) {
|
||||||
userWidth: undefined,
|
userWidth: undefined,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ function getFormattedDate(commits: ICommit[]) {
|
||||||
function getTags(commits: ICommit[]) {
|
function getTags(commits: ICommit[]) {
|
||||||
const uniqueTypes = new Set(commits.map((commit: ICommit) => commit.type));
|
const uniqueTypes = new Set(commits.map((commit: ICommit) => commit.type));
|
||||||
const tags = Array.from(uniqueTypes)
|
const tags = Array.from(uniqueTypes)
|
||||||
.filter((title: string) => title && title !== '—')
|
.filter((title: string) => title)
|
||||||
.map((title: string) => (
|
.map((title: string) => (
|
||||||
<p
|
<p
|
||||||
key={title}
|
key={title}
|
||||||
|
@ -43,7 +43,13 @@ interface ITaskProps {
|
||||||
|
|
||||||
function Task({ title, commits }: ITaskProps) {
|
function Task({ title, commits }: ITaskProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const prId = dataGrip.pr.prByTask.get(title);
|
const task = dataGrip.tasks.statisticByName.get(title);
|
||||||
|
const milliseconds = commits[0].milliseconds;
|
||||||
|
const prId = task?.prIds?.find((id: string) => {
|
||||||
|
const pr = dataGrip.pr.pr.get(id);
|
||||||
|
return pr.dateMerge >= milliseconds;
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={title}
|
key={title}
|
||||||
|
|
|
@ -27,5 +27,5 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui_kit_tags_item + .ui_kit_tags_item {
|
.ui_kit_tags_item + .ui_kit_tags_item {
|
||||||
margin-right: var(--space-xs);
|
margin-left: var(--space-xs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,9 @@ import ICommit from 'ts/interfaces/Commit';
|
||||||
import IHashMap, { HashMap } from 'ts/interfaces/HashMap';
|
import IHashMap, { HashMap } from 'ts/interfaces/HashMap';
|
||||||
|
|
||||||
import { ONE_DAY } from 'ts/helpers/formatter';
|
import { ONE_DAY } from 'ts/helpers/formatter';
|
||||||
|
import getCountryByTimeZone from 'ts/helpers/Parser/getCountryByTimeZone';
|
||||||
|
import getCountryBySymbol from 'ts/helpers/Parser/getCountryBySymbol';
|
||||||
import { createHashMap, createIncrement, increment } from 'ts/helpers/Math';
|
import { createHashMap, createIncrement, increment } from 'ts/helpers/Math';
|
||||||
import getCompany from 'ts/helpers/Parser/getCompany';
|
|
||||||
|
|
||||||
import userSettings from 'ts/store/UserSettings';
|
import userSettings from 'ts/store/UserSettings';
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ export default class DataGripByAuthor {
|
||||||
#updateCommitByAuthor(statistic: any, commit: ICommit) {
|
#updateCommitByAuthor(statistic: any, commit: ICommit) {
|
||||||
statistic.commits += 1;
|
statistic.commits += 1;
|
||||||
statistic.lastCommit = commit;
|
statistic.lastCommit = commit;
|
||||||
|
statistic.device = statistic.device || commit.device;
|
||||||
statistic.days[commit.timestamp] = true;
|
statistic.days[commit.timestamp] = true;
|
||||||
statistic.tasks[commit.task] = commit.added + commit.changes + commit.removed
|
statistic.tasks[commit.task] = commit.added + commit.changes + commit.removed
|
||||||
+ (statistic.tasks[commit.task] ? statistic.tasks[commit.task] : 0);
|
+ (statistic.tasks[commit.task] ? statistic.tasks[commit.task] : 0);
|
||||||
|
@ -61,6 +63,10 @@ export default class DataGripByAuthor {
|
||||||
statistic.lastCompany = commit.company;
|
statistic.lastCompany = commit.company;
|
||||||
statistic.company.push({ title: commit.company, from: commit.timestamp });
|
statistic.company.push({ title: commit.company, from: commit.timestamp });
|
||||||
}
|
}
|
||||||
|
if (commit.country && statistic.lastCountry !== commit.country) {
|
||||||
|
statistic.lastCountry = commit.country;
|
||||||
|
statistic.country.add(commit.country);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#addCommitByAuthor(commit: ICommit) {
|
#addCommitByAuthor(commit: ICommit) {
|
||||||
|
@ -70,6 +76,11 @@ export default class DataGripByAuthor {
|
||||||
const commitsByHour = new Array(24).fill(0);
|
const commitsByHour = new Array(24).fill(0);
|
||||||
commitsByHour[commit.hours] += 1;
|
commitsByHour[commit.hours] += 1;
|
||||||
|
|
||||||
|
const country = commit.country
|
||||||
|
|| getCountryBySymbol(commit.author)
|
||||||
|
|| getCountryBySymbol(commit.message)
|
||||||
|
|| getCountryByTimeZone(commit.timezone, commit.author);
|
||||||
|
|
||||||
this.commits.set(commit.author, {
|
this.commits.set(commit.author, {
|
||||||
author: commit.author,
|
author: commit.author,
|
||||||
commits: 1,
|
commits: 1,
|
||||||
|
@ -81,9 +92,12 @@ export default class DataGripByAuthor {
|
||||||
scopes: createIncrement(commit.scope),
|
scopes: createIncrement(commit.scope),
|
||||||
hours: [commit.hours],
|
hours: [commit.hours],
|
||||||
company: commit.company
|
company: commit.company
|
||||||
? [{ title: commit.company, from: commit.timestamp }]
|
? [{ title: commit.company, from: commit.milliseconds }]
|
||||||
: [],
|
: [],
|
||||||
lastCompany: commit.company,
|
lastCompany: commit.company,
|
||||||
|
country: new Set([country]),
|
||||||
|
lastCountry: country,
|
||||||
|
device: commit.device,
|
||||||
commitsByDayAndHour,
|
commitsByDayAndHour,
|
||||||
commitsByHour,
|
commitsByHour,
|
||||||
messageLength: [commit.text.length || 0],
|
messageLength: [commit.text.length || 0],
|
||||||
|
@ -195,7 +209,6 @@ export default class DataGripByAuthor {
|
||||||
daysForTask: isStaff ? 0 : workDays / tasks.length,
|
daysForTask: isStaff ? 0 : workDays / tasks.length,
|
||||||
taskInDay: isStaff ? 0 : tasks.length / workDays,
|
taskInDay: isStaff ? 0 : tasks.length / workDays,
|
||||||
changesForTask: DataGripByAuthor.getMiddleValue(tasksSize),
|
changesForTask: DataGripByAuthor.getMiddleValue(tasksSize),
|
||||||
lastCompany: getCompany(dot.author, dot.lastCommit.email),
|
|
||||||
|
|
||||||
days: workDays,
|
days: workDays,
|
||||||
money: isStaff ? 0 : moneyWorked,
|
money: isStaff ? 0 : moneyWorked,
|
||||||
|
|
|
@ -1,87 +1,76 @@
|
||||||
import ICommit from 'ts/interfaces/Commit';
|
|
||||||
import IHashMap, { HashMap } from 'ts/interfaces/HashMap';
|
import IHashMap, { HashMap } from 'ts/interfaces/HashMap';
|
||||||
import { createIncrement, increment } from 'ts/helpers/Math';
|
import { ONE_DAY } from 'ts/helpers/formatter';
|
||||||
|
|
||||||
export default class DataGripByCompany {
|
export default class DataGripByCompany {
|
||||||
commits: HashMap<any> = new Map();
|
companies: HashMap<any> = new Map();
|
||||||
|
|
||||||
statistic: any = [];
|
statistic: any = [];
|
||||||
|
|
||||||
statisticByName: IHashMap<any> = {};
|
statisticByName: IHashMap<any> = {};
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.commits.clear();
|
this.companies.clear();
|
||||||
this.statistic = [];
|
this.statistic = [];
|
||||||
this.statisticByName = {};
|
this.statisticByName = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
addCommit(commit: ICommit) {
|
#addAuthor(statByAuthor: any) {
|
||||||
if (!commit.company) return;
|
const company = statByAuthor.company[statByAuthor.company.length - 1]?.title;
|
||||||
const statistic = this.commits.get(commit.company);
|
if (!company) return;
|
||||||
|
const statistic = this.companies.get(company);
|
||||||
if (statistic) {
|
if (statistic) {
|
||||||
this.#updateCommitByCompany(statistic, commit);
|
this.#updateCommitByCompany(statistic, statByAuthor);
|
||||||
} else {
|
} else {
|
||||||
this.#addCommitByCompany(commit);
|
this.#addCommitByCompany(company, statByAuthor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#addCommitByCompany(commit: ICommit) {
|
#addCommitByCompany(company: string, statByAuthor: any) {
|
||||||
this.commits.set(commit.company, {
|
this.companies.set(company, {
|
||||||
company: commit.company,
|
company,
|
||||||
commits: 1,
|
isActive: !statByAuthor.isDismissed && !statByAuthor.isStaff,
|
||||||
firstCommit: commit,
|
commits: statByAuthor.commits,
|
||||||
lastCommit: commit,
|
from: statByAuthor.firstCommit.milliseconds,
|
||||||
days: createIncrement(commit.timestamp),
|
to: statByAuthor.lastCommit.milliseconds,
|
||||||
employments: createIncrement(commit.author),
|
daysWorked: statByAuthor.daysWorked,
|
||||||
tasks: createIncrement(commit.task),
|
employments: [statByAuthor.author],
|
||||||
types: createIncrement(commit.type),
|
tasks: [statByAuthor.tasks],
|
||||||
scopes: createIncrement(commit.scope),
|
// types: statByAuthor.types,
|
||||||
|
// scopes: createIncrement(commit.scope),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateCommitByCompany(statistic: any, commit: ICommit) {
|
#updateCommitByCompany(statistic: any, statByAuthor: any) {
|
||||||
statistic.commits += 1;
|
if (!statistic.isActive) {
|
||||||
statistic.lastCommit = commit;
|
statistic.isActive = !statByAuthor.isDismissed && !statByAuthor.isStaff;
|
||||||
statistic.days[commit.timestamp] = true;
|
}
|
||||||
statistic.employments[commit.author] = true;
|
statistic.commits += statByAuthor.commits;
|
||||||
|
statistic.from = statistic.from > statByAuthor.firstCommit.milliseconds
|
||||||
increment(statistic.tasks, commit.task);
|
? statByAuthor.firstCommit.milliseconds
|
||||||
increment(statistic.types, commit.type);
|
: statistic.from;
|
||||||
increment(statistic.scopes, commit.scope);
|
statistic.to = statistic.to < statByAuthor.lastCommit.milliseconds
|
||||||
|
? statByAuthor.lastCommit.milliseconds
|
||||||
|
: statistic.to;
|
||||||
|
statistic.daysWorked += statByAuthor.daysWorked;
|
||||||
|
statistic.employments.push(statByAuthor.author);
|
||||||
|
statistic.tasks.push(statByAuthor.tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTotalInfo(dataGripByAuthor: any) {
|
updateTotalInfo(dataGripByAuthor: any) {
|
||||||
console.dir(dataGripByAuthor);
|
dataGripByAuthor.statistic.forEach((data: any) => {
|
||||||
this.statistic = Array.from(this.commits.values())
|
this.#addAuthor(data);
|
||||||
.sort((dotA: any, dotB: any) => dotB.commits - dotA.commits)
|
});
|
||||||
.map((statistic: any) => {
|
|
||||||
const tasks = Object.keys(statistic.tasks);
|
|
||||||
const days = Object.keys(statistic.days);
|
|
||||||
const employments = Object.keys(statistic.employments);
|
|
||||||
|
|
||||||
let isActive = false;
|
this.statistic = Array.from(this.companies.values())
|
||||||
employments.forEach((name) => {
|
.map((company: any) => {
|
||||||
const author = dataGripByAuthor.statisticByName[name];
|
company.tasks = Array.from(new Set(company.tasks.flat(1))).length;
|
||||||
if (!author) return;
|
company.totalDays = Math.max(((company.to - company.from) / ONE_DAY), 1);
|
||||||
if (author.lastCompany === statistic.company) isActive = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const companyInfo = {
|
this.statisticByName[company.company] = company;
|
||||||
...statistic,
|
|
||||||
employments,
|
|
||||||
tasks,
|
|
||||||
totalTasks: tasks.length,
|
|
||||||
totalDays: days.length,
|
|
||||||
totalEmployments: employments.length,
|
|
||||||
isActive,
|
|
||||||
};
|
|
||||||
delete companyInfo.days;
|
|
||||||
|
|
||||||
this.statisticByName[statistic.company] = companyInfo;
|
return company;
|
||||||
|
|
||||||
return companyInfo;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.commits.clear();
|
this.companies.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,105 +1,83 @@
|
||||||
import { COMMIT_TYPE, ISystemCommit } from 'ts/interfaces/Commit';
|
import { ISystemCommit } from 'ts/interfaces/Commit';
|
||||||
import IHashMap, { HashMap } from 'ts/interfaces/HashMap';
|
import IHashMap, { HashMap } from 'ts/interfaces/HashMap';
|
||||||
import { createIncrement, increment, WeightedAverage } from 'ts/helpers/Math';
|
import { WeightedAverage } from 'ts/helpers/Math';
|
||||||
|
import { ONE_DAY } from 'ts/helpers/formatter';
|
||||||
const IS_PR = {
|
|
||||||
[COMMIT_TYPE.PR_BITBUCKET]: true,
|
|
||||||
[COMMIT_TYPE.PR_GITHUB]: true,
|
|
||||||
[COMMIT_TYPE.PR_GITLAB]: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class DataGripByPR {
|
export default class DataGripByPR {
|
||||||
pr: HashMap<any> = new Map();
|
pr: HashMap<any> = new Map();
|
||||||
|
|
||||||
prByTask: HashMap<any> = new Map();
|
|
||||||
|
|
||||||
lastCommitByTaskNumber: HashMap<any> = new Map();
|
|
||||||
|
|
||||||
statistic: any[] = [];
|
statistic: any[] = [];
|
||||||
|
|
||||||
statisticByName: IHashMap<any> = [];
|
statisticByName: IHashMap<any> = {};
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.pr.clear();
|
this.pr.clear();
|
||||||
this.prByTask.clear();
|
|
||||||
this.lastCommitByTaskNumber.clear();
|
|
||||||
this.statistic = [];
|
this.statistic = [];
|
||||||
|
this.statisticByName = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
addCommit(commit: ISystemCommit) {
|
addCommit(commit: ISystemCommit) {
|
||||||
if (!commit.commitType) {
|
// commitType PR
|
||||||
const commitByTaskNumber = this.lastCommitByTaskNumber.get(commit.task);
|
if (!commit.prId) return;
|
||||||
if (commitByTaskNumber) {
|
const statistic = this.pr.get(commit.prId);
|
||||||
this.#updateCommitByTaskNumber(commitByTaskNumber, commit);
|
if (statistic) {
|
||||||
} else {
|
console.log('PR error');
|
||||||
this.#addCommitByTaskNumber(commit);
|
} else {
|
||||||
}
|
|
||||||
} else if (!this.pr.has(commit.prId) && IS_PR[commit.commitType || '']) {
|
|
||||||
this.#addCommitByPR(commit);
|
this.#addCommitByPR(commit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#addCommitByTaskNumber(commit: ISystemCommit) {
|
|
||||||
this.lastCommitByTaskNumber.set(commit.task, {
|
|
||||||
commits : 1,
|
|
||||||
beginTaskTime: commit.milliseconds,
|
|
||||||
endTaskTime: commit.milliseconds,
|
|
||||||
commitsByAuthors: createIncrement(commit.author),
|
|
||||||
firstCommit: commit,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#updateCommitByTaskNumber(statistic: any, commit: ISystemCommit) {
|
|
||||||
statistic.endTaskTime = commit.milliseconds;
|
|
||||||
statistic.commits += 1;
|
|
||||||
increment(statistic.commitsByAuthors, commit.author);
|
|
||||||
}
|
|
||||||
|
|
||||||
#addCommitByPR(commit: ISystemCommit) {
|
#addCommitByPR(commit: ISystemCommit) {
|
||||||
const lastCommit = this.lastCommitByTaskNumber.get(commit.task);
|
this.pr.set(commit.prId, {
|
||||||
if (lastCommit) {
|
prId : commit.prId,
|
||||||
// коммиты после влития PR сгорают, чтобы не засчитать технические PR мержи веток
|
author: commit.author,
|
||||||
this.lastCommitByTaskNumber.delete(commit.task);
|
task: commit.task,
|
||||||
const delay = commit.milliseconds - lastCommit.endTaskTime;
|
type: commit.type,
|
||||||
const work = lastCommit.endTaskTime - lastCommit.beginTaskTime;
|
branch: commit.branch,
|
||||||
this.pr.set(commit.prId, {
|
message: commit.message,
|
||||||
...commit,
|
dateCreate: commit.milliseconds, // last commit date before PR
|
||||||
...lastCommit,
|
dateMerge: commit.milliseconds,
|
||||||
delay,
|
daysReview: 1,
|
||||||
delayDays: delay / (24 * 60 * 60 * 1000),
|
daysInWork: 1,
|
||||||
workDays: work === 0 ? 1 : (work / (24 * 60 * 60 * 1000)),
|
});
|
||||||
});
|
|
||||||
this.prByTask.set(commit.task, commit.prId);
|
|
||||||
} else {
|
|
||||||
this.pr.set(commit.prId, { ...commit });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTotalInfo(dataGripByAuthor: any) {
|
updateTotalInfo(dataGripByTasks: any, dataGripByAuthor: any) {
|
||||||
const employment = dataGripByAuthor.employment;
|
const byAuthor = new Map();
|
||||||
const authors = [...employment.active, ...employment.dismissed];
|
|
||||||
const refAuthorPR: any = Object.fromEntries(authors.map((name: string) => ([name, []])));
|
|
||||||
|
|
||||||
this.statistic = Object.values(this.pr)
|
this.pr.forEach((pr: any) => {
|
||||||
.filter((item: any) => item.delay && item.task)
|
if (!pr.task) return;
|
||||||
.sort((a: any, b: any) => b.delay - a.delay);
|
const task = dataGripByTasks.statisticByName.get(pr.task);
|
||||||
|
if (!task) return;
|
||||||
|
|
||||||
this.statistic = [];
|
task.prIds.push(pr.prId);
|
||||||
this.statisticByName = {};
|
|
||||||
|
|
||||||
Object.values(this.pr).forEach((item: any) => {
|
pr.daysInWork = task.daysInWork;
|
||||||
if (!item.delay || !item.task) return;
|
let lastCommitDateBeforePR = task?.to || task?.from;
|
||||||
|
if (lastCommitDateBeforePR > pr.dateMerge) {
|
||||||
this.statistic.push(item);
|
if (!task.timestamps) {
|
||||||
if (refAuthorPR[item.firstCommit.author]) {
|
console.log('x');
|
||||||
refAuthorPR[item.firstCommit.author].push(item);
|
return;
|
||||||
|
}
|
||||||
|
const more = task.timestamps.find((milliseconds: number) => milliseconds > pr.dateMerge);
|
||||||
|
const index = task.timestamps.indexOf(more);
|
||||||
|
lastCommitDateBeforePR = task.timestamps[index - 1];
|
||||||
|
task.timestamps = task.timestamps.slice(index);
|
||||||
|
pr.daysInWork = index;
|
||||||
}
|
}
|
||||||
|
// TODO он не мог быть пустым. Надо расследовать TASK-110 в тестовой выборке.
|
||||||
|
pr.dateCreate = lastCommitDateBeforePR || pr.dateCreate;
|
||||||
|
pr.daysReview = ((pr.dateMerge - pr.dateCreate) || ONE_DAY) / ONE_DAY;
|
||||||
|
|
||||||
|
const list = byAuthor.get(pr.author);
|
||||||
|
if (list) list.push(pr);
|
||||||
|
else byAuthor.set(pr.author, [pr]);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.statistic.sort((a: any, b: any) => b.delay - a.delay);
|
this.statistic = Array.from(this.pr.values())
|
||||||
this.updateTotalByAuthor(authors, refAuthorPR);
|
.sort((a: any, b: any) => b.daysReview - a.daysReview);
|
||||||
|
|
||||||
this.lastCommitByTaskNumber.clear();
|
this.updateTotalByAuthor(byAuthor, dataGripByAuthor);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getPRByGroups(list: any, propertyName: string) {
|
static getPRByGroups(list: any, propertyName: string) {
|
||||||
|
@ -121,11 +99,14 @@ export default class DataGripByPR {
|
||||||
[TITLES.MORE]: 0,
|
[TITLES.MORE]: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let max = 0;
|
||||||
|
|
||||||
const weightedAverage = new WeightedAverage();
|
const weightedAverage = new WeightedAverage();
|
||||||
|
|
||||||
list.forEach((pr: any) => {
|
list.forEach((pr: any) => {
|
||||||
const value = pr[propertyName];
|
const value = pr[propertyName];
|
||||||
|
|
||||||
|
if (value > max) max = value;
|
||||||
weightedAverage.update(value);
|
weightedAverage.update(value);
|
||||||
|
|
||||||
if (value <= 1) details[TITLES.DAY]++;
|
if (value <= 1) details[TITLES.DAY]++;
|
||||||
|
@ -138,36 +119,33 @@ export default class DataGripByPR {
|
||||||
|
|
||||||
const order = Object.keys(details);
|
const order = Object.keys(details);
|
||||||
|
|
||||||
return { details, order, weightedAverage: weightedAverage.get() };
|
return { details, order, weightedAverage: weightedAverage.get(), max };
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTotalByAuthor(authors: any, refAuthorPR: IHashMap<any>) {
|
updateTotalByAuthor(refAuthorPR: HashMap<any>, dataGripByAuthor: any) {
|
||||||
this.statisticByName = {};
|
this.statisticByName = {};
|
||||||
authors.map((name: string) => {
|
refAuthorPR.forEach((prs: any) => {
|
||||||
|
const author = prs[0].author;
|
||||||
|
const stat = dataGripByAuthor.statisticByName[author];
|
||||||
|
if (!stat || stat?.isStaff) return;
|
||||||
|
|
||||||
let maxDelayDays = 0;
|
const daysReview = DataGripByPR.getPRByGroups(prs, 'daysReview');
|
||||||
refAuthorPR[name].forEach((pr: any) => {
|
const daysReviewWeightedAverage = parseInt(daysReview.weightedAverage.toFixed(1), 10);
|
||||||
if (pr.delayDays > maxDelayDays) maxDelayDays = pr.delayDays;
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: сложын и не интересные показатели. Гистаграмму?
|
const daysInWork = DataGripByPR.getPRByGroups(prs, 'daysInWork');
|
||||||
const delayDays = DataGripByPR.getPRByGroups(refAuthorPR[name], 'delayDays');
|
const daysInWorkWeightedAverage = parseInt(daysInWork.weightedAverage.toFixed(1), 10);
|
||||||
const delayDaysWeightedAverage = parseInt(delayDays.weightedAverage.toFixed(1), 10);
|
|
||||||
|
|
||||||
const workDays = DataGripByPR.getPRByGroups(refAuthorPR[name], 'workDays');
|
this.statisticByName[author] = {
|
||||||
const workDaysWeightedAverage = parseInt(workDays.weightedAverage.toFixed(1), 10);
|
author,
|
||||||
|
maxDelayDays: daysReview.max,
|
||||||
|
numberMergedPr: prs.length,
|
||||||
|
|
||||||
this.statisticByName[name] = {
|
workDays: daysInWork.details,
|
||||||
author: name,
|
delayDays: daysReview.details,
|
||||||
maxDelayDays,
|
weightedAverage: daysInWorkWeightedAverage + daysReviewWeightedAverage,
|
||||||
numberMergedPr: refAuthorPR[name].length,
|
|
||||||
|
|
||||||
workDays: workDays.details,
|
|
||||||
delayDays: delayDays.details,
|
|
||||||
weightedAverage: workDaysWeightedAverage + delayDaysWeightedAverage,
|
|
||||||
weightedAverageDetails: {
|
weightedAverageDetails: {
|
||||||
workDays: workDaysWeightedAverage,
|
workDays: daysInWorkWeightedAverage,
|
||||||
delayDays: delayDaysWeightedAverage,
|
delayDays: daysReviewWeightedAverage,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -28,34 +28,54 @@ export default class DataGripByRelease {
|
||||||
addCommit(commit: ISystemCommit) {
|
addCommit(commit: ISystemCommit) {
|
||||||
if (commit.commitType === COMMIT_TYPE.AUTO_MERGE) {
|
if (commit.commitType === COMMIT_TYPE.AUTO_MERGE) {
|
||||||
if (this.release[commit.branch]) {
|
if (this.release[commit.branch]) {
|
||||||
this.#updateRelease(commit);
|
this.#updateDateInRelease(commit.branch, commit);
|
||||||
} else {
|
} else {
|
||||||
this.#addRelease(commit);
|
this.#addReleaseForBB(commit);
|
||||||
}
|
}
|
||||||
} else if (commit.commitType === COMMIT_TYPE.PR_GITHUB || commit.commitType === COMMIT_TYPE.PR_BITBUCKET) {
|
} else if (commit.commitType === COMMIT_TYPE.PR_GITHUB) {
|
||||||
this.lastPrList.push(commit);
|
if (!this.release[commit.toBranch]) this.#addRelease(commit.toBranch, commit);
|
||||||
|
if (!this.release[commit.toBranch]) return;
|
||||||
|
this.#updateDateInRelease(commit.toBranch, commit);
|
||||||
|
this.#updatePRInRelease(commit.toBranch, commit);
|
||||||
|
} else if (commit.commitType === COMMIT_TYPE.PR_BITBUCKET) {
|
||||||
|
this.lastPrList.push(commit.prId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateRelease(commit: ISystemCommit) {
|
#updateDateInRelease(branch: string, commit: ISystemCommit) {
|
||||||
const statistic = this.release[commit.branch];
|
const statistic = this.release[branch];
|
||||||
statistic.lastCommit = commit;
|
statistic.lastCommit = commit;
|
||||||
statistic.to = commit.timestamp;
|
statistic.to = commit.timestamp;
|
||||||
statistic.delayInDays = getRangeInDay(statistic.firstCommit, commit) || statistic.delayInDays;
|
statistic.delayInDays = getRangeInDay(statistic.firstCommit, commit) || statistic.delayInDays;
|
||||||
}
|
}
|
||||||
|
|
||||||
#addRelease(commit: ISystemCommit) {
|
#updatePRInRelease(branch: string, commit: ISystemCommit) {
|
||||||
|
const statistic = this.release[branch];
|
||||||
|
statistic.prIds.push(commit);
|
||||||
|
statistic.prLength = statistic.prIds.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
#addReleaseForBB(commit: ISystemCommit) {
|
||||||
if (!commit.branch) return;
|
if (!commit.branch) return;
|
||||||
|
|
||||||
const index = commit.branch.lastIndexOf('release');
|
const status = this.#addRelease(commit.branch, commit);
|
||||||
if (index === -1) return;
|
if (!status) return;
|
||||||
|
|
||||||
const title = commit.branch
|
this.release[commit.branch].prIds = this.lastPrList;
|
||||||
|
this.release[commit.branch].prLength = this.lastPrList.length;
|
||||||
|
this.lastPrList = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
#addRelease(branch: string, commit: ISystemCommit) {
|
||||||
|
const index = branch.lastIndexOf('release');
|
||||||
|
if (index === -1) return false;
|
||||||
|
|
||||||
|
const title = branch
|
||||||
.substring(index + 7)
|
.substring(index + 7)
|
||||||
.replace(/([^\w.]*)/, '')
|
.replace(/([^\w.]*)|(["']*)/gim, '')
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
this.release[commit.branch] = {
|
this.release[branch] = {
|
||||||
title,
|
title,
|
||||||
firstCommit: commit,
|
firstCommit: commit,
|
||||||
lastCommit: commit,
|
lastCommit: commit,
|
||||||
|
@ -63,14 +83,14 @@ export default class DataGripByRelease {
|
||||||
to: null,
|
to: null,
|
||||||
delayInDays: 0,
|
delayInDays: 0,
|
||||||
waitingInDays: 0,
|
waitingInDays: 0,
|
||||||
pr: this.lastPrList,
|
prIds: [],
|
||||||
prLength: this.lastPrList.length,
|
prLength: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.lastPrList = [];
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTotalInfo() {
|
updateTotalInfo(dataGripByTasks: any, dataGripByPR: any) {
|
||||||
let prev: any = null;
|
let prev: any = null;
|
||||||
|
|
||||||
this.lastPrList = [];
|
this.lastPrList = [];
|
||||||
|
@ -80,6 +100,14 @@ export default class DataGripByRelease {
|
||||||
.map((a: any) => {
|
.map((a: any) => {
|
||||||
const item = a[1];
|
const item = a[1];
|
||||||
|
|
||||||
|
item.prIds.forEach((prId: string) => {
|
||||||
|
const pr = dataGripByPR.pr.get(prId);
|
||||||
|
if (!pr) return;
|
||||||
|
const task = dataGripByTasks.statisticByName.get(pr.task);
|
||||||
|
if (!task) return;
|
||||||
|
task.releaseIds.add(a[0]);
|
||||||
|
});
|
||||||
|
|
||||||
item.to = item.from !== item.to && item.to
|
item.to = item.from !== item.to && item.to
|
||||||
? item.lastCommit.date
|
? item.lastCommit.date
|
||||||
: null;
|
: null;
|
||||||
|
|
|
@ -1,46 +1,40 @@
|
||||||
import ICommit from 'ts/interfaces/Commit';
|
import ICommit from 'ts/interfaces/Commit';
|
||||||
import IHashMap from 'ts/interfaces/HashMap';
|
import IHashMap, { HashMap } from 'ts/interfaces/HashMap';
|
||||||
|
|
||||||
import { ONE_DAY } from 'ts/helpers/formatter';
|
import { increment } from 'ts/helpers/Math';
|
||||||
|
|
||||||
export default class DataGripByTasks {
|
export default class DataGripByTasks {
|
||||||
commits: IHashMap<ICommit[]> = {};
|
commits: HashMap<ICommit[]> = new Map();
|
||||||
|
|
||||||
statistic: any = [];
|
statistic: any = [];
|
||||||
|
|
||||||
|
statisticByName: HashMap<any> = new Map();
|
||||||
|
|
||||||
// achievements
|
// achievements
|
||||||
longTaskByAuthor: IHashMap<number> = {};
|
longTaskByAuthor: IHashMap<number> = {};
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.commits = {};
|
this.commits.clear();
|
||||||
this.statistic = [];
|
this.statistic = [];
|
||||||
|
this.statisticByName.clear();
|
||||||
this.longTaskByAuthor = {};
|
this.longTaskByAuthor = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
addCommit(commit: ICommit) {
|
addCommit(commit: ICommit) {
|
||||||
if (this.commits.hasOwnProperty(commit.task)) {
|
if (this.commits.has(commit.task)) {
|
||||||
this.#updateCommitByTask(commit);
|
this.commits.get(commit.task)?.push(commit);
|
||||||
} else {
|
} else {
|
||||||
this.#addCommitByTask(commit);
|
this.commits.set(commit.task, [commit]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateCommitByTask(commit: ICommit) {
|
|
||||||
this.commits[commit.task].push(commit);
|
|
||||||
}
|
|
||||||
|
|
||||||
#addCommitByTask(commit: ICommit) {
|
|
||||||
this.commits[commit.task] = [commit];
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: тут двойной пробег получился. А должен был частями собрать инфу
|
// TODO: тут двойной пробег получился. А должен был частями собрать инфу
|
||||||
updateTotalInfo(PRs: any) {
|
updateTotalInfo() {
|
||||||
this.statistic = Object.entries(this.commits)
|
this.statistic = Array.from(this.commits, ([k, v]) => [k, v]) // @ts-ignore
|
||||||
.map(([task, commits]: [string, ICommit[]]) => {
|
.map(([task, commits]: [string, ICommit[]]) => {
|
||||||
const firstCommit = commits[0];
|
const firstCommit = commits[0];
|
||||||
const lastCommit = commits[commits.length - 1];
|
const lastCommit = commits[commits.length - 1];
|
||||||
const from = firstCommit.milliseconds;
|
const from = firstCommit.milliseconds;
|
||||||
const pr = PRs.prByTask.get(task) ? PRs.pr.get(PRs.prByTask.get(task)) : null;
|
|
||||||
|
|
||||||
const shortInfo = {
|
const shortInfo = {
|
||||||
task,
|
task,
|
||||||
|
@ -48,30 +42,31 @@ export default class DataGripByTasks {
|
||||||
from,
|
from,
|
||||||
commits: 1,
|
commits: 1,
|
||||||
daysInWork: 1,
|
daysInWork: 1,
|
||||||
prDate: pr?.milliseconds,
|
prIds: [],
|
||||||
prDelayDays: pr?.delayDays,
|
releaseIds: new Set(),
|
||||||
prAuthor: firstCommit.author === pr?.author ? null : pr?.author,
|
|
||||||
comments: firstCommit.text,
|
comments: firstCommit.text,
|
||||||
types: firstCommit.type && firstCommit.type !== '—' ? [firstCommit.type] : [],
|
types: firstCommit.type ? { [firstCommit.type]: 1 } : {},
|
||||||
scope: firstCommit.scope && firstCommit.scope !== '—' ? [firstCommit.scope] : [],
|
scope: firstCommit.scope ? { [firstCommit.scope]: 1 } : {},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (commits.length === 1) return shortInfo;
|
if (commits.length === 1) return shortInfo;
|
||||||
|
|
||||||
const authors = new Set();
|
|
||||||
const messages = new Set();
|
const messages = new Set();
|
||||||
const types = new Set();
|
const timestamps = new Set();
|
||||||
const scope = new Set();
|
const authors = {};
|
||||||
|
const types = {};
|
||||||
|
const scope = {};
|
||||||
commits.forEach((commit: ICommit) => {
|
commits.forEach((commit: ICommit) => {
|
||||||
authors.add(commit.author);
|
|
||||||
messages.add(commit.text);
|
messages.add(commit.text);
|
||||||
if (commit.type !== '—') types.add(commit.type);
|
timestamps.add(commit.milliseconds);
|
||||||
if (commit.scope !== '—') scope.add(commit.scope);
|
increment(authors, commit.author);
|
||||||
|
increment(types, commit.type);
|
||||||
|
increment(scope, commit.scope);
|
||||||
});
|
});
|
||||||
|
|
||||||
const comments = Array.from(messages).join(', ');
|
const comments = Array.from(messages).join(', ');
|
||||||
const to = lastCommit.milliseconds;
|
const to = lastCommit.milliseconds;
|
||||||
const daysInWork = Math.ceil((to - from) / ONE_DAY) + 1;
|
const daysInWork = timestamps.size;
|
||||||
|
|
||||||
const longTaskByAuthor = this.longTaskByAuthor[shortInfo.author];
|
const longTaskByAuthor = this.longTaskByAuthor[shortInfo.author];
|
||||||
if (!longTaskByAuthor || longTaskByAuthor < daysInWork) {
|
if (!longTaskByAuthor || longTaskByAuthor < daysInWork) {
|
||||||
|
@ -82,16 +77,21 @@ export default class DataGripByTasks {
|
||||||
...shortInfo,
|
...shortInfo,
|
||||||
to: to !== from ? to : undefined,
|
to: to !== from ? to : undefined,
|
||||||
commits: commits.length,
|
commits: commits.length,
|
||||||
|
timestamps: Array.from(timestamps),
|
||||||
daysInWork,
|
daysInWork,
|
||||||
authors: Array.from(authors),
|
|
||||||
comments,
|
comments,
|
||||||
types: Array.from(types),
|
authors,
|
||||||
scope: Array.from(scope),
|
types,
|
||||||
|
scope,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter((dot) => dot.task)
|
.filter((dot) => dot.task)
|
||||||
.sort((dotA, dotB) => dotB.from - dotA.from);
|
.sort((dotA, dotB) => dotB.from - dotA.from);
|
||||||
|
|
||||||
this.commits = {};
|
this.statistic.forEach((item: any) => {
|
||||||
|
this.statisticByName.set(item.task, item);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.commits.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import DataGripByTasks from './components/tasks';
|
||||||
import DataGripByRelease from './components/release';
|
import DataGripByRelease from './components/release';
|
||||||
import DataGripByScoring from './components/scoring';
|
import DataGripByScoring from './components/scoring';
|
||||||
import DataGripByCompany from './components/company';
|
import DataGripByCompany from './components/company';
|
||||||
|
import DataGripByCountry from './components/country';
|
||||||
|
|
||||||
class DataGrip {
|
class DataGrip {
|
||||||
firstLastCommit: any = new MinMaxCounter();
|
firstLastCommit: any = new MinMaxCounter();
|
||||||
|
@ -23,6 +24,8 @@ class DataGrip {
|
||||||
|
|
||||||
company: any = new DataGripByCompany();
|
company: any = new DataGripByCompany();
|
||||||
|
|
||||||
|
country: any = new DataGripByCountry();
|
||||||
|
|
||||||
team: any = new DataGripByTeam();
|
team: any = new DataGripByTeam();
|
||||||
|
|
||||||
scope: any = new DataGripByScope();
|
scope: any = new DataGripByScope();
|
||||||
|
@ -49,6 +52,7 @@ class DataGrip {
|
||||||
this.firstLastCommit.clear();
|
this.firstLastCommit.clear();
|
||||||
this.author.clear();
|
this.author.clear();
|
||||||
this.company.clear();
|
this.company.clear();
|
||||||
|
this.country.clear();
|
||||||
this.team.clear();
|
this.team.clear();
|
||||||
this.scope.clear();
|
this.scope.clear();
|
||||||
this.type.clear();
|
this.type.clear();
|
||||||
|
@ -75,7 +79,6 @@ class DataGrip {
|
||||||
this.get.addCommit(commit);
|
this.get.addCommit(commit);
|
||||||
this.week.addCommit(commit);
|
this.week.addCommit(commit);
|
||||||
this.tasks.addCommit(commit);
|
this.tasks.addCommit(commit);
|
||||||
this.company.addCommit(commit);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,11 +90,12 @@ class DataGrip {
|
||||||
this.timestamp.updateTotalInfo(this.author);
|
this.timestamp.updateTotalInfo(this.author);
|
||||||
this.week.updateTotalInfo(this.author);
|
this.week.updateTotalInfo(this.author);
|
||||||
this.recommendations.updateTotalInfo(this);
|
this.recommendations.updateTotalInfo(this);
|
||||||
this.pr.updateTotalInfo(this.author);
|
this.tasks.updateTotalInfo();
|
||||||
this.tasks.updateTotalInfo(this.pr);
|
this.pr.updateTotalInfo(this.tasks, this.author);
|
||||||
this.release.updateTotalInfo();
|
this.release.updateTotalInfo(this.tasks, this.pr);
|
||||||
this.scoring.updateTotalInfo(this.author, this.timestamp);
|
this.scoring.updateTotalInfo(this.author, this.timestamp);
|
||||||
this.company.updateTotalInfo(this.author);
|
this.company.updateTotalInfo(this.author);
|
||||||
|
this.country.updateTotalInfo(this.author);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,3 +109,24 @@ export const FAKE_EMAILS = FAKE_AUTHORS
|
||||||
export const FAKE_TASK_PREFIXES = 'axeurtyqwpsdfghjklzcvbnm'
|
export const FAKE_TASK_PREFIXES = 'axeurtyqwpsdfghjklzcvbnm'
|
||||||
.split('')
|
.split('')
|
||||||
.map((symbol) => (new Array(5)).fill(symbol.toUpperCase()).join(''));
|
.map((symbol) => (new Array(5)).fill(symbol.toUpperCase()).join(''));
|
||||||
|
|
||||||
|
export const FAKE_COMPANIES = [
|
||||||
|
'Apple',
|
||||||
|
'Microsoft',
|
||||||
|
'Samsung Electronics',
|
||||||
|
'Alphabet',
|
||||||
|
'AT&T', 'Amazon',
|
||||||
|
'Verizon Communications',
|
||||||
|
'China Mobile',
|
||||||
|
'Walt Disney',
|
||||||
|
'Facebook',
|
||||||
|
'Alibaba',
|
||||||
|
'Intel',
|
||||||
|
'Softbank',
|
||||||
|
'IBM',
|
||||||
|
'Tencent Holdings',
|
||||||
|
'Cisco Systems',
|
||||||
|
'Oracle',
|
||||||
|
'Deutsche Telekom',
|
||||||
|
'Taiwan Semiconductor',
|
||||||
|
];
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
FAKE_AUTHORS,
|
FAKE_AUTHORS,
|
||||||
FAKE_EMAILS,
|
FAKE_EMAILS,
|
||||||
FAKE_TASK_PREFIXES,
|
FAKE_TASK_PREFIXES,
|
||||||
|
FAKE_COMPANIES,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import FakeName from './FakeName';
|
import FakeName from './FakeName';
|
||||||
|
|
||||||
|
@ -14,21 +15,26 @@ export default class Depersonalized {
|
||||||
|
|
||||||
fakeTaskPrefix: any = null;
|
fakeTaskPrefix: any = null;
|
||||||
|
|
||||||
|
fakeCompany: any = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.fakeName = new FakeName('User', FAKE_AUTHORS);
|
this.fakeName = new FakeName('User', FAKE_AUTHORS);
|
||||||
this.fakeEmail = new FakeName('user', FAKE_EMAILS);
|
this.fakeEmail = new FakeName('user', FAKE_EMAILS);
|
||||||
this.fakeTaskPrefix = new FakeName('JIRA', FAKE_TASK_PREFIXES);
|
this.fakeTaskPrefix = new FakeName('JIRA', FAKE_TASK_PREFIXES);
|
||||||
|
this.fakeCompany = new FakeName('Company', FAKE_COMPANIES);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCommit(commit: ICommit | ISystemCommit): ICommit | ISystemCommit {
|
getCommit(commit: ICommit | ISystemCommit): ICommit | ISystemCommit {
|
||||||
const author = this.fakeName.get(commit.author);
|
const author = this.fakeName.get(commit.author);
|
||||||
const email = this.fakeEmail.get(commit.author);
|
const email = this.fakeEmail.get(commit.author);
|
||||||
|
const company = this.fakeCompany.get(commit.company);
|
||||||
|
|
||||||
if (!commit.task) {
|
if (!commit.task) {
|
||||||
return {
|
return {
|
||||||
...commit,
|
...commit,
|
||||||
author,
|
author,
|
||||||
email,
|
email,
|
||||||
|
company,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +59,7 @@ export default class Depersonalized {
|
||||||
message,
|
message,
|
||||||
author,
|
author,
|
||||||
email,
|
email,
|
||||||
|
company,
|
||||||
branch,
|
branch,
|
||||||
toBranch,
|
toBranch,
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,6 +35,7 @@ export default class FileBuilderCommon {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const parts = file.path.split('/');
|
const parts = file.path.split('/');
|
||||||
parts.pop();
|
parts.pop();
|
||||||
|
file.pathString = file.path;
|
||||||
file.path = parts;
|
file.path = parts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { increment } from 'ts/helpers/Math';
|
||||||
|
|
||||||
import FileBuilderCommon from './Common';
|
import FileBuilderCommon from './Common';
|
||||||
import FileBuilderLineStat from './LineStat';
|
import FileBuilderLineStat from './LineStat';
|
||||||
|
import FileBuilderTasks from './Tasks';
|
||||||
|
|
||||||
export default class FileGripByPaths {
|
export default class FileGripByPaths {
|
||||||
list: IDirtyFile[] = [];
|
list: IDirtyFile[] = [];
|
||||||
|
@ -39,17 +40,20 @@ export default class FileGripByPaths {
|
||||||
#getNewDirtyFile(fileChange: IFileChange, commit: ICommit): any {
|
#getNewDirtyFile(fileChange: IFileChange, commit: ICommit): any {
|
||||||
const commonProps = FileBuilderCommon.getProps(fileChange, commit);
|
const commonProps = FileBuilderCommon.getProps(fileChange, commit);
|
||||||
const statProps = FileBuilderLineStat.getProps(fileChange, commit);
|
const statProps = FileBuilderLineStat.getProps(fileChange, commit);
|
||||||
|
const tasksProps = FileBuilderTasks.getProps(commit);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: fileChange.id,
|
id: fileChange.id,
|
||||||
...commonProps,
|
...commonProps,
|
||||||
...statProps,
|
...statProps,
|
||||||
|
...tasksProps,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateDirtyFile(file: any, fileChange: IFileChange, commit: ICommit) {
|
#updateDirtyFile(file: any, fileChange: IFileChange, commit: ICommit) {
|
||||||
FileBuilderCommon.updateProps(file, fileChange, commit);
|
FileBuilderCommon.updateProps(file, fileChange, commit);
|
||||||
FileBuilderLineStat.updateProps(file, fileChange, commit);
|
FileBuilderLineStat.updateProps(file, fileChange, commit);
|
||||||
|
FileBuilderTasks.updateProps(file, commit);
|
||||||
}
|
}
|
||||||
|
|
||||||
#renameFile(file: any, newId: string) {
|
#renameFile(file: any, newId: string) {
|
||||||
|
@ -74,6 +78,7 @@ export default class FileGripByPaths {
|
||||||
|
|
||||||
FileBuilderCommon.updateTotal(file);
|
FileBuilderCommon.updateTotal(file);
|
||||||
FileBuilderLineStat.updateTotal(file);
|
FileBuilderLineStat.updateTotal(file);
|
||||||
|
FileBuilderTasks.updateTotal(file);
|
||||||
|
|
||||||
if (file.type) {
|
if (file.type) {
|
||||||
let refExtensionType = this.refExtensionType.get(file.extension);
|
let refExtensionType = this.refExtensionType.get(file.extension);
|
||||||
|
|
|
@ -4,12 +4,25 @@ import IHashMap from 'ts/interfaces/HashMap';
|
||||||
import { getValuesInPercent } from '../helpers';
|
import { getValuesInPercent } from '../helpers';
|
||||||
|
|
||||||
function getFolder(name?: string, path?: string[], file?: IDirtyFile): IFolder {
|
function getFolder(name?: string, path?: string[], file?: IDirtyFile): IFolder {
|
||||||
|
const tasks = file?.tasks
|
||||||
|
? new Set(file.tasks)
|
||||||
|
: new Set();
|
||||||
|
|
||||||
|
const timestamp = file?.timestamp
|
||||||
|
? new Set(file.timestamp) as Set<string>
|
||||||
|
: new Set();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: Math.random(),
|
id: Math.random(),
|
||||||
name: name || '', // @ts-ignore
|
name: name || '', // @ts-ignore
|
||||||
path: path || [],
|
path: path || [],
|
||||||
pathString: `${(path || []).join('/')}/${name || ''}`,
|
pathString: `${(path || []).join('/')}/${name || ''}`,
|
||||||
content: {},
|
content: new Map(),
|
||||||
|
|
||||||
|
tasks: tasks as Set<string>,
|
||||||
|
timestamp: timestamp as Set<string>,
|
||||||
|
totalTasks: tasks.size,
|
||||||
|
totalDays: timestamp.size,
|
||||||
|
|
||||||
lines: file?.lines || 0,
|
lines: file?.lines || 0,
|
||||||
|
|
||||||
|
@ -48,6 +61,12 @@ function updateFolder(folder: any, file: IDirtyFile) {
|
||||||
folder.removedLines += file.removedLines || 0;
|
folder.removedLines += file.removedLines || 0;
|
||||||
folder.changedLines += file.changedLines || 0;
|
folder.changedLines += file.changedLines || 0;
|
||||||
|
|
||||||
|
// TODO: bad performance
|
||||||
|
folder.tasks = new Set([...folder.tasks, ...file.tasks]);
|
||||||
|
folder.timestamp = new Set([...folder.timestamp, ...file.timestamp]);
|
||||||
|
folder.totalTasks = folder.tasks.size;
|
||||||
|
folder.totalDays = folder.timestamp.size;
|
||||||
|
|
||||||
updateFolderBy(folder, file, 'addedLinesByAuthor');
|
updateFolderBy(folder, file, 'addedLinesByAuthor');
|
||||||
updateFolderBy(folder, file, 'removedLinesByAuthor');
|
updateFolderBy(folder, file, 'removedLinesByAuthor');
|
||||||
updateFolderBy(folder, file, 'changedLinesByAuthor');
|
updateFolderBy(folder, file, 'changedLinesByAuthor');
|
||||||
|
@ -69,17 +88,19 @@ export default class FileGripByFolder {
|
||||||
addFile(file: IDirtyFile) {
|
addFile(file: IDirtyFile) {
|
||||||
let prev: any = this.tree.content;
|
let prev: any = this.tree.content;
|
||||||
file.path.forEach((folderName: any, index: number) => {
|
file.path.forEach((folderName: any, index: number) => {
|
||||||
let folder = prev[folderName];
|
const folder = prev.get(folderName);
|
||||||
if (!folder || !folder.content) {
|
if (!folder?.content) {
|
||||||
const path = file.path.slice(0, index);
|
const path = file.path.slice(0, index);
|
||||||
prev[folderName] = getFolder(folderName, path, file);
|
const newFolder = getFolder(folderName, path, file);
|
||||||
this.folders.push(prev[folderName]);
|
prev.set(folderName, newFolder);
|
||||||
|
this.folders.push(newFolder);
|
||||||
|
prev = newFolder.content;
|
||||||
} else {
|
} else {
|
||||||
updateFolder(folder, file);
|
updateFolder(folder, file);
|
||||||
|
prev = folder.content;
|
||||||
}
|
}
|
||||||
prev = prev[folderName].content;
|
|
||||||
});
|
});
|
||||||
prev[file.name] = file;
|
prev.set(file.name, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTotalInfo() {
|
updateTotalInfo() {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import FileGripByExtension from './components/extension';
|
||||||
import FileGripByType from './components/type';
|
import FileGripByType from './components/type';
|
||||||
import FileGripByFolder from './components/folder';
|
import FileGripByFolder from './components/folder';
|
||||||
import FileGripByAuthor from './components/author';
|
import FileGripByAuthor from './components/author';
|
||||||
|
import FileGripByRefactor from './components/refactor';
|
||||||
|
|
||||||
class FileGrip {
|
class FileGrip {
|
||||||
files: any = new FileBuilder();
|
files: any = new FileBuilder();
|
||||||
|
@ -20,6 +21,8 @@ class FileGrip {
|
||||||
|
|
||||||
author: any = new FileGripByAuthor();
|
author: any = new FileGripByAuthor();
|
||||||
|
|
||||||
|
refactor: any = new FileGripByRefactor();
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.files.clear();
|
this.files.clear();
|
||||||
this.extension.clear();
|
this.extension.clear();
|
||||||
|
@ -27,6 +30,7 @@ class FileGrip {
|
||||||
this.tree.clear();
|
this.tree.clear();
|
||||||
this.removedTree.clear();
|
this.removedTree.clear();
|
||||||
this.author.clear();
|
this.author.clear();
|
||||||
|
this.refactor.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
addCommit(commit: ICommit | ISystemCommit) {
|
addCommit(commit: ICommit | ISystemCommit) {
|
||||||
|
@ -54,6 +58,7 @@ class FileGrip {
|
||||||
this.author.updateTotalInfo();
|
this.author.updateTotalInfo();
|
||||||
this.tree.updateTotalInfo();
|
this.tree.updateTotalInfo();
|
||||||
this.removedTree.updateTotalInfo();
|
this.removedTree.updateTotalInfo();
|
||||||
|
this.refactor.updateTotalInfo(this.files.list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@ import ICommit, { COMMIT_TYPE, ISystemCommit } from 'ts/interfaces/Commit';
|
||||||
import IHashMap from 'ts/interfaces/HashMap';
|
import IHashMap from 'ts/interfaces/HashMap';
|
||||||
|
|
||||||
import { getTypeAndScope, getTask, getTaskNumber } from './getTypeAndScope';
|
import { getTypeAndScope, getTask, getTaskNumber } from './getTypeAndScope';
|
||||||
import getCompany from './getCompany';
|
import getInfoFromNameAndEmail from './getCompany';
|
||||||
|
import { getGithubPrInfo } from './getMergeInfo';
|
||||||
|
|
||||||
const MASTER_BRANCH = {
|
const MASTER_BRANCH = {
|
||||||
master: true,
|
master: true,
|
||||||
|
@ -35,6 +36,7 @@ export default function getCommitInfo(
|
||||||
prevDate = date;
|
prevDate = date;
|
||||||
const day = date.getDay() - 1;
|
const day = date.getDay() - 1;
|
||||||
const timestamp = sourceDate.substring(0, 10); // split('T')[0];
|
const timestamp = sourceDate.substring(0, 10); // split('T')[0];
|
||||||
|
const timezone = sourceDate.substring(19, 25);
|
||||||
let milliseconds = refTimestampTime.get(timestamp);
|
let milliseconds = refTimestampTime.get(timestamp);
|
||||||
if (!milliseconds) {
|
if (!milliseconds) {
|
||||||
milliseconds = (new Date(timestamp)).getTime();
|
milliseconds = (new Date(timestamp)).getTime();
|
||||||
|
@ -47,12 +49,11 @@ export default function getCommitInfo(
|
||||||
|
|
||||||
const companyKey = `${author}>in>${email}`;
|
const companyKey = `${author}>in>${email}`;
|
||||||
if (!refEmailAuthor[companyKey]) {
|
if (!refEmailAuthor[companyKey]) {
|
||||||
const companyForKey = getCompany(author, email);
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
refEmailAuthor[companyKey] = { company: companyForKey };
|
refEmailAuthor[companyKey] = getInfoFromNameAndEmail(author, email);
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const company = refEmailAuthor[companyKey].company;
|
const { company, country, device } = refEmailAuthor[companyKey];
|
||||||
|
|
||||||
const authorID = author.replace(/\s|\t/gm, '');
|
const authorID = author.replace(/\s|\t/gm, '');
|
||||||
if (authorID && refEmailAuthor[authorID] && refEmailAuthor[authorID] !== author) {
|
if (authorID && refEmailAuthor[authorID] && refEmailAuthor[authorID] !== author) {
|
||||||
|
@ -86,6 +87,7 @@ export default function getCommitInfo(
|
||||||
month: date.getMonth(),
|
month: date.getMonth(),
|
||||||
year: date.getUTCFullYear(),
|
year: date.getUTCFullYear(),
|
||||||
week: 0,
|
week: 0,
|
||||||
|
timezone,
|
||||||
timestamp,
|
timestamp,
|
||||||
milliseconds,
|
milliseconds,
|
||||||
|
|
||||||
|
@ -93,10 +95,12 @@ export default function getCommitInfo(
|
||||||
email,
|
email,
|
||||||
message,
|
message,
|
||||||
company,
|
company,
|
||||||
|
country,
|
||||||
|
device,
|
||||||
|
|
||||||
text: '',
|
text: '',
|
||||||
type: '—',
|
type: '',
|
||||||
scope: '—',
|
scope: '',
|
||||||
fileChanges: [],
|
fileChanges: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -114,24 +118,29 @@ export default function getCommitInfo(
|
||||||
|
|
||||||
if (isSystemCommit) {
|
if (isSystemCommit) {
|
||||||
let commitType = COMMIT_TYPE.MERGE;
|
let commitType = COMMIT_TYPE.MERGE;
|
||||||
let prId, repository, branch, toBranch, task, taskNumber;
|
let prId, repository, branch, toBranch, task, taskNumber, type, scope;
|
||||||
if (isGithubPR) {
|
if (isGithubPR) {
|
||||||
|
// "Merge pull request #3 in repository from TASK-123-add-profile to master"
|
||||||
|
// "Merge pull request #3 from facebook/compiler"
|
||||||
commitType = COMMIT_TYPE.PR_GITHUB;
|
commitType = COMMIT_TYPE.PR_GITHUB;
|
||||||
[, prId, repository, branch, toBranch ] = message
|
[prId, repository, branch, toBranch] = getGithubPrInfo(message);
|
||||||
.replace(/(Merge\spull\srequest\s#)|(\sfrom\s)|(\sin\s)|(\sto\s)/gim, ',')
|
|
||||||
.split(',');
|
|
||||||
task = getTask(branch);
|
task = getTask(branch);
|
||||||
} else if (isBitbucketPR) {
|
|
||||||
|
} else if (isBitbucketPR) { // "Pull request #3: TASK-123 fix: Add profile"
|
||||||
commitType = COMMIT_TYPE.PR_BITBUCKET;
|
commitType = COMMIT_TYPE.PR_BITBUCKET;
|
||||||
const messageParts = message.substring(14).split(':');
|
const messageParts = message.substring(14).split(':');
|
||||||
prId = messageParts.shift();
|
prId = messageParts.shift();
|
||||||
task = getTask(messageParts.join(':'));
|
const description = messageParts.join(':');
|
||||||
} else if (isAutoMerge) {
|
task = getTask(description);
|
||||||
|
[type, scope] = getTypeAndScope(description, task);
|
||||||
|
|
||||||
|
} else if (isAutoMerge) { // "Automatic merge from release/release-2.8.0 -> master"
|
||||||
commitType = COMMIT_TYPE.AUTO_MERGE;
|
commitType = COMMIT_TYPE.AUTO_MERGE;
|
||||||
[, branch, toBranch ] = message
|
[, branch, toBranch ] = message
|
||||||
.replace(/(Automatic\smerge\sfrom\s)|(\s->\s)/gim, ',')
|
.replace(/(Automatic\smerge\sfrom\s)|(\s->\s)/gim, ',')
|
||||||
.replace(/(Merge\sremote-tracking\sbranch\s')|('\sinto\s)/gim, ',')
|
.replace(/(Merge\sremote-tracking\sbranch\s')|('\sinto\s)/gim, ',')
|
||||||
.split(',');
|
.split(',');
|
||||||
|
|
||||||
} else if (isGitlabPR) {
|
} else if (isGitlabPR) {
|
||||||
commitType = COMMIT_TYPE.PR_GITLAB;
|
commitType = COMMIT_TYPE.PR_GITLAB;
|
||||||
[, branch, toBranch ] = message
|
[, branch, toBranch ] = message
|
||||||
|
@ -147,6 +156,8 @@ export default function getCommitInfo(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...commonInfo,
|
...commonInfo,
|
||||||
|
type,
|
||||||
|
scope,
|
||||||
prId: prId || '',
|
prId: prId || '',
|
||||||
task: task || '',
|
task: task || '',
|
||||||
taskNumber: taskNumber || '',
|
taskNumber: taskNumber || '',
|
||||||
|
@ -155,25 +166,25 @@ export default function getCommitInfo(
|
||||||
toBranch: toBranch || '',
|
toBranch: toBranch || '',
|
||||||
commitType,
|
commitType,
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
const textIndex = (message || '').indexOf(':');
|
||||||
|
const text = textIndex > 1
|
||||||
|
? message.substring(textIndex + 2).trim()
|
||||||
|
: message;
|
||||||
|
const task = getTask(message);
|
||||||
|
const taskNumber = getTaskNumber(task);
|
||||||
|
const [type, scope] = getTypeAndScope(message, task);
|
||||||
|
return {
|
||||||
|
...commonInfo,
|
||||||
|
task,
|
||||||
|
taskNumber,
|
||||||
|
text,
|
||||||
|
type: type || '',
|
||||||
|
scope: scope || '',
|
||||||
|
|
||||||
|
changes: 0,
|
||||||
|
added: 0,
|
||||||
|
removed: 0,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const textIndex = (message || '').indexOf(':');
|
|
||||||
const text = textIndex > 1
|
|
||||||
? message.substring(textIndex + 2).trim()
|
|
||||||
: message;
|
|
||||||
const task = getTask(message);
|
|
||||||
const taskNumber = getTaskNumber(task);
|
|
||||||
const [type, scope] = getTypeAndScope(message, task);
|
|
||||||
return {
|
|
||||||
...commonInfo,
|
|
||||||
task,
|
|
||||||
taskNumber,
|
|
||||||
text,
|
|
||||||
type: type || '—',
|
|
||||||
scope: scope || '—',
|
|
||||||
|
|
||||||
changes: 0,
|
|
||||||
added: 0,
|
|
||||||
removed: 0,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
|
import getCountryByDomain from './getCountryByDomain';
|
||||||
|
import getDevice from './getDevice';
|
||||||
|
|
||||||
const PUBLIC_SERVICES = [
|
const PUBLIC_SERVICES = [
|
||||||
'icloud',
|
'icloud',
|
||||||
'google',
|
'google',
|
||||||
|
'inbox',
|
||||||
'yahoo',
|
'yahoo',
|
||||||
'aol',
|
'aol',
|
||||||
'zoho',
|
'zoho',
|
||||||
|
@ -25,6 +29,8 @@ const isPublicService = Object.fromEntries(
|
||||||
Object.values(PUBLIC_SERVICES).map(key => [key.toUpperCase(), true]),
|
Object.values(PUBLIC_SERVICES).map(key => [key.toUpperCase(), true]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isIP = /([0-9]{1,3}(-|_|.)[0-9]{1,3}(-|_|.)[0-9]{1,3}(-|_|.)[0-9]{1,3})/;
|
||||||
|
|
||||||
function getCompanyByName(author?: string): string {
|
function getCompanyByName(author?: string): string {
|
||||||
const tags = (author || '')
|
const tags = (author || '')
|
||||||
.toUpperCase()
|
.toUpperCase()
|
||||||
|
@ -39,11 +45,12 @@ function getCompanyByName(author?: string): string {
|
||||||
: '';
|
: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCompanyByEmail(email?: string) {
|
function getCompanyAndDomainByEmail(email?: string) {
|
||||||
const domain = (email || '').split('@').pop() || '';
|
const fullDomain = (email || '').split('@').pop() || '';
|
||||||
const parts = domain.split('.');
|
const parts = fullDomain.split('.');
|
||||||
parts.pop();
|
const domain = parts.pop();
|
||||||
return (parts.pop() || '').toUpperCase();
|
const company = (parts.pop() || '').toUpperCase();
|
||||||
|
return [company, domain];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getClearText(text: string) {
|
function getClearText(text: string) {
|
||||||
|
@ -63,12 +70,19 @@ function isUserName(author?: string, company?: string): boolean {
|
||||||
return !!clearAuthor.match(clearCompany);
|
return !!clearAuthor.match(clearCompany);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCompany(author?: string, email?: string) {
|
export default function getInfoFromNameAndEmail(author?: string, email?: string) {
|
||||||
const company = getCompanyByName(author) || getCompanyByEmail(email) || '';
|
const companyByAuthor = getCompanyByName(author);
|
||||||
const isMailService = company.indexOf('MAIL') !== -1;
|
const [companyByEmail, domain] = getCompanyAndDomainByEmail(email);
|
||||||
return isPublicService[company] || isMailService || isUserName(author, company)
|
const country = getCountryByDomain(domain);
|
||||||
? ''
|
const device = getDevice(companyByEmail);
|
||||||
: company;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default getCompany;
|
const companyName = companyByAuthor || companyByEmail || '';
|
||||||
|
const isMailService = companyName.indexOf('MAIL') !== -1;
|
||||||
|
const isInCorrect = isPublicService[companyName]
|
||||||
|
|| isMailService
|
||||||
|
|| isUserName(author, companyName)
|
||||||
|
|| isIP.test(companyName);
|
||||||
|
const company = (!isInCorrect && !device) ? companyName : '';
|
||||||
|
|
||||||
|
return { company, country, device };
|
||||||
|
}
|
||||||
|
|
|
@ -21,14 +21,17 @@ export interface ILog {
|
||||||
minutes: number; // 59,
|
minutes: number; // 59,
|
||||||
month: number; // 1,
|
month: number; // 1,
|
||||||
year: number; // 2021,
|
year: number; // 2021,
|
||||||
timestamp: string; // 2021-02-09",
|
timezone: string; // "+03:00",
|
||||||
|
timestamp: string; // "2021-02-09",
|
||||||
milliseconds: number; // 1612828800000,
|
milliseconds: number; // 1612828800000,
|
||||||
week: number; // 42,
|
week: number; // 42,
|
||||||
|
|
||||||
// user
|
// user
|
||||||
author: string; // "Dart Vader",
|
author: string; // "Dart Vader",
|
||||||
email: string; // "d.vader@emap.com",
|
email: string; // "d.vader@emap.ru",
|
||||||
company: string; // "emap",
|
company: string; // "emap",
|
||||||
|
country: string; // "ru",
|
||||||
|
device: string; // "Macbook",
|
||||||
|
|
||||||
// task
|
// task
|
||||||
message: string; // "JIRA-0000 fix(profile): add new avatar",
|
message: string; // "JIRA-0000 fix(profile): add new avatar",
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import ICommit, { ISystemCommit } from './Commit';
|
import ICommit, { ISystemCommit } from './Commit';
|
||||||
import IHashMap from './HashMap';
|
import IHashMap, { HashMap } from './HashMap';
|
||||||
|
|
||||||
interface IFileStat {
|
interface IFileStat {
|
||||||
|
tasks: Set<string>; // ['JIRA-123', 'JIRA-444']
|
||||||
|
timestamp: Set<string>; // ['2021-02-09', '2021-03-09', '2021-04-09']
|
||||||
|
totalTasks: number; // 2
|
||||||
|
totalDays: number; // 3
|
||||||
|
|
||||||
lines: number; // 38, line in file for this moment
|
lines: number; // 38, line in file for this moment
|
||||||
|
|
||||||
addedLines: number;
|
addedLines: number;
|
||||||
|
@ -35,5 +40,5 @@ export interface IFolder extends IFileStat {
|
||||||
name?: string;
|
name?: string;
|
||||||
path: string[]; // ['src']
|
path: string[]; // ['src']
|
||||||
pathString: string; // 'src\\ts'
|
pathString: string; // 'src\\ts'
|
||||||
content: IHashMap<IDirtyFile>,
|
content: HashMap<IDirtyFile>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,16 @@ export const TEAM = [
|
||||||
icon: './assets/menu/team_type.svg',
|
icon: './assets/menu/team_type.svg',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'pr',
|
id: 'company',
|
||||||
link: '/team/pr',
|
link: '/team/company',
|
||||||
title: 'sidebar.team.pr',
|
title: 'sidebar.team.company',
|
||||||
icon: './assets/menu/pull_request.svg',
|
icon: './assets/menu/company.svg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'country',
|
||||||
|
link: '/team/country',
|
||||||
|
title: 'sidebar.team.country',
|
||||||
|
icon: './assets/menu/country.svg',
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
|
@ -86,6 +92,19 @@ export const TEAM = [
|
||||||
title: 'sidebar.team.extension',
|
title: 'sidebar.team.extension',
|
||||||
icon: './assets/menu/team_files_ext.svg',
|
icon: './assets/menu/team_files_ext.svg',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'refactor',
|
||||||
|
link: '/team/refactor',
|
||||||
|
title: 'sidebar.team.refactor',
|
||||||
|
icon: './assets/menu/refactor.svg',
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
id: 'release',
|
||||||
|
link: '/team/release',
|
||||||
|
title: 'sidebar.team.release',
|
||||||
|
icon: './assets/menu/team_release.svg',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'tasks',
|
id: 'tasks',
|
||||||
link: '/team/tasks',
|
link: '/team/tasks',
|
||||||
|
@ -93,10 +112,10 @@ export const TEAM = [
|
||||||
icon: './assets/menu/team_tasks.svg',
|
icon: './assets/menu/team_tasks.svg',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'release',
|
id: 'pr',
|
||||||
link: '/team/release',
|
link: '/team/pr',
|
||||||
title: 'sidebar.team.release',
|
title: 'sidebar.team.pr',
|
||||||
icon: './assets/menu/team_release.svg',
|
icon: './assets/menu/pull_request.svg',
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,7 +10,7 @@ import Pagination from 'ts/components/DataLoader/components/Pagination';
|
||||||
import getFakeLoader from 'ts/components/DataLoader/helpers/formatter';
|
import getFakeLoader from 'ts/components/DataLoader/helpers/formatter';
|
||||||
import NothingFound from 'ts/components/NothingFound';
|
import NothingFound from 'ts/components/NothingFound';
|
||||||
|
|
||||||
import { TasksView } from 'ts/pages/Team/components/Tasks';
|
import TasksView from 'ts/pages/Team/components/Tasks/View';
|
||||||
|
|
||||||
import IPersonCommonProps from '../interfaces/CommonProps';
|
import IPersonCommonProps from '../interfaces/CommonProps';
|
||||||
|
|
||||||
|
|
|
@ -1,262 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { observer } from 'mobx-react-lite';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import ISort from 'ts/interfaces/Sort';
|
|
||||||
import ICommit from 'ts/interfaces/Commit';
|
|
||||||
import IHashMap from 'ts/interfaces/HashMap';
|
|
||||||
import { IPaginationRequest, IPagination } from 'ts/interfaces/Pagination';
|
|
||||||
import { getDate, getMoney, getShortNumber } from 'ts/helpers/formatter';
|
|
||||||
import dataGripStore from 'ts/store/DataGrip';
|
|
||||||
|
|
||||||
import ICommonPageProps from 'ts/components/Page/interfaces/CommonPageProps';
|
|
||||||
import PageWrapper from 'ts/components/Page/wrapper';
|
|
||||||
import PageColumn from 'ts/components/Page/column';
|
|
||||||
import UiKitTags from 'ts/components/UiKit/components/Tags';
|
|
||||||
import DataLoader from 'ts/components/DataLoader';
|
|
||||||
import Pagination from 'ts/components/DataLoader/components/Pagination';
|
|
||||||
import getFakeLoader from 'ts/components/DataLoader/helpers/formatter';
|
|
||||||
import NothingFound from 'ts/components/NothingFound';
|
|
||||||
import Title from 'ts/components/Title';
|
|
||||||
import DataView from 'ts/components/DataView';
|
|
||||||
import Column from 'ts/components/Table/components/Column';
|
|
||||||
import { ColumnTypesEnum } from 'ts/components/Table/interfaces/Column';
|
|
||||||
import LineChart from 'ts/components/LineChart';
|
|
||||||
import getOptions from 'ts/components/LineChart/helpers/getOptions';
|
|
||||||
import Recommendations from 'ts/components/Recommendations';
|
|
||||||
|
|
||||||
import { getMax, getMaxByLength } from 'ts/pages/Common/helpers/getMax';
|
|
||||||
import Description from 'ts/components/Description';
|
|
||||||
|
|
||||||
interface AuthorViewProps {
|
|
||||||
response?: IPagination<any>;
|
|
||||||
updateSort?: Function;
|
|
||||||
rowsForExcel?: any[];
|
|
||||||
mode?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AuthorView({ response, updateSort, rowsForExcel, mode }: AuthorViewProps) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
if (!response) return null;
|
|
||||||
|
|
||||||
const [works, dismissed, staff] = [
|
|
||||||
t('page.team.author.type.work'),
|
|
||||||
t('page.team.author.type.dismissed'),
|
|
||||||
t('page.team.author.type.staff'),
|
|
||||||
];
|
|
||||||
|
|
||||||
const textWork = t('page.team.author.worked');
|
|
||||||
const textLosses = t('page.team.author.losses');
|
|
||||||
const daysWorked = getOptions({ order: [textWork, textLosses], suffix: 'page.team.author.days' });
|
|
||||||
const taskChart = getOptions({ max: getMaxByLength(response, 'tasks'), suffix: 'page.team.author.tasksSmall' });
|
|
||||||
const commitsChart = getOptions({ max: getMax(response, 'commits') });
|
|
||||||
const typeChart = getOptions({ order: dataGripStore.dataGrip.type.list });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DataView
|
|
||||||
rowsForExcel={rowsForExcel}
|
|
||||||
rows={response.content}
|
|
||||||
sort={response.sort}
|
|
||||||
updateSort={updateSort}
|
|
||||||
mode={mode}
|
|
||||||
type={mode === 'print' ? 'cards' : undefined}
|
|
||||||
columnCount={mode === 'print' ? 3 : undefined}
|
|
||||||
>
|
|
||||||
<Column
|
|
||||||
isFixed
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
formatter={(row: any, index: number) => (index + 1)}
|
|
||||||
width={40}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isFixed
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
properties="author"
|
|
||||||
title="page.team.pr.author"
|
|
||||||
width={200}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
title="page.team.author.status"
|
|
||||||
formatter={(row: any) => {
|
|
||||||
if (row.isStaff) return staff;
|
|
||||||
if (row.isDismissed) return dismissed;
|
|
||||||
return works;
|
|
||||||
}}
|
|
||||||
template={(value: string) => <UiKitTags value={value} />}
|
|
||||||
width={100}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isSortable="company"
|
|
||||||
title="page.team.author.company"
|
|
||||||
properties="lastCompany"
|
|
||||||
template={(value: string) => <UiKitTags value={value} />}
|
|
||||||
width={150}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
properties="firstCommit"
|
|
||||||
title="page.team.author.firstCommit"
|
|
||||||
width={130}
|
|
||||||
formatter={(commit: ICommit) => getDate(commit.timestamp)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
properties="lastCommit"
|
|
||||||
title="page.team.author.lastCommit"
|
|
||||||
width={130}
|
|
||||||
formatter={(commit: ICommit) => getDate(commit.timestamp)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
title="page.team.author.daysAll"
|
|
||||||
properties="daysAll"
|
|
||||||
formatter={(value: number) => value || 1}
|
|
||||||
width={90}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isSortable="daysWorked"
|
|
||||||
title="page.team.author.workedLosses"
|
|
||||||
minWidth={300}
|
|
||||||
template={(details: any) => (
|
|
||||||
<LineChart
|
|
||||||
options={daysWorked}
|
|
||||||
details={details}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
formatter={(row: any) => {
|
|
||||||
return { [textWork]: row.daysWorked, [textLosses]: row.daysLosses };
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
properties="tasks"
|
|
||||||
formatter={(tasks: string[]) => (tasks?.length || 0)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isSortable
|
|
||||||
properties="tasks"
|
|
||||||
title="page.team.author.tasks"
|
|
||||||
minWidth={200}
|
|
||||||
template={(value: number) => (
|
|
||||||
<LineChart
|
|
||||||
options={taskChart}
|
|
||||||
value={value}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
formatter={(tasks: any) => (tasks?.length || 0)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
title="page.team.author.daysForTask"
|
|
||||||
properties="daysForTask"
|
|
||||||
formatter={getShortNumber}
|
|
||||||
width={120}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
title="page.team.author.scopes"
|
|
||||||
properties="scopes"
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
properties="commits"
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isSortable
|
|
||||||
title="page.team.author.commits"
|
|
||||||
properties="commits"
|
|
||||||
minWidth={100}
|
|
||||||
template={(value: number) => (
|
|
||||||
<LineChart
|
|
||||||
options={commitsChart}
|
|
||||||
value={value}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
title="page.team.author.types"
|
|
||||||
properties="types"
|
|
||||||
width={400}
|
|
||||||
template={(details: IHashMap<number>) => (
|
|
||||||
<LineChart
|
|
||||||
options={typeChart}
|
|
||||||
details={details}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.NUMBER}
|
|
||||||
title="page.team.author.moneyAll"
|
|
||||||
properties="moneyAll"
|
|
||||||
formatter={getMoney}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.NUMBER}
|
|
||||||
title="page.team.author.moneyWorked"
|
|
||||||
properties="moneyWorked"
|
|
||||||
formatter={getMoney}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.NUMBER}
|
|
||||||
title="page.team.author.moneyLosses"
|
|
||||||
properties="moneyLosses"
|
|
||||||
formatter={getMoney}
|
|
||||||
/>
|
|
||||||
</DataView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
AuthorView.defaultProps = {
|
|
||||||
response: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Author = observer(({
|
|
||||||
mode,
|
|
||||||
}: ICommonPageProps): React.ReactElement | null => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const rows = dataGripStore.dataGrip.author.statistic;
|
|
||||||
|
|
||||||
if (!rows?.length) {
|
|
||||||
return mode !== 'print' ? (<NothingFound />) : null;
|
|
||||||
}
|
|
||||||
const recommendations = dataGripStore.dataGrip.recommendations.team?.byAuthor;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{mode !== 'fullscreen' && (
|
|
||||||
<Recommendations
|
|
||||||
mode={mode}
|
|
||||||
recommendations={recommendations}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Title title="page.team.author.title"/>
|
|
||||||
<DataLoader
|
|
||||||
to="response"
|
|
||||||
loader={(pagination?: IPaginationRequest, sort?: ISort[]) => getFakeLoader({
|
|
||||||
content: rows, pagination, sort, mode,
|
|
||||||
})}
|
|
||||||
watch={`${mode}${dataGripStore.hash}`}
|
|
||||||
>
|
|
||||||
<AuthorView
|
|
||||||
mode={mode}
|
|
||||||
rowsForExcel={rows}
|
|
||||||
/>
|
|
||||||
<Pagination />
|
|
||||||
</DataLoader>
|
|
||||||
<PageWrapper>
|
|
||||||
<PageColumn>
|
|
||||||
<Description
|
|
||||||
text={t('page.team.author.description1')}
|
|
||||||
/>
|
|
||||||
</PageColumn>
|
|
||||||
<PageColumn>
|
|
||||||
<Description
|
|
||||||
text={t('page.team.author.description2')}
|
|
||||||
/>
|
|
||||||
</PageColumn>
|
|
||||||
</PageWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Author;
|
|
|
@ -1,143 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import ICommit from 'ts/interfaces/Commit';
|
|
||||||
import { IPagination } from 'ts/interfaces/Pagination';
|
|
||||||
import { getDate } from 'ts/helpers/formatter';
|
|
||||||
import dataGripStore from 'ts/store/DataGrip';
|
|
||||||
|
|
||||||
import UiKitTags from 'ts/components/UiKit/components/Tags';
|
|
||||||
import DataView from 'ts/components/DataView';
|
|
||||||
import Column from 'ts/components/Table/components/Column';
|
|
||||||
import { ColumnTypesEnum } from 'ts/components/Table/interfaces/Column';
|
|
||||||
import LineChart from 'ts/components/LineChart';
|
|
||||||
import getOptions from 'ts/components/LineChart/helpers/getOptions';
|
|
||||||
|
|
||||||
import { getMax } from 'ts/pages/Common/helpers/getMax';
|
|
||||||
|
|
||||||
import Employments from './Employments';
|
|
||||||
|
|
||||||
interface CompaniesProps {
|
|
||||||
response?: IPagination<any>;
|
|
||||||
updateSort?: Function;
|
|
||||||
rowsForExcel?: any[];
|
|
||||||
mode?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Companies({ response, updateSort, rowsForExcel, mode }: CompaniesProps) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
if (!response) return null;
|
|
||||||
|
|
||||||
const [works, dismissed] = [
|
|
||||||
t('page.team.author.type.work'),
|
|
||||||
t('page.team.author.type.dismissed'),
|
|
||||||
];
|
|
||||||
|
|
||||||
const taskChart = getOptions({ max: getMax(response, 'totalTasks'), suffix: 'page.team.author.tasksSmall' });
|
|
||||||
const daysChart = getOptions({ max: getMax(response, 'totalDays'), suffix: 'page.team.author.days' });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DataView
|
|
||||||
rowsForExcel={rowsForExcel}
|
|
||||||
rows={response.content}
|
|
||||||
sort={response.sort}
|
|
||||||
updateSort={updateSort}
|
|
||||||
type={mode === 'print' ? 'cards' : undefined}
|
|
||||||
columnCount={mode === 'print' ? 3 : undefined}
|
|
||||||
>
|
|
||||||
<Column
|
|
||||||
isFixed
|
|
||||||
template={ColumnTypesEnum.DETAILS}
|
|
||||||
width={40}
|
|
||||||
formatter={(row: any) => {
|
|
||||||
const content = row.employments.map((name: string) => (
|
|
||||||
dataGripStore?.dataGrip?.author?.statisticByName?.[name]
|
|
||||||
)).filter((v: any) => v);
|
|
||||||
return (
|
|
||||||
<Employments // @ts-ignore
|
|
||||||
response={{ content }}
|
|
||||||
mode="details"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isFixed
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
properties="company"
|
|
||||||
title="page.team.pr.author"
|
|
||||||
width={200}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
title="page.team.author.status"
|
|
||||||
formatter={(row: any) => (row.isActive ? works : dismissed)}
|
|
||||||
template={(value: string) => <UiKitTags value={value} />}
|
|
||||||
width={100}
|
|
||||||
/>
|
|
||||||
{/*<Column*/}
|
|
||||||
{/* template={ColumnTypesEnum.SHORT_NUMBER}*/}
|
|
||||||
{/* title="page.team.company.people"*/}
|
|
||||||
{/* properties="totalEmployments"*/}
|
|
||||||
{/* width={90}*/}
|
|
||||||
{/*/>*/}
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
properties="firstCommit"
|
|
||||||
title="page.team.author.firstCommit"
|
|
||||||
width={130}
|
|
||||||
formatter={(commit: ICommit) => getDate(commit.timestamp)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
properties="lastCommit"
|
|
||||||
title="page.team.author.lastCommit"
|
|
||||||
width={130}
|
|
||||||
formatter={(commit: ICommit) => getDate(commit.timestamp)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
properties="totalDays"
|
|
||||||
width={90}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isSortable="totalDays"
|
|
||||||
title="page.team.author.daysAll"
|
|
||||||
properties="totalDays"
|
|
||||||
width={150}
|
|
||||||
template={(value: number) => (
|
|
||||||
<LineChart
|
|
||||||
options={daysChart}
|
|
||||||
value={value}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
properties="totalTasks"
|
|
||||||
width={90}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isSortable="totalTasks"
|
|
||||||
title="page.team.author.tasks"
|
|
||||||
properties="totalTasks"
|
|
||||||
width={150}
|
|
||||||
template={(value: number) => (
|
|
||||||
<LineChart
|
|
||||||
options={taskChart}
|
|
||||||
value={value}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
properties="emptyCell"
|
|
||||||
minWidth={40}
|
|
||||||
/>
|
|
||||||
</DataView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Companies.defaultProps = {
|
|
||||||
response: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Companies;
|
|
|
@ -1,146 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import ICommit from 'ts/interfaces/Commit';
|
|
||||||
import { IPagination } from 'ts/interfaces/Pagination';
|
|
||||||
import { getDate, getMoney } from 'ts/helpers/formatter';
|
|
||||||
import dataGripStore from 'ts/store/DataGrip';
|
|
||||||
|
|
||||||
import UiKitTags from 'ts/components/UiKit/components/Tags';
|
|
||||||
import DataView from 'ts/components/DataView';
|
|
||||||
import Column from 'ts/components/Table/components/Column';
|
|
||||||
import { ColumnTypesEnum } from 'ts/components/Table/interfaces/Column';
|
|
||||||
import LineChart from 'ts/components/LineChart';
|
|
||||||
import getOptions from 'ts/components/LineChart/helpers/getOptions';
|
|
||||||
|
|
||||||
interface EmploymentsProps {
|
|
||||||
response?: IPagination<any>;
|
|
||||||
updateSort?: Function;
|
|
||||||
rowsForExcel?: any[];
|
|
||||||
mode?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Employments({ response, updateSort, rowsForExcel, mode }: EmploymentsProps) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
if (!response) return null;
|
|
||||||
|
|
||||||
const [works, dismissed, staff] = [
|
|
||||||
t('page.team.author.type.work'),
|
|
||||||
t('page.team.author.type.dismissed'),
|
|
||||||
t('page.team.author.type.staff'),
|
|
||||||
];
|
|
||||||
|
|
||||||
const textWork = t('page.team.author.worked');
|
|
||||||
const textLosses = t('page.team.author.losses');
|
|
||||||
const daysWorked = getOptions({ order: [textWork, textLosses], suffix: 'page.team.author.days' });
|
|
||||||
const typeChart = getOptions({
|
|
||||||
suffix: 'page.team.author.tasksSmall',
|
|
||||||
order: dataGripStore.dataGrip.type.list,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DataView
|
|
||||||
rowsForExcel={rowsForExcel}
|
|
||||||
rows={response.content}
|
|
||||||
sort={response.sort}
|
|
||||||
updateSort={updateSort}
|
|
||||||
mode={mode}
|
|
||||||
type={mode === 'print' ? 'cards' : undefined}
|
|
||||||
columnCount={mode === 'print' ? 3 : undefined}
|
|
||||||
>
|
|
||||||
<Column
|
|
||||||
isFixed
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
formatter={(row: any, index: number) => (index + 1)}
|
|
||||||
width={40}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isFixed
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
properties="author"
|
|
||||||
title="page.team.pr.author"
|
|
||||||
width={158}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
formatter={(row: any) => {
|
|
||||||
if (row.isStaff) return staff;
|
|
||||||
if (row.isDismissed) return dismissed;
|
|
||||||
return works;
|
|
||||||
}}
|
|
||||||
template={(value: string) => <UiKitTags value={value} />}
|
|
||||||
width={100}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
properties="firstCommit"
|
|
||||||
width={130}
|
|
||||||
formatter={(commit: ICommit) => getDate(commit.timestamp)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
properties="lastCommit"
|
|
||||||
width={130}
|
|
||||||
formatter={(commit: ICommit) => getDate(commit.timestamp)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
properties="daysAll"
|
|
||||||
formatter={(value: number) => value || 1}
|
|
||||||
width={90}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isSortable="daysWorked"
|
|
||||||
width={150}
|
|
||||||
template={(details: any) => (
|
|
||||||
<LineChart
|
|
||||||
options={daysWorked}
|
|
||||||
details={details}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
formatter={(row: any) => {
|
|
||||||
return { [textWork]: row.daysWorked, [textLosses]: row.daysLosses };
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
properties="tasks"
|
|
||||||
formatter={(tasks: string[]) => (tasks?.length || 0)}
|
|
||||||
width={90}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isSortable
|
|
||||||
width={150}
|
|
||||||
template={(row: any) => (
|
|
||||||
<LineChart
|
|
||||||
options={typeChart}
|
|
||||||
details={row.types}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.NUMBER}
|
|
||||||
title="page.team.author.moneyAll"
|
|
||||||
properties="moneyAll"
|
|
||||||
formatter={getMoney}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.NUMBER}
|
|
||||||
title="page.team.author.moneyWorked"
|
|
||||||
properties="moneyWorked"
|
|
||||||
formatter={getMoney}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.NUMBER}
|
|
||||||
title="page.team.author.moneyLosses"
|
|
||||||
properties="moneyLosses"
|
|
||||||
formatter={getMoney}
|
|
||||||
/>
|
|
||||||
</DataView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Employments.defaultProps = {
|
|
||||||
response: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Employments;
|
|
|
@ -11,30 +11,32 @@ import Pagination from 'ts/components/DataLoader/components/Pagination';
|
||||||
import getFakeLoader from 'ts/components/DataLoader/helpers/formatter';
|
import getFakeLoader from 'ts/components/DataLoader/helpers/formatter';
|
||||||
import NothingFound from 'ts/components/NothingFound';
|
import NothingFound from 'ts/components/NothingFound';
|
||||||
import Title from 'ts/components/Title';
|
import Title from 'ts/components/Title';
|
||||||
import Companies from './Companies';
|
import Companies from './components/Companies';
|
||||||
|
import CompanyCharts from './components/Charts';
|
||||||
|
|
||||||
const Company = observer(({
|
const Company = observer(({
|
||||||
mode,
|
mode,
|
||||||
}: ICommonPageProps): React.ReactElement | null => {
|
}: ICommonPageProps): React.ReactElement | null => {
|
||||||
const rows = dataGripStore.dataGrip.company.statistic;
|
const companyRows = dataGripStore.dataGrip.company.statistic;
|
||||||
|
|
||||||
if (!rows?.length) {
|
if (!companyRows?.length) {
|
||||||
return mode !== 'print' ? (<NothingFound />) : null;
|
return mode !== 'print' ? (<NothingFound />) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title title="page.team.author.title"/>
|
<CompanyCharts />
|
||||||
|
<Title title="page.team.company.title"/>
|
||||||
<DataLoader
|
<DataLoader
|
||||||
to="response"
|
to="response"
|
||||||
loader={(pagination?: IPaginationRequest, sort?: ISort[]) => getFakeLoader({
|
loader={(pagination?: IPaginationRequest, sort?: ISort[]) => getFakeLoader({
|
||||||
content: rows, pagination, sort, mode,
|
content: companyRows, pagination, sort, mode,
|
||||||
})}
|
})}
|
||||||
watch={`${mode}${dataGripStore.hash}`}
|
watch={`${mode}${dataGripStore.hash}`}
|
||||||
>
|
>
|
||||||
<Companies
|
<Companies
|
||||||
mode={mode}
|
mode={mode}
|
||||||
rowsForExcel={rows}
|
rowsForExcel={companyRows}
|
||||||
/>
|
/>
|
||||||
<Pagination />
|
<Pagination />
|
||||||
</DataLoader>
|
</DataLoader>
|
||||||
|
|
|
@ -7,24 +7,20 @@ import treeStore from '../../store/Tree';
|
||||||
const FileBreadcrumbs = observer((): React.ReactElement => {
|
const FileBreadcrumbs = observer((): React.ReactElement => {
|
||||||
const directories = treeStore.selectedPath
|
const directories = treeStore.selectedPath
|
||||||
.map((dirName: string, index: number) => (
|
.map((dirName: string, index: number) => (
|
||||||
<>
|
<span key={dirName}>
|
||||||
<span
|
<span className={style.file_breadcrumbs_text}>
|
||||||
key={`${dirName}.`}
|
{'/'}
|
||||||
className={style.file_breadcrumbs_text}
|
</span>
|
||||||
>
|
<span
|
||||||
{'/'}
|
className={`${style.file_breadcrumbs_text} ${style.file_breadcrumbs_link}`}
|
||||||
|
onClick={() => {
|
||||||
|
const newPath = treeStore.selectedPath.slice(0, index + 1);
|
||||||
|
treeStore.updateFilter('selectedPath', newPath);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{dirName}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span
|
|
||||||
key={dirName}
|
|
||||||
className={`${style.file_breadcrumbs_text} ${style.file_breadcrumbs_link}`}
|
|
||||||
onClick={() => {
|
|
||||||
const newPath = treeStore.selectedPath.slice(0, index + 1);
|
|
||||||
treeStore.updateFilter('selectedPath', newPath);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{dirName}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
));
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { getMax } from 'ts/pages/Common/helpers/getMax';
|
||||||
import { getDate } from 'ts/helpers/formatter';
|
import { getDate } from 'ts/helpers/formatter';
|
||||||
|
|
||||||
import treeStore from '../../store/Tree';
|
import treeStore from '../../store/Tree';
|
||||||
|
import Tasks from './Tasks';
|
||||||
|
|
||||||
interface IViewProps {
|
interface IViewProps {
|
||||||
response?: IPagination<any>;
|
response?: IPagination<any>;
|
||||||
|
@ -23,6 +24,8 @@ function View({ response }: IViewProps) {
|
||||||
if (!response) return null;
|
if (!response) return null;
|
||||||
|
|
||||||
const fileSizeChart = getOptions({ max: getMax(response, 'lines'), suffix: 'page.team.tree.line' });
|
const fileSizeChart = getOptions({ max: getMax(response, 'lines'), suffix: 'page.team.tree.line' });
|
||||||
|
const totalTasksChart = getOptions({ max: getMax(response, 'totalTasks'), suffix: 'page.team.tree.tasks' });
|
||||||
|
const totalDaysChart = getOptions({ max: getMax(response, 'totalDays'), suffix: 'page.team.tree.days' });
|
||||||
const addedLinesChart = getOptions({ order: dataGripStore.dataGrip.author.list, suffix: 'page.team.tree.line' });
|
const addedLinesChart = getOptions({ order: dataGripStore.dataGrip.author.list, suffix: 'page.team.tree.line' });
|
||||||
const addedRemovedChangedChart = getOptions({ order: [
|
const addedRemovedChangedChart = getOptions({ order: [
|
||||||
'page.team.tree.linesAdded',
|
'page.team.tree.linesAdded',
|
||||||
|
@ -44,6 +47,24 @@ function View({ response }: IViewProps) {
|
||||||
return (treeStore.authorId && !author) || (commits < limit);
|
return (treeStore.authorId && !author) || (commits < limit);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Column
|
||||||
|
isFixed
|
||||||
|
template={ColumnTypesEnum.DETAILS}
|
||||||
|
width={40}
|
||||||
|
properties="tasks"
|
||||||
|
formatter={(row: any) => {
|
||||||
|
const content = Array.from(row?.tasks)
|
||||||
|
.reverse()
|
||||||
|
.map((taskId: any) => dataGripStore.dataGrip.tasks.statisticByName.get(taskId))
|
||||||
|
.filter(v => v);
|
||||||
|
return (
|
||||||
|
<Tasks // @ts-ignore
|
||||||
|
response={{ content }}
|
||||||
|
mode="details"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Column
|
<Column
|
||||||
isFixed
|
isFixed
|
||||||
template={ColumnTypesEnum.STRING}
|
template={ColumnTypesEnum.STRING}
|
||||||
|
@ -56,13 +77,14 @@ function View({ response }: IViewProps) {
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
isSortable
|
isSortable
|
||||||
width={50}
|
width={60}
|
||||||
properties="lines"
|
properties="lines"
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
template={ColumnTypesEnum.SHORT_NUMBER}
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
isSortable
|
isSortable
|
||||||
properties="lines"
|
properties="lines"
|
||||||
|
title="page.team.tree.totalLines"
|
||||||
minWidth={100}
|
minWidth={100}
|
||||||
template={(value: any) => (
|
template={(value: any) => (
|
||||||
<LineChart
|
<LineChart
|
||||||
|
@ -71,6 +93,42 @@ function View({ response }: IViewProps) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<Column
|
||||||
|
isSortable
|
||||||
|
width={50}
|
||||||
|
properties="totalTasks"
|
||||||
|
template={ColumnTypesEnum.SHORT_NUMBER}
|
||||||
|
/>
|
||||||
|
<Column
|
||||||
|
isSortable
|
||||||
|
properties="totalTasks"
|
||||||
|
title="page.team.tree.totalTasks"
|
||||||
|
minWidth={100}
|
||||||
|
template={(value: any) => (
|
||||||
|
<LineChart
|
||||||
|
options={totalTasksChart}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Column
|
||||||
|
isSortable
|
||||||
|
width={50}
|
||||||
|
properties="totalDays"
|
||||||
|
template={ColumnTypesEnum.SHORT_NUMBER}
|
||||||
|
/>
|
||||||
|
<Column
|
||||||
|
isSortable
|
||||||
|
properties="totalDays"
|
||||||
|
title="page.team.tree.totalDays"
|
||||||
|
minWidth={100}
|
||||||
|
template={(value: any) => (
|
||||||
|
<LineChart
|
||||||
|
options={totalDaysChart}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<Column
|
<Column
|
||||||
isSortable
|
isSortable
|
||||||
template={ColumnTypesEnum.STRING}
|
template={ColumnTypesEnum.STRING}
|
||||||
|
|
|
@ -28,9 +28,9 @@ function AllPR({
|
||||||
}: IPRViewProps) {
|
}: IPRViewProps) {
|
||||||
if (!response) return null;
|
if (!response) return null;
|
||||||
|
|
||||||
const workChart = getOptions({ max: getMax(response, 'workDays') });
|
const tasks = dataGripStore.dataGrip.tasks.statisticByName;
|
||||||
const delayChart = getOptions({ max: getMax(response, 'delayDays') });
|
const workChart = getOptions({ max: getMax(response, 'daysInWork') });
|
||||||
const commitsChart = getOptions({ order: dataGripStore.dataGrip.author.list });
|
const delayChart = getOptions({ max: getMax(response, 'daysReview') });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataView
|
<DataView
|
||||||
|
@ -38,6 +38,7 @@ function AllPR({
|
||||||
rows={response.content}
|
rows={response.content}
|
||||||
sort={response.sort}
|
sort={response.sort}
|
||||||
updateSort={updateSort}
|
updateSort={updateSort}
|
||||||
|
mode={mode}
|
||||||
type={mode === 'print' ? 'cards' : undefined}
|
type={mode === 'print' ? 'cards' : undefined}
|
||||||
columnCount={mode === 'print' ? 2 : undefined}
|
columnCount={mode === 'print' ? 2 : undefined}
|
||||||
fullScreenMode="all"
|
fullScreenMode="all"
|
||||||
|
@ -47,7 +48,7 @@ function AllPR({
|
||||||
isSortable
|
isSortable
|
||||||
title="page.team.pr.task"
|
title="page.team.pr.task"
|
||||||
properties="task"
|
properties="task"
|
||||||
width={120}
|
width={140}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Column
|
<Column
|
||||||
|
@ -66,31 +67,29 @@ function AllPR({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Column
|
<Column
|
||||||
isSortable
|
|
||||||
template={ColumnTypesEnum.STRING}
|
template={ColumnTypesEnum.STRING}
|
||||||
title="page.team.pr.firstCommitTime"
|
title="page.team.pr.firstCommitTime"
|
||||||
properties="beginTaskTime"
|
formatter={(row: any) => getDate(tasks.get(row.task)?.from || row.beginTaskTime)}
|
||||||
formatter={getDate}
|
|
||||||
width={130}
|
width={130}
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
isSortable
|
isSortable
|
||||||
template={ColumnTypesEnum.STRING}
|
template={ColumnTypesEnum.STRING}
|
||||||
title="page.team.pr.lastCommitTime"
|
title="page.team.pr.lastCommitTime"
|
||||||
properties="endTaskTime"
|
properties="dateCreate"
|
||||||
formatter={getDate}
|
formatter={getDate}
|
||||||
width={130}
|
width={130}
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
template={ColumnTypesEnum.SHORT_NUMBER}
|
||||||
properties="workDays"
|
properties="daysInWork"
|
||||||
width={40}
|
width={40}
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
isSortable
|
isSortable
|
||||||
title="page.team.pr.all.workDays"
|
title="page.team.pr.all.workDays"
|
||||||
properties="workDays"
|
properties="daysInWork"
|
||||||
minWidth={100}
|
minWidth={170}
|
||||||
template={(value: any) => (
|
template={(value: any) => (
|
||||||
<LineChart
|
<LineChart
|
||||||
options={workChart}
|
options={workChart}
|
||||||
|
@ -100,31 +99,14 @@ function AllPR({
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
template={ColumnTypesEnum.SHORT_NUMBER}
|
||||||
properties="commits"
|
properties="daysReview"
|
||||||
width={40}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isSortable
|
|
||||||
title="page.team.pr.commits"
|
|
||||||
properties="commitsByAuthors"
|
|
||||||
minWidth={100}
|
|
||||||
template={(details: any) => (
|
|
||||||
<LineChart
|
|
||||||
options={commitsChart}
|
|
||||||
details={details}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
properties="delayDays"
|
|
||||||
width={40}
|
width={40}
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
isSortable
|
isSortable
|
||||||
title="page.team.pr.all.delayDays"
|
title="page.team.pr.all.delayDays"
|
||||||
properties="delayDays"
|
properties="daysReview"
|
||||||
minWidth={200}
|
minWidth={170}
|
||||||
template={(value: any) => (
|
template={(value: any) => (
|
||||||
<LineChart
|
<LineChart
|
||||||
options={delayChart}
|
options={delayChart}
|
||||||
|
@ -136,7 +118,7 @@ function AllPR({
|
||||||
isSortable
|
isSortable
|
||||||
template={ColumnTypesEnum.STRING}
|
template={ColumnTypesEnum.STRING}
|
||||||
title="page.team.pr.date"
|
title="page.team.pr.date"
|
||||||
properties="milliseconds"
|
properties="dateMerge"
|
||||||
formatter={getDate}
|
formatter={getDate}
|
||||||
width={130}
|
width={130}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -15,10 +15,10 @@ function Total() {
|
||||||
|
|
||||||
const allPR = dataGripStore.dataGrip.pr.statistic;
|
const allPR = dataGripStore.dataGrip.pr.statistic;
|
||||||
|
|
||||||
const workChart = DataGripByPR.getPRByGroups(allPR, 'workDays');
|
const workChart = DataGripByPR.getPRByGroups(allPR, 'daysInWork');
|
||||||
const workChartOptions = getOptions({ order: workChart.order, limit: 3, suffix: 'page.team.pr.tasks' });
|
const workChartOptions = getOptions({ order: workChart.order, limit: 3, suffix: 'page.team.pr.tasks' });
|
||||||
|
|
||||||
const delayChart = DataGripByPR.getPRByGroups(allPR, 'delayDays');
|
const delayChart = DataGripByPR.getPRByGroups(allPR, 'daysReview');
|
||||||
const delayChartOptions = getOptions({ order: delayChart.order, limit: 3, suffix: 'PR' });
|
const delayChartOptions = getOptions({ order: delayChart.order, limit: 3, suffix: 'PR' });
|
||||||
|
|
||||||
const workDaysWeightedAverage = Math.round(workChart.weightedAverage);
|
const workDaysWeightedAverage = Math.round(workChart.weightedAverage);
|
||||||
|
|
|
@ -16,28 +16,46 @@ import fullScreen from 'ts/store/FullScreen';
|
||||||
|
|
||||||
import Total from './Total';
|
import Total from './Total';
|
||||||
import Authors from './Authors';
|
import Authors from './Authors';
|
||||||
|
import Anonymous from './Anonymous';
|
||||||
import All from './All';
|
import All from './All';
|
||||||
|
|
||||||
|
function getGroupsByTasks(list: any[]) {
|
||||||
|
const withTask: any[] = [];
|
||||||
|
const withoutTask: any[] = [];
|
||||||
|
list.forEach((pr: any) => {
|
||||||
|
if (pr.task) withTask.push(pr);
|
||||||
|
else withoutTask.push(pr);
|
||||||
|
});
|
||||||
|
return [withTask, withoutTask.reverse()];
|
||||||
|
}
|
||||||
|
|
||||||
const PR = observer(({
|
const PR = observer(({
|
||||||
mode,
|
mode,
|
||||||
}: ICommonPageProps): React.ReactElement | null => {
|
}: ICommonPageProps): React.ReactElement | null => {
|
||||||
const allPR = dataGripStore.dataGrip.pr.statistic;
|
const allPR = dataGripStore.dataGrip.pr.statistic;
|
||||||
const rows = allPR.filter((item: any) => item.delayDays > 3);
|
const [withTask, withoutTask] = getGroupsByTasks(allPR);
|
||||||
if (rows?.length < 2) return mode !== 'print' ? (<NothingFound />) : null;
|
const longReview = withTask.filter((item: any) => item.daysReview > 4);
|
||||||
|
|
||||||
|
const canShowByReview = (!fullScreen.isOpen || fullScreen.mode === 'all') && longReview.length > 1;
|
||||||
|
const canShowByAnonymous = (!fullScreen.isOpen || fullScreen.mode === 'anonymous') && withoutTask.length;
|
||||||
|
|
||||||
|
if (!canShowByReview && !canShowByAnonymous) {
|
||||||
|
return mode !== 'print' ? (<NothingFound />) : null;
|
||||||
|
}
|
||||||
|
|
||||||
const PRbyName = dataGripStore.dataGrip.pr.statisticByName;
|
const PRbyName = dataGripStore.dataGrip.pr.statisticByName;
|
||||||
const authorsStat = Object.values(PRbyName);
|
const authorsStat = Object.values(PRbyName);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!fullScreen.isOpen && (
|
{!fullScreen.isOpen && canShowByReview && (
|
||||||
<>
|
<>
|
||||||
<Title title="page.team.pr.oneTaskDays"/>
|
<Title title="page.team.pr.oneTaskDays"/>
|
||||||
<Total/>
|
<Total/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!fullScreen.isOpen || fullScreen.mode === 'author' ? (
|
{canShowByReview ? (
|
||||||
<>
|
<>
|
||||||
<Title title="page.team.pr.statByAuthors"/>
|
<Title title="page.team.pr.statByAuthors"/>
|
||||||
<DataLoader
|
<DataLoader
|
||||||
|
@ -52,18 +70,12 @@ const PR = observer(({
|
||||||
/>
|
/>
|
||||||
<Pagination/>
|
<Pagination/>
|
||||||
</DataLoader>
|
</DataLoader>
|
||||||
</>
|
<PageBreak/>
|
||||||
) : null}
|
|
||||||
|
|
||||||
<PageBreak/>
|
|
||||||
|
|
||||||
{!fullScreen.isOpen || fullScreen.mode === 'all' ? (
|
|
||||||
<>
|
|
||||||
<Title title="page.team.pr.longDelay"/>
|
<Title title="page.team.pr.longDelay"/>
|
||||||
<DataLoader
|
<DataLoader
|
||||||
to="response"
|
to="response"
|
||||||
loader={(pagination?: IPaginationRequest, sort?: ISort[]) => getFakeLoader({
|
loader={(pagination?: IPaginationRequest, sort?: ISort[]) => getFakeLoader({
|
||||||
content: rows,
|
content: longReview,
|
||||||
pagination: mode === 'print'
|
pagination: mode === 'print'
|
||||||
? { size: 20 }
|
? { size: 20 }
|
||||||
: pagination,
|
: pagination,
|
||||||
|
@ -72,7 +84,31 @@ const PR = observer(({
|
||||||
>
|
>
|
||||||
<All
|
<All
|
||||||
mode={mode}
|
mode={mode}
|
||||||
rowsForExcel={rows}
|
rowsForExcel={longReview}
|
||||||
|
/>
|
||||||
|
{mode !== 'print' && <Pagination/>}
|
||||||
|
</DataLoader>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<PageBreak/>
|
||||||
|
|
||||||
|
{canShowByAnonymous ? (
|
||||||
|
<>
|
||||||
|
<Title title="page.team.pr.anonymous"/>
|
||||||
|
<DataLoader
|
||||||
|
to="response"
|
||||||
|
loader={(pagination?: IPaginationRequest, sort?: ISort[]) => getFakeLoader({
|
||||||
|
content: withoutTask,
|
||||||
|
pagination: mode === 'print'
|
||||||
|
? { size: 20 }
|
||||||
|
: pagination,
|
||||||
|
sort,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Anonymous
|
||||||
|
mode={mode}
|
||||||
|
rowsForExcel={withoutTask}
|
||||||
/>
|
/>
|
||||||
{mode !== 'print' && <Pagination/>}
|
{mode !== 'print' && <Pagination/>}
|
||||||
</DataLoader>
|
</DataLoader>
|
||||||
|
|
|
@ -1,164 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { observer } from 'mobx-react-lite';
|
|
||||||
|
|
||||||
import { IPaginationRequest, IPagination } from 'ts/interfaces/Pagination';
|
|
||||||
import dataGripStore from 'ts/store/DataGrip';
|
|
||||||
|
|
||||||
import ICommonPageProps from 'ts/components/Page/interfaces/CommonPageProps';
|
|
||||||
import DataLoader from 'ts/components/DataLoader';
|
|
||||||
import Pagination from 'ts/components/DataLoader/components/Pagination';
|
|
||||||
import getFakeLoader from 'ts/components/DataLoader/helpers/formatter';
|
|
||||||
import NothingFound from 'ts/components/NothingFound';
|
|
||||||
import Title from 'ts/components/Title';
|
|
||||||
import DataView from 'ts/components/DataView';
|
|
||||||
import Column from 'ts/components/Table/components/Column';
|
|
||||||
import { ColumnTypesEnum } from 'ts/components/Table/interfaces/Column';
|
|
||||||
import getOptions from 'ts/components/LineChart/helpers/getOptions';
|
|
||||||
import LineChart from 'ts/components/LineChart';
|
|
||||||
import { getMax } from 'ts/pages/Common/helpers/getMax';
|
|
||||||
import { getDate } from 'ts/helpers/formatter';
|
|
||||||
|
|
||||||
import AllPR from './PR/All';
|
|
||||||
|
|
||||||
interface IReleaseViewProps {
|
|
||||||
response?: IPagination<any>;
|
|
||||||
updateSort?: Function;
|
|
||||||
rowsForExcel?: any[];
|
|
||||||
mode?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ReleaseView({ response, updateSort, rowsForExcel, mode }: IReleaseViewProps) {
|
|
||||||
if (!response) return null;
|
|
||||||
|
|
||||||
const delay = getMax(response, 'delayInDays');
|
|
||||||
const waiting = getMax(response, 'waitingInDays');
|
|
||||||
const max = Math.max(delay, waiting);
|
|
||||||
const delayChart = getOptions({ max, suffix: 'page.team.release.chart' });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DataView
|
|
||||||
rowsForExcel={rowsForExcel}
|
|
||||||
rows={response.content}
|
|
||||||
sort={response.sort}
|
|
||||||
updateSort={updateSort}
|
|
||||||
type={mode === 'print' ? 'cards' : undefined}
|
|
||||||
columnCount={mode === 'print' ? 3 : undefined}
|
|
||||||
>
|
|
||||||
<Column
|
|
||||||
isFixed
|
|
||||||
template={ColumnTypesEnum.DETAILS}
|
|
||||||
width={40}
|
|
||||||
formatter={(row: any) => {
|
|
||||||
const content = row.pr.map((commit: any) => (
|
|
||||||
dataGripStore?.dataGrip?.pr?.pr?.get(commit.prId)
|
|
||||||
)).filter((item: any) => item?.firstCommit);
|
|
||||||
return (
|
|
||||||
<AllPR // @ts-ignore
|
|
||||||
response={{ content }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isFixed
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
title="page.team.release.title"
|
|
||||||
properties="title"
|
|
||||||
width={200}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
title="page.team.release.from"
|
|
||||||
width={150}
|
|
||||||
properties="from"
|
|
||||||
formatter={getDate}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
title="page.team.release.to"
|
|
||||||
width={150}
|
|
||||||
properties="to"
|
|
||||||
formatter={getDate}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
title="page.team.release.prLength"
|
|
||||||
properties="prLength"
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
properties="delayInDays"
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isSortable
|
|
||||||
title="page.team.release.delay"
|
|
||||||
properties="delayInDays"
|
|
||||||
width={170}
|
|
||||||
minWidth={170}
|
|
||||||
template={(value: number) => (
|
|
||||||
<LineChart
|
|
||||||
options={delayChart}
|
|
||||||
value={value}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
properties="waitingInDays"
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isSortable
|
|
||||||
title="page.team.release.waiting"
|
|
||||||
properties="waitingInDays"
|
|
||||||
width={170}
|
|
||||||
minWidth={170}
|
|
||||||
template={(value: number) => (
|
|
||||||
<LineChart
|
|
||||||
options={delayChart}
|
|
||||||
value={value}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</DataView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseView.defaultProps = {
|
|
||||||
response: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Release = observer(({
|
|
||||||
mode,
|
|
||||||
}: ICommonPageProps): React.ReactElement | null => {
|
|
||||||
const rows = dataGripStore.dataGrip.release.statistic;
|
|
||||||
if (rows?.length < 2) return mode !== 'print' ? (<NothingFound />) : null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{mode === 'print' ? (
|
|
||||||
<Title title="sidebar.team.extension"/>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<DataLoader
|
|
||||||
to="response"
|
|
||||||
loader={(pagination?: IPaginationRequest) => getFakeLoader({
|
|
||||||
content: rows, pagination, mode,
|
|
||||||
})}
|
|
||||||
watch={`${mode}${dataGripStore.hash}`}
|
|
||||||
>
|
|
||||||
<ReleaseView
|
|
||||||
mode={mode}
|
|
||||||
rowsForExcel={rows}
|
|
||||||
/>
|
|
||||||
<Pagination />
|
|
||||||
</DataLoader>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Release;
|
|
|
@ -1,176 +0,0 @@
|
||||||
import React, { useState } from 'react';
|
|
||||||
import { observer } from 'mobx-react-lite';
|
|
||||||
|
|
||||||
import ISort from 'ts/interfaces/Sort';
|
|
||||||
import { IPaginationRequest, IPagination } from 'ts/interfaces/Pagination';
|
|
||||||
import dataGripStore from 'ts/store/DataGrip';
|
|
||||||
|
|
||||||
import ICommonPageProps from 'ts/components/Page/interfaces/CommonPageProps';
|
|
||||||
import DataLoader from 'ts/components/DataLoader';
|
|
||||||
import Pagination from 'ts/components/DataLoader/components/Pagination';
|
|
||||||
import getFakeLoader from 'ts/components/DataLoader/helpers/formatter';
|
|
||||||
import NothingFound from 'ts/components/NothingFound';
|
|
||||||
import DataView from 'ts/components/DataView';
|
|
||||||
import Column from 'ts/components/Table/components/Column';
|
|
||||||
import { ColumnTypesEnum } from 'ts/components/Table/interfaces/Column';
|
|
||||||
import LineChart from 'ts/components/LineChart';
|
|
||||||
import getOptions from 'ts/components/LineChart/helpers/getOptions';
|
|
||||||
import UiKitTags from 'ts/components/UiKit/components/Tags';
|
|
||||||
import { PRLink, TaskLink } from 'ts/components/ExternalLink';
|
|
||||||
import Title from 'ts/components/Title';
|
|
||||||
import PageWrapper from 'ts/components/Page/wrapper';
|
|
||||||
|
|
||||||
import { getMax } from 'ts/pages/Common/helpers/getMax';
|
|
||||||
import { getDate } from 'ts/helpers/formatter';
|
|
||||||
|
|
||||||
import TasksFilters from './TasksFilters';
|
|
||||||
|
|
||||||
interface ITasksViewProps {
|
|
||||||
response?: IPagination<any>;
|
|
||||||
updateSort?: Function;
|
|
||||||
rowsForExcel?: any[];
|
|
||||||
mode?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TasksView({ response, updateSort, rowsForExcel, mode }: ITasksViewProps) {
|
|
||||||
if (!response) return null;
|
|
||||||
|
|
||||||
const commitsChart = getOptions({ max: getMax(response, 'commits'), suffix: 'page.team.type.tasksSmall' });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DataView
|
|
||||||
rowsForExcel={rowsForExcel}
|
|
||||||
rows={response.content}
|
|
||||||
sort={response.sort}
|
|
||||||
updateSort={updateSort}
|
|
||||||
type={mode === 'print' ? 'cards' : undefined}
|
|
||||||
columnCount={mode === 'print' ? 3 : undefined}
|
|
||||||
>
|
|
||||||
<Column
|
|
||||||
isFixed
|
|
||||||
isSortable
|
|
||||||
template={(value: string) => (
|
|
||||||
<TaskLink task={value} />
|
|
||||||
)}
|
|
||||||
title="page.team.tasks.task"
|
|
||||||
properties="task"
|
|
||||||
width={120}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
properties="types"
|
|
||||||
template={(value: string) => (
|
|
||||||
<UiKitTags value={value} />
|
|
||||||
)}
|
|
||||||
width={100}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
properties="scope"
|
|
||||||
template={(value: string) => (
|
|
||||||
<UiKitTags value={value} />
|
|
||||||
)}
|
|
||||||
width={100}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isSortable
|
|
||||||
template={(value: string, row: any) => (
|
|
||||||
<PRLink prId={row?.prId}/>
|
|
||||||
)}
|
|
||||||
properties="task"
|
|
||||||
width={40}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
properties="comments"
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
title="page.team.tasks.author"
|
|
||||||
properties="author"
|
|
||||||
width={170}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
title="page.team.tasks.from"
|
|
||||||
properties="from"
|
|
||||||
width={150}
|
|
||||||
formatter={getDate}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
title="page.team.tasks.to"
|
|
||||||
properties="to"
|
|
||||||
width={150}
|
|
||||||
formatter={getDate}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
title="page.team.tasks.pr"
|
|
||||||
properties="to"
|
|
||||||
width={150}
|
|
||||||
formatter={getDate}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.STRING}
|
|
||||||
title="page.team.tasks.prAuthor"
|
|
||||||
properties="prAuthor"
|
|
||||||
width={170}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
|
||||||
properties="commits"
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
isSortable
|
|
||||||
title="page.team.tasks.commits"
|
|
||||||
properties="commits"
|
|
||||||
minWidth={170}
|
|
||||||
template={(value: number) => (
|
|
||||||
<LineChart
|
|
||||||
options={commitsChart}
|
|
||||||
value={value}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</DataView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TasksView.defaultProps = {
|
|
||||||
response: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Tasks = observer(({
|
|
||||||
mode,
|
|
||||||
}: ICommonPageProps): React.ReactElement | null => {
|
|
||||||
const rows = dataGripStore.dataGrip.tasks.statistic;
|
|
||||||
const [filters, setFilters] = useState<any>({ user: 0, company: 0 });
|
|
||||||
|
|
||||||
if (!rows?.length) return mode !== 'print' ? (<NothingFound />) : null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Title title="common.filters" />
|
|
||||||
<PageWrapper>
|
|
||||||
<TasksFilters
|
|
||||||
filters={filters}
|
|
||||||
onChange={setFilters}
|
|
||||||
/>
|
|
||||||
</PageWrapper>
|
|
||||||
<DataLoader
|
|
||||||
to="response"
|
|
||||||
loader={(pagination?: IPaginationRequest, sort?: ISort[]) => getFakeLoader({
|
|
||||||
content: rows, pagination, sort, mode,
|
|
||||||
})}
|
|
||||||
watch={`${mode}${dataGripStore.hash}`}
|
|
||||||
>
|
|
||||||
<TasksView
|
|
||||||
mode={mode}
|
|
||||||
rowsForExcel={rows}
|
|
||||||
/>
|
|
||||||
<Pagination />
|
|
||||||
</DataLoader>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Tasks;
|
|
|
@ -1,60 +0,0 @@
|
||||||
import React, { useMemo } from 'react';
|
|
||||||
import { observer } from 'mobx-react-lite';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import SelectWithButtons from 'ts/components/UiKit/components/SelectWithButtons';
|
|
||||||
import dataGripStore from 'ts/store/DataGrip';
|
|
||||||
|
|
||||||
import style from '../styles/filters.module.scss';
|
|
||||||
|
|
||||||
function getFormattedUsers(rows: any[], t: Function) {
|
|
||||||
const options = rows.map((title: string, id: number) => ({ id: id + 1, title }));
|
|
||||||
options.unshift({ id: 0, title: t('page.team.tree.filters.all') });
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ITempoFiltersProps {
|
|
||||||
filters: {
|
|
||||||
company?: number;
|
|
||||||
user?: number;
|
|
||||||
};
|
|
||||||
onChange: Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TasksFilters = observer(({
|
|
||||||
filters,
|
|
||||||
onChange,
|
|
||||||
}: ITempoFiltersProps): React.ReactElement => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const users = dataGripStore.dataGrip.author.list;
|
|
||||||
const userOptions = useMemo(() => getFormattedUsers(users, t), [users]);
|
|
||||||
|
|
||||||
const companies = dataGripStore.dataGrip.company.statistic.map((v: any) => v.company);
|
|
||||||
const companyOptions = useMemo(() => getFormattedUsers(companies, t), [companies]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={style.table_filters}>
|
|
||||||
<SelectWithButtons
|
|
||||||
title="page.team.tree.filters.author"
|
|
||||||
value={filters.user}
|
|
||||||
className={style.table_filters_item}
|
|
||||||
options={userOptions}
|
|
||||||
onChange={(user: number) => {
|
|
||||||
onChange({ ...filters, user, company: 0 });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<SelectWithButtons
|
|
||||||
title="page.team.tree.filters.author"
|
|
||||||
value={filters.company}
|
|
||||||
className={style.table_filters_item}
|
|
||||||
options={companyOptions}
|
|
||||||
onChange={(company: number) => {
|
|
||||||
onChange({ ...filters, user: 0, company });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default TasksFilters;
|
|
|
@ -32,6 +32,9 @@ interface ITypeViewProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function TypeView({ response, updateSort, rowsForExcel, mode }: ITypeViewProps) {
|
function TypeView({ response, updateSort, rowsForExcel, mode }: ITypeViewProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const unknown = t('page.team.type.unknown');
|
||||||
|
|
||||||
if (!response) return null;
|
if (!response) return null;
|
||||||
|
|
||||||
const taskChart = getOptions({ max: getMax(response, 'tasks'), suffix: 'page.team.type.tasksSmall' });
|
const taskChart = getOptions({ max: getMax(response, 'tasks'), suffix: 'page.team.type.tasksSmall' });
|
||||||
|
@ -52,6 +55,7 @@ function TypeView({ response, updateSort, rowsForExcel, mode }: ITypeViewProps)
|
||||||
template={ColumnTypesEnum.STRING}
|
template={ColumnTypesEnum.STRING}
|
||||||
title="page.team.type.type"
|
title="page.team.type.type"
|
||||||
properties="type"
|
properties="type"
|
||||||
|
formatter={(type: string) => type || unknown}
|
||||||
width={150}
|
width={150}
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import { IFolder } from 'ts/interfaces/FileInfo';
|
import { IFolder } from 'ts/interfaces/FileInfo';
|
||||||
|
|
||||||
function getSubTree(tree: IFolder, path: string[]) {
|
function getSubTree(tree: IFolder, path: string[]) {
|
||||||
return (path || []).reduce((subTree: any, folderName: string) => {
|
return (path || [])
|
||||||
subTree = subTree.content[folderName] || { content: [] };
|
.reduce((subTree: any, folderName: string) => {
|
||||||
return subTree;
|
subTree = subTree.content.get(folderName) || { content: new Map() };
|
||||||
}, tree || { content: [] });
|
return subTree;
|
||||||
|
}, tree || { content: new Map() });
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSortedContent(subTree: any) {
|
function getSortedContent(subTree: IFolder) {
|
||||||
return Object.values(subTree.content)
|
return Array.from(subTree.content.values())
|
||||||
.sort((a: any, b: any) => {
|
.sort((a: any, b: any) => {
|
||||||
if (a.content && !b.content) return -1;
|
if (a.content && !b.content) return -1;
|
||||||
if (!a.content && b.content) return 1;
|
if (!a.content && b.content) return 1;
|
||||||
|
|
|
@ -9,6 +9,7 @@ import fullScreen from 'ts/store/FullScreen';
|
||||||
import Author from './components/Author';
|
import Author from './components/Author';
|
||||||
import Commits from './components/Commits';
|
import Commits from './components/Commits';
|
||||||
import Company from './components/Company';
|
import Company from './components/Company';
|
||||||
|
import Country from './components/Country';
|
||||||
import Changes from './components/Changes';
|
import Changes from './components/Changes';
|
||||||
import Hours from './components/Hours';
|
import Hours from './components/Hours';
|
||||||
import PopularWords from './components/PopularWords';
|
import PopularWords from './components/PopularWords';
|
||||||
|
@ -25,6 +26,7 @@ import Building from './components/Building';
|
||||||
import Pr from './components/PR';
|
import Pr from './components/PR';
|
||||||
import Print from './components/Print';
|
import Print from './components/Print';
|
||||||
import Release from './components/Release';
|
import Release from './components/Release';
|
||||||
|
import Refactor from './components/Refactor';
|
||||||
|
|
||||||
interface ViewProps {
|
interface ViewProps {
|
||||||
page?: string;
|
page?: string;
|
||||||
|
@ -39,6 +41,7 @@ const View = observer(({ page }: ViewProps): React.ReactElement => {
|
||||||
if (page === 'scope') return <Scope mode={mode}/>;
|
if (page === 'scope') return <Scope mode={mode}/>;
|
||||||
if (page === 'author') return <Author mode={mode}/>;
|
if (page === 'author') return <Author mode={mode}/>;
|
||||||
if (page === 'company') return <Company mode={mode}/>;
|
if (page === 'company') return <Company mode={mode}/>;
|
||||||
|
if (page === 'country') return <Country mode={mode}/>;
|
||||||
if (page === 'type') return <Type mode={mode}/>;
|
if (page === 'type') return <Type mode={mode}/>;
|
||||||
if (page === 'pr') return <Pr mode={mode}/>;
|
if (page === 'pr') return <Pr mode={mode}/>;
|
||||||
if (page === 'day') return <Tempo/>;
|
if (page === 'day') return <Tempo/>;
|
||||||
|
@ -55,6 +58,7 @@ const View = observer(({ page }: ViewProps): React.ReactElement => {
|
||||||
if (page === 'building') return <Building/>;
|
if (page === 'building') return <Building/>;
|
||||||
if (page === 'print') return <Print/>;
|
if (page === 'print') return <Print/>;
|
||||||
if (page === 'tasks') return <Tasks/>;
|
if (page === 'tasks') return <Tasks/>;
|
||||||
|
if (page === 'refactor') return <Refactor/>;
|
||||||
return <Total/>;
|
return <Total/>;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,7 @@ class DataGripStore {
|
||||||
this.#updateRender();
|
this.#updateRender();
|
||||||
|
|
||||||
console.dir(this.dataGrip);
|
console.dir(this.dataGrip);
|
||||||
|
console.dir(this.fileGrip);
|
||||||
if (!applicationHasCustom.title) {
|
if (!applicationHasCustom.title) {
|
||||||
document.title = getTitle(this.dataGrip, this.fileGrip, this.commits);
|
document.title = getTitle(this.dataGrip, this.fileGrip, this.commits);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,9 @@ export default `
|
||||||
§ sidebar.team.changes: Alle Änderungen
|
§ sidebar.team.changes: Alle Änderungen
|
||||||
§ sidebar.team.words: Beliebte Wörter
|
§ sidebar.team.words: Beliebte Wörter
|
||||||
§ sidebar.team.building: Quiz
|
§ sidebar.team.building: Quiz
|
||||||
|
§ sidebar.team.refactor: Refactoring
|
||||||
|
§ sidebar.team.company: Companies
|
||||||
|
§ sidebar.team.country: Locations
|
||||||
§ sidebar.team.settings: Die Einstellungen
|
§ sidebar.team.settings: Die Einstellungen
|
||||||
§ sidebar.person.total: Allgemeine Informationen
|
§ sidebar.person.total: Allgemeine Informationen
|
||||||
§ sidebar.person.money: Arbeitskosten
|
§ sidebar.person.money: Arbeitskosten
|
||||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
||||||
§ page.print.title: Git repository report
|
§ page.print.title: Git repository report
|
||||||
§ page.print.sub_title: «$1»
|
§ page.print.sub_title: «$1»
|
||||||
§ page.print.description: The data for the report was obtained from the commit history.
|
§ page.print.description: The data for the report was obtained from the commit history.
|
||||||
§ page.team.author.title: Employee statistics
|
§ page.team.author.statusChart.title: Status
|
||||||
|
§ page.team.author.daysChart.title: Days of work
|
||||||
|
§ page.team.author.daysChart.item: days
|
||||||
|
§ page.team.author.days.half: half year
|
||||||
|
§ page.team.author.days.one: year
|
||||||
|
§ page.team.author.days.15: year and a half
|
||||||
|
§ page.team.author.days.two: two years
|
||||||
|
§ page.team.author.days.more: more
|
||||||
|
§ page.team.author.title: Details
|
||||||
§ page.team.author.description1: *Part of the statistics* (work speed, costs, etc.) *for employees with the 'Assistant' type is not counted*, as it is an episodic role in the project. It is assumed that they do not affect the project, and their edits can be disregarded in the context of the overall volume of work.
|
§ page.team.author.description1: *Part of the statistics* (work speed, costs, etc.) *for employees with the 'Assistant' type is not counted*, as it is an episodic role in the project. It is assumed that they do not affect the project, and their edits can be disregarded in the context of the overall volume of work.
|
||||||
§ page.team.author.description2: *Default sorting* is by the number of tasks and groups (current, fired, assisting employees).
|
§ page.team.author.description2: *Default sorting* is by the number of tasks and groups (current, fired, assisting employees).
|
||||||
§ page.team.author.status: Status
|
§ page.team.author.status: Status
|
||||||
|
@ -62,6 +70,7 @@ export default `
|
||||||
§ page.team.type.title: Task type statistics
|
§ page.team.type.title: Task type statistics
|
||||||
§ page.team.type.description: *Personal contribution* is counted by the number of commits, not the volume of changed lines or files. Therefore, the "File Analysis" section should also be consulted to assess the scale of changes.
|
§ page.team.type.description: *Personal contribution* is counted by the number of commits, not the volume of changed lines or files. Therefore, the "File Analysis" section should also be consulted to assess the scale of changes.
|
||||||
§ page.team.type.type: Type of work
|
§ page.team.type.type: Type of work
|
||||||
|
§ page.team.type.unknown: unknown
|
||||||
§ page.team.type.tasks: Tasks
|
§ page.team.type.tasks: Tasks
|
||||||
§ page.team.type.tasksSmall: tasks
|
§ page.team.type.tasksSmall: tasks
|
||||||
§ page.team.type.days: Days
|
§ page.team.type.days: Days
|
||||||
|
@ -101,6 +110,11 @@ export default `
|
||||||
§ page.team.tree.filters.commits: Number of commits
|
§ page.team.tree.filters.commits: Number of commits
|
||||||
§ page.team.tree.filters.help: The minimum number of commits an employee has made in a file
|
§ page.team.tree.filters.help: The minimum number of commits an employee has made in a file
|
||||||
§ page.team.tree.filters.all: All employees
|
§ page.team.tree.filters.all: All employees
|
||||||
|
§ page.team.tree.totalLines: Lines
|
||||||
|
§ page.team.tree.totalTasks: Tasks
|
||||||
|
§ page.team.tree.totalDays: Days
|
||||||
|
§ page.team.tree.tasks: tasks
|
||||||
|
§ page.team.tree.days: days
|
||||||
§ page.team.tree.add: Who added
|
§ page.team.tree.add: Who added
|
||||||
§ page.team.tree.change: Who changed
|
§ page.team.tree.change: Who changed
|
||||||
§ page.team.tree.remove: Who removed
|
§ page.team.tree.remove: Who removed
|
||||||
|
@ -108,6 +122,29 @@ export default `
|
||||||
§ page.team.tree.linesAdded: added
|
§ page.team.tree.linesAdded: added
|
||||||
§ page.team.tree.linesChanged: changed
|
§ page.team.tree.linesChanged: changed
|
||||||
§ page.team.tree.linesRemoved: removed
|
§ page.team.tree.linesRemoved: removed
|
||||||
|
§ page.team.company.title: Details
|
||||||
|
§ page.team.company.employments.title: By number of employees
|
||||||
|
§ page.team.company.employments.item: employments
|
||||||
|
§ page.team.company.daysChart.title: By duration of the contract
|
||||||
|
§ page.team.company.daysChart.item: days
|
||||||
|
§ page.team.company.active.yes: active
|
||||||
|
§ page.team.company.active.no: contract has expired
|
||||||
|
§ page.team.country.byTimezone: By the time of the last commit
|
||||||
|
§ page.team.country.pieByDomain.title: By email, timezone and language
|
||||||
|
§ page.team.country.pieByTimezone.title: By timezone
|
||||||
|
§ page.team.country.chart.item: employments
|
||||||
|
§ page.team.country.table.title: List of employees
|
||||||
|
§ page.team.country.table.country: Country
|
||||||
|
§ page.team.country.table.employments: Employments
|
||||||
|
§ page.team.refactor.title: Candidates for refactoring
|
||||||
|
§ page.team.refactor.lines: lines
|
||||||
|
§ page.team.refactor.tasks: tasks
|
||||||
|
§ page.team.refactor.days: days
|
||||||
|
§ page.team.refactor.path: Path
|
||||||
|
§ page.team.refactor.firstCommit: First commit
|
||||||
|
§ page.team.refactor.totalLines: Lines
|
||||||
|
§ page.team.refactor.totalTasks: Tasks
|
||||||
|
§ page.team.refactor.totalDays: Days in development
|
||||||
§ page.team.day.commits: Commits
|
§ page.team.day.commits: Commits
|
||||||
§ page.team.day.activity: Activity
|
§ page.team.day.activity: Activity
|
||||||
§ page.team.week.title: Weekly statistics
|
§ page.team.week.title: Weekly statistics
|
||||||
|
@ -137,7 +174,6 @@ export default `
|
||||||
§ page.team.pr.chart.14day: two weeks
|
§ page.team.pr.chart.14day: two weeks
|
||||||
§ page.team.pr.chart.30day: month
|
§ page.team.pr.chart.30day: month
|
||||||
§ page.team.pr.chart.more: more
|
§ page.team.pr.chart.more: more
|
||||||
§ page.team.pr.commits: Commits
|
|
||||||
§ page.team.pr.date: Merge Date
|
§ page.team.pr.date: Merge Date
|
||||||
§ page.team.pr.mergeAuthor: Merged by
|
§ page.team.pr.mergeAuthor: Merged by
|
||||||
§ page.team.pr.author: Employee
|
§ page.team.pr.author: Employee
|
||||||
|
@ -155,10 +191,6 @@ export default `
|
||||||
§ page.team.tasks.from: First commit
|
§ page.team.tasks.from: First commit
|
||||||
§ page.team.tasks.to: Last commit
|
§ page.team.tasks.to: Last commit
|
||||||
§ page.team.tasks.daysInWork: Days in work
|
§ page.team.tasks.daysInWork: Days in work
|
||||||
§ page.team.tasks.commits: Commits number
|
|
||||||
§ page.team.tasks.pr: Merge date
|
|
||||||
§ page.team.tasks.prAuthor: Merged by user
|
|
||||||
§ page.team.tasks.prDelayDays: Delay before merge in days
|
|
||||||
§ page.team.tasks.comments: Comments
|
§ page.team.tasks.comments: Comments
|
||||||
§ page.team.extension.extension: File extensions
|
§ page.team.extension.extension: File extensions
|
||||||
§ page.team.extension.type: File sub types
|
§ page.team.extension.type: File sub types
|
||||||
|
@ -167,6 +199,7 @@ export default `
|
||||||
§ page.team.extension.current.count: Number
|
§ page.team.extension.current.count: Number
|
||||||
§ page.team.extension.removed.count: Number of removed
|
§ page.team.extension.removed.count: Number of removed
|
||||||
§ page.team.extension.files: files
|
§ page.team.extension.files: files
|
||||||
|
§ page.team.release.download: Download
|
||||||
§ page.team.release.title: Release
|
§ page.team.release.title: Release
|
||||||
§ page.team.release.from: Created date
|
§ page.team.release.from: Created date
|
||||||
§ page.team.release.to: Delivery date
|
§ page.team.release.to: Delivery date
|
||||||
|
|
|
@ -28,6 +28,9 @@ export default `
|
||||||
§ sidebar.team.changes: All changes
|
§ sidebar.team.changes: All changes
|
||||||
§ sidebar.team.words: Popular words
|
§ sidebar.team.words: Popular words
|
||||||
§ sidebar.team.building: Quiz
|
§ sidebar.team.building: Quiz
|
||||||
|
§ sidebar.team.refactor: Refactoring
|
||||||
|
§ sidebar.team.company: Companies
|
||||||
|
§ sidebar.team.country: Locations
|
||||||
§ sidebar.team.settings: Settings
|
§ sidebar.team.settings: Settings
|
||||||
§ sidebar.person.total: Common info
|
§ sidebar.person.total: Common info
|
||||||
§ sidebar.person.money: Work cost
|
§ sidebar.person.money: Work cost
|
||||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
||||||
§ page.print.title: Git repository report
|
§ page.print.title: Git repository report
|
||||||
§ page.print.sub_title: «$1»
|
§ page.print.sub_title: «$1»
|
||||||
§ page.print.description: The data for the report was obtained from the commit history.
|
§ page.print.description: The data for the report was obtained from the commit history.
|
||||||
§ page.team.author.title: Employee statistics
|
§ page.team.author.statusChart.title: Status
|
||||||
|
§ page.team.author.daysChart.title: Days of work
|
||||||
|
§ page.team.author.daysChart.item: days
|
||||||
|
§ page.team.author.days.half: half year
|
||||||
|
§ page.team.author.days.one: year
|
||||||
|
§ page.team.author.days.15: year and a half
|
||||||
|
§ page.team.author.days.two: two years
|
||||||
|
§ page.team.author.days.more: more
|
||||||
|
§ page.team.author.title: Details
|
||||||
§ page.team.author.description1: *Part of the statistics* (work speed, costs, etc.) *for employees with the 'Assistant' type is not counted*, as it is an episodic role in the project. It is assumed that they do not affect the project, and their edits can be disregarded in the context of the overall volume of work.
|
§ page.team.author.description1: *Part of the statistics* (work speed, costs, etc.) *for employees with the 'Assistant' type is not counted*, as it is an episodic role in the project. It is assumed that they do not affect the project, and their edits can be disregarded in the context of the overall volume of work.
|
||||||
§ page.team.author.description2: *Default sorting* is by the number of tasks and groups (current, fired, assisting employees).
|
§ page.team.author.description2: *Default sorting* is by the number of tasks and groups (current, fired, assisting employees).
|
||||||
§ page.team.author.status: Status
|
§ page.team.author.status: Status
|
||||||
|
@ -62,6 +70,7 @@ export default `
|
||||||
§ page.team.type.title: Task type statistics
|
§ page.team.type.title: Task type statistics
|
||||||
§ page.team.type.description: *Personal contribution* is counted by the number of commits, not the volume of changed lines or files. Therefore, the "File Analysis" section should also be consulted to assess the scale of changes.
|
§ page.team.type.description: *Personal contribution* is counted by the number of commits, not the volume of changed lines or files. Therefore, the "File Analysis" section should also be consulted to assess the scale of changes.
|
||||||
§ page.team.type.type: Type of work
|
§ page.team.type.type: Type of work
|
||||||
|
§ page.team.type.unknown: unknown
|
||||||
§ page.team.type.tasks: Tasks
|
§ page.team.type.tasks: Tasks
|
||||||
§ page.team.type.tasksSmall: tasks
|
§ page.team.type.tasksSmall: tasks
|
||||||
§ page.team.type.days: Days
|
§ page.team.type.days: Days
|
||||||
|
@ -101,6 +110,11 @@ export default `
|
||||||
§ page.team.tree.filters.commits: Number of commits
|
§ page.team.tree.filters.commits: Number of commits
|
||||||
§ page.team.tree.filters.help: The minimum number of commits an employee has made in a file
|
§ page.team.tree.filters.help: The minimum number of commits an employee has made in a file
|
||||||
§ page.team.tree.filters.all: All employees
|
§ page.team.tree.filters.all: All employees
|
||||||
|
§ page.team.tree.totalLines: Lines
|
||||||
|
§ page.team.tree.totalTasks: Tasks
|
||||||
|
§ page.team.tree.totalDays: Days
|
||||||
|
§ page.team.tree.tasks: tasks
|
||||||
|
§ page.team.tree.days: days
|
||||||
§ page.team.tree.add: Who added
|
§ page.team.tree.add: Who added
|
||||||
§ page.team.tree.change: Who changed
|
§ page.team.tree.change: Who changed
|
||||||
§ page.team.tree.remove: Who removed
|
§ page.team.tree.remove: Who removed
|
||||||
|
@ -108,6 +122,29 @@ export default `
|
||||||
§ page.team.tree.linesAdded: added
|
§ page.team.tree.linesAdded: added
|
||||||
§ page.team.tree.linesChanged: changed
|
§ page.team.tree.linesChanged: changed
|
||||||
§ page.team.tree.linesRemoved: removed
|
§ page.team.tree.linesRemoved: removed
|
||||||
|
§ page.team.company.title: Details
|
||||||
|
§ page.team.company.employments.title: By number of employees
|
||||||
|
§ page.team.company.employments.item: employments
|
||||||
|
§ page.team.company.daysChart.title: By duration of the contract
|
||||||
|
§ page.team.company.daysChart.item: days
|
||||||
|
§ page.team.company.active.yes: active
|
||||||
|
§ page.team.company.active.no: contract has expired
|
||||||
|
§ page.team.country.byTimezone: By the time of the last commit
|
||||||
|
§ page.team.country.pieByDomain.title: By email, timezone and language
|
||||||
|
§ page.team.country.pieByTimezone.title: By timezone
|
||||||
|
§ page.team.country.chart.item: employments
|
||||||
|
§ page.team.country.table.title: List of employees
|
||||||
|
§ page.team.country.table.country: Country
|
||||||
|
§ page.team.country.table.employments: Employments
|
||||||
|
§ page.team.refactor.title: Candidates for refactoring
|
||||||
|
§ page.team.refactor.lines: lines
|
||||||
|
§ page.team.refactor.tasks: tasks
|
||||||
|
§ page.team.refactor.days: days
|
||||||
|
§ page.team.refactor.path: Path
|
||||||
|
§ page.team.refactor.firstCommit: First commit
|
||||||
|
§ page.team.refactor.totalLines: Lines
|
||||||
|
§ page.team.refactor.totalTasks: Tasks
|
||||||
|
§ page.team.refactor.totalDays: Days in development
|
||||||
§ page.team.day.commits: Commits
|
§ page.team.day.commits: Commits
|
||||||
§ page.team.day.activity: Activity
|
§ page.team.day.activity: Activity
|
||||||
§ page.team.week.title: Weekly statistics
|
§ page.team.week.title: Weekly statistics
|
||||||
|
@ -139,7 +176,6 @@ export default `
|
||||||
§ page.team.pr.chart.14day: two weeks
|
§ page.team.pr.chart.14day: two weeks
|
||||||
§ page.team.pr.chart.30day: month
|
§ page.team.pr.chart.30day: month
|
||||||
§ page.team.pr.chart.more: more
|
§ page.team.pr.chart.more: more
|
||||||
§ page.team.pr.commits: Commits
|
|
||||||
§ page.team.pr.date: Merge Date
|
§ page.team.pr.date: Merge Date
|
||||||
§ page.team.pr.mergeAuthor: Merged by
|
§ page.team.pr.mergeAuthor: Merged by
|
||||||
§ page.team.pr.author: Employee
|
§ page.team.pr.author: Employee
|
||||||
|
@ -157,10 +193,6 @@ export default `
|
||||||
§ page.team.tasks.from: First commit
|
§ page.team.tasks.from: First commit
|
||||||
§ page.team.tasks.to: Last commit
|
§ page.team.tasks.to: Last commit
|
||||||
§ page.team.tasks.daysInWork: Days in work
|
§ page.team.tasks.daysInWork: Days in work
|
||||||
§ page.team.tasks.commits: Commits number
|
|
||||||
§ page.team.tasks.pr: Merge date
|
|
||||||
§ page.team.tasks.prAuthor: Merged by user
|
|
||||||
§ page.team.tasks.prDelayDays: Delay before merge in days
|
|
||||||
§ page.team.tasks.comments: Comments
|
§ page.team.tasks.comments: Comments
|
||||||
§ page.team.extension.extension: File extensions
|
§ page.team.extension.extension: File extensions
|
||||||
§ page.team.extension.type: File sub types
|
§ page.team.extension.type: File sub types
|
||||||
|
@ -169,6 +201,7 @@ export default `
|
||||||
§ page.team.extension.current.count: Number
|
§ page.team.extension.current.count: Number
|
||||||
§ page.team.extension.removed.count: Number of removed
|
§ page.team.extension.removed.count: Number of removed
|
||||||
§ page.team.extension.files: files
|
§ page.team.extension.files: files
|
||||||
|
§ page.team.release.download: Download
|
||||||
§ page.team.release.title: Release
|
§ page.team.release.title: Release
|
||||||
§ page.team.release.from: Created date
|
§ page.team.release.from: Created date
|
||||||
§ page.team.release.to: Delivery date
|
§ page.team.release.to: Delivery date
|
||||||
|
|
|
@ -30,6 +30,9 @@ export default `
|
||||||
§ sidebar.team.changes: Todos los cambios
|
§ sidebar.team.changes: Todos los cambios
|
||||||
§ sidebar.team.words: Palabras populares
|
§ sidebar.team.words: Palabras populares
|
||||||
§ sidebar.team.building: Concurso
|
§ sidebar.team.building: Concurso
|
||||||
|
§ sidebar.team.refactor: Refactoring
|
||||||
|
§ sidebar.team.company: Companies
|
||||||
|
§ sidebar.team.country: Locations
|
||||||
§ sidebar.team.settings: Ajustes
|
§ sidebar.team.settings: Ajustes
|
||||||
§ sidebar.person.total: Información general
|
§ sidebar.person.total: Información general
|
||||||
§ sidebar.person.money: Costo del trabajo
|
§ sidebar.person.money: Costo del trabajo
|
||||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
||||||
§ page.print.title: Informe del repositorio git
|
§ page.print.title: Informe del repositorio git
|
||||||
§ page.print.sub_title: «$1»
|
§ page.print.sub_title: «$1»
|
||||||
§ page.print.description: Los datos para el informe se obtuvieron del historial de commits.
|
§ page.print.description: Los datos para el informe se obtuvieron del historial de commits.
|
||||||
§ page.team.author.title: Estadísticas de los empleados
|
§ page.team.author.statusChart.title: Status
|
||||||
|
§ page.team.author.daysChart.title: Days of work
|
||||||
|
§ page.team.author.daysChart.item: days
|
||||||
|
§ page.team.author.days.half: half year
|
||||||
|
§ page.team.author.days.one: year
|
||||||
|
§ page.team.author.days.15: year and a half
|
||||||
|
§ page.team.author.days.two: two years
|
||||||
|
§ page.team.author.days.more: more
|
||||||
|
§ page.team.author.title: Details
|
||||||
§ page.team.author.description1: Parte de las estadísticas (la velocidad del trabajo, el dinero gastado, etc.) para los empleados con el tipo de "Asistente" no cuenta, ya que no es un rol permanente en el proyecto. Su trabajo es insignificante y puede ser ignorado.
|
§ page.team.author.description1: Parte de las estadísticas (la velocidad del trabajo, el dinero gastado, etc.) para los empleados con el tipo de "Asistente" no cuenta, ya que no es un rol permanente en el proyecto. Su trabajo es insignificante y puede ser ignorado.
|
||||||
§ page.team.author.description2: La clasificación predeterminada es la clasificación por número de tareas y grupos(empleados actuales, despedidos, ayudantes).
|
§ page.team.author.description2: La clasificación predeterminada es la clasificación por número de tareas y grupos(empleados actuales, despedidos, ayudantes).
|
||||||
§ page.team.author.status: Status
|
§ page.team.author.status: Status
|
||||||
|
@ -62,6 +70,7 @@ export default `
|
||||||
§ page.team.type.title: Estadísticas por tipo de tarea
|
§ page.team.type.title: Estadísticas por tipo de tarea
|
||||||
§ page.team.type.description: *Contribución personal* se considera por el número de Commits, no por el volumen de líneas o archivos modificados. Por lo tanto, también debe ver la sección "Análisis de archivos" para evaluar el alcance de los cambios
|
§ page.team.type.description: *Contribución personal* se considera por el número de Commits, no por el volumen de líneas o archivos modificados. Por lo tanto, también debe ver la sección "Análisis de archivos" para evaluar el alcance de los cambios
|
||||||
§ page.team.type.type: Tipo de trabajo
|
§ page.team.type.type: Tipo de trabajo
|
||||||
|
§ page.team.type.unknown: unknown
|
||||||
§ page.team.type.tasks: Tareas
|
§ page.team.type.tasks: Tareas
|
||||||
§ page.team.type.tasksSmall: Tareas
|
§ page.team.type.tasksSmall: Tareas
|
||||||
§ page.team.type.days: Día
|
§ page.team.type.days: Día
|
||||||
|
@ -101,6 +110,11 @@ export default `
|
||||||
§ page.team.tree.filters.commits: Número de commits
|
§ page.team.tree.filters.commits: Número de commits
|
||||||
§ page.team.tree.filters.help: El número mínimo de commits que hizo un empleado en el archivo
|
§ page.team.tree.filters.help: El número mínimo de commits que hizo un empleado en el archivo
|
||||||
§ page.team.tree.filters.all: Todos los empleados
|
§ page.team.tree.filters.all: Todos los empleados
|
||||||
|
§ page.team.tree.totalLines: Lines
|
||||||
|
§ page.team.tree.totalTasks: Tasks
|
||||||
|
§ page.team.tree.totalDays: Days
|
||||||
|
§ page.team.tree.tasks: tasks
|
||||||
|
§ page.team.tree.days: days
|
||||||
§ page.team.tree.add: Quien ha añadido
|
§ page.team.tree.add: Quien ha añadido
|
||||||
§ page.team.tree.change: Quien cambió
|
§ page.team.tree.change: Quien cambió
|
||||||
§ page.team.tree.remove: Quién borró
|
§ page.team.tree.remove: Quién borró
|
||||||
|
@ -108,6 +122,29 @@ export default `
|
||||||
§ page.team.tree.linesAdded: agregaron
|
§ page.team.tree.linesAdded: agregaron
|
||||||
§ page.team.tree.linesChanged: changed
|
§ page.team.tree.linesChanged: changed
|
||||||
§ page.team.tree.linesRemoved: cambiaron
|
§ page.team.tree.linesRemoved: cambiaron
|
||||||
|
§ page.team.company.title: Details
|
||||||
|
§ page.team.company.employments.title: By number of employees
|
||||||
|
§ page.team.company.employments.item: employments
|
||||||
|
§ page.team.company.daysChart.title: By duration of the contract
|
||||||
|
§ page.team.company.daysChart.item: days
|
||||||
|
§ page.team.company.active.yes: active
|
||||||
|
§ page.team.company.active.no: contract has expired
|
||||||
|
§ page.team.country.byTimezone: By the time of the last commit
|
||||||
|
§ page.team.country.pieByDomain.title: By email, timezone and language
|
||||||
|
§ page.team.country.pieByTimezone.title: By timezone
|
||||||
|
§ page.team.country.chart.item: employments
|
||||||
|
§ page.team.country.table.title: List of employees
|
||||||
|
§ page.team.country.table.country: Country
|
||||||
|
§ page.team.country.table.employments: Employments
|
||||||
|
§ page.team.refactor.title: Candidates for refactoring
|
||||||
|
§ page.team.refactor.lines: lines
|
||||||
|
§ page.team.refactor.tasks: tasks
|
||||||
|
§ page.team.refactor.days: days
|
||||||
|
§ page.team.refactor.path: Path
|
||||||
|
§ page.team.refactor.firstCommit: First commit
|
||||||
|
§ page.team.refactor.totalLines: Lines
|
||||||
|
§ page.team.refactor.totalTasks: Tasks
|
||||||
|
§ page.team.refactor.totalDays: Days in development
|
||||||
§ page.team.day.commits: Commits
|
§ page.team.day.commits: Commits
|
||||||
§ page.team.day.activity: Activity
|
§ page.team.day.activity: Activity
|
||||||
§ page.team.week.title: Estadísticas semanales
|
§ page.team.week.title: Estadísticas semanales
|
||||||
|
@ -139,7 +176,6 @@ export default `
|
||||||
§ page.team.pr.chart.14day: two weeks
|
§ page.team.pr.chart.14day: two weeks
|
||||||
§ page.team.pr.chart.30day: month
|
§ page.team.pr.chart.30day: month
|
||||||
§ page.team.pr.chart.more: more
|
§ page.team.pr.chart.more: more
|
||||||
§ page.team.pr.commits: commits
|
|
||||||
§ page.team.pr.date: Date of injection
|
§ page.team.pr.date: Date of injection
|
||||||
§ page.team.pr.mergeAuthor: I poured it in
|
§ page.team.pr.mergeAuthor: I poured it in
|
||||||
§ page.team.pr.author: Employee
|
§ page.team.pr.author: Employee
|
||||||
|
@ -157,10 +193,6 @@ export default `
|
||||||
§ page.team.tasks.from: The first commits
|
§ page.team.tasks.from: The first commits
|
||||||
§ page.team.tasks.to: Last commits
|
§ page.team.tasks.to: Last commits
|
||||||
§ page.team.tasks.daysInWork: Days in the work
|
§ page.team.tasks.daysInWork: Days in the work
|
||||||
§ page.team.tasks.commits: Number of commits
|
|
||||||
§ page.team.tasks.pr: Date of injection
|
|
||||||
§ page.team.tasks.prAuthor: I poured it in
|
|
||||||
§ page.team.tasks.prDelayDays: Days of waiting for the infusion
|
|
||||||
§ page.team.tasks.comments: Comments
|
§ page.team.tasks.comments: Comments
|
||||||
§ page.team.extension.extension: File extensions
|
§ page.team.extension.extension: File extensions
|
||||||
§ page.team.extension.type: File sub types
|
§ page.team.extension.type: File sub types
|
||||||
|
@ -169,6 +201,7 @@ export default `
|
||||||
§ page.team.extension.current.count: Number
|
§ page.team.extension.current.count: Number
|
||||||
§ page.team.extension.removed.count: Number of removed
|
§ page.team.extension.removed.count: Number of removed
|
||||||
§ page.team.extension.files: files
|
§ page.team.extension.files: files
|
||||||
|
§ page.team.release.download: Download
|
||||||
§ page.team.release.title: Release
|
§ page.team.release.title: Release
|
||||||
§ page.team.release.from: Created date
|
§ page.team.release.from: Created date
|
||||||
§ page.team.release.to: Delivery date
|
§ page.team.release.to: Delivery date
|
||||||
|
|
|
@ -27,7 +27,10 @@ export default `
|
||||||
§ sidebar.team.commits: all commits
|
§ sidebar.team.commits: all commits
|
||||||
§ sidebar.team.changes: Tous les changements
|
§ sidebar.team.changes: Tous les changements
|
||||||
§ sidebar.team.words: Mots populaires
|
§ sidebar.team.words: Mots populaires
|
||||||
§ sidebar.team.building: quiz
|
§ sidebar.team.building: Quiz
|
||||||
|
§ sidebar.team.refactor: Refactoring
|
||||||
|
§ sidebar.team.company: Companies
|
||||||
|
§ sidebar.team.country: Locations
|
||||||
§ sidebar.team.settings: Réglages
|
§ sidebar.team.settings: Réglages
|
||||||
§ sidebar.person.total: Informations générales
|
§ sidebar.person.total: Informations générales
|
||||||
§ sidebar.person.money: Coût des travaux
|
§ sidebar.person.money: Coût des travaux
|
||||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
||||||
§ page.print.title: Rapport sur dépôt git
|
§ page.print.title: Rapport sur dépôt git
|
||||||
§ page.print.sub_title: «$1»
|
§ page.print.sub_title: «$1»
|
||||||
§ page.print.description: Les données du rapport ont été extraites de l'historique des commits.
|
§ page.print.description: Les données du rapport ont été extraites de l'historique des commits.
|
||||||
§ page.team.author.title: Statistiques du personnel
|
§ page.team.author.statusChart.title: Status
|
||||||
|
§ page.team.author.daysChart.title: Days of work
|
||||||
|
§ page.team.author.daysChart.item: days
|
||||||
|
§ page.team.author.days.half: half year
|
||||||
|
§ page.team.author.days.one: year
|
||||||
|
§ page.team.author.days.15: year and a half
|
||||||
|
§ page.team.author.days.two: two years
|
||||||
|
§ page.team.author.days.more: more
|
||||||
|
§ page.team.author.title: Details
|
||||||
§ page.team.author.description1: Partie des statistiques (vitesse de travail, argent dépensé, etc.) pour les collaborateurs de type “Assistant”, ce n’est pas une rôle permanente dans le projet. Leur travail est insignifiant et peut être ignoré.
|
§ page.team.author.description1: Partie des statistiques (vitesse de travail, argent dépensé, etc.) pour les collaborateurs de type “Assistant”, ce n’est pas une rôle permanente dans le projet. Leur travail est insignifiant et peut être ignoré.
|
||||||
§ page.team.author.description2: Le tri par défaut est le tri par nombre de tâches et de groupes (employés actuels, licenciés et aidants).
|
§ page.team.author.description2: Le tri par défaut est le tri par nombre de tâches et de groupes (employés actuels, licenciés et aidants).
|
||||||
§ page.team.author.status: Status
|
§ page.team.author.status: Status
|
||||||
|
@ -62,6 +70,7 @@ export default `
|
||||||
§ page.team.type.title: Statistiques par type de tâche
|
§ page.team.type.title: Statistiques par type de tâche
|
||||||
§ page.team.type.description: *Contribution personnelle* compte tenu du nombre de commits plutôt que de la taille des lignes ou fichiers modifiés. Vous devez donc également consulter la section “Analyse des fichiers” afin d’évaluer l’ampleur des modifications.
|
§ page.team.type.description: *Contribution personnelle* compte tenu du nombre de commits plutôt que de la taille des lignes ou fichiers modifiés. Vous devez donc également consulter la section “Analyse des fichiers” afin d’évaluer l’ampleur des modifications.
|
||||||
§ page.team.type.type: Type de travail
|
§ page.team.type.type: Type de travail
|
||||||
|
§ page.team.type.unknown: unknown
|
||||||
§ page.team.type.tasks: Задач
|
§ page.team.type.tasks: Задач
|
||||||
§ page.team.type.tasksSmall: Tâche
|
§ page.team.type.tasksSmall: Tâche
|
||||||
§ page.team.type.days: Jours
|
§ page.team.type.days: Jours
|
||||||
|
@ -101,6 +110,11 @@ export default `
|
||||||
§ page.team.tree.filters.commits: Nombre de commits
|
§ page.team.tree.filters.commits: Nombre de commits
|
||||||
§ page.team.tree.filters.help: Minimum commits que l'employé a fait dans le fichier
|
§ page.team.tree.filters.help: Minimum commits que l'employé a fait dans le fichier
|
||||||
§ page.team.tree.filters.all: Tous les employés
|
§ page.team.tree.filters.all: Tous les employés
|
||||||
|
§ page.team.tree.totalLines: Lines
|
||||||
|
§ page.team.tree.totalTasks: Tasks
|
||||||
|
§ page.team.tree.totalDays: Days
|
||||||
|
§ page.team.tree.tasks: tasks
|
||||||
|
§ page.team.tree.days: days
|
||||||
§ page.team.tree.add: Qui a Ajouté
|
§ page.team.tree.add: Qui a Ajouté
|
||||||
§ page.team.tree.change: Qui a changé
|
§ page.team.tree.change: Qui a changé
|
||||||
§ page.team.tree.remove: Qui a supprimé
|
§ page.team.tree.remove: Qui a supprimé
|
||||||
|
@ -108,6 +122,29 @@ export default `
|
||||||
§ page.team.tree.linesAdded: ajoutâtes
|
§ page.team.tree.linesAdded: ajoutâtes
|
||||||
§ page.team.tree.linesChanged: changed
|
§ page.team.tree.linesChanged: changed
|
||||||
§ page.team.tree.linesRemoved: modifiâtes
|
§ page.team.tree.linesRemoved: modifiâtes
|
||||||
|
§ page.team.company.title: Details
|
||||||
|
§ page.team.company.employments.title: By number of employees
|
||||||
|
§ page.team.company.employments.item: employments
|
||||||
|
§ page.team.company.daysChart.title: By duration of the contract
|
||||||
|
§ page.team.company.daysChart.item: days
|
||||||
|
§ page.team.company.active.yes: active
|
||||||
|
§ page.team.company.active.no: contract has expired
|
||||||
|
§ page.team.country.byTimezone: By the time of the last commit
|
||||||
|
§ page.team.country.pieByDomain.title: By email, timezone and language
|
||||||
|
§ page.team.country.pieByTimezone.title: By timezone
|
||||||
|
§ page.team.country.chart.item: employments
|
||||||
|
§ page.team.country.table.title: List of employees
|
||||||
|
§ page.team.country.table.country: Country
|
||||||
|
§ page.team.country.table.employments: Employments
|
||||||
|
§ page.team.refactor.title: Candidates for refactoring
|
||||||
|
§ page.team.refactor.lines: lines
|
||||||
|
§ page.team.refactor.tasks: tasks
|
||||||
|
§ page.team.refactor.days: days
|
||||||
|
§ page.team.refactor.path: Path
|
||||||
|
§ page.team.refactor.firstCommit: First commit
|
||||||
|
§ page.team.refactor.totalLines: Lines
|
||||||
|
§ page.team.refactor.totalTasks: Tasks
|
||||||
|
§ page.team.refactor.totalDays: Days in development
|
||||||
§ page.team.day.commits: Commits
|
§ page.team.day.commits: Commits
|
||||||
§ page.team.day.activity: Activity
|
§ page.team.day.activity: Activity
|
||||||
§ page.team.week.title: Statistiques par semaine
|
§ page.team.week.title: Statistiques par semaine
|
||||||
|
@ -139,7 +176,6 @@ export default `
|
||||||
§ page.team.pr.chart.14day: two weeks
|
§ page.team.pr.chart.14day: two weeks
|
||||||
§ page.team.pr.chart.30day: month
|
§ page.team.pr.chart.30day: month
|
||||||
§ page.team.pr.chart.more: more
|
§ page.team.pr.chart.more: more
|
||||||
§ page.team.pr.commits: Commits
|
|
||||||
§ page.team.pr.date: Date de diffusion
|
§ page.team.pr.date: Date de diffusion
|
||||||
§ page.team.pr.mergeAuthor: Versai
|
§ page.team.pr.mergeAuthor: Versai
|
||||||
§ page.team.pr.author: Employé
|
§ page.team.pr.author: Employé
|
||||||
|
@ -157,10 +193,6 @@ export default `
|
||||||
§ page.team.tasks.from: Premier commit
|
§ page.team.tasks.from: Premier commit
|
||||||
§ page.team.tasks.to: Dernier commit
|
§ page.team.tasks.to: Dernier commit
|
||||||
§ page.team.tasks.daysInWork: Jours de travail
|
§ page.team.tasks.daysInWork: Jours de travail
|
||||||
§ page.team.tasks.commits: Nombre de commits
|
|
||||||
§ page.team.tasks.pr: Date de diffusion
|
|
||||||
§ page.team.tasks.prAuthor: Versai
|
|
||||||
§ page.team.tasks.prDelayDays: Jours d'attente
|
|
||||||
§ page.team.tasks.comments: Commentaires
|
§ page.team.tasks.comments: Commentaires
|
||||||
§ page.team.extension.extension: File extensions
|
§ page.team.extension.extension: File extensions
|
||||||
§ page.team.extension.type: File sub types
|
§ page.team.extension.type: File sub types
|
||||||
|
@ -169,6 +201,7 @@ export default `
|
||||||
§ page.team.extension.current.count: Number
|
§ page.team.extension.current.count: Number
|
||||||
§ page.team.extension.removed.count: Number of removed
|
§ page.team.extension.removed.count: Number of removed
|
||||||
§ page.team.extension.files: files
|
§ page.team.extension.files: files
|
||||||
|
§ page.team.release.download: Download
|
||||||
§ page.team.release.title: Release
|
§ page.team.release.title: Release
|
||||||
§ page.team.release.from: Created date
|
§ page.team.release.from: Created date
|
||||||
§ page.team.release.to: Delivery date
|
§ page.team.release.to: Delivery date
|
||||||
|
|
|
@ -29,6 +29,9 @@ export default `
|
||||||
§ sidebar.team.changes: すべての変更
|
§ sidebar.team.changes: すべての変更
|
||||||
§ sidebar.team.words: 人気のある言葉
|
§ sidebar.team.words: 人気のある言葉
|
||||||
§ sidebar.team.building: クイズ
|
§ sidebar.team.building: クイズ
|
||||||
|
§ sidebar.team.refactor: Refactoring
|
||||||
|
§ sidebar.team.company: Companies
|
||||||
|
§ sidebar.team.country: Locations
|
||||||
§ sidebar.team.settings: 設定
|
§ sidebar.team.settings: 設定
|
||||||
§ sidebar.person.total: 一般的な情報
|
§ sidebar.person.total: 一般的な情報
|
||||||
§ sidebar.person.money: 仕事のコスト
|
§ sidebar.person.money: 仕事のコスト
|
||||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
||||||
§ page.print.title: Git repository report
|
§ page.print.title: Git repository report
|
||||||
§ page.print.sub_title: «$1»
|
§ page.print.sub_title: «$1»
|
||||||
§ page.print.description: The data for the report was obtained from the commit history.
|
§ page.print.description: The data for the report was obtained from the commit history.
|
||||||
§ page.team.author.title: Employee statistics
|
§ page.team.author.statusChart.title: Status
|
||||||
|
§ page.team.author.daysChart.title: Days of work
|
||||||
|
§ page.team.author.daysChart.item: days
|
||||||
|
§ page.team.author.days.half: half year
|
||||||
|
§ page.team.author.days.one: year
|
||||||
|
§ page.team.author.days.15: year and a half
|
||||||
|
§ page.team.author.days.two: two years
|
||||||
|
§ page.team.author.days.more: more
|
||||||
|
§ page.team.author.title: Details
|
||||||
§ page.team.author.description1: *Part of the statistics* (work speed, costs, etc.) *for employees with the 'Assistant' type is not counted*, as it is an episodic role in the project. It is assumed that they do not affect the project, and their edits can be disregarded in the context of the overall volume of work.
|
§ page.team.author.description1: *Part of the statistics* (work speed, costs, etc.) *for employees with the 'Assistant' type is not counted*, as it is an episodic role in the project. It is assumed that they do not affect the project, and their edits can be disregarded in the context of the overall volume of work.
|
||||||
§ page.team.author.description2: *Default sorting* is by the number of tasks and groups (current, fired, assisting employees).
|
§ page.team.author.description2: *Default sorting* is by the number of tasks and groups (current, fired, assisting employees).
|
||||||
§ page.team.author.status: Status
|
§ page.team.author.status: Status
|
||||||
|
@ -62,6 +70,7 @@ export default `
|
||||||
§ page.team.type.title: Task type statistics
|
§ page.team.type.title: Task type statistics
|
||||||
§ page.team.type.description: *Personal contribution* is counted by the number of commits, not the volume of changed lines or files. Therefore, the "File Analysis" section should also be consulted to assess the scale of changes.
|
§ page.team.type.description: *Personal contribution* is counted by the number of commits, not the volume of changed lines or files. Therefore, the "File Analysis" section should also be consulted to assess the scale of changes.
|
||||||
§ page.team.type.type: Type of work
|
§ page.team.type.type: Type of work
|
||||||
|
§ page.team.type.unknown: unknown
|
||||||
§ page.team.type.tasks: Tasks
|
§ page.team.type.tasks: Tasks
|
||||||
§ page.team.type.tasksSmall: tasks
|
§ page.team.type.tasksSmall: tasks
|
||||||
§ page.team.type.days: Days
|
§ page.team.type.days: Days
|
||||||
|
@ -101,6 +110,11 @@ export default `
|
||||||
§ page.team.tree.filters.commits: Number of commits
|
§ page.team.tree.filters.commits: Number of commits
|
||||||
§ page.team.tree.filters.help: The minimum number of commits an employee has made in a file
|
§ page.team.tree.filters.help: The minimum number of commits an employee has made in a file
|
||||||
§ page.team.tree.filters.all: All employees
|
§ page.team.tree.filters.all: All employees
|
||||||
|
§ page.team.tree.totalLines: Lines
|
||||||
|
§ page.team.tree.totalTasks: Tasks
|
||||||
|
§ page.team.tree.totalDays: Days
|
||||||
|
§ page.team.tree.tasks: tasks
|
||||||
|
§ page.team.tree.days: days
|
||||||
§ page.team.tree.add: Who added
|
§ page.team.tree.add: Who added
|
||||||
§ page.team.tree.change: Who changed
|
§ page.team.tree.change: Who changed
|
||||||
§ page.team.tree.remove: Who removed
|
§ page.team.tree.remove: Who removed
|
||||||
|
@ -108,6 +122,29 @@ export default `
|
||||||
§ page.team.tree.linesAdded: added
|
§ page.team.tree.linesAdded: added
|
||||||
§ page.team.tree.linesChanged: changed
|
§ page.team.tree.linesChanged: changed
|
||||||
§ page.team.tree.linesRemoved: removed
|
§ page.team.tree.linesRemoved: removed
|
||||||
|
§ page.team.company.title: Details
|
||||||
|
§ page.team.company.employments.title: By number of employees
|
||||||
|
§ page.team.company.employments.item: employments
|
||||||
|
§ page.team.company.daysChart.title: By duration of the contract
|
||||||
|
§ page.team.company.daysChart.item: days
|
||||||
|
§ page.team.company.active.yes: active
|
||||||
|
§ page.team.company.active.no: contract has expired
|
||||||
|
§ page.team.country.byTimezone: By the time of the last commit
|
||||||
|
§ page.team.country.pieByDomain.title: By email, timezone and language
|
||||||
|
§ page.team.country.pieByTimezone.title: By timezone
|
||||||
|
§ page.team.country.chart.item: employments
|
||||||
|
§ page.team.country.table.title: List of employees
|
||||||
|
§ page.team.country.table.country: Country
|
||||||
|
§ page.team.country.table.employments: Employments
|
||||||
|
§ page.team.refactor.title: Candidates for refactoring
|
||||||
|
§ page.team.refactor.lines: lines
|
||||||
|
§ page.team.refactor.tasks: tasks
|
||||||
|
§ page.team.refactor.days: days
|
||||||
|
§ page.team.refactor.path: Path
|
||||||
|
§ page.team.refactor.firstCommit: First commit
|
||||||
|
§ page.team.refactor.totalLines: Lines
|
||||||
|
§ page.team.refactor.totalTasks: Tasks
|
||||||
|
§ page.team.refactor.totalDays: Days in development
|
||||||
§ page.team.day.commits: Commits
|
§ page.team.day.commits: Commits
|
||||||
§ page.team.day.activity: Activity
|
§ page.team.day.activity: Activity
|
||||||
§ page.team.week.title: Weekly statistics
|
§ page.team.week.title: Weekly statistics
|
||||||
|
@ -139,7 +176,6 @@ export default `
|
||||||
§ page.team.pr.chart.14day: two weeks
|
§ page.team.pr.chart.14day: two weeks
|
||||||
§ page.team.pr.chart.30day: month
|
§ page.team.pr.chart.30day: month
|
||||||
§ page.team.pr.chart.more: more
|
§ page.team.pr.chart.more: more
|
||||||
§ page.team.pr.commits: Commits
|
|
||||||
§ page.team.pr.date: Merge Date
|
§ page.team.pr.date: Merge Date
|
||||||
§ page.team.pr.mergeAuthor: Merged by
|
§ page.team.pr.mergeAuthor: Merged by
|
||||||
§ page.team.pr.author: Employee
|
§ page.team.pr.author: Employee
|
||||||
|
@ -157,10 +193,6 @@ export default `
|
||||||
§ page.team.tasks.from: First commit
|
§ page.team.tasks.from: First commit
|
||||||
§ page.team.tasks.to: Last commit
|
§ page.team.tasks.to: Last commit
|
||||||
§ page.team.tasks.daysInWork: Days in work
|
§ page.team.tasks.daysInWork: Days in work
|
||||||
§ page.team.tasks.commits: Commits number
|
|
||||||
§ page.team.tasks.pr: Merge date
|
|
||||||
§ page.team.tasks.prAuthor: Merged by user
|
|
||||||
§ page.team.tasks.prDelayDays: Delay before merge in days
|
|
||||||
§ page.team.tasks.comments: Comments
|
§ page.team.tasks.comments: Comments
|
||||||
§ page.team.extension.extension: File extensions
|
§ page.team.extension.extension: File extensions
|
||||||
§ page.team.extension.type: File sub types
|
§ page.team.extension.type: File sub types
|
||||||
|
@ -169,6 +201,7 @@ export default `
|
||||||
§ page.team.extension.current.count: Number
|
§ page.team.extension.current.count: Number
|
||||||
§ page.team.extension.removed.count: Number of removed
|
§ page.team.extension.removed.count: Number of removed
|
||||||
§ page.team.extension.files: files
|
§ page.team.extension.files: files
|
||||||
|
§ page.team.release.download: Download
|
||||||
§ page.team.release.title: Release
|
§ page.team.release.title: Release
|
||||||
§ page.team.release.from: Created date
|
§ page.team.release.from: Created date
|
||||||
§ page.team.release.to: Delivery date
|
§ page.team.release.to: Delivery date
|
||||||
|
|
|
@ -29,6 +29,9 @@ export default `
|
||||||
§ sidebar.team.changes: Todas as alterações
|
§ sidebar.team.changes: Todas as alterações
|
||||||
§ sidebar.team.words: Palavras populares
|
§ sidebar.team.words: Palavras populares
|
||||||
§ sidebar.team.building: Concurso
|
§ sidebar.team.building: Concurso
|
||||||
|
§ sidebar.team.refactor: Refactoring
|
||||||
|
§ sidebar.team.company: Companies
|
||||||
|
§ sidebar.team.country: Locations
|
||||||
§ sidebar.team.settings: Sintonização
|
§ sidebar.team.settings: Sintonização
|
||||||
§ sidebar.person.total: Informação geral
|
§ sidebar.person.total: Informação geral
|
||||||
§ sidebar.person.money: Custo do trabalho
|
§ sidebar.person.money: Custo do trabalho
|
||||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
||||||
§ page.print.title: Git repository report
|
§ page.print.title: Git repository report
|
||||||
§ page.print.sub_title: «$1»
|
§ page.print.sub_title: «$1»
|
||||||
§ page.print.description: The data for the report was obtained from the commit history.
|
§ page.print.description: The data for the report was obtained from the commit history.
|
||||||
§ page.team.author.title: Employee statistics
|
§ page.team.author.statusChart.title: Status
|
||||||
|
§ page.team.author.daysChart.title: Days of work
|
||||||
|
§ page.team.author.daysChart.item: days
|
||||||
|
§ page.team.author.days.half: half year
|
||||||
|
§ page.team.author.days.one: year
|
||||||
|
§ page.team.author.days.15: year and a half
|
||||||
|
§ page.team.author.days.two: two years
|
||||||
|
§ page.team.author.days.more: more
|
||||||
|
§ page.team.author.title: Details
|
||||||
§ page.team.author.description1: *Part of the statistics* (work speed, costs, etc.) *for employees with the 'Assistant' type is not counted*, as it is an episodic role in the project. It is assumed that they do not affect the project, and their edits can be disregarded in the context of the overall volume of work.
|
§ page.team.author.description1: *Part of the statistics* (work speed, costs, etc.) *for employees with the 'Assistant' type is not counted*, as it is an episodic role in the project. It is assumed that they do not affect the project, and their edits can be disregarded in the context of the overall volume of work.
|
||||||
§ page.team.author.description2: *Default sorting* is by the number of tasks and groups (current, fired, assisting employees).
|
§ page.team.author.description2: *Default sorting* is by the number of tasks and groups (current, fired, assisting employees).
|
||||||
§ page.team.author.status: Status
|
§ page.team.author.status: Status
|
||||||
|
@ -62,6 +70,7 @@ export default `
|
||||||
§ page.team.type.title: Task type statistics
|
§ page.team.type.title: Task type statistics
|
||||||
§ page.team.type.description: *Personal contribution* is counted by the number of commits, not the volume of changed lines or files. Therefore, the "File Analysis" section should also be consulted to assess the scale of changes.
|
§ page.team.type.description: *Personal contribution* is counted by the number of commits, not the volume of changed lines or files. Therefore, the "File Analysis" section should also be consulted to assess the scale of changes.
|
||||||
§ page.team.type.type: Type of work
|
§ page.team.type.type: Type of work
|
||||||
|
§ page.team.type.unknown: unknown
|
||||||
§ page.team.type.tasks: Tasks
|
§ page.team.type.tasks: Tasks
|
||||||
§ page.team.type.tasksSmall: tasks
|
§ page.team.type.tasksSmall: tasks
|
||||||
§ page.team.type.days: Days
|
§ page.team.type.days: Days
|
||||||
|
@ -101,6 +110,11 @@ export default `
|
||||||
§ page.team.tree.filters.commits: Number of commits
|
§ page.team.tree.filters.commits: Number of commits
|
||||||
§ page.team.tree.filters.help: The minimum number of commits an employee has made in a file
|
§ page.team.tree.filters.help: The minimum number of commits an employee has made in a file
|
||||||
§ page.team.tree.filters.all: All employees
|
§ page.team.tree.filters.all: All employees
|
||||||
|
§ page.team.tree.totalLines: Lines
|
||||||
|
§ page.team.tree.totalTasks: Tasks
|
||||||
|
§ page.team.tree.totalDays: Days
|
||||||
|
§ page.team.tree.tasks: tasks
|
||||||
|
§ page.team.tree.days: days
|
||||||
§ page.team.tree.add: Who added
|
§ page.team.tree.add: Who added
|
||||||
§ page.team.tree.change: Who changed
|
§ page.team.tree.change: Who changed
|
||||||
§ page.team.tree.remove: Who removed
|
§ page.team.tree.remove: Who removed
|
||||||
|
@ -108,6 +122,29 @@ export default `
|
||||||
§ page.team.tree.linesAdded: added
|
§ page.team.tree.linesAdded: added
|
||||||
§ page.team.tree.linesChanged: changed
|
§ page.team.tree.linesChanged: changed
|
||||||
§ page.team.tree.linesRemoved: removed
|
§ page.team.tree.linesRemoved: removed
|
||||||
|
§ page.team.company.title: Details
|
||||||
|
§ page.team.company.employments.title: By number of employees
|
||||||
|
§ page.team.company.employments.item: employments
|
||||||
|
§ page.team.company.daysChart.title: By duration of the contract
|
||||||
|
§ page.team.company.daysChart.item: days
|
||||||
|
§ page.team.company.active.yes: active
|
||||||
|
§ page.team.company.active.no: contract has expired
|
||||||
|
§ page.team.country.byTimezone: By the time of the last commit
|
||||||
|
§ page.team.country.pieByDomain.title: By email, timezone and language
|
||||||
|
§ page.team.country.pieByTimezone.title: By timezone
|
||||||
|
§ page.team.country.chart.item: employments
|
||||||
|
§ page.team.country.table.title: List of employees
|
||||||
|
§ page.team.country.table.country: Country
|
||||||
|
§ page.team.country.table.employments: Employments
|
||||||
|
§ page.team.refactor.title: Candidates for refactoring
|
||||||
|
§ page.team.refactor.lines: lines
|
||||||
|
§ page.team.refactor.tasks: tasks
|
||||||
|
§ page.team.refactor.days: days
|
||||||
|
§ page.team.refactor.path: Path
|
||||||
|
§ page.team.refactor.firstCommit: First commit
|
||||||
|
§ page.team.refactor.totalLines: Lines
|
||||||
|
§ page.team.refactor.totalTasks: Tasks
|
||||||
|
§ page.team.refactor.totalDays: Days in development
|
||||||
§ page.team.day.commits: Commits
|
§ page.team.day.commits: Commits
|
||||||
§ page.team.day.activity: Activity
|
§ page.team.day.activity: Activity
|
||||||
§ page.team.week.title: Weekly statistics
|
§ page.team.week.title: Weekly statistics
|
||||||
|
@ -139,7 +176,6 @@ export default `
|
||||||
§ page.team.pr.chart.14day: two weeks
|
§ page.team.pr.chart.14day: two weeks
|
||||||
§ page.team.pr.chart.30day: month
|
§ page.team.pr.chart.30day: month
|
||||||
§ page.team.pr.chart.more: more
|
§ page.team.pr.chart.more: more
|
||||||
§ page.team.pr.commits: Commits
|
|
||||||
§ page.team.pr.date: Merge Date
|
§ page.team.pr.date: Merge Date
|
||||||
§ page.team.pr.mergeAuthor: Merged by
|
§ page.team.pr.mergeAuthor: Merged by
|
||||||
§ page.team.pr.author: Employee
|
§ page.team.pr.author: Employee
|
||||||
|
@ -157,10 +193,6 @@ export default `
|
||||||
§ page.team.tasks.from: First commit
|
§ page.team.tasks.from: First commit
|
||||||
§ page.team.tasks.to: Last commit
|
§ page.team.tasks.to: Last commit
|
||||||
§ page.team.tasks.daysInWork: Days in work
|
§ page.team.tasks.daysInWork: Days in work
|
||||||
§ page.team.tasks.commits: Commits number
|
|
||||||
§ page.team.tasks.pr: Merge date
|
|
||||||
§ page.team.tasks.prAuthor: Merged by user
|
|
||||||
§ page.team.tasks.prDelayDays: Delay before merge in days
|
|
||||||
§ page.team.tasks.comments: Comments
|
§ page.team.tasks.comments: Comments
|
||||||
§ page.team.extension.extension: File extensions
|
§ page.team.extension.extension: File extensions
|
||||||
§ page.team.extension.type: File sub types
|
§ page.team.extension.type: File sub types
|
||||||
|
@ -169,6 +201,7 @@ export default `
|
||||||
§ page.team.extension.current.count: Number
|
§ page.team.extension.current.count: Number
|
||||||
§ page.team.extension.removed.count: Number of removed
|
§ page.team.extension.removed.count: Number of removed
|
||||||
§ page.team.extension.files: files
|
§ page.team.extension.files: files
|
||||||
|
§ page.team.release.download: Download
|
||||||
§ page.team.release.title: Release
|
§ page.team.release.title: Release
|
||||||
§ page.team.release.from: Created date
|
§ page.team.release.from: Created date
|
||||||
§ page.team.release.to: Delivery date
|
§ page.team.release.to: Delivery date
|
||||||
|
|
|
@ -28,6 +28,9 @@ export default `
|
||||||
§ sidebar.team.changes: Все изменения
|
§ sidebar.team.changes: Все изменения
|
||||||
§ sidebar.team.words: Популярные слова
|
§ sidebar.team.words: Популярные слова
|
||||||
§ sidebar.team.building: Викторина
|
§ sidebar.team.building: Викторина
|
||||||
|
§ sidebar.team.refactor: Рефакторинг
|
||||||
|
§ sidebar.team.company: Компании
|
||||||
|
§ sidebar.team.country: Местоположение
|
||||||
§ sidebar.team.settings: Настройки
|
§ sidebar.team.settings: Настройки
|
||||||
§ sidebar.person.total: Общая информация
|
§ sidebar.person.total: Общая информация
|
||||||
§ sidebar.person.money: Стоимость работы
|
§ sidebar.person.money: Стоимость работы
|
||||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
||||||
§ page.print.title: Отчёт по git-репозиторию
|
§ page.print.title: Отчёт по git-репозиторию
|
||||||
§ page.print.sub_title: «$1»
|
§ page.print.sub_title: «$1»
|
||||||
§ page.print.description: Данные для отчёта были получены из истории коммитов.
|
§ page.print.description: Данные для отчёта были получены из истории коммитов.
|
||||||
§ page.team.author.title: Статистика по сотрудникам
|
§ page.team.author.statusChart.title: Текущий статус
|
||||||
|
§ page.team.author.daysChart.title: Время работы
|
||||||
|
§ page.team.author.daysChart.item: дней
|
||||||
|
§ page.team.author.days.half: пол года
|
||||||
|
§ page.team.author.days.one: год
|
||||||
|
§ page.team.author.days.15: полтора
|
||||||
|
§ page.team.author.days.two: два года
|
||||||
|
§ page.team.author.days.more: больше
|
||||||
|
§ page.team.author.title: Детализация
|
||||||
§ page.team.author.description1: *Часть статистики* (скорость работы, затраченные деньги и т.п.) *по сотрудникам с типом «Помощник» не считается*, т.к. это эпизодическая роль в проекте. Предполагаем, что они не влияют на проект, а их правками можно пренебречь на фоне общего объема работы.
|
§ page.team.author.description1: *Часть статистики* (скорость работы, затраченные деньги и т.п.) *по сотрудникам с типом «Помощник» не считается*, т.к. это эпизодическая роль в проекте. Предполагаем, что они не влияют на проект, а их правками можно пренебречь на фоне общего объема работы.
|
||||||
§ page.team.author.description2: *Сортировка по умолчанию* — это сортировка по количеству задач и группам (текущие, уволенные, помогающие сотрудники).
|
§ page.team.author.description2: *Сортировка по умолчанию* — это сортировка по количеству задач и группам (текущие, уволенные, помогающие сотрудники).
|
||||||
§ page.team.author.status: Статус
|
§ page.team.author.status: Статус
|
||||||
|
@ -62,6 +70,7 @@ export default `
|
||||||
§ page.team.type.title: Статистика по типам задач
|
§ page.team.type.title: Статистика по типам задач
|
||||||
§ page.team.type.description: *Персональный вклад* считается по количеству коммитов, а не объему измененных строк или файлов. Поэтому следует так же смотреть раздел «Анализ файлов», чтобы оценить масштаб изменений.
|
§ page.team.type.description: *Персональный вклад* считается по количеству коммитов, а не объему измененных строк или файлов. Поэтому следует так же смотреть раздел «Анализ файлов», чтобы оценить масштаб изменений.
|
||||||
§ page.team.type.type: Тип работы
|
§ page.team.type.type: Тип работы
|
||||||
|
§ page.team.type.unknown: неизвестный
|
||||||
§ page.team.type.tasks: Задач
|
§ page.team.type.tasks: Задач
|
||||||
§ page.team.type.tasksSmall: задач
|
§ page.team.type.tasksSmall: задач
|
||||||
§ page.team.type.days: Дней
|
§ page.team.type.days: Дней
|
||||||
|
@ -101,6 +110,11 @@ export default `
|
||||||
§ page.team.tree.filters.commits: Количество коммитов
|
§ page.team.tree.filters.commits: Количество коммитов
|
||||||
§ page.team.tree.filters.help: Минимальное количество коммитов, которое сделал сотрудник в файле
|
§ page.team.tree.filters.help: Минимальное количество коммитов, которое сделал сотрудник в файле
|
||||||
§ page.team.tree.filters.all: Все сотрудники
|
§ page.team.tree.filters.all: Все сотрудники
|
||||||
|
§ page.team.tree.totalLines: Строк
|
||||||
|
§ page.team.tree.totalTasks: Задач
|
||||||
|
§ page.team.tree.totalDays: Дней
|
||||||
|
§ page.team.tree.tasks: задач
|
||||||
|
§ page.team.tree.days: дней
|
||||||
§ page.team.tree.add: Кто добавлял
|
§ page.team.tree.add: Кто добавлял
|
||||||
§ page.team.tree.change: Кто менял
|
§ page.team.tree.change: Кто менял
|
||||||
§ page.team.tree.remove: Кто удалял
|
§ page.team.tree.remove: Кто удалял
|
||||||
|
@ -108,6 +122,29 @@ export default `
|
||||||
§ page.team.tree.linesAdded: добавили
|
§ page.team.tree.linesAdded: добавили
|
||||||
§ page.team.tree.linesChanged: изменили
|
§ page.team.tree.linesChanged: изменили
|
||||||
§ page.team.tree.linesRemoved: удалили
|
§ page.team.tree.linesRemoved: удалили
|
||||||
|
§ page.team.company.title: Детализация
|
||||||
|
§ page.team.company.employments.title: По количеству сотрудников
|
||||||
|
§ page.team.company.employments.item: сотрудников
|
||||||
|
§ page.team.company.daysChart.title: По длительности контракта
|
||||||
|
§ page.team.company.daysChart.item: дней
|
||||||
|
§ page.team.company.active.yes: активна
|
||||||
|
§ page.team.company.active.no: контракт истёк
|
||||||
|
§ page.team.country.byTimezone: По времени последнего коммита
|
||||||
|
§ page.team.country.pieByDomain.title: По почте, времени и языку
|
||||||
|
§ page.team.country.pieByTimezone.title: По времени
|
||||||
|
§ page.team.country.chart.item: сотрудников
|
||||||
|
§ page.team.country.table.title: Список сотрудников
|
||||||
|
§ page.team.country.table.country: Местоположение
|
||||||
|
§ page.team.country.table.employments: Сотрудники
|
||||||
|
§ page.team.refactor.title: Кандидаты на рефакторинг
|
||||||
|
§ page.team.refactor.lines: строк
|
||||||
|
§ page.team.refactor.tasks: задач
|
||||||
|
§ page.team.refactor.days: дней
|
||||||
|
§ page.team.refactor.path: Путь
|
||||||
|
§ page.team.refactor.firstCommit: Первый коммит
|
||||||
|
§ page.team.refactor.totalLines: Строк
|
||||||
|
§ page.team.refactor.totalTasks: Задач
|
||||||
|
§ page.team.refactor.totalDays: Дней в разработке
|
||||||
§ page.team.day.commits: Коммиты
|
§ page.team.day.commits: Коммиты
|
||||||
§ page.team.day.activity: Активность
|
§ page.team.day.activity: Активность
|
||||||
§ page.team.week.title: Статистика по неделям
|
§ page.team.week.title: Статистика по неделям
|
||||||
|
@ -139,7 +176,6 @@ export default `
|
||||||
§ page.team.pr.chart.14day: две недели
|
§ page.team.pr.chart.14day: две недели
|
||||||
§ page.team.pr.chart.30day: месяц
|
§ page.team.pr.chart.30day: месяц
|
||||||
§ page.team.pr.chart.more: более
|
§ page.team.pr.chart.more: более
|
||||||
§ page.team.pr.commits: Коммиты
|
|
||||||
§ page.team.pr.date: Дата влития
|
§ page.team.pr.date: Дата влития
|
||||||
§ page.team.pr.mergeAuthor: Влил
|
§ page.team.pr.mergeAuthor: Влил
|
||||||
§ page.team.pr.author: Сотрудник
|
§ page.team.pr.author: Сотрудник
|
||||||
|
@ -157,10 +193,6 @@ export default `
|
||||||
§ page.team.tasks.from: Первый коммит
|
§ page.team.tasks.from: Первый коммит
|
||||||
§ page.team.tasks.to: Последний коммит
|
§ page.team.tasks.to: Последний коммит
|
||||||
§ page.team.tasks.daysInWork: Дней в работе
|
§ page.team.tasks.daysInWork: Дней в работе
|
||||||
§ page.team.tasks.commits: Количество коммитов
|
|
||||||
§ page.team.tasks.pr: Дата влития
|
|
||||||
§ page.team.tasks.prAuthor: Влил
|
|
||||||
§ page.team.tasks.prDelayDays: Дней ожидания влития
|
|
||||||
§ page.team.tasks.comments: Комментарии
|
§ page.team.tasks.comments: Комментарии
|
||||||
§ page.team.extension.extension: Расширения файлов
|
§ page.team.extension.extension: Расширения файлов
|
||||||
§ page.team.extension.type: Подтип файлов
|
§ page.team.extension.type: Подтип файлов
|
||||||
|
@ -169,6 +201,7 @@ export default `
|
||||||
§ page.team.extension.current.count: Количество
|
§ page.team.extension.current.count: Количество
|
||||||
§ page.team.extension.removed.count: Количество удалённых
|
§ page.team.extension.removed.count: Количество удалённых
|
||||||
§ page.team.extension.files: файлов
|
§ page.team.extension.files: файлов
|
||||||
|
§ page.team.release.download: Скачать
|
||||||
§ page.team.release.title: Релиз
|
§ page.team.release.title: Релиз
|
||||||
§ page.team.release.from: Дата создания
|
§ page.team.release.from: Дата создания
|
||||||
§ page.team.release.to: Дата завершения
|
§ page.team.release.to: Дата завершения
|
||||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
||||||
§ page.print.title: Отчёт по git-репозиторию
|
§ page.print.title: Отчёт по git-репозиторию
|
||||||
§ page.print.sub_title: «$1»
|
§ page.print.sub_title: «$1»
|
||||||
§ page.print.description: Данные для отчёта были получены из истории коммитов.
|
§ page.print.description: Данные для отчёта были получены из истории коммитов.
|
||||||
§ page.team.author.title: Статистика по сотрудникам
|
§ page.team.author.statusChart.title: Status
|
||||||
|
§ page.team.author.daysChart.title: Days of work
|
||||||
|
§ page.team.author.daysChart.item: days
|
||||||
|
§ page.team.author.days.half: half year
|
||||||
|
§ page.team.author.days.one: year
|
||||||
|
§ page.team.author.days.15: year and a half
|
||||||
|
§ page.team.author.days.two: two years
|
||||||
|
§ page.team.author.days.more: more
|
||||||
|
§ page.team.author.title: Details
|
||||||
§ page.team.author.description1: Часть статистики (скорость работы, затраченные деньги и т.п.) по сотрудникам с типом «Помощник» не считается, т.к. это не постоянная роль в проекте. Их работа незначительно и её можно не учитывать.
|
§ page.team.author.description1: Часть статистики (скорость работы, затраченные деньги и т.п.) по сотрудникам с типом «Помощник» не считается, т.к. это не постоянная роль в проекте. Их работа незначительно и её можно не учитывать.
|
||||||
§ page.team.author.description2: Сортировка по умолчанию — это сортировка по количеству задач и группам (текущие, уволенные, помогающие сотрудники).
|
§ page.team.author.description2: Сортировка по умолчанию — это сортировка по количеству задач и группам (текущие, уволенные, помогающие сотрудники).
|
||||||
§ page.team.author.status: Status
|
§ page.team.author.status: Status
|
||||||
|
@ -62,6 +70,7 @@ export default `
|
||||||
§ page.team.type.title: Статистика по типам задач
|
§ page.team.type.title: Статистика по типам задач
|
||||||
§ page.team.type.description: *Персональный вклад* считается по количеству коммитов, а не объему измененных строк или файлов. Поэтому следует так же смотреть раздел «Анализ файлов», чтобы оценить масштаб изменений.
|
§ page.team.type.description: *Персональный вклад* считается по количеству коммитов, а не объему измененных строк или файлов. Поэтому следует так же смотреть раздел «Анализ файлов», чтобы оценить масштаб изменений.
|
||||||
§ page.team.type.type: Тип работы
|
§ page.team.type.type: Тип работы
|
||||||
|
§ page.team.type.unknown: unknown
|
||||||
§ page.team.type.tasks: Задач
|
§ page.team.type.tasks: Задач
|
||||||
§ page.team.type.tasksSmall: задач
|
§ page.team.type.tasksSmall: задач
|
||||||
§ page.team.type.days: Дней
|
§ page.team.type.days: Дней
|
||||||
|
@ -101,6 +110,11 @@ export default `
|
||||||
§ page.team.tree.filters.commits: Количество коммитов
|
§ page.team.tree.filters.commits: Количество коммитов
|
||||||
§ page.team.tree.filters.help: Минимальное количество коммитов, которое сделал сотрудник в файле
|
§ page.team.tree.filters.help: Минимальное количество коммитов, которое сделал сотрудник в файле
|
||||||
§ page.team.tree.filters.all: Все сотрудники
|
§ page.team.tree.filters.all: Все сотрудники
|
||||||
|
§ page.team.tree.totalLines: Lines
|
||||||
|
§ page.team.tree.totalTasks: Tasks
|
||||||
|
§ page.team.tree.totalDays: Days
|
||||||
|
§ page.team.tree.tasks: tasks
|
||||||
|
§ page.team.tree.days: days
|
||||||
§ page.team.tree.add: Кто добавлял
|
§ page.team.tree.add: Кто добавлял
|
||||||
§ page.team.tree.change: Кто менял
|
§ page.team.tree.change: Кто менял
|
||||||
§ page.team.tree.remove: Кто удалял
|
§ page.team.tree.remove: Кто удалял
|
||||||
|
@ -132,7 +146,6 @@ export default `
|
||||||
§ page.team.pr.delayDays: Дней ожидания влития
|
§ page.team.pr.delayDays: Дней ожидания влития
|
||||||
§ page.team.pr.all.workDays: Время работы над задачей
|
§ page.team.pr.all.workDays: Время работы над задачей
|
||||||
§ page.team.pr.all.delayDays: Время ревью PR
|
§ page.team.pr.all.delayDays: Время ревью PR
|
||||||
§ page.team.pr.commits: Коммиты
|
|
||||||
§ page.team.pr.date: Дата влития
|
§ page.team.pr.date: Дата влития
|
||||||
§ page.team.pr.mergeAuthor: Влил
|
§ page.team.pr.mergeAuthor: Влил
|
||||||
§ page.team.pr.author: Сотрудник
|
§ page.team.pr.author: Сотрудник
|
||||||
|
@ -157,10 +170,6 @@ export default `
|
||||||
§ page.team.tasks.from: Первый коммит
|
§ page.team.tasks.from: Первый коммит
|
||||||
§ page.team.tasks.to: Последний коммит
|
§ page.team.tasks.to: Последний коммит
|
||||||
§ page.team.tasks.daysInWork: Дней в работе
|
§ page.team.tasks.daysInWork: Дней в работе
|
||||||
§ page.team.tasks.commits: Количество коммитов
|
|
||||||
§ page.team.tasks.pr: Дата влития
|
|
||||||
§ page.team.tasks.prAuthor: Влил
|
|
||||||
§ page.team.tasks.prDelayDays: Дней ожидания влития
|
|
||||||
§ page.team.tasks.comments: Комментарии
|
§ page.team.tasks.comments: Комментарии
|
||||||
§ page.person.print.photo.title: Фотография
|
§ page.person.print.photo.title: Фотография
|
||||||
§ page.person.print.photo.description: место для фотографии
|
§ page.person.print.photo.description: место для фотографии
|
||||||
|
|
|
@ -29,6 +29,9 @@ export default `
|
||||||
§ sidebar.team.changes: 所有更改
|
§ sidebar.team.changes: 所有更改
|
||||||
§ sidebar.team.words: 流行语
|
§ sidebar.team.words: 流行语
|
||||||
§ sidebar.team.building: 测验
|
§ sidebar.team.building: 测验
|
||||||
|
§ sidebar.team.refactor: Refactoring
|
||||||
|
§ sidebar.team.company: Companies
|
||||||
|
§ sidebar.team.country: Locations
|
||||||
§ sidebar.team.settings: 设置
|
§ sidebar.team.settings: 设置
|
||||||
§ sidebar.person.total: 般资料
|
§ sidebar.person.total: 般资料
|
||||||
§ sidebar.person.money: 工作的成本
|
§ sidebar.person.money: 工作的成本
|
||||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
||||||
§ page.print.title: Git仓库报告
|
§ page.print.title: Git仓库报告
|
||||||
§ page.print.sub_title: «$1»
|
§ page.print.sub_title: «$1»
|
||||||
§ page.print.description: 报告的数据是从历史记录中获得的 Commits.
|
§ page.print.description: 报告的数据是从历史记录中获得的 Commits.
|
||||||
§ page.team.author.title: 雇员统计数字
|
§ page.team.author.statusChart.title: Status
|
||||||
|
§ page.team.author.daysChart.title: Days of work
|
||||||
|
§ page.team.author.daysChart.item: days
|
||||||
|
§ page.team.author.days.half: half year
|
||||||
|
§ page.team.author.days.one: year
|
||||||
|
§ page.team.author.days.15: year and a half
|
||||||
|
§ page.team.author.days.two: two years
|
||||||
|
§ page.team.author.days.more: more
|
||||||
|
§ page.team.author.title: Details
|
||||||
§ page.team.author.description1: 部分统计数字 (工作的速度,花费的钱等。.) 不考虑具有"助理"类型的员工,因为这不是项目中的永久角色。 他们的工作微不足道,可以忽略。.
|
§ page.team.author.description1: 部分统计数字 (工作的速度,花费的钱等。.) 不考虑具有"助理"类型的员工,因为这不是项目中的永久角色。 他们的工作微不足道,可以忽略。.
|
||||||
§ page.team.author.description2: 默认排序是按任务和组数排序 (现任、被解雇、帮助雇员).
|
§ page.team.author.description2: 默认排序是按任务和组数排序 (现任、被解雇、帮助雇员).
|
||||||
§ page.team.author.types: 工作类别
|
§ page.team.author.types: 工作类别
|
||||||
|
@ -57,6 +65,7 @@ export default `
|
||||||
§ page.team.type.title: 按任务类型划分的统计信息
|
§ page.team.type.title: 按任务类型划分的统计信息
|
||||||
§ page.team.type.description: *个人贡献* 它是按数字计算的 Commits, 而不是修改的字符串或文件的体积。 因此,您还应该查看"文件分析"部分以评估更改的规模。
|
§ page.team.type.description: *个人贡献* 它是按数字计算的 Commits, 而不是修改的字符串或文件的体积。 因此,您还应该查看"文件分析"部分以评估更改的规模。
|
||||||
§ page.team.type.type: 工作类别
|
§ page.team.type.type: 工作类别
|
||||||
|
§ page.team.type.unknown: unknown
|
||||||
§ page.team.type.tasks: 任务
|
§ page.team.type.tasks: 任务
|
||||||
§ page.team.type.tasksSmall: 任务
|
§ page.team.type.tasksSmall: 任务
|
||||||
§ page.team.type.days: 天数
|
§ page.team.type.days: 天数
|
||||||
|
@ -96,6 +105,11 @@ export default `
|
||||||
§ page.team.tree.filters.commits: 数量 Commits
|
§ page.team.tree.filters.commits: 数量 Commits
|
||||||
§ page.team.tree.filters.help: 最低数量 Commits, 雇员在档案中所做的
|
§ page.team.tree.filters.help: 最低数量 Commits, 雇员在档案中所做的
|
||||||
§ page.team.tree.filters.all: 所有员工
|
§ page.team.tree.filters.all: 所有员工
|
||||||
|
§ page.team.tree.totalLines: Lines
|
||||||
|
§ page.team.tree.totalTasks: Tasks
|
||||||
|
§ page.team.tree.totalDays: Days
|
||||||
|
§ page.team.tree.tasks: tasks
|
||||||
|
§ page.team.tree.days: days
|
||||||
§ page.team.tree.add: 谁加的
|
§ page.team.tree.add: 谁加的
|
||||||
§ page.team.tree.change: 谁改变了它
|
§ page.team.tree.change: 谁改变了它
|
||||||
§ page.team.tree.remove: 谁删除了它
|
§ page.team.tree.remove: 谁删除了它
|
||||||
|
@ -103,6 +117,29 @@ export default `
|
||||||
§ page.team.tree.linesAdded: 补充道
|
§ page.team.tree.linesAdded: 补充道
|
||||||
§ page.team.tree.linesChanged: changed
|
§ page.team.tree.linesChanged: changed
|
||||||
§ page.team.tree.linesRemoved: 改变了
|
§ page.team.tree.linesRemoved: 改变了
|
||||||
|
§ page.team.company.title: Details
|
||||||
|
§ page.team.company.employments.title: By number of employees
|
||||||
|
§ page.team.company.employments.item: employments
|
||||||
|
§ page.team.company.daysChart.title: By duration of the contract
|
||||||
|
§ page.team.company.daysChart.item: days
|
||||||
|
§ page.team.company.active.yes: active
|
||||||
|
§ page.team.company.active.no: contract has expired
|
||||||
|
§ page.team.country.byTimezone: By the time of the last commit
|
||||||
|
§ page.team.country.pieByDomain.title: By email, timezone and language
|
||||||
|
§ page.team.country.pieByTimezone.title: By timezone
|
||||||
|
§ page.team.country.chart.item: employments
|
||||||
|
§ page.team.country.table.title: List of employees
|
||||||
|
§ page.team.country.table.country: Country
|
||||||
|
§ page.team.country.table.employments: Employments
|
||||||
|
§ page.team.refactor.title: Candidates for refactoring
|
||||||
|
§ page.team.refactor.lines: lines
|
||||||
|
§ page.team.refactor.tasks: tasks
|
||||||
|
§ page.team.refactor.days: days
|
||||||
|
§ page.team.refactor.path: Path
|
||||||
|
§ page.team.refactor.firstCommit: First commit
|
||||||
|
§ page.team.refactor.totalLines: Lines
|
||||||
|
§ page.team.refactor.totalTasks: Tasks
|
||||||
|
§ page.team.refactor.totalDays: Days in development
|
||||||
§ page.team.day.commits: Commits
|
§ page.team.day.commits: Commits
|
||||||
§ page.team.day.activity: Activity
|
§ page.team.day.activity: Activity
|
||||||
§ page.team.week.title: 按周划分的统计数字
|
§ page.team.week.title: 按周划分的统计数字
|
||||||
|
@ -134,7 +171,6 @@ export default `
|
||||||
§ page.team.pr.chart.14day: two weeks
|
§ page.team.pr.chart.14day: two weeks
|
||||||
§ page.team.pr.chart.30day: month
|
§ page.team.pr.chart.30day: month
|
||||||
§ page.team.pr.chart.more: more
|
§ page.team.pr.chart.more: more
|
||||||
§ page.team.pr.commits: Commits
|
|
||||||
§ page.team.pr.date: 注射日期
|
§ page.team.pr.date: 注射日期
|
||||||
§ page.team.pr.mergeAuthor: 填写
|
§ page.team.pr.mergeAuthor: 填写
|
||||||
§ page.team.pr.author: 雇员
|
§ page.team.pr.author: 雇员
|
||||||
|
@ -152,10 +188,6 @@ export default `
|
||||||
§ page.team.tasks.from: 第一个 Commits
|
§ page.team.tasks.from: 第一个 Commits
|
||||||
§ page.team.tasks.to: 最后一次 Commits
|
§ page.team.tasks.to: 最后一次 Commits
|
||||||
§ page.team.tasks.daysInWork: 工作中的日子
|
§ page.team.tasks.daysInWork: 工作中的日子
|
||||||
§ page.team.tasks.commits: 数量 Commits
|
|
||||||
§ page.team.tasks.pr: 注射日期
|
|
||||||
§ page.team.tasks.prAuthor: 灌
|
|
||||||
§ page.team.tasks.prDelayDays: 等待输液的日子
|
|
||||||
§ page.team.tasks.comments: 评论
|
§ page.team.tasks.comments: 评论
|
||||||
§ page.team.extension.extension: File extensions
|
§ page.team.extension.extension: File extensions
|
||||||
§ page.team.extension.type: File sub types
|
§ page.team.extension.type: File sub types
|
||||||
|
@ -164,6 +196,7 @@ export default `
|
||||||
§ page.team.extension.current.count: Number
|
§ page.team.extension.current.count: Number
|
||||||
§ page.team.extension.removed.count: Number of removed
|
§ page.team.extension.removed.count: Number of removed
|
||||||
§ page.team.extension.files: files
|
§ page.team.extension.files: files
|
||||||
|
§ page.team.release.download: Download
|
||||||
§ page.team.release.title: Release
|
§ page.team.release.title: Release
|
||||||
§ page.team.release.from: Created date
|
§ page.team.release.from: Created date
|
||||||
§ page.team.release.to: Delivery date
|
§ page.team.release.to: Delivery date
|
||||||
|
|
Loading…
Reference in a new issue