This commit is contained in:
bakhirev 2024-10-25 00:08:51 +03:00
parent 6362f71a80
commit 57008ac20e
23 changed files with 207 additions and 56 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { getClassNameForTimeZone } from '../helpers'; import { getPositionForTimeZone, getColorForTimeZone } from '../helpers';
import style from '../styles/index.module.scss'; import style from '../styles/index.module.scss';
@ -13,11 +13,12 @@ function Point({
timezone, timezone,
authors, authors,
}: PointProps): React.ReactElement | null { }: PointProps): React.ReactElement | null {
const className = getClassNameForTimeZone(timezone); const position = getPositionForTimeZone(timezone);
const color = getColorForTimeZone(authors);
return ( return (
<div <div
title={authors.join(', ')} title={authors.join(', ')}
className={`${style.time_zone_map_point} ${className}`} className={`${style.time_zone_map_point} ${position} ${color}`}
> >
{authors.length} {authors.length}
</div> </div>

View file

@ -1,3 +1,5 @@
import dataGripStore from 'ts/store/DataGrip';
import style from '../styles/index.module.scss'; import style from '../styles/index.module.scss';
const REF_TIMEZONE_CLASS = { const REF_TIMEZONE_CLASS = {
@ -46,10 +48,23 @@ export function getGroupsByTimeZone(authors: any[]) {
}, {}); }, {});
} }
export function getClassNameForTimeZone(timezone?: string) { export function getPositionForTimeZone(timezone?: string) {
const suffix = (timezone || '') const suffix = (timezone || '')
.replace('+', 'p') .replace('+', 'p')
.replace('-', 'm') .replace('-', 'm')
.replace(':', ''); .replace(':', '');
return REF_TIMEZONE_CLASS[suffix] || style.time_zone_map_point_hide; return REF_TIMEZONE_CLASS[suffix] || style.time_zone_map_point_hide;
} }
export function getColorForTimeZone(authors: string[]) {
let isDismissed = false;
for (let i = 0, l = authors.length; i < l; i++) {
const item = dataGripStore.dataGrip.author.statisticByName[authors[i]];
if (item?.isStaff) continue;
if (!item?.isDismissed) return style.time_zone_map_point_active;
if (item?.isDismissed) isDismissed = true;
}
return isDismissed
? style.time_zone_map_point_dismissed
: '';
}

View file

@ -42,7 +42,7 @@
color: var(--color-white); color: var(--color-white);
border-radius: var(--border-radius-l); border-radius: var(--border-radius-l);
background-color: var(--color-second); background-color: var(--color-black);
} }
} }
@ -82,4 +82,6 @@
&_m1100 { top: 67%; left: 0; } &_m1100 { top: 67%; left: 0; }
&_m1200 { top: 62%; left: 97%; } &_m1200 { top: 62%; left: 97%; }
&_hide { display: none } &_hide { display: none }
&_active { background-color: var(--color-first) }
&_dismissed { background-color: var(--color-second) }
} }

View file

@ -2,7 +2,7 @@ import ICommit from 'ts/interfaces/Commit';
import IHashMap, { HashMap } from 'ts/interfaces/HashMap'; import IHashMap, { HashMap } from 'ts/interfaces/HashMap';
import { ONE_DAY } from 'ts/helpers/formatter'; import { ONE_DAY } from 'ts/helpers/formatter';
import { createHashMap, createIncrement, increment } from 'ts/helpers/Math'; import { createIncrement, increment } from 'ts/helpers/Math';
import userSettings from 'ts/store/UserSettings'; import userSettings from 'ts/store/UserSettings';
@ -24,21 +24,21 @@ export default class DataGripByAuthor {
this.statisticByName = {}; this.statisticByName = {};
} }
addCommit(commit: ICommit) { addCommit(commit: ICommit, totalCommits: number) {
const statistic = this.commits.get(commit.author); const statistic = this.commits.get(commit.author);
if (statistic) { if (statistic) {
this.#updateCommitByAuthor(statistic, commit); this.#updateCommitByAuthor(statistic, commit, totalCommits);
} else { } else {
this.#addCommitByAuthor(commit); this.#addCommitByAuthor(commit);
} }
this.#setMoneyByMonth(commit); this.#setMoneyByMonth(commit);
} }
#updateCommitByAuthor(statistic: any, commit: ICommit) { #updateCommitByAuthor(statistic: any, commit: ICommit, totalCommits: number) {
statistic.commits += 1; statistic.commits += 1;
statistic.lastCommit = commit; statistic.lastCommit = commit;
statistic.device = statistic.device || commit.device; statistic.device = statistic.device || commit.device;
statistic.days[commit.timestamp] = true; statistic.days.set(commit.timestamp, true);
statistic.tasks[commit.task] = commit.added + commit.changes + commit.removed statistic.tasks[commit.task] = commit.added + commit.changes + commit.removed
+ (statistic.tasks[commit.task] ? statistic.tasks[commit.task] : 0); + (statistic.tasks[commit.task] ? statistic.tasks[commit.task] : 0);
increment(statistic.types, commit.type); increment(statistic.types, commit.type);
@ -55,7 +55,9 @@ export default class DataGripByAuthor {
debugger; debugger;
} }
statistic.commitsByHour[commit.hours] += 1; statistic.commitsByHour[commit.hours] += 1;
if (totalCommits < 50000) {
statistic.wordStatistics = DataGripByAuthor.#updateWordStatistics(commit, statistic.wordStatistics); statistic.wordStatistics = DataGripByAuthor.#updateWordStatistics(commit, statistic.wordStatistics);
}
if (commit.company && statistic.lastCompany !== commit.company) { if (commit.company && statistic.lastCompany !== commit.company) {
statistic.lastCompany = commit.company; statistic.lastCompany = commit.company;
@ -80,7 +82,7 @@ export default class DataGripByAuthor {
commits: 1, commits: 1,
firstCommit: commit, firstCommit: commit,
lastCommit: commit, lastCommit: commit,
days: createHashMap(commit.timestamp), days: new Map([[commit.timestamp, true]]),
tasks: { [commit.task]: commit.added + commit.changes + commit.removed }, tasks: { [commit.task]: commit.added + commit.changes + commit.removed },
types: createIncrement(commit.type), types: createIncrement(commit.type),
scopes: createIncrement(commit.scope), scopes: createIncrement(commit.scope),
@ -171,7 +173,7 @@ export default class DataGripByAuthor {
const from = dot.firstCommit.milliseconds; const from = dot.firstCommit.milliseconds;
const to = dot.lastCommit.milliseconds; const to = dot.lastCommit.milliseconds;
const workDays = Object.keys(dot.days).length; const workDays = dot.days.size;
const allDaysInProject = Math.ceil((to - from) / ONE_DAY); const allDaysInProject = Math.ceil((to - from) / ONE_DAY);
const lazyDays = Math.floor((allDaysInProject * WORK_AND_HOLIDAYS) - workDays) + 1; const lazyDays = Math.floor((allDaysInProject * WORK_AND_HOLIDAYS) - workDays) + 1;
@ -242,8 +244,16 @@ export default class DataGripByAuthor {
...this.employment.staff, ...this.employment.staff,
]; ];
this.updateSort();
}
updateSort() {
const position = new Map();
this.list.forEach((name: string, index: number) => {
position.set(name, index);
});
this.statistic.sort((a: any, b: any) => ( this.statistic.sort((a: any, b: any) => (
this.list.indexOf(a.author) - this.list.indexOf(b.author) position.get(a.author) - position.get(b.author)
)); ));
} }

View file

@ -1,19 +1,19 @@
import ICommit from 'ts/interfaces/Commit'; import ICommit from 'ts/interfaces/Commit';
import IHashMap from 'ts/interfaces/HashMap'; import IHashMap, { HashMap } from 'ts/interfaces/HashMap';
import userSettings from 'ts/store/UserSettings'; import userSettings from 'ts/store/UserSettings';
import { createHashMap, createIncrement, increment } from 'ts/helpers/Math'; import { createIncrement, increment } from 'ts/helpers/Math';
interface IStatByAuthor { interface IStatByAuthor {
commits: number; // number of commits by author in this scope commits: number; // number of commits by author in this scope
days: IHashMap<boolean>; // commit timestamp days: HashMap<boolean>; // commit timestamp
types: IHashMap<number>; // commit type by author in this scope (fix, feat) types: IHashMap<number>; // commit type by author in this scope (fix, feat)
} }
interface IStatByScope { interface IStatByScope {
scope: string; // scope name scope: string; // scope name
commits: number; // number of commits in this scope commits: number; // number of commits in this scope
days: IHashMap<boolean>; // commit timestamp days: HashMap<boolean>; // commit timestamp
tasks: IHashMap<boolean>; // task name in this scope (JIRA-123) tasks: HashMap<boolean>; // task name in this scope (JIRA-123)
types: IHashMap<number>; // commit type in this scope (fix, feat) types: IHashMap<number>; // commit type in this scope (fix, feat)
authors: IHashMap<IStatByAuthor>; // stat by author for this scope authors: IHashMap<IStatByAuthor>; // stat by author for this scope
} }
@ -42,14 +42,14 @@ export default class DataGripByScope {
#updateCommitByScope(commit: ICommit) { #updateCommitByScope(commit: ICommit) {
const statistic = this.commits[commit.scope] as IStatByScope; const statistic = this.commits[commit.scope] as IStatByScope;
statistic.commits += 1; statistic.commits += 1;
statistic.days[commit.timestamp] = true; statistic.days.set(commit.timestamp, true);
statistic.tasks[commit.task] = true; statistic.tasks.set(commit.task, true);
increment(statistic.types, commit.type); increment(statistic.types, commit.type);
const author = statistic.authors[commit.author]; const author = statistic.authors[commit.author];
if (author) { if (author) {
author.commits += 1; author.commits += 1;
author.days[commit.timestamp] = true; author.days.set(commit.timestamp, true);
increment(author.types, commit.type); increment(author.types, commit.type);
} else { } else {
statistic.authors[commit.author] = this.#getDefaultAuthorForScope(commit); statistic.authors[commit.author] = this.#getDefaultAuthorForScope(commit);
@ -60,8 +60,8 @@ export default class DataGripByScope {
this.commits[commit.scope] = { this.commits[commit.scope] = {
scope: commit.scope, scope: commit.scope,
commits: 1, commits: 1,
days: createHashMap(commit.timestamp), days: new Map([[commit.timestamp, true]]),
tasks: createHashMap(commit.task), tasks: new Map([[commit.task, true]]),
types: createIncrement(commit.type), types: createIncrement(commit.type),
authors: createIncrement(commit.author, this.#getDefaultAuthorForScope(commit)), authors: createIncrement(commit.author, this.#getDefaultAuthorForScope(commit)),
}; };
@ -70,7 +70,7 @@ export default class DataGripByScope {
#getDefaultAuthorForScope(commit: ICommit): IStatByAuthor { #getDefaultAuthorForScope(commit: ICommit): IStatByAuthor {
return { return {
commits: 1, commits: 1,
days: { [commit.timestamp]: true }, days: new Map([[commit.timestamp, true]]),
types: { [commit.type]: 1 }, types: { [commit.type]: 1 },
}; };
} }
@ -84,18 +84,20 @@ export default class DataGripByScope {
let cost = 0; let cost = 0;
for (let name in dot.authors) { for (let name in dot.authors) {
const user = dot.authors[name]; const user = dot.authors[name];
const days: number = Object.keys(user.days).length; const days: number = user.days.size;
// TODO: need middle salary in month; // TODO: need middle salary in month;
salaryCache[name] = salaryCache[name] || userSettings.getCurrentSalaryInDay(name); salaryCache[name] = salaryCache[name] || userSettings.getCurrentSalaryInDay(name);
cost += days * salaryCache[name]; cost += days * salaryCache[name];
dot.authors[name] = { ...user, days }; dot.authors[name] = { ...user, days };
} }
dot.tasks.delete('');
return { return {
...dot, ...dot,
days: Object.keys(dot.days).length, days: dot.days.size,
cost, cost,
tasks: Object.keys(dot.tasks).filter(t => t), tasks: dot.tasks.size,
}; };
}); });

View file

@ -27,8 +27,8 @@ export default class DataGripByType {
#updateCommitByType(commit: ICommit) { #updateCommitByType(commit: ICommit) {
const statistic = this.commits[commit.type]; const statistic = this.commits[commit.type];
statistic.commits += 1; statistic.commits += 1;
statistic.days[commit.timestamp] = true; statistic.days.set(commit.timestamp, true);
statistic.tasks[commit.task] = true; statistic.tasks.set(commit.task, true);
increment(statistic.commitsByAuthors, commit.author); increment(statistic.commitsByAuthors, commit.author);
if (!statistic.daysByAuthors[commit.author]) statistic.daysByAuthors[commit.author] = {}; if (!statistic.daysByAuthors[commit.author]) statistic.daysByAuthors[commit.author] = {};
@ -39,8 +39,8 @@ export default class DataGripByType {
this.commits[commit.type] = { this.commits[commit.type] = {
type: commit.type, type: commit.type,
commits: 1, commits: 1,
days: createIncrement(commit.timestamp, true), days: new Map([[commit.timestamp, true]]),
tasks: createIncrement(commit.task, true), tasks: new Map([[commit.task, true]]),
commitsByAuthors: createIncrement(commit.author, true), commitsByAuthors: createIncrement(commit.author, true),
daysByAuthors: { daysByAuthors: {
[commit.author]: createIncrement(commit.timestamp, true), [commit.author]: createIncrement(commit.timestamp, true),
@ -56,8 +56,8 @@ export default class DataGripByType {
.filter((dot: any) => dot.commits > 5 || isCorrectType[dot?.type || '']) .filter((dot: any) => dot.commits > 5 || isCorrectType[dot?.type || ''])
.map((dot: any) => ({ .map((dot: any) => ({
...dot, ...dot,
tasks: Object.keys(dot.tasks).length, tasks: dot.tasks.size,
days: Object.keys(dot.days).length, days: dot.days.size,
daysByAuthorsTotal: Object.values(dot.daysByAuthors) daysByAuthorsTotal: Object.values(dot.daysByAuthors)
.reduce((t: number, v: any) => (t + Object.keys(v).length), 0), .reduce((t: number, v: any) => (t + Object.keys(v).length), 0),
})) }))

View file

@ -66,13 +66,13 @@ class DataGrip {
this.scoring.clear(); this.scoring.clear();
} }
addCommit(commit: ICommit | ISystemCommit) { addCommit(commit: ICommit | ISystemCommit, totalCommits: number) {
if (commit.author === 'GitHub') return; if (commit.author === 'GitHub') return;
this.pr.addCommit(commit); // @ts-ignore this.pr.addCommit(commit); // @ts-ignore
this.release.addCommit(commit); // @ts-ignore this.release.addCommit(commit); // @ts-ignore
if (!commit.commitType) { if (!commit.commitType) {
this.firstLastCommit.update(commit.milliseconds, commit); this.firstLastCommit.update(commit.milliseconds, commit);
this.author.addCommit(commit); this.author.addCommit(commit, totalCommits);
this.scope.addCommit(commit); this.scope.addCommit(commit);
this.type.addCommit(commit); this.type.addCommit(commit);
this.timestamp.addCommit(commit); this.timestamp.addCommit(commit);

View file

@ -28,7 +28,9 @@ export default class RecommendationsTeamByType {
getBusFactor(dataGrip: any) { getBusFactor(dataGrip: any) {
if (dataGrip.author.list.length < 2) return null; if (dataGrip.author.list.length < 2) return null;
if (dataGrip.type.statistic.length > 200) return null; // for performance
// TODO: bad performance
const oneMaintainer = dataGrip.type.statistic.filter((statistic: any) => { const oneMaintainer = dataGrip.type.statistic.filter((statistic: any) => {
const limit = statistic.commits * 0.8; const limit = statistic.commits * 0.8;
return dataGrip.author.list.some((name: string) => statistic.commitsByAuthors[name] >= limit); return dataGrip.author.list.some((name: string) => statistic.commitsByAuthors[name] >= limit);

View file

@ -29,9 +29,17 @@ const TIMESTAMP = [
ONE_DAY * 3, ONE_DAY * 3,
]; ];
export function getDayName(index:number, weekday: 'long' | 'short') { // for performance
const dayNameCache = new Map();
export function getDayName(index:number, weekday: 'long' | 'short') { // @ts-ignore
const code = window?.localization?.language || 'ru';
const response = dayNameCache.get(`${code}${index}${weekday}`);
if (response) return response;
const date = new Date(TIMESTAMP[index]); const date = new Date(TIMESTAMP[index]);
return date.toLocaleString(getLangPrefix(), { weekday: weekday || 'long' }); const dayName = date.toLocaleString(getLangPrefix(), { weekday: weekday || 'long' });
dayNameCache.set(`${code}${index}${weekday}`, dayName);
return dayName;
} }
export function getDateByTimestamp(timestamp: string) { export function getDateByTimestamp(timestamp: string) {

View file

@ -0,0 +1,77 @@
import React, { useMemo, useState } from 'react';
import { observer } from 'mobx-react-lite';
import dataGripStore from 'ts/store/DataGrip';
import SelectWithButtons from 'ts/components/UiKit/components/SelectWithButtons';
import UiKitCheckbox from 'ts/components/UiKit/components/Checkbox';
import TimeZoneMap from 'ts/components/TimeZoneMap';
import PageWrapper from 'ts/components/Page/Box';
import Title from 'ts/components/Title';
import { t } from 'ts/helpers/Localization';
import style from '../../../styles/country.module.scss';
function getOptions(companies: any[]) {
const options = companies.map((item: any) => ({ id: item.company, title: item.company }));
return [
{ id: '', title: t('page.common.filter.allUsers') },
{ id: Math.random(), title: 'Unknown' },
...options,
];
}
const CustomMap = observer(() => {
const companies = dataGripStore.dataGrip.company.statistic;
const companyOptions = useMemo(() => getOptions(companies), companies);
const [company, setCompany] = useState<string>('');
const [isStaff, setIsStaff] = useState<boolean>(true);
const [isActive, setIsActive] = useState<boolean>(true);
const [isDismissed, setIsDismissed] = useState<boolean>(true);
const authors = dataGripStore.dataGrip.author.statistic
.filter((author: any) => {
if (company && author.lastCompany !== company) return false;
if (!isStaff && author.isStaff) return false;
if (!isActive && !author.isDismissed && !author.isStaff) return false;
if (!isDismissed && author.isDismissed && !author.isStaff) return false;
return true;
});
return (
<PageWrapper>
<Title title="page.team.country.byTimezone"/>
<TimeZoneMap authors={authors}/>
<div className={style.team_country_filter}>
<UiKitCheckbox
title="page.team.country.filters.active"
className={style.team_country_filter_checkbox}
value={isActive}
onChange={() => setIsActive(!isActive)}
/>
<UiKitCheckbox
title="page.team.country.filters.dismissed"
className={style.team_country_filter_checkbox}
value={isDismissed}
onChange={() => setIsDismissed(!isDismissed)}
/>
<UiKitCheckbox
title="page.team.country.filters.staff"
className={style.team_country_filter_checkbox}
value={isStaff}
onChange={() => setIsStaff(!isStaff)}
/>
<SelectWithButtons
title="page.team.tree.filters.author"
className={style.team_country_filter_select}
value={company}
options={companyOptions}
onChange={(id: string) => setCompany(id)}
/>
</div>
</PageWrapper>
);
});
export default CustomMap;

View file

@ -13,10 +13,9 @@ import NothingFound from 'ts/components/NothingFound';
import Title from 'ts/components/Title'; import Title from 'ts/components/Title';
import Countries from './components/Countries'; import Countries from './components/Countries';
import CountryCharts from './components/Charts'; import CountryCharts from './components/Charts';
import TimeZoneMap from 'ts/components/TimeZoneMap';
import PageWrapper from 'ts/components/Page/Box';
import fullScreen from 'ts/store/FullScreen'; import fullScreen from 'ts/store/FullScreen';
import CustomMap from './components/Map';
import Travel from './components/Travel'; import Travel from './components/Travel';
const Country = observer(({ const Country = observer(({
@ -36,15 +35,8 @@ const Country = observer(({
return ( return (
<> <>
{!fullScreen.isOpen && ( {!fullScreen.isOpen && <CustomMap />}
<> {!fullScreen.isOpen && <CountryCharts />}
<PageWrapper>
<Title title="page.team.country.byTimezone"/>
<TimeZoneMap authors={authors}/>
</PageWrapper>
<CountryCharts/>
</>
)}
{canShowByCountries ? ( {canShowByCountries ? (
<> <>

View file

@ -0,0 +1,16 @@
@import 'src/styles/variables';
.team_country_filter {
margin-top: var(--space-m);
&_checkbox,
&_select {
display: inline-block;
margin-right: var(--space-xxl);
vertical-align: middle;
}
&_select {
min-width: 350px;
}
}

View file

@ -65,8 +65,10 @@ class DataGripStore {
processingCommitGrouping(commits: (ICommit | ISystemCommit)[]) { processingCommitGrouping(commits: (ICommit | ISystemCommit)[]) {
commits.sort((a, b) => a.milliseconds - b.milliseconds); commits.sort((a, b) => a.milliseconds - b.milliseconds);
const totalCommits = commits.length;
commits.forEach((commit: ICommit | ISystemCommit) => { commits.forEach((commit: ICommit | ISystemCommit) => {
dataGrip.addCommit(commit); dataGrip.addCommit(commit, totalCommits);
}); });
setTimeout(() => this.processingFileGrouping(commits), PROCESSING_DELAY); setTimeout(() => this.processingFileGrouping(commits), PROCESSING_DELAY);
@ -128,7 +130,7 @@ class DataGripStore {
? depersonalized.getCommit(commit) ? depersonalized.getCommit(commit)
: commit; : commit;
dataGrip.addCommit(localCommit); dataGrip.addCommit(localCommit, 0);
fileGrip.addCommit(localCommit); fileGrip.addCommit(localCommit);
}); });

View file

@ -130,6 +130,9 @@ export default `
§ page.team.company.active.yes: active § page.team.company.active.yes: active
§ page.team.company.active.no: contract has expired § page.team.company.active.no: contract has expired
§ page.team.country.byTimezone: By the time of the last commit § page.team.country.byTimezone: By the time of the last commit
§ page.team.country.filters.active: Works
§ page.team.country.filters.dismissed: Dismissed
§ page.team.country.filters.staff: Staff
§ page.team.country.pieByDomain.title: By email, timezone and language § page.team.country.pieByDomain.title: By email, timezone and language
§ page.team.country.pieByTimezone.title: By timezone § page.team.country.pieByTimezone.title: By timezone
§ page.team.country.chart.item: employments § page.team.country.chart.item: employments

View file

@ -130,6 +130,9 @@ export default `
§ page.team.company.active.yes: active § page.team.company.active.yes: active
§ page.team.company.active.no: contract has expired § page.team.company.active.no: contract has expired
§ page.team.country.byTimezone: By the time of the last commit § page.team.country.byTimezone: By the time of the last commit
§ page.team.country.filters.active: Works
§ page.team.country.filters.dismissed: Dismissed
§ page.team.country.filters.staff: Staff
§ page.team.country.pieByDomain.title: By email, timezone and language § page.team.country.pieByDomain.title: By email, timezone and language
§ page.team.country.pieByTimezone.title: By timezone § page.team.country.pieByTimezone.title: By timezone
§ page.team.country.chart.item: employments § page.team.country.chart.item: employments

View file

@ -130,6 +130,9 @@ export default `
§ page.team.company.active.yes: active § page.team.company.active.yes: active
§ page.team.company.active.no: contract has expired § page.team.company.active.no: contract has expired
§ page.team.country.byTimezone: By the time of the last commit § page.team.country.byTimezone: By the time of the last commit
§ page.team.country.filters.active: Works
§ page.team.country.filters.dismissed: Dismissed
§ page.team.country.filters.staff: Staff
§ page.team.country.pieByDomain.title: By email, timezone and language § page.team.country.pieByDomain.title: By email, timezone and language
§ page.team.country.pieByTimezone.title: By timezone § page.team.country.pieByTimezone.title: By timezone
§ page.team.country.chart.item: employments § page.team.country.chart.item: employments

View file

@ -130,6 +130,9 @@ export default `
§ page.team.company.active.yes: active § page.team.company.active.yes: active
§ page.team.company.active.no: contract has expired § page.team.company.active.no: contract has expired
§ page.team.country.byTimezone: By the time of the last commit § page.team.country.byTimezone: By the time of the last commit
§ page.team.country.filters.active: Works
§ page.team.country.filters.dismissed: Dismissed
§ page.team.country.filters.staff: Staff
§ page.team.country.pieByDomain.title: By email, timezone and language § page.team.country.pieByDomain.title: By email, timezone and language
§ page.team.country.pieByTimezone.title: By timezone § page.team.country.pieByTimezone.title: By timezone
§ page.team.country.chart.item: employments § page.team.country.chart.item: employments

View file

@ -130,6 +130,9 @@ export default `
§ page.team.company.active.yes: active § page.team.company.active.yes: active
§ page.team.company.active.no: contract has expired § page.team.company.active.no: contract has expired
§ page.team.country.byTimezone: By the time of the last commit § page.team.country.byTimezone: By the time of the last commit
§ page.team.country.filters.active: Works
§ page.team.country.filters.dismissed: Dismissed
§ page.team.country.filters.staff: Staff
§ page.team.country.pieByDomain.title: By email, timezone and language § page.team.country.pieByDomain.title: By email, timezone and language
§ page.team.country.pieByTimezone.title: By timezone § page.team.country.pieByTimezone.title: By timezone
§ page.team.country.chart.item: employments § page.team.country.chart.item: employments

View file

@ -130,6 +130,9 @@ export default `
§ page.team.company.active.yes: active § page.team.company.active.yes: active
§ page.team.company.active.no: contract has expired § page.team.company.active.no: contract has expired
§ page.team.country.byTimezone: By the time of the last commit § page.team.country.byTimezone: By the time of the last commit
§ page.team.country.filters.active: Works
§ page.team.country.filters.dismissed: Dismissed
§ page.team.country.filters.staff: Staff
§ page.team.country.pieByDomain.title: By email, timezone and language § page.team.country.pieByDomain.title: By email, timezone and language
§ page.team.country.pieByTimezone.title: By timezone § page.team.country.pieByTimezone.title: By timezone
§ page.team.country.chart.item: employments § page.team.country.chart.item: employments

View file

@ -130,6 +130,9 @@ export default `
§ page.team.company.active.yes: активна § page.team.company.active.yes: активна
§ page.team.company.active.no: контракт истёк § page.team.company.active.no: контракт истёк
§ page.team.country.byTimezone: По времени последнего коммита § page.team.country.byTimezone: По времени последнего коммита
§ page.team.country.filters.active: Работают
§ page.team.country.filters.dismissed: Уволенные
§ page.team.country.filters.staff: Помощники
§ page.team.country.pieByDomain.title: По почте, времени и языку § page.team.country.pieByDomain.title: По почте, времени и языку
§ page.team.country.pieByTimezone.title: По времени § page.team.country.pieByTimezone.title: По времени
§ page.team.country.chart.item: сотрудников § page.team.country.chart.item: сотрудников

View file

@ -125,6 +125,9 @@ export default `
§ page.team.company.active.yes: active § page.team.company.active.yes: active
§ page.team.company.active.no: contract has expired § page.team.company.active.no: contract has expired
§ page.team.country.byTimezone: By the time of the last commit § page.team.country.byTimezone: By the time of the last commit
§ page.team.country.filters.active: Works
§ page.team.country.filters.dismissed: Dismissed
§ page.team.country.filters.staff: Staff
§ page.team.country.pieByDomain.title: By email, timezone and language § page.team.country.pieByDomain.title: By email, timezone and language
§ page.team.country.pieByTimezone.title: By timezone § page.team.country.pieByTimezone.title: By timezone
§ page.team.country.chart.item: employments § page.team.country.chart.item: employments