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
build/static
src/ts
components/TimeZoneMap
components
helpers
styles
helpers
DataGrip
Recommendations/components
formatter.ts
pages/Team
components/Country
styles
store
translations

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;
statistic.wordStatistics = DataGripByAuthor.#updateWordStatistics(commit, statistic.wordStatistics); if (totalCommits < 50000) {
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