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 {
|
||||
const items = Object.entries(tasks)
|
||||
.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 (
|
||||
<>
|
||||
<div className={style.day_info_link}>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
border-radius: var(--border-radius-s);
|
||||
background-color: var(--color-white);
|
||||
|
||||
animation: notification_item 3s linear 0.5s forwards;
|
||||
animation: notification_item 0.5s linear 3s forwards;
|
||||
|
||||
&_icon {
|
||||
position: absolute;
|
||||
|
@ -56,7 +56,7 @@
|
|||
padding: 0;
|
||||
text-align: left;
|
||||
color: var(--color-white);
|
||||
animation: notification_title 3s linear 0.5s forwards;
|
||||
animation: notification_title 0.5s linear 3s forwards;
|
||||
}
|
||||
|
||||
&_title {
|
||||
|
@ -72,12 +72,10 @@
|
|||
@keyframes notification_item {
|
||||
80% {
|
||||
overflow: hidden;
|
||||
height: auto;
|
||||
padding: var(--space-xxl) var(--space-xxl) var(--space-xxl) 56px;
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
height: 0;
|
||||
padding: 0 var(--space-xxl) 0 64px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
|
|
@ -19,16 +19,17 @@
|
|||
display: inline-block;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&_icon {
|
||||
width: 160px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
&_legend {
|
||||
min-width: 160px;
|
||||
padding-left: var(--space-xxl);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&_line {
|
||||
|
@ -55,6 +56,7 @@
|
|||
line-height: 1.3;
|
||||
white-space: normal;
|
||||
text-decoration: none;
|
||||
vertical-align: top;
|
||||
color: var(--color-black);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,10 +31,11 @@ function DetailsCell({
|
|||
|
||||
const localClassName = getClassName(style.table_cell, column, ['body', row], className);
|
||||
|
||||
const hasIcon = ((column.properties && row[column.properties])
|
||||
|| !column.properties
|
||||
|| !column.properties?.length)
|
||||
&& column.formatter;
|
||||
const value = row?.[column?.properties || ''];
|
||||
const notNull = (Array.isArray(value) || value instanceof Set) // @ts-ignore
|
||||
? (value?.length || value?.size)
|
||||
: !!value;
|
||||
const hasIcon = notNull || !column?.properties;
|
||||
|
||||
const onClick = () => {
|
||||
if (!hasIcon || !updateRowsConfig) return;
|
||||
|
|
|
@ -33,5 +33,5 @@ export default function getAdaptiveColumnWidth(
|
|||
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
|
||||
const defaultWidth = child?.props?.width || {
|
||||
[ColumnTypesEnum.STRING]: 200,
|
||||
[ColumnTypesEnum.NUMBER]: 110,
|
||||
[ColumnTypesEnum.SHORT_NUMBER]: 70,
|
||||
}[template || ''] || 0;
|
||||
|
||||
// @ts-ignore
|
||||
const minWidth = child?.props?.minWidth || 40;
|
||||
const minWidth = child?.props?.minWidth || {
|
||||
[ColumnTypesEnum.STRING]: 200,
|
||||
[ColumnTypesEnum.NUMBER]: 110,
|
||||
}[template || ''] || 40;
|
||||
|
||||
// @ts-ignore
|
||||
const isSortable = child?.props?.isSortable // @ts-ignore
|
||||
|
@ -42,4 +43,4 @@ export default function getDefaultProps(children: React.ReactNode) {
|
|||
userWidth: undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ function getFormattedDate(commits: ICommit[]) {
|
|||
function getTags(commits: ICommit[]) {
|
||||
const uniqueTypes = new Set(commits.map((commit: ICommit) => commit.type));
|
||||
const tags = Array.from(uniqueTypes)
|
||||
.filter((title: string) => title && title !== '—')
|
||||
.filter((title: string) => title)
|
||||
.map((title: string) => (
|
||||
<p
|
||||
key={title}
|
||||
|
@ -43,7 +43,13 @@ interface ITaskProps {
|
|||
|
||||
function Task({ title, commits }: ITaskProps) {
|
||||
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 (
|
||||
<div
|
||||
key={title}
|
||||
|
|
|
@ -27,5 +27,5 @@
|
|||
}
|
||||
|
||||
.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 { 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 getCompany from 'ts/helpers/Parser/getCompany';
|
||||
|
||||
import userSettings from 'ts/store/UserSettings';
|
||||
|
||||
|
@ -38,6 +39,7 @@ export default class DataGripByAuthor {
|
|||
#updateCommitByAuthor(statistic: any, commit: ICommit) {
|
||||
statistic.commits += 1;
|
||||
statistic.lastCommit = commit;
|
||||
statistic.device = statistic.device || commit.device;
|
||||
statistic.days[commit.timestamp] = true;
|
||||
statistic.tasks[commit.task] = commit.added + commit.changes + commit.removed
|
||||
+ (statistic.tasks[commit.task] ? statistic.tasks[commit.task] : 0);
|
||||
|
@ -61,6 +63,10 @@ export default class DataGripByAuthor {
|
|||
statistic.lastCompany = commit.company;
|
||||
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) {
|
||||
|
@ -70,6 +76,11 @@ export default class DataGripByAuthor {
|
|||
const commitsByHour = new Array(24).fill(0);
|
||||
commitsByHour[commit.hours] += 1;
|
||||
|
||||
const country = commit.country
|
||||
|| getCountryBySymbol(commit.author)
|
||||
|| getCountryBySymbol(commit.message)
|
||||
|| getCountryByTimeZone(commit.timezone, commit.author);
|
||||
|
||||
this.commits.set(commit.author, {
|
||||
author: commit.author,
|
||||
commits: 1,
|
||||
|
@ -81,9 +92,12 @@ export default class DataGripByAuthor {
|
|||
scopes: createIncrement(commit.scope),
|
||||
hours: [commit.hours],
|
||||
company: commit.company
|
||||
? [{ title: commit.company, from: commit.timestamp }]
|
||||
? [{ title: commit.company, from: commit.milliseconds }]
|
||||
: [],
|
||||
lastCompany: commit.company,
|
||||
country: new Set([country]),
|
||||
lastCountry: country,
|
||||
device: commit.device,
|
||||
commitsByDayAndHour,
|
||||
commitsByHour,
|
||||
messageLength: [commit.text.length || 0],
|
||||
|
@ -195,7 +209,6 @@ export default class DataGripByAuthor {
|
|||
daysForTask: isStaff ? 0 : workDays / tasks.length,
|
||||
taskInDay: isStaff ? 0 : tasks.length / workDays,
|
||||
changesForTask: DataGripByAuthor.getMiddleValue(tasksSize),
|
||||
lastCompany: getCompany(dot.author, dot.lastCommit.email),
|
||||
|
||||
days: workDays,
|
||||
money: isStaff ? 0 : moneyWorked,
|
||||
|
|
|
@ -1,87 +1,76 @@
|
|||
import ICommit from 'ts/interfaces/Commit';
|
||||
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 {
|
||||
commits: HashMap<any> = new Map();
|
||||
companies: HashMap<any> = new Map();
|
||||
|
||||
statistic: any = [];
|
||||
|
||||
statisticByName: IHashMap<any> = {};
|
||||
|
||||
clear() {
|
||||
this.commits.clear();
|
||||
this.companies.clear();
|
||||
this.statistic = [];
|
||||
this.statisticByName = {};
|
||||
}
|
||||
|
||||
addCommit(commit: ICommit) {
|
||||
if (!commit.company) return;
|
||||
const statistic = this.commits.get(commit.company);
|
||||
#addAuthor(statByAuthor: any) {
|
||||
const company = statByAuthor.company[statByAuthor.company.length - 1]?.title;
|
||||
if (!company) return;
|
||||
const statistic = this.companies.get(company);
|
||||
if (statistic) {
|
||||
this.#updateCommitByCompany(statistic, commit);
|
||||
this.#updateCommitByCompany(statistic, statByAuthor);
|
||||
} else {
|
||||
this.#addCommitByCompany(commit);
|
||||
this.#addCommitByCompany(company, statByAuthor);
|
||||
}
|
||||
}
|
||||
|
||||
#addCommitByCompany(commit: ICommit) {
|
||||
this.commits.set(commit.company, {
|
||||
company: commit.company,
|
||||
commits: 1,
|
||||
firstCommit: commit,
|
||||
lastCommit: commit,
|
||||
days: createIncrement(commit.timestamp),
|
||||
employments: createIncrement(commit.author),
|
||||
tasks: createIncrement(commit.task),
|
||||
types: createIncrement(commit.type),
|
||||
scopes: createIncrement(commit.scope),
|
||||
#addCommitByCompany(company: string, statByAuthor: any) {
|
||||
this.companies.set(company, {
|
||||
company,
|
||||
isActive: !statByAuthor.isDismissed && !statByAuthor.isStaff,
|
||||
commits: statByAuthor.commits,
|
||||
from: statByAuthor.firstCommit.milliseconds,
|
||||
to: statByAuthor.lastCommit.milliseconds,
|
||||
daysWorked: statByAuthor.daysWorked,
|
||||
employments: [statByAuthor.author],
|
||||
tasks: [statByAuthor.tasks],
|
||||
// types: statByAuthor.types,
|
||||
// scopes: createIncrement(commit.scope),
|
||||
});
|
||||
}
|
||||
|
||||
#updateCommitByCompany(statistic: any, commit: ICommit) {
|
||||
statistic.commits += 1;
|
||||
statistic.lastCommit = commit;
|
||||
statistic.days[commit.timestamp] = true;
|
||||
statistic.employments[commit.author] = true;
|
||||
|
||||
increment(statistic.tasks, commit.task);
|
||||
increment(statistic.types, commit.type);
|
||||
increment(statistic.scopes, commit.scope);
|
||||
#updateCommitByCompany(statistic: any, statByAuthor: any) {
|
||||
if (!statistic.isActive) {
|
||||
statistic.isActive = !statByAuthor.isDismissed && !statByAuthor.isStaff;
|
||||
}
|
||||
statistic.commits += statByAuthor.commits;
|
||||
statistic.from = statistic.from > statByAuthor.firstCommit.milliseconds
|
||||
? statByAuthor.firstCommit.milliseconds
|
||||
: statistic.from;
|
||||
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) {
|
||||
console.dir(dataGripByAuthor);
|
||||
this.statistic = Array.from(this.commits.values())
|
||||
.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);
|
||||
dataGripByAuthor.statistic.forEach((data: any) => {
|
||||
this.#addAuthor(data);
|
||||
});
|
||||
|
||||
let isActive = false;
|
||||
employments.forEach((name) => {
|
||||
const author = dataGripByAuthor.statisticByName[name];
|
||||
if (!author) return;
|
||||
if (author.lastCompany === statistic.company) isActive = true;
|
||||
});
|
||||
this.statistic = Array.from(this.companies.values())
|
||||
.map((company: any) => {
|
||||
company.tasks = Array.from(new Set(company.tasks.flat(1))).length;
|
||||
company.totalDays = Math.max(((company.to - company.from) / ONE_DAY), 1);
|
||||
|
||||
const companyInfo = {
|
||||
...statistic,
|
||||
employments,
|
||||
tasks,
|
||||
totalTasks: tasks.length,
|
||||
totalDays: days.length,
|
||||
totalEmployments: employments.length,
|
||||
isActive,
|
||||
};
|
||||
delete companyInfo.days;
|
||||
this.statisticByName[company.company] = company;
|
||||
|
||||
this.statisticByName[statistic.company] = companyInfo;
|
||||
|
||||
return companyInfo;
|
||||
return company;
|
||||
});
|
||||
|
||||
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 { createIncrement, increment, WeightedAverage } from 'ts/helpers/Math';
|
||||
|
||||
const IS_PR = {
|
||||
[COMMIT_TYPE.PR_BITBUCKET]: true,
|
||||
[COMMIT_TYPE.PR_GITHUB]: true,
|
||||
[COMMIT_TYPE.PR_GITLAB]: true,
|
||||
};
|
||||
import { WeightedAverage } from 'ts/helpers/Math';
|
||||
import { ONE_DAY } from 'ts/helpers/formatter';
|
||||
|
||||
export default class DataGripByPR {
|
||||
pr: HashMap<any> = new Map();
|
||||
|
||||
prByTask: HashMap<any> = new Map();
|
||||
|
||||
lastCommitByTaskNumber: HashMap<any> = new Map();
|
||||
|
||||
statistic: any[] = [];
|
||||
|
||||
statisticByName: IHashMap<any> = [];
|
||||
statisticByName: IHashMap<any> = {};
|
||||
|
||||
clear() {
|
||||
this.pr.clear();
|
||||
this.prByTask.clear();
|
||||
this.lastCommitByTaskNumber.clear();
|
||||
this.statistic = [];
|
||||
this.statisticByName = {};
|
||||
}
|
||||
|
||||
addCommit(commit: ISystemCommit) {
|
||||
if (!commit.commitType) {
|
||||
const commitByTaskNumber = this.lastCommitByTaskNumber.get(commit.task);
|
||||
if (commitByTaskNumber) {
|
||||
this.#updateCommitByTaskNumber(commitByTaskNumber, commit);
|
||||
} else {
|
||||
this.#addCommitByTaskNumber(commit);
|
||||
}
|
||||
} else if (!this.pr.has(commit.prId) && IS_PR[commit.commitType || '']) {
|
||||
// commitType PR
|
||||
if (!commit.prId) return;
|
||||
const statistic = this.pr.get(commit.prId);
|
||||
if (statistic) {
|
||||
console.log('PR error');
|
||||
} else {
|
||||
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) {
|
||||
const lastCommit = this.lastCommitByTaskNumber.get(commit.task);
|
||||
if (lastCommit) {
|
||||
// коммиты после влития PR сгорают, чтобы не засчитать технические PR мержи веток
|
||||
this.lastCommitByTaskNumber.delete(commit.task);
|
||||
const delay = commit.milliseconds - lastCommit.endTaskTime;
|
||||
const work = lastCommit.endTaskTime - lastCommit.beginTaskTime;
|
||||
this.pr.set(commit.prId, {
|
||||
...commit,
|
||||
...lastCommit,
|
||||
delay,
|
||||
delayDays: delay / (24 * 60 * 60 * 1000),
|
||||
workDays: work === 0 ? 1 : (work / (24 * 60 * 60 * 1000)),
|
||||
});
|
||||
this.prByTask.set(commit.task, commit.prId);
|
||||
} else {
|
||||
this.pr.set(commit.prId, { ...commit });
|
||||
}
|
||||
this.pr.set(commit.prId, {
|
||||
prId : commit.prId,
|
||||
author: commit.author,
|
||||
task: commit.task,
|
||||
type: commit.type,
|
||||
branch: commit.branch,
|
||||
message: commit.message,
|
||||
dateCreate: commit.milliseconds, // last commit date before PR
|
||||
dateMerge: commit.milliseconds,
|
||||
daysReview: 1,
|
||||
daysInWork: 1,
|
||||
});
|
||||
}
|
||||
|
||||
updateTotalInfo(dataGripByAuthor: any) {
|
||||
const employment = dataGripByAuthor.employment;
|
||||
const authors = [...employment.active, ...employment.dismissed];
|
||||
const refAuthorPR: any = Object.fromEntries(authors.map((name: string) => ([name, []])));
|
||||
updateTotalInfo(dataGripByTasks: any, dataGripByAuthor: any) {
|
||||
const byAuthor = new Map();
|
||||
|
||||
this.statistic = Object.values(this.pr)
|
||||
.filter((item: any) => item.delay && item.task)
|
||||
.sort((a: any, b: any) => b.delay - a.delay);
|
||||
this.pr.forEach((pr: any) => {
|
||||
if (!pr.task) return;
|
||||
const task = dataGripByTasks.statisticByName.get(pr.task);
|
||||
if (!task) return;
|
||||
|
||||
this.statistic = [];
|
||||
this.statisticByName = {};
|
||||
task.prIds.push(pr.prId);
|
||||
|
||||
Object.values(this.pr).forEach((item: any) => {
|
||||
if (!item.delay || !item.task) return;
|
||||
|
||||
this.statistic.push(item);
|
||||
if (refAuthorPR[item.firstCommit.author]) {
|
||||
refAuthorPR[item.firstCommit.author].push(item);
|
||||
pr.daysInWork = task.daysInWork;
|
||||
let lastCommitDateBeforePR = task?.to || task?.from;
|
||||
if (lastCommitDateBeforePR > pr.dateMerge) {
|
||||
if (!task.timestamps) {
|
||||
console.log('x');
|
||||
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.updateTotalByAuthor(authors, refAuthorPR);
|
||||
this.statistic = Array.from(this.pr.values())
|
||||
.sort((a: any, b: any) => b.daysReview - a.daysReview);
|
||||
|
||||
this.lastCommitByTaskNumber.clear();
|
||||
this.updateTotalByAuthor(byAuthor, dataGripByAuthor);
|
||||
}
|
||||
|
||||
static getPRByGroups(list: any, propertyName: string) {
|
||||
|
@ -121,11 +99,14 @@ export default class DataGripByPR {
|
|||
[TITLES.MORE]: 0,
|
||||
};
|
||||
|
||||
let max = 0;
|
||||
|
||||
const weightedAverage = new WeightedAverage();
|
||||
|
||||
list.forEach((pr: any) => {
|
||||
const value = pr[propertyName];
|
||||
|
||||
if (value > max) max = value;
|
||||
weightedAverage.update(value);
|
||||
|
||||
if (value <= 1) details[TITLES.DAY]++;
|
||||
|
@ -138,36 +119,33 @@ export default class DataGripByPR {
|
|||
|
||||
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 = {};
|
||||
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;
|
||||
refAuthorPR[name].forEach((pr: any) => {
|
||||
if (pr.delayDays > maxDelayDays) maxDelayDays = pr.delayDays;
|
||||
});
|
||||
const daysReview = DataGripByPR.getPRByGroups(prs, 'daysReview');
|
||||
const daysReviewWeightedAverage = parseInt(daysReview.weightedAverage.toFixed(1), 10);
|
||||
|
||||
// TODO: сложын и не интересные показатели. Гистаграмму?
|
||||
const delayDays = DataGripByPR.getPRByGroups(refAuthorPR[name], 'delayDays');
|
||||
const delayDaysWeightedAverage = parseInt(delayDays.weightedAverage.toFixed(1), 10);
|
||||
const daysInWork = DataGripByPR.getPRByGroups(prs, 'daysInWork');
|
||||
const daysInWorkWeightedAverage = parseInt(daysInWork.weightedAverage.toFixed(1), 10);
|
||||
|
||||
const workDays = DataGripByPR.getPRByGroups(refAuthorPR[name], 'workDays');
|
||||
const workDaysWeightedAverage = parseInt(workDays.weightedAverage.toFixed(1), 10);
|
||||
this.statisticByName[author] = {
|
||||
author,
|
||||
maxDelayDays: daysReview.max,
|
||||
numberMergedPr: prs.length,
|
||||
|
||||
this.statisticByName[name] = {
|
||||
author: name,
|
||||
maxDelayDays,
|
||||
numberMergedPr: refAuthorPR[name].length,
|
||||
|
||||
workDays: workDays.details,
|
||||
delayDays: delayDays.details,
|
||||
weightedAverage: workDaysWeightedAverage + delayDaysWeightedAverage,
|
||||
workDays: daysInWork.details,
|
||||
delayDays: daysReview.details,
|
||||
weightedAverage: daysInWorkWeightedAverage + daysReviewWeightedAverage,
|
||||
weightedAverageDetails: {
|
||||
workDays: workDaysWeightedAverage,
|
||||
delayDays: delayDaysWeightedAverage,
|
||||
workDays: daysInWorkWeightedAverage,
|
||||
delayDays: daysReviewWeightedAverage,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
@ -28,34 +28,54 @@ export default class DataGripByRelease {
|
|||
addCommit(commit: ISystemCommit) {
|
||||
if (commit.commitType === COMMIT_TYPE.AUTO_MERGE) {
|
||||
if (this.release[commit.branch]) {
|
||||
this.#updateRelease(commit);
|
||||
this.#updateDateInRelease(commit.branch, commit);
|
||||
} else {
|
||||
this.#addRelease(commit);
|
||||
this.#addReleaseForBB(commit);
|
||||
}
|
||||
} else if (commit.commitType === COMMIT_TYPE.PR_GITHUB || commit.commitType === COMMIT_TYPE.PR_BITBUCKET) {
|
||||
this.lastPrList.push(commit);
|
||||
} else if (commit.commitType === COMMIT_TYPE.PR_GITHUB) {
|
||||
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) {
|
||||
const statistic = this.release[commit.branch];
|
||||
#updateDateInRelease(branch: string, commit: ISystemCommit) {
|
||||
const statistic = this.release[branch];
|
||||
statistic.lastCommit = commit;
|
||||
statistic.to = commit.timestamp;
|
||||
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;
|
||||
|
||||
const index = commit.branch.lastIndexOf('release');
|
||||
if (index === -1) return;
|
||||
const status = this.#addRelease(commit.branch, commit);
|
||||
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)
|
||||
.replace(/([^\w.]*)/, '')
|
||||
.replace(/([^\w.]*)|(["']*)/gim, '')
|
||||
.trim();
|
||||
|
||||
this.release[commit.branch] = {
|
||||
this.release[branch] = {
|
||||
title,
|
||||
firstCommit: commit,
|
||||
lastCommit: commit,
|
||||
|
@ -63,14 +83,14 @@ export default class DataGripByRelease {
|
|||
to: null,
|
||||
delayInDays: 0,
|
||||
waitingInDays: 0,
|
||||
pr: this.lastPrList,
|
||||
prLength: this.lastPrList.length,
|
||||
prIds: [],
|
||||
prLength: 0,
|
||||
};
|
||||
|
||||
this.lastPrList = [];
|
||||
return true;
|
||||
}
|
||||
|
||||
updateTotalInfo() {
|
||||
updateTotalInfo(dataGripByTasks: any, dataGripByPR: any) {
|
||||
let prev: any = null;
|
||||
|
||||
this.lastPrList = [];
|
||||
|
@ -80,6 +100,14 @@ export default class DataGripByRelease {
|
|||
.map((a: any) => {
|
||||
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.lastCommit.date
|
||||
: null;
|
||||
|
|
|
@ -1,46 +1,40 @@
|
|||
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 {
|
||||
commits: IHashMap<ICommit[]> = {};
|
||||
commits: HashMap<ICommit[]> = new Map();
|
||||
|
||||
statistic: any = [];
|
||||
|
||||
statisticByName: HashMap<any> = new Map();
|
||||
|
||||
// achievements
|
||||
longTaskByAuthor: IHashMap<number> = {};
|
||||
|
||||
clear() {
|
||||
this.commits = {};
|
||||
this.commits.clear();
|
||||
this.statistic = [];
|
||||
this.statisticByName.clear();
|
||||
this.longTaskByAuthor = {};
|
||||
}
|
||||
|
||||
addCommit(commit: ICommit) {
|
||||
if (this.commits.hasOwnProperty(commit.task)) {
|
||||
this.#updateCommitByTask(commit);
|
||||
if (this.commits.has(commit.task)) {
|
||||
this.commits.get(commit.task)?.push(commit);
|
||||
} 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: тут двойной пробег получился. А должен был частями собрать инфу
|
||||
updateTotalInfo(PRs: any) {
|
||||
this.statistic = Object.entries(this.commits)
|
||||
updateTotalInfo() {
|
||||
this.statistic = Array.from(this.commits, ([k, v]) => [k, v]) // @ts-ignore
|
||||
.map(([task, commits]: [string, ICommit[]]) => {
|
||||
const firstCommit = commits[0];
|
||||
const lastCommit = commits[commits.length - 1];
|
||||
const from = firstCommit.milliseconds;
|
||||
const pr = PRs.prByTask.get(task) ? PRs.pr.get(PRs.prByTask.get(task)) : null;
|
||||
|
||||
const shortInfo = {
|
||||
task,
|
||||
|
@ -48,30 +42,31 @@ export default class DataGripByTasks {
|
|||
from,
|
||||
commits: 1,
|
||||
daysInWork: 1,
|
||||
prDate: pr?.milliseconds,
|
||||
prDelayDays: pr?.delayDays,
|
||||
prAuthor: firstCommit.author === pr?.author ? null : pr?.author,
|
||||
prIds: [],
|
||||
releaseIds: new Set(),
|
||||
comments: firstCommit.text,
|
||||
types: firstCommit.type && firstCommit.type !== '—' ? [firstCommit.type] : [],
|
||||
scope: firstCommit.scope && firstCommit.scope !== '—' ? [firstCommit.scope] : [],
|
||||
types: firstCommit.type ? { [firstCommit.type]: 1 } : {},
|
||||
scope: firstCommit.scope ? { [firstCommit.scope]: 1 } : {},
|
||||
};
|
||||
|
||||
if (commits.length === 1) return shortInfo;
|
||||
|
||||
const authors = new Set();
|
||||
const messages = new Set();
|
||||
const types = new Set();
|
||||
const scope = new Set();
|
||||
const timestamps = new Set();
|
||||
const authors = {};
|
||||
const types = {};
|
||||
const scope = {};
|
||||
commits.forEach((commit: ICommit) => {
|
||||
authors.add(commit.author);
|
||||
messages.add(commit.text);
|
||||
if (commit.type !== '—') types.add(commit.type);
|
||||
if (commit.scope !== '—') scope.add(commit.scope);
|
||||
timestamps.add(commit.milliseconds);
|
||||
increment(authors, commit.author);
|
||||
increment(types, commit.type);
|
||||
increment(scope, commit.scope);
|
||||
});
|
||||
|
||||
const comments = Array.from(messages).join(', ');
|
||||
const to = lastCommit.milliseconds;
|
||||
const daysInWork = Math.ceil((to - from) / ONE_DAY) + 1;
|
||||
const daysInWork = timestamps.size;
|
||||
|
||||
const longTaskByAuthor = this.longTaskByAuthor[shortInfo.author];
|
||||
if (!longTaskByAuthor || longTaskByAuthor < daysInWork) {
|
||||
|
@ -82,16 +77,21 @@ export default class DataGripByTasks {
|
|||
...shortInfo,
|
||||
to: to !== from ? to : undefined,
|
||||
commits: commits.length,
|
||||
timestamps: Array.from(timestamps),
|
||||
daysInWork,
|
||||
authors: Array.from(authors),
|
||||
comments,
|
||||
types: Array.from(types),
|
||||
scope: Array.from(scope),
|
||||
authors,
|
||||
types,
|
||||
scope,
|
||||
};
|
||||
})
|
||||
.filter((dot) => dot.task)
|
||||
.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 DataGripByScoring from './components/scoring';
|
||||
import DataGripByCompany from './components/company';
|
||||
import DataGripByCountry from './components/country';
|
||||
|
||||
class DataGrip {
|
||||
firstLastCommit: any = new MinMaxCounter();
|
||||
|
@ -23,6 +24,8 @@ class DataGrip {
|
|||
|
||||
company: any = new DataGripByCompany();
|
||||
|
||||
country: any = new DataGripByCountry();
|
||||
|
||||
team: any = new DataGripByTeam();
|
||||
|
||||
scope: any = new DataGripByScope();
|
||||
|
@ -49,6 +52,7 @@ class DataGrip {
|
|||
this.firstLastCommit.clear();
|
||||
this.author.clear();
|
||||
this.company.clear();
|
||||
this.country.clear();
|
||||
this.team.clear();
|
||||
this.scope.clear();
|
||||
this.type.clear();
|
||||
|
@ -75,7 +79,6 @@ class DataGrip {
|
|||
this.get.addCommit(commit);
|
||||
this.week.addCommit(commit);
|
||||
this.tasks.addCommit(commit);
|
||||
this.company.addCommit(commit);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,11 +90,12 @@ class DataGrip {
|
|||
this.timestamp.updateTotalInfo(this.author);
|
||||
this.week.updateTotalInfo(this.author);
|
||||
this.recommendations.updateTotalInfo(this);
|
||||
this.pr.updateTotalInfo(this.author);
|
||||
this.tasks.updateTotalInfo(this.pr);
|
||||
this.release.updateTotalInfo();
|
||||
this.tasks.updateTotalInfo();
|
||||
this.pr.updateTotalInfo(this.tasks, this.author);
|
||||
this.release.updateTotalInfo(this.tasks, this.pr);
|
||||
this.scoring.updateTotalInfo(this.author, this.timestamp);
|
||||
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'
|
||||
.split('')
|
||||
.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_EMAILS,
|
||||
FAKE_TASK_PREFIXES,
|
||||
FAKE_COMPANIES,
|
||||
} from './constants';
|
||||
import FakeName from './FakeName';
|
||||
|
||||
|
@ -14,21 +15,26 @@ export default class Depersonalized {
|
|||
|
||||
fakeTaskPrefix: any = null;
|
||||
|
||||
fakeCompany: any = null;
|
||||
|
||||
constructor() {
|
||||
this.fakeName = new FakeName('User', FAKE_AUTHORS);
|
||||
this.fakeEmail = new FakeName('user', FAKE_EMAILS);
|
||||
this.fakeTaskPrefix = new FakeName('JIRA', FAKE_TASK_PREFIXES);
|
||||
this.fakeCompany = new FakeName('Company', FAKE_COMPANIES);
|
||||
}
|
||||
|
||||
getCommit(commit: ICommit | ISystemCommit): ICommit | ISystemCommit {
|
||||
const author = this.fakeName.get(commit.author);
|
||||
const email = this.fakeEmail.get(commit.author);
|
||||
const company = this.fakeCompany.get(commit.company);
|
||||
|
||||
if (!commit.task) {
|
||||
return {
|
||||
...commit,
|
||||
author,
|
||||
email,
|
||||
company,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -53,6 +59,7 @@ export default class Depersonalized {
|
|||
message,
|
||||
author,
|
||||
email,
|
||||
company,
|
||||
branch,
|
||||
toBranch,
|
||||
};
|
||||
|
|
|
@ -35,6 +35,7 @@ export default class FileBuilderCommon {
|
|||
// @ts-ignore
|
||||
const parts = file.path.split('/');
|
||||
parts.pop();
|
||||
file.pathString = file.path;
|
||||
file.path = parts;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { increment } from 'ts/helpers/Math';
|
|||
|
||||
import FileBuilderCommon from './Common';
|
||||
import FileBuilderLineStat from './LineStat';
|
||||
import FileBuilderTasks from './Tasks';
|
||||
|
||||
export default class FileGripByPaths {
|
||||
list: IDirtyFile[] = [];
|
||||
|
@ -39,17 +40,20 @@ export default class FileGripByPaths {
|
|||
#getNewDirtyFile(fileChange: IFileChange, commit: ICommit): any {
|
||||
const commonProps = FileBuilderCommon.getProps(fileChange, commit);
|
||||
const statProps = FileBuilderLineStat.getProps(fileChange, commit);
|
||||
const tasksProps = FileBuilderTasks.getProps(commit);
|
||||
|
||||
return {
|
||||
id: fileChange.id,
|
||||
...commonProps,
|
||||
...statProps,
|
||||
...tasksProps,
|
||||
};
|
||||
}
|
||||
|
||||
#updateDirtyFile(file: any, fileChange: IFileChange, commit: ICommit) {
|
||||
FileBuilderCommon.updateProps(file, fileChange, commit);
|
||||
FileBuilderLineStat.updateProps(file, fileChange, commit);
|
||||
FileBuilderTasks.updateProps(file, commit);
|
||||
}
|
||||
|
||||
#renameFile(file: any, newId: string) {
|
||||
|
@ -74,6 +78,7 @@ export default class FileGripByPaths {
|
|||
|
||||
FileBuilderCommon.updateTotal(file);
|
||||
FileBuilderLineStat.updateTotal(file);
|
||||
FileBuilderTasks.updateTotal(file);
|
||||
|
||||
if (file.type) {
|
||||
let refExtensionType = this.refExtensionType.get(file.extension);
|
||||
|
|
|
@ -4,12 +4,25 @@ import IHashMap from 'ts/interfaces/HashMap';
|
|||
import { getValuesInPercent } from '../helpers';
|
||||
|
||||
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 {
|
||||
id: Math.random(),
|
||||
name: name || '', // @ts-ignore
|
||||
path: path || [],
|
||||
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,
|
||||
|
||||
|
@ -48,6 +61,12 @@ function updateFolder(folder: any, file: IDirtyFile) {
|
|||
folder.removedLines += file.removedLines || 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, 'removedLinesByAuthor');
|
||||
updateFolderBy(folder, file, 'changedLinesByAuthor');
|
||||
|
@ -69,17 +88,19 @@ export default class FileGripByFolder {
|
|||
addFile(file: IDirtyFile) {
|
||||
let prev: any = this.tree.content;
|
||||
file.path.forEach((folderName: any, index: number) => {
|
||||
let folder = prev[folderName];
|
||||
if (!folder || !folder.content) {
|
||||
const folder = prev.get(folderName);
|
||||
if (!folder?.content) {
|
||||
const path = file.path.slice(0, index);
|
||||
prev[folderName] = getFolder(folderName, path, file);
|
||||
this.folders.push(prev[folderName]);
|
||||
const newFolder = getFolder(folderName, path, file);
|
||||
prev.set(folderName, newFolder);
|
||||
this.folders.push(newFolder);
|
||||
prev = newFolder.content;
|
||||
} else {
|
||||
updateFolder(folder, file);
|
||||
prev = folder.content;
|
||||
}
|
||||
prev = prev[folderName].content;
|
||||
});
|
||||
prev[file.name] = file;
|
||||
prev.set(file.name, file);
|
||||
}
|
||||
|
||||
updateTotalInfo() {
|
||||
|
|
|
@ -6,6 +6,7 @@ import FileGripByExtension from './components/extension';
|
|||
import FileGripByType from './components/type';
|
||||
import FileGripByFolder from './components/folder';
|
||||
import FileGripByAuthor from './components/author';
|
||||
import FileGripByRefactor from './components/refactor';
|
||||
|
||||
class FileGrip {
|
||||
files: any = new FileBuilder();
|
||||
|
@ -20,6 +21,8 @@ class FileGrip {
|
|||
|
||||
author: any = new FileGripByAuthor();
|
||||
|
||||
refactor: any = new FileGripByRefactor();
|
||||
|
||||
clear() {
|
||||
this.files.clear();
|
||||
this.extension.clear();
|
||||
|
@ -27,6 +30,7 @@ class FileGrip {
|
|||
this.tree.clear();
|
||||
this.removedTree.clear();
|
||||
this.author.clear();
|
||||
this.refactor.clear();
|
||||
}
|
||||
|
||||
addCommit(commit: ICommit | ISystemCommit) {
|
||||
|
@ -54,6 +58,7 @@ class FileGrip {
|
|||
this.author.updateTotalInfo();
|
||||
this.tree.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 { getTypeAndScope, getTask, getTaskNumber } from './getTypeAndScope';
|
||||
import getCompany from './getCompany';
|
||||
import getInfoFromNameAndEmail from './getCompany';
|
||||
import { getGithubPrInfo } from './getMergeInfo';
|
||||
|
||||
const MASTER_BRANCH = {
|
||||
master: true,
|
||||
|
@ -35,6 +36,7 @@ export default function getCommitInfo(
|
|||
prevDate = date;
|
||||
const day = date.getDay() - 1;
|
||||
const timestamp = sourceDate.substring(0, 10); // split('T')[0];
|
||||
const timezone = sourceDate.substring(19, 25);
|
||||
let milliseconds = refTimestampTime.get(timestamp);
|
||||
if (!milliseconds) {
|
||||
milliseconds = (new Date(timestamp)).getTime();
|
||||
|
@ -47,12 +49,11 @@ export default function getCommitInfo(
|
|||
|
||||
const companyKey = `${author}>in>${email}`;
|
||||
if (!refEmailAuthor[companyKey]) {
|
||||
const companyForKey = getCompany(author, email);
|
||||
// @ts-ignore
|
||||
refEmailAuthor[companyKey] = { company: companyForKey };
|
||||
refEmailAuthor[companyKey] = getInfoFromNameAndEmail(author, email);
|
||||
}
|
||||
// @ts-ignore
|
||||
const company = refEmailAuthor[companyKey].company;
|
||||
const { company, country, device } = refEmailAuthor[companyKey];
|
||||
|
||||
const authorID = author.replace(/\s|\t/gm, '');
|
||||
if (authorID && refEmailAuthor[authorID] && refEmailAuthor[authorID] !== author) {
|
||||
|
@ -86,6 +87,7 @@ export default function getCommitInfo(
|
|||
month: date.getMonth(),
|
||||
year: date.getUTCFullYear(),
|
||||
week: 0,
|
||||
timezone,
|
||||
timestamp,
|
||||
milliseconds,
|
||||
|
||||
|
@ -93,10 +95,12 @@ export default function getCommitInfo(
|
|||
email,
|
||||
message,
|
||||
company,
|
||||
country,
|
||||
device,
|
||||
|
||||
text: '',
|
||||
type: '—',
|
||||
scope: '—',
|
||||
type: '',
|
||||
scope: '',
|
||||
fileChanges: [],
|
||||
};
|
||||
|
||||
|
@ -114,24 +118,29 @@ export default function getCommitInfo(
|
|||
|
||||
if (isSystemCommit) {
|
||||
let commitType = COMMIT_TYPE.MERGE;
|
||||
let prId, repository, branch, toBranch, task, taskNumber;
|
||||
let prId, repository, branch, toBranch, task, taskNumber, type, scope;
|
||||
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;
|
||||
[, prId, repository, branch, toBranch ] = message
|
||||
.replace(/(Merge\spull\srequest\s#)|(\sfrom\s)|(\sin\s)|(\sto\s)/gim, ',')
|
||||
.split(',');
|
||||
[prId, repository, branch, toBranch] = getGithubPrInfo(message);
|
||||
task = getTask(branch);
|
||||
} else if (isBitbucketPR) {
|
||||
|
||||
} else if (isBitbucketPR) { // "Pull request #3: TASK-123 fix: Add profile"
|
||||
commitType = COMMIT_TYPE.PR_BITBUCKET;
|
||||
const messageParts = message.substring(14).split(':');
|
||||
prId = messageParts.shift();
|
||||
task = getTask(messageParts.join(':'));
|
||||
} else if (isAutoMerge) {
|
||||
const description = messageParts.join(':');
|
||||
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;
|
||||
[, branch, toBranch ] = message
|
||||
.replace(/(Automatic\smerge\sfrom\s)|(\s->\s)/gim, ',')
|
||||
.replace(/(Merge\sremote-tracking\sbranch\s')|('\sinto\s)/gim, ',')
|
||||
.split(',');
|
||||
|
||||
} else if (isGitlabPR) {
|
||||
commitType = COMMIT_TYPE.PR_GITLAB;
|
||||
[, branch, toBranch ] = message
|
||||
|
@ -147,6 +156,8 @@ export default function getCommitInfo(
|
|||
|
||||
return {
|
||||
...commonInfo,
|
||||
type,
|
||||
scope,
|
||||
prId: prId || '',
|
||||
task: task || '',
|
||||
taskNumber: taskNumber || '',
|
||||
|
@ -155,25 +166,25 @@ export default function getCommitInfo(
|
|||
toBranch: toBranch || '',
|
||||
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 = [
|
||||
'icloud',
|
||||
'google',
|
||||
'inbox',
|
||||
'yahoo',
|
||||
'aol',
|
||||
'zoho',
|
||||
|
@ -25,6 +29,8 @@ const isPublicService = Object.fromEntries(
|
|||
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 {
|
||||
const tags = (author || '')
|
||||
.toUpperCase()
|
||||
|
@ -39,11 +45,12 @@ function getCompanyByName(author?: string): string {
|
|||
: '';
|
||||
}
|
||||
|
||||
function getCompanyByEmail(email?: string) {
|
||||
const domain = (email || '').split('@').pop() || '';
|
||||
const parts = domain.split('.');
|
||||
parts.pop();
|
||||
return (parts.pop() || '').toUpperCase();
|
||||
function getCompanyAndDomainByEmail(email?: string) {
|
||||
const fullDomain = (email || '').split('@').pop() || '';
|
||||
const parts = fullDomain.split('.');
|
||||
const domain = parts.pop();
|
||||
const company = (parts.pop() || '').toUpperCase();
|
||||
return [company, domain];
|
||||
}
|
||||
|
||||
function getClearText(text: string) {
|
||||
|
@ -63,12 +70,19 @@ function isUserName(author?: string, company?: string): boolean {
|
|||
return !!clearAuthor.match(clearCompany);
|
||||
}
|
||||
|
||||
function getCompany(author?: string, email?: string) {
|
||||
const company = getCompanyByName(author) || getCompanyByEmail(email) || '';
|
||||
const isMailService = company.indexOf('MAIL') !== -1;
|
||||
return isPublicService[company] || isMailService || isUserName(author, company)
|
||||
? ''
|
||||
: company;
|
||||
}
|
||||
export default function getInfoFromNameAndEmail(author?: string, email?: string) {
|
||||
const companyByAuthor = getCompanyByName(author);
|
||||
const [companyByEmail, domain] = getCompanyAndDomainByEmail(email);
|
||||
const country = getCountryByDomain(domain);
|
||||
const device = getDevice(companyByEmail);
|
||||
|
||||
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,
|
||||
month: number; // 1,
|
||||
year: number; // 2021,
|
||||
timestamp: string; // 2021-02-09",
|
||||
timezone: string; // "+03:00",
|
||||
timestamp: string; // "2021-02-09",
|
||||
milliseconds: number; // 1612828800000,
|
||||
week: number; // 42,
|
||||
|
||||
// user
|
||||
author: string; // "Dart Vader",
|
||||
email: string; // "d.vader@emap.com",
|
||||
email: string; // "d.vader@emap.ru",
|
||||
company: string; // "emap",
|
||||
country: string; // "ru",
|
||||
device: string; // "Macbook",
|
||||
|
||||
// task
|
||||
message: string; // "JIRA-0000 fix(profile): add new avatar",
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import ICommit, { ISystemCommit } from './Commit';
|
||||
import IHashMap from './HashMap';
|
||||
import IHashMap, { HashMap } from './HashMap';
|
||||
|
||||
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
|
||||
|
||||
addedLines: number;
|
||||
|
@ -35,5 +40,5 @@ export interface IFolder extends IFileStat {
|
|||
name?: string;
|
||||
path: string[]; // ['src']
|
||||
pathString: string; // 'src\\ts'
|
||||
content: IHashMap<IDirtyFile>,
|
||||
content: HashMap<IDirtyFile>,
|
||||
}
|
||||
|
|
|
@ -37,10 +37,16 @@ export const TEAM = [
|
|||
icon: './assets/menu/team_type.svg',
|
||||
},
|
||||
{
|
||||
id: 'pr',
|
||||
link: '/team/pr',
|
||||
title: 'sidebar.team.pr',
|
||||
icon: './assets/menu/pull_request.svg',
|
||||
id: 'company',
|
||||
link: '/team/company',
|
||||
title: 'sidebar.team.company',
|
||||
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',
|
||||
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',
|
||||
link: '/team/tasks',
|
||||
|
@ -93,10 +112,10 @@ export const TEAM = [
|
|||
icon: './assets/menu/team_tasks.svg',
|
||||
},
|
||||
{
|
||||
id: 'release',
|
||||
link: '/team/release',
|
||||
title: 'sidebar.team.release',
|
||||
icon: './assets/menu/team_release.svg',
|
||||
id: 'pr',
|
||||
link: '/team/pr',
|
||||
title: 'sidebar.team.pr',
|
||||
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 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';
|
||||
|
||||
|
|
|
@ -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 NothingFound from 'ts/components/NothingFound';
|
||||
import Title from 'ts/components/Title';
|
||||
import Companies from './Companies';
|
||||
import Companies from './components/Companies';
|
||||
import CompanyCharts from './components/Charts';
|
||||
|
||||
const Company = observer(({
|
||||
mode,
|
||||
}: 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 (
|
||||
<>
|
||||
<Title title="page.team.author.title"/>
|
||||
<CompanyCharts />
|
||||
<Title title="page.team.company.title"/>
|
||||
<DataLoader
|
||||
to="response"
|
||||
loader={(pagination?: IPaginationRequest, sort?: ISort[]) => getFakeLoader({
|
||||
content: rows, pagination, sort, mode,
|
||||
content: companyRows, pagination, sort, mode,
|
||||
})}
|
||||
watch={`${mode}${dataGripStore.hash}`}
|
||||
>
|
||||
<Companies
|
||||
mode={mode}
|
||||
rowsForExcel={rows}
|
||||
rowsForExcel={companyRows}
|
||||
/>
|
||||
<Pagination />
|
||||
</DataLoader>
|
||||
|
|
|
@ -7,24 +7,20 @@ import treeStore from '../../store/Tree';
|
|||
const FileBreadcrumbs = observer((): React.ReactElement => {
|
||||
const directories = treeStore.selectedPath
|
||||
.map((dirName: string, index: number) => (
|
||||
<>
|
||||
<span
|
||||
key={`${dirName}.`}
|
||||
className={style.file_breadcrumbs_text}
|
||||
>
|
||||
{'/'}
|
||||
<span key={dirName}>
|
||||
<span 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
|
||||
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 (
|
||||
|
|
|
@ -14,6 +14,7 @@ import { getMax } from 'ts/pages/Common/helpers/getMax';
|
|||
import { getDate } from 'ts/helpers/formatter';
|
||||
|
||||
import treeStore from '../../store/Tree';
|
||||
import Tasks from './Tasks';
|
||||
|
||||
interface IViewProps {
|
||||
response?: IPagination<any>;
|
||||
|
@ -23,6 +24,8 @@ function View({ response }: IViewProps) {
|
|||
if (!response) return null;
|
||||
|
||||
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 addedRemovedChangedChart = getOptions({ order: [
|
||||
'page.team.tree.linesAdded',
|
||||
|
@ -44,6 +47,24 @@ function View({ response }: IViewProps) {
|
|||
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
|
||||
isFixed
|
||||
template={ColumnTypesEnum.STRING}
|
||||
|
@ -56,13 +77,14 @@ function View({ response }: IViewProps) {
|
|||
/>
|
||||
<Column
|
||||
isSortable
|
||||
width={50}
|
||||
width={60}
|
||||
properties="lines"
|
||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
||||
/>
|
||||
<Column
|
||||
isSortable
|
||||
properties="lines"
|
||||
title="page.team.tree.totalLines"
|
||||
minWidth={100}
|
||||
template={(value: any) => (
|
||||
<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
|
||||
isSortable
|
||||
template={ColumnTypesEnum.STRING}
|
||||
|
|
|
@ -28,9 +28,9 @@ function AllPR({
|
|||
}: IPRViewProps) {
|
||||
if (!response) return null;
|
||||
|
||||
const workChart = getOptions({ max: getMax(response, 'workDays') });
|
||||
const delayChart = getOptions({ max: getMax(response, 'delayDays') });
|
||||
const commitsChart = getOptions({ order: dataGripStore.dataGrip.author.list });
|
||||
const tasks = dataGripStore.dataGrip.tasks.statisticByName;
|
||||
const workChart = getOptions({ max: getMax(response, 'daysInWork') });
|
||||
const delayChart = getOptions({ max: getMax(response, 'daysReview') });
|
||||
|
||||
return (
|
||||
<DataView
|
||||
|
@ -38,6 +38,7 @@ function AllPR({
|
|||
rows={response.content}
|
||||
sort={response.sort}
|
||||
updateSort={updateSort}
|
||||
mode={mode}
|
||||
type={mode === 'print' ? 'cards' : undefined}
|
||||
columnCount={mode === 'print' ? 2 : undefined}
|
||||
fullScreenMode="all"
|
||||
|
@ -47,7 +48,7 @@ function AllPR({
|
|||
isSortable
|
||||
title="page.team.pr.task"
|
||||
properties="task"
|
||||
width={120}
|
||||
width={140}
|
||||
/>
|
||||
) : (
|
||||
<Column
|
||||
|
@ -66,31 +67,29 @@ function AllPR({
|
|||
/>
|
||||
)}
|
||||
<Column
|
||||
isSortable
|
||||
template={ColumnTypesEnum.STRING}
|
||||
title="page.team.pr.firstCommitTime"
|
||||
properties="beginTaskTime"
|
||||
formatter={getDate}
|
||||
formatter={(row: any) => getDate(tasks.get(row.task)?.from || row.beginTaskTime)}
|
||||
width={130}
|
||||
/>
|
||||
<Column
|
||||
isSortable
|
||||
template={ColumnTypesEnum.STRING}
|
||||
title="page.team.pr.lastCommitTime"
|
||||
properties="endTaskTime"
|
||||
properties="dateCreate"
|
||||
formatter={getDate}
|
||||
width={130}
|
||||
/>
|
||||
<Column
|
||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
||||
properties="workDays"
|
||||
properties="daysInWork"
|
||||
width={40}
|
||||
/>
|
||||
<Column
|
||||
isSortable
|
||||
title="page.team.pr.all.workDays"
|
||||
properties="workDays"
|
||||
minWidth={100}
|
||||
properties="daysInWork"
|
||||
minWidth={170}
|
||||
template={(value: any) => (
|
||||
<LineChart
|
||||
options={workChart}
|
||||
|
@ -100,31 +99,14 @@ function AllPR({
|
|||
/>
|
||||
<Column
|
||||
template={ColumnTypesEnum.SHORT_NUMBER}
|
||||
properties="commits"
|
||||
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"
|
||||
properties="daysReview"
|
||||
width={40}
|
||||
/>
|
||||
<Column
|
||||
isSortable
|
||||
title="page.team.pr.all.delayDays"
|
||||
properties="delayDays"
|
||||
minWidth={200}
|
||||
properties="daysReview"
|
||||
minWidth={170}
|
||||
template={(value: any) => (
|
||||
<LineChart
|
||||
options={delayChart}
|
||||
|
@ -136,7 +118,7 @@ function AllPR({
|
|||
isSortable
|
||||
template={ColumnTypesEnum.STRING}
|
||||
title="page.team.pr.date"
|
||||
properties="milliseconds"
|
||||
properties="dateMerge"
|
||||
formatter={getDate}
|
||||
width={130}
|
||||
/>
|
||||
|
|
|
@ -15,10 +15,10 @@ function Total() {
|
|||
|
||||
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 delayChart = DataGripByPR.getPRByGroups(allPR, 'delayDays');
|
||||
const delayChart = DataGripByPR.getPRByGroups(allPR, 'daysReview');
|
||||
const delayChartOptions = getOptions({ order: delayChart.order, limit: 3, suffix: 'PR' });
|
||||
|
||||
const workDaysWeightedAverage = Math.round(workChart.weightedAverage);
|
||||
|
|
|
@ -16,28 +16,46 @@ import fullScreen from 'ts/store/FullScreen';
|
|||
|
||||
import Total from './Total';
|
||||
import Authors from './Authors';
|
||||
import Anonymous from './Anonymous';
|
||||
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(({
|
||||
mode,
|
||||
}: ICommonPageProps): React.ReactElement | null => {
|
||||
const allPR = dataGripStore.dataGrip.pr.statistic;
|
||||
const rows = allPR.filter((item: any) => item.delayDays > 3);
|
||||
if (rows?.length < 2) return mode !== 'print' ? (<NothingFound />) : null;
|
||||
const [withTask, withoutTask] = getGroupsByTasks(allPR);
|
||||
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 authorsStat = Object.values(PRbyName);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!fullScreen.isOpen && (
|
||||
{!fullScreen.isOpen && canShowByReview && (
|
||||
<>
|
||||
<Title title="page.team.pr.oneTaskDays"/>
|
||||
<Total/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!fullScreen.isOpen || fullScreen.mode === 'author' ? (
|
||||
{canShowByReview ? (
|
||||
<>
|
||||
<Title title="page.team.pr.statByAuthors"/>
|
||||
<DataLoader
|
||||
|
@ -52,18 +70,12 @@ const PR = observer(({
|
|||
/>
|
||||
<Pagination/>
|
||||
</DataLoader>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<PageBreak/>
|
||||
|
||||
{!fullScreen.isOpen || fullScreen.mode === 'all' ? (
|
||||
<>
|
||||
<PageBreak/>
|
||||
<Title title="page.team.pr.longDelay"/>
|
||||
<DataLoader
|
||||
to="response"
|
||||
loader={(pagination?: IPaginationRequest, sort?: ISort[]) => getFakeLoader({
|
||||
content: rows,
|
||||
content: longReview,
|
||||
pagination: mode === 'print'
|
||||
? { size: 20 }
|
||||
: pagination,
|
||||
|
@ -72,7 +84,31 @@ const PR = observer(({
|
|||
>
|
||||
<All
|
||||
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/>}
|
||||
</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) {
|
||||
const { t } = useTranslation();
|
||||
const unknown = t('page.team.type.unknown');
|
||||
|
||||
if (!response) return null;
|
||||
|
||||
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}
|
||||
title="page.team.type.type"
|
||||
properties="type"
|
||||
formatter={(type: string) => type || unknown}
|
||||
width={150}
|
||||
/>
|
||||
<Column
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import { IFolder } from 'ts/interfaces/FileInfo';
|
||||
|
||||
function getSubTree(tree: IFolder, path: string[]) {
|
||||
return (path || []).reduce((subTree: any, folderName: string) => {
|
||||
subTree = subTree.content[folderName] || { content: [] };
|
||||
return subTree;
|
||||
}, tree || { content: [] });
|
||||
return (path || [])
|
||||
.reduce((subTree: any, folderName: string) => {
|
||||
subTree = subTree.content.get(folderName) || { content: new Map() };
|
||||
return subTree;
|
||||
}, tree || { content: new Map() });
|
||||
}
|
||||
|
||||
function getSortedContent(subTree: any) {
|
||||
return Object.values(subTree.content)
|
||||
function getSortedContent(subTree: IFolder) {
|
||||
return Array.from(subTree.content.values())
|
||||
.sort((a: any, b: any) => {
|
||||
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 Commits from './components/Commits';
|
||||
import Company from './components/Company';
|
||||
import Country from './components/Country';
|
||||
import Changes from './components/Changes';
|
||||
import Hours from './components/Hours';
|
||||
import PopularWords from './components/PopularWords';
|
||||
|
@ -25,6 +26,7 @@ import Building from './components/Building';
|
|||
import Pr from './components/PR';
|
||||
import Print from './components/Print';
|
||||
import Release from './components/Release';
|
||||
import Refactor from './components/Refactor';
|
||||
|
||||
interface ViewProps {
|
||||
page?: string;
|
||||
|
@ -39,6 +41,7 @@ const View = observer(({ page }: ViewProps): React.ReactElement => {
|
|||
if (page === 'scope') return <Scope mode={mode}/>;
|
||||
if (page === 'author') return <Author 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 === 'pr') return <Pr mode={mode}/>;
|
||||
if (page === 'day') return <Tempo/>;
|
||||
|
@ -55,6 +58,7 @@ const View = observer(({ page }: ViewProps): React.ReactElement => {
|
|||
if (page === 'building') return <Building/>;
|
||||
if (page === 'print') return <Print/>;
|
||||
if (page === 'tasks') return <Tasks/>;
|
||||
if (page === 'refactor') return <Refactor/>;
|
||||
return <Total/>;
|
||||
});
|
||||
|
||||
|
|
|
@ -90,6 +90,7 @@ class DataGripStore {
|
|||
this.#updateRender();
|
||||
|
||||
console.dir(this.dataGrip);
|
||||
console.dir(this.fileGrip);
|
||||
if (!applicationHasCustom.title) {
|
||||
document.title = getTitle(this.dataGrip, this.fileGrip, this.commits);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,9 @@ export default `
|
|||
§ sidebar.team.changes: Alle Änderungen
|
||||
§ sidebar.team.words: Beliebte Wörter
|
||||
§ sidebar.team.building: Quiz
|
||||
§ sidebar.team.refactor: Refactoring
|
||||
§ sidebar.team.company: Companies
|
||||
§ sidebar.team.country: Locations
|
||||
§ sidebar.team.settings: Die Einstellungen
|
||||
§ sidebar.person.total: Allgemeine Informationen
|
||||
§ sidebar.person.money: Arbeitskosten
|
||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
|||
§ page.print.title: Git repository report
|
||||
§ page.print.sub_title: «$1»
|
||||
§ 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.description2: *Default sorting* is by the number of tasks and groups (current, fired, assisting employees).
|
||||
§ page.team.author.status: Status
|
||||
|
@ -62,6 +70,7 @@ export default `
|
|||
§ 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.type: Type of work
|
||||
§ page.team.type.unknown: unknown
|
||||
§ page.team.type.tasks: Tasks
|
||||
§ page.team.type.tasksSmall: tasks
|
||||
§ page.team.type.days: Days
|
||||
|
@ -101,6 +110,11 @@ export default `
|
|||
§ 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.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.change: Who changed
|
||||
§ page.team.tree.remove: Who removed
|
||||
|
@ -108,6 +122,29 @@ export default `
|
|||
§ page.team.tree.linesAdded: added
|
||||
§ page.team.tree.linesChanged: changed
|
||||
§ 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.activity: Activity
|
||||
§ page.team.week.title: Weekly statistics
|
||||
|
@ -137,7 +174,6 @@ export default `
|
|||
§ page.team.pr.chart.14day: two weeks
|
||||
§ page.team.pr.chart.30day: month
|
||||
§ page.team.pr.chart.more: more
|
||||
§ page.team.pr.commits: Commits
|
||||
§ page.team.pr.date: Merge Date
|
||||
§ page.team.pr.mergeAuthor: Merged by
|
||||
§ page.team.pr.author: Employee
|
||||
|
@ -155,10 +191,6 @@ export default `
|
|||
§ page.team.tasks.from: First commit
|
||||
§ page.team.tasks.to: Last commit
|
||||
§ 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.extension.extension: File extensions
|
||||
§ page.team.extension.type: File sub types
|
||||
|
@ -167,6 +199,7 @@ export default `
|
|||
§ page.team.extension.current.count: Number
|
||||
§ page.team.extension.removed.count: Number of removed
|
||||
§ page.team.extension.files: files
|
||||
§ page.team.release.download: Download
|
||||
§ page.team.release.title: Release
|
||||
§ page.team.release.from: Created date
|
||||
§ page.team.release.to: Delivery date
|
||||
|
|
|
@ -28,6 +28,9 @@ export default `
|
|||
§ sidebar.team.changes: All changes
|
||||
§ sidebar.team.words: Popular words
|
||||
§ sidebar.team.building: Quiz
|
||||
§ sidebar.team.refactor: Refactoring
|
||||
§ sidebar.team.company: Companies
|
||||
§ sidebar.team.country: Locations
|
||||
§ sidebar.team.settings: Settings
|
||||
§ sidebar.person.total: Common info
|
||||
§ sidebar.person.money: Work cost
|
||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
|||
§ page.print.title: Git repository report
|
||||
§ page.print.sub_title: «$1»
|
||||
§ 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.description2: *Default sorting* is by the number of tasks and groups (current, fired, assisting employees).
|
||||
§ page.team.author.status: Status
|
||||
|
@ -62,6 +70,7 @@ export default `
|
|||
§ 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.type: Type of work
|
||||
§ page.team.type.unknown: unknown
|
||||
§ page.team.type.tasks: Tasks
|
||||
§ page.team.type.tasksSmall: tasks
|
||||
§ page.team.type.days: Days
|
||||
|
@ -101,6 +110,11 @@ export default `
|
|||
§ 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.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.change: Who changed
|
||||
§ page.team.tree.remove: Who removed
|
||||
|
@ -108,6 +122,29 @@ export default `
|
|||
§ page.team.tree.linesAdded: added
|
||||
§ page.team.tree.linesChanged: changed
|
||||
§ 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.activity: Activity
|
||||
§ page.team.week.title: Weekly statistics
|
||||
|
@ -139,7 +176,6 @@ export default `
|
|||
§ page.team.pr.chart.14day: two weeks
|
||||
§ page.team.pr.chart.30day: month
|
||||
§ page.team.pr.chart.more: more
|
||||
§ page.team.pr.commits: Commits
|
||||
§ page.team.pr.date: Merge Date
|
||||
§ page.team.pr.mergeAuthor: Merged by
|
||||
§ page.team.pr.author: Employee
|
||||
|
@ -157,10 +193,6 @@ export default `
|
|||
§ page.team.tasks.from: First commit
|
||||
§ page.team.tasks.to: Last commit
|
||||
§ 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.extension.extension: File extensions
|
||||
§ page.team.extension.type: File sub types
|
||||
|
@ -169,6 +201,7 @@ export default `
|
|||
§ page.team.extension.current.count: Number
|
||||
§ page.team.extension.removed.count: Number of removed
|
||||
§ page.team.extension.files: files
|
||||
§ page.team.release.download: Download
|
||||
§ page.team.release.title: Release
|
||||
§ page.team.release.from: Created date
|
||||
§ page.team.release.to: Delivery date
|
||||
|
|
|
@ -30,6 +30,9 @@ export default `
|
|||
§ sidebar.team.changes: Todos los cambios
|
||||
§ sidebar.team.words: Palabras populares
|
||||
§ sidebar.team.building: Concurso
|
||||
§ sidebar.team.refactor: Refactoring
|
||||
§ sidebar.team.company: Companies
|
||||
§ sidebar.team.country: Locations
|
||||
§ sidebar.team.settings: Ajustes
|
||||
§ sidebar.person.total: Información general
|
||||
§ sidebar.person.money: Costo del trabajo
|
||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
|||
§ page.print.title: Informe del repositorio git
|
||||
§ page.print.sub_title: «$1»
|
||||
§ 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.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
|
||||
|
@ -62,6 +70,7 @@ export default `
|
|||
§ 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.type: Tipo de trabajo
|
||||
§ page.team.type.unknown: unknown
|
||||
§ page.team.type.tasks: Tareas
|
||||
§ page.team.type.tasksSmall: Tareas
|
||||
§ 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.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.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.change: Quien cambió
|
||||
§ page.team.tree.remove: Quién borró
|
||||
|
@ -108,6 +122,29 @@ export default `
|
|||
§ page.team.tree.linesAdded: agregaron
|
||||
§ page.team.tree.linesChanged: changed
|
||||
§ 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.activity: Activity
|
||||
§ page.team.week.title: Estadísticas semanales
|
||||
|
@ -139,7 +176,6 @@ export default `
|
|||
§ page.team.pr.chart.14day: two weeks
|
||||
§ page.team.pr.chart.30day: month
|
||||
§ page.team.pr.chart.more: more
|
||||
§ page.team.pr.commits: commits
|
||||
§ page.team.pr.date: Date of injection
|
||||
§ page.team.pr.mergeAuthor: I poured it in
|
||||
§ page.team.pr.author: Employee
|
||||
|
@ -157,10 +193,6 @@ export default `
|
|||
§ page.team.tasks.from: The first commits
|
||||
§ page.team.tasks.to: Last commits
|
||||
§ 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.extension.extension: File extensions
|
||||
§ page.team.extension.type: File sub types
|
||||
|
@ -169,6 +201,7 @@ export default `
|
|||
§ page.team.extension.current.count: Number
|
||||
§ page.team.extension.removed.count: Number of removed
|
||||
§ page.team.extension.files: files
|
||||
§ page.team.release.download: Download
|
||||
§ page.team.release.title: Release
|
||||
§ page.team.release.from: Created date
|
||||
§ page.team.release.to: Delivery date
|
||||
|
|
|
@ -27,7 +27,10 @@ export default `
|
|||
§ sidebar.team.commits: all commits
|
||||
§ sidebar.team.changes: Tous les changements
|
||||
§ 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.person.total: Informations générales
|
||||
§ sidebar.person.money: Coût des travaux
|
||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
|||
§ page.print.title: Rapport sur dépôt git
|
||||
§ page.print.sub_title: «$1»
|
||||
§ 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.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
|
||||
|
@ -62,6 +70,7 @@ export default `
|
|||
§ 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.type: Type de travail
|
||||
§ page.team.type.unknown: unknown
|
||||
§ page.team.type.tasks: Задач
|
||||
§ page.team.type.tasksSmall: Tâche
|
||||
§ page.team.type.days: Jours
|
||||
|
@ -101,6 +110,11 @@ export default `
|
|||
§ 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.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.change: Qui a changé
|
||||
§ page.team.tree.remove: Qui a supprimé
|
||||
|
@ -108,6 +122,29 @@ export default `
|
|||
§ page.team.tree.linesAdded: ajoutâtes
|
||||
§ page.team.tree.linesChanged: changed
|
||||
§ 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.activity: Activity
|
||||
§ page.team.week.title: Statistiques par semaine
|
||||
|
@ -139,7 +176,6 @@ export default `
|
|||
§ page.team.pr.chart.14day: two weeks
|
||||
§ page.team.pr.chart.30day: month
|
||||
§ page.team.pr.chart.more: more
|
||||
§ page.team.pr.commits: Commits
|
||||
§ page.team.pr.date: Date de diffusion
|
||||
§ page.team.pr.mergeAuthor: Versai
|
||||
§ page.team.pr.author: Employé
|
||||
|
@ -157,10 +193,6 @@ export default `
|
|||
§ page.team.tasks.from: Premier commit
|
||||
§ page.team.tasks.to: Dernier commit
|
||||
§ 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.extension.extension: File extensions
|
||||
§ page.team.extension.type: File sub types
|
||||
|
@ -169,6 +201,7 @@ export default `
|
|||
§ page.team.extension.current.count: Number
|
||||
§ page.team.extension.removed.count: Number of removed
|
||||
§ page.team.extension.files: files
|
||||
§ page.team.release.download: Download
|
||||
§ page.team.release.title: Release
|
||||
§ page.team.release.from: Created date
|
||||
§ page.team.release.to: Delivery date
|
||||
|
|
|
@ -29,6 +29,9 @@ export default `
|
|||
§ sidebar.team.changes: すべての変更
|
||||
§ sidebar.team.words: 人気のある言葉
|
||||
§ sidebar.team.building: クイズ
|
||||
§ sidebar.team.refactor: Refactoring
|
||||
§ sidebar.team.company: Companies
|
||||
§ sidebar.team.country: Locations
|
||||
§ sidebar.team.settings: 設定
|
||||
§ sidebar.person.total: 一般的な情報
|
||||
§ sidebar.person.money: 仕事のコスト
|
||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
|||
§ page.print.title: Git repository report
|
||||
§ page.print.sub_title: «$1»
|
||||
§ 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.description2: *Default sorting* is by the number of tasks and groups (current, fired, assisting employees).
|
||||
§ page.team.author.status: Status
|
||||
|
@ -62,6 +70,7 @@ export default `
|
|||
§ 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.type: Type of work
|
||||
§ page.team.type.unknown: unknown
|
||||
§ page.team.type.tasks: Tasks
|
||||
§ page.team.type.tasksSmall: tasks
|
||||
§ page.team.type.days: Days
|
||||
|
@ -101,6 +110,11 @@ export default `
|
|||
§ 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.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.change: Who changed
|
||||
§ page.team.tree.remove: Who removed
|
||||
|
@ -108,6 +122,29 @@ export default `
|
|||
§ page.team.tree.linesAdded: added
|
||||
§ page.team.tree.linesChanged: changed
|
||||
§ 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.activity: Activity
|
||||
§ page.team.week.title: Weekly statistics
|
||||
|
@ -139,7 +176,6 @@ export default `
|
|||
§ page.team.pr.chart.14day: two weeks
|
||||
§ page.team.pr.chart.30day: month
|
||||
§ page.team.pr.chart.more: more
|
||||
§ page.team.pr.commits: Commits
|
||||
§ page.team.pr.date: Merge Date
|
||||
§ page.team.pr.mergeAuthor: Merged by
|
||||
§ page.team.pr.author: Employee
|
||||
|
@ -157,10 +193,6 @@ export default `
|
|||
§ page.team.tasks.from: First commit
|
||||
§ page.team.tasks.to: Last commit
|
||||
§ 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.extension.extension: File extensions
|
||||
§ page.team.extension.type: File sub types
|
||||
|
@ -169,6 +201,7 @@ export default `
|
|||
§ page.team.extension.current.count: Number
|
||||
§ page.team.extension.removed.count: Number of removed
|
||||
§ page.team.extension.files: files
|
||||
§ page.team.release.download: Download
|
||||
§ page.team.release.title: Release
|
||||
§ page.team.release.from: Created date
|
||||
§ page.team.release.to: Delivery date
|
||||
|
|
|
@ -29,6 +29,9 @@ export default `
|
|||
§ sidebar.team.changes: Todas as alterações
|
||||
§ sidebar.team.words: Palavras populares
|
||||
§ sidebar.team.building: Concurso
|
||||
§ sidebar.team.refactor: Refactoring
|
||||
§ sidebar.team.company: Companies
|
||||
§ sidebar.team.country: Locations
|
||||
§ sidebar.team.settings: Sintonização
|
||||
§ sidebar.person.total: Informação geral
|
||||
§ sidebar.person.money: Custo do trabalho
|
||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
|||
§ page.print.title: Git repository report
|
||||
§ page.print.sub_title: «$1»
|
||||
§ 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.description2: *Default sorting* is by the number of tasks and groups (current, fired, assisting employees).
|
||||
§ page.team.author.status: Status
|
||||
|
@ -62,6 +70,7 @@ export default `
|
|||
§ 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.type: Type of work
|
||||
§ page.team.type.unknown: unknown
|
||||
§ page.team.type.tasks: Tasks
|
||||
§ page.team.type.tasksSmall: tasks
|
||||
§ page.team.type.days: Days
|
||||
|
@ -101,6 +110,11 @@ export default `
|
|||
§ 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.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.change: Who changed
|
||||
§ page.team.tree.remove: Who removed
|
||||
|
@ -108,6 +122,29 @@ export default `
|
|||
§ page.team.tree.linesAdded: added
|
||||
§ page.team.tree.linesChanged: changed
|
||||
§ 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.activity: Activity
|
||||
§ page.team.week.title: Weekly statistics
|
||||
|
@ -139,7 +176,6 @@ export default `
|
|||
§ page.team.pr.chart.14day: two weeks
|
||||
§ page.team.pr.chart.30day: month
|
||||
§ page.team.pr.chart.more: more
|
||||
§ page.team.pr.commits: Commits
|
||||
§ page.team.pr.date: Merge Date
|
||||
§ page.team.pr.mergeAuthor: Merged by
|
||||
§ page.team.pr.author: Employee
|
||||
|
@ -157,10 +193,6 @@ export default `
|
|||
§ page.team.tasks.from: First commit
|
||||
§ page.team.tasks.to: Last commit
|
||||
§ 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.extension.extension: File extensions
|
||||
§ page.team.extension.type: File sub types
|
||||
|
@ -169,6 +201,7 @@ export default `
|
|||
§ page.team.extension.current.count: Number
|
||||
§ page.team.extension.removed.count: Number of removed
|
||||
§ page.team.extension.files: files
|
||||
§ page.team.release.download: Download
|
||||
§ page.team.release.title: Release
|
||||
§ page.team.release.from: Created date
|
||||
§ page.team.release.to: Delivery date
|
||||
|
|
|
@ -28,6 +28,9 @@ export default `
|
|||
§ sidebar.team.changes: Все изменения
|
||||
§ sidebar.team.words: Популярные слова
|
||||
§ sidebar.team.building: Викторина
|
||||
§ sidebar.team.refactor: Рефакторинг
|
||||
§ sidebar.team.company: Компании
|
||||
§ sidebar.team.country: Местоположение
|
||||
§ sidebar.team.settings: Настройки
|
||||
§ sidebar.person.total: Общая информация
|
||||
§ sidebar.person.money: Стоимость работы
|
||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
|||
§ page.print.title: Отчёт по git-репозиторию
|
||||
§ page.print.sub_title: «$1»
|
||||
§ 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.description2: *Сортировка по умолчанию* — это сортировка по количеству задач и группам (текущие, уволенные, помогающие сотрудники).
|
||||
§ page.team.author.status: Статус
|
||||
|
@ -62,6 +70,7 @@ export default `
|
|||
§ page.team.type.title: Статистика по типам задач
|
||||
§ page.team.type.description: *Персональный вклад* считается по количеству коммитов, а не объему измененных строк или файлов. Поэтому следует так же смотреть раздел «Анализ файлов», чтобы оценить масштаб изменений.
|
||||
§ page.team.type.type: Тип работы
|
||||
§ page.team.type.unknown: неизвестный
|
||||
§ page.team.type.tasks: Задач
|
||||
§ page.team.type.tasksSmall: задач
|
||||
§ page.team.type.days: Дней
|
||||
|
@ -101,6 +110,11 @@ export default `
|
|||
§ page.team.tree.filters.commits: Количество коммитов
|
||||
§ page.team.tree.filters.help: Минимальное количество коммитов, которое сделал сотрудник в файле
|
||||
§ 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.change: Кто менял
|
||||
§ page.team.tree.remove: Кто удалял
|
||||
|
@ -108,6 +122,29 @@ export default `
|
|||
§ page.team.tree.linesAdded: добавили
|
||||
§ page.team.tree.linesChanged: изменили
|
||||
§ 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.activity: Активность
|
||||
§ page.team.week.title: Статистика по неделям
|
||||
|
@ -139,7 +176,6 @@ export default `
|
|||
§ page.team.pr.chart.14day: две недели
|
||||
§ page.team.pr.chart.30day: месяц
|
||||
§ page.team.pr.chart.more: более
|
||||
§ page.team.pr.commits: Коммиты
|
||||
§ page.team.pr.date: Дата влития
|
||||
§ page.team.pr.mergeAuthor: Влил
|
||||
§ page.team.pr.author: Сотрудник
|
||||
|
@ -157,10 +193,6 @@ export default `
|
|||
§ page.team.tasks.from: Первый коммит
|
||||
§ page.team.tasks.to: Последний коммит
|
||||
§ 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.extension.extension: Расширения файлов
|
||||
§ page.team.extension.type: Подтип файлов
|
||||
|
@ -169,6 +201,7 @@ export default `
|
|||
§ page.team.extension.current.count: Количество
|
||||
§ page.team.extension.removed.count: Количество удалённых
|
||||
§ page.team.extension.files: файлов
|
||||
§ page.team.release.download: Скачать
|
||||
§ page.team.release.title: Релиз
|
||||
§ page.team.release.from: Дата создания
|
||||
§ page.team.release.to: Дата завершения
|
||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
|||
§ page.print.title: Отчёт по git-репозиторию
|
||||
§ page.print.sub_title: «$1»
|
||||
§ 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.description2: Сортировка по умолчанию — это сортировка по количеству задач и группам (текущие, уволенные, помогающие сотрудники).
|
||||
§ page.team.author.status: Status
|
||||
|
@ -62,6 +70,7 @@ export default `
|
|||
§ page.team.type.title: Статистика по типам задач
|
||||
§ page.team.type.description: *Персональный вклад* считается по количеству коммитов, а не объему измененных строк или файлов. Поэтому следует так же смотреть раздел «Анализ файлов», чтобы оценить масштаб изменений.
|
||||
§ page.team.type.type: Тип работы
|
||||
§ page.team.type.unknown: unknown
|
||||
§ page.team.type.tasks: Задач
|
||||
§ page.team.type.tasksSmall: задач
|
||||
§ page.team.type.days: Дней
|
||||
|
@ -101,6 +110,11 @@ export default `
|
|||
§ page.team.tree.filters.commits: Количество коммитов
|
||||
§ page.team.tree.filters.help: Минимальное количество коммитов, которое сделал сотрудник в файле
|
||||
§ 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.change: Кто менял
|
||||
§ page.team.tree.remove: Кто удалял
|
||||
|
@ -132,7 +146,6 @@ export default `
|
|||
§ page.team.pr.delayDays: Дней ожидания влития
|
||||
§ page.team.pr.all.workDays: Время работы над задачей
|
||||
§ page.team.pr.all.delayDays: Время ревью PR
|
||||
§ page.team.pr.commits: Коммиты
|
||||
§ page.team.pr.date: Дата влития
|
||||
§ page.team.pr.mergeAuthor: Влил
|
||||
§ page.team.pr.author: Сотрудник
|
||||
|
@ -157,10 +170,6 @@ export default `
|
|||
§ page.team.tasks.from: Первый коммит
|
||||
§ page.team.tasks.to: Последний коммит
|
||||
§ 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.person.print.photo.title: Фотография
|
||||
§ page.person.print.photo.description: место для фотографии
|
||||
|
|
|
@ -29,6 +29,9 @@ export default `
|
|||
§ sidebar.team.changes: 所有更改
|
||||
§ sidebar.team.words: 流行语
|
||||
§ sidebar.team.building: 测验
|
||||
§ sidebar.team.refactor: Refactoring
|
||||
§ sidebar.team.company: Companies
|
||||
§ sidebar.team.country: Locations
|
||||
§ sidebar.team.settings: 设置
|
||||
§ sidebar.person.total: 般资料
|
||||
§ sidebar.person.money: 工作的成本
|
||||
|
|
|
@ -20,7 +20,15 @@ export default `
|
|||
§ page.print.title: Git仓库报告
|
||||
§ page.print.sub_title: «$1»
|
||||
§ 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.description2: 默认排序是按任务和组数排序 (现任、被解雇、帮助雇员).
|
||||
§ page.team.author.types: 工作类别
|
||||
|
@ -57,6 +65,7 @@ export default `
|
|||
§ page.team.type.title: 按任务类型划分的统计信息
|
||||
§ page.team.type.description: *个人贡献* 它是按数字计算的 Commits, 而不是修改的字符串或文件的体积。 因此,您还应该查看"文件分析"部分以评估更改的规模。
|
||||
§ page.team.type.type: 工作类别
|
||||
§ page.team.type.unknown: unknown
|
||||
§ page.team.type.tasks: 任务
|
||||
§ page.team.type.tasksSmall: 任务
|
||||
§ page.team.type.days: 天数
|
||||
|
@ -96,6 +105,11 @@ export default `
|
|||
§ page.team.tree.filters.commits: 数量 Commits
|
||||
§ page.team.tree.filters.help: 最低数量 Commits, 雇员在档案中所做的
|
||||
§ 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.change: 谁改变了它
|
||||
§ page.team.tree.remove: 谁删除了它
|
||||
|
@ -103,6 +117,29 @@ export default `
|
|||
§ page.team.tree.linesAdded: 补充道
|
||||
§ page.team.tree.linesChanged: changed
|
||||
§ 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.activity: Activity
|
||||
§ page.team.week.title: 按周划分的统计数字
|
||||
|
@ -134,7 +171,6 @@ export default `
|
|||
§ page.team.pr.chart.14day: two weeks
|
||||
§ page.team.pr.chart.30day: month
|
||||
§ page.team.pr.chart.more: more
|
||||
§ page.team.pr.commits: Commits
|
||||
§ page.team.pr.date: 注射日期
|
||||
§ page.team.pr.mergeAuthor: 填写
|
||||
§ page.team.pr.author: 雇员
|
||||
|
@ -152,10 +188,6 @@ export default `
|
|||
§ page.team.tasks.from: 第一个 Commits
|
||||
§ page.team.tasks.to: 最后一次 Commits
|
||||
§ 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.extension.extension: File extensions
|
||||
§ page.team.extension.type: File sub types
|
||||
|
@ -164,6 +196,7 @@ export default `
|
|||
§ page.team.extension.current.count: Number
|
||||
§ page.team.extension.removed.count: Number of removed
|
||||
§ page.team.extension.files: files
|
||||
§ page.team.release.download: Download
|
||||
§ page.team.release.title: Release
|
||||
§ page.team.release.from: Created date
|
||||
§ page.team.release.to: Delivery date
|
||||
|
|
Loading…
Reference in a new issue