This commit is contained in:
bakhirev 2024-10-21 16:12:21 +03:00
parent 7b9e41d928
commit 2c4528c941
25 changed files with 155 additions and 84 deletions
build/static
src/ts
components
BarChart/styles
LineChart/helpers
Page
PieSVG
helpers
pages
Common/components
Team/components
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

@ -14,6 +14,7 @@
&_item {
display: inline-block;
width: 10px;
min-width: 1px;
vertical-align: bottom;
cursor: pointer;

View file

@ -6,13 +6,12 @@ function getWidth(value: number, max: number) {
return Math.round(value * (100 / max));
}
function getFormattedOther(other: any[], options: any): ISubLine {
let width = 0;
function getFormattedOther(other: any[], normalWidth: number, options: any): ISubLine {
let value = 0;
const width = 100 - normalWidth;
const titles: string[] = [];
other.forEach((field: any) => {
width += field.width;
value += field.value;
if (field.title) titles.push(field.title);
});
@ -36,6 +35,7 @@ export default function getSubLines(
const normal: ISubLine[] = [];
const other: ISubLine[] = [];
let normalWidth = 0;
list.forEach(([title, value]: any) => {
const width = getWidth(value || 0, currentMax);
const field: ISubLine = { title, value, width };
@ -43,6 +43,7 @@ export default function getSubLines(
allItems.push(field);
if (width >= options.limit) {
normal.push(field);
normalWidth += width;
} else {
other.push(field);
}
@ -50,6 +51,6 @@ export default function getSubLines(
if (other.length === 0) return normal;
if (other.length === 1) return allItems;
return [...normal, getFormattedOther(other, options)]
return [...normal, getFormattedOther(other, normalWidth, options)]
.filter((item: any) => item.width > 1);
}

View file

@ -68,7 +68,7 @@
&_item {
display: block;
width: 100%;
margin: 0 0 24px 0;
margin: 0;
}
&_white {
display: block;
@ -76,7 +76,7 @@
}
.main_wrapper_item + .main_wrapper_item {
display: block;
margin: 0 0 24px 0;
margin: 0;
}
}

View file

@ -1,4 +1,5 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { IOptions, ISubLine } from 'ts/components/LineChart/interfaces';
import { getSegmentPath } from './helpers';
@ -17,16 +18,23 @@ function PieSVG({
parts,
center,
}: IPieSVGProps): React.ReactElement | null {
const { t } = useTranslation();
const centerRadius = 49 * ((center || 72) / 100);
const suffix = options.suffix ? t(options.suffix) : '';
let prev = 0;
const paths = parts.map((item: ISubLine) => {
const fill = options.color.get(item.title).first;
const angle = 360 * item.width / 100;
const next = Math.min(prev + angle, 360);
const formattedAngle = angle === 360 ? 359.9 : angle;
const next = Math.min(prev + formattedAngle, 360);
const d = getSegmentPath(50, 50, centerRadius, 50, prev + ROTATE, next + ROTATE);
prev += angle;
const formattedValue = item.value && suffix
? ` (${item.value || ''} ${suffix})`
: '';
return (
<path
key={item.title}
@ -35,7 +43,7 @@ function PieSVG({
className={style.pie_svg_sector}
>
<title>
{`${item.title} ${item.description || ''}`}
{`${t(item.title)}${formattedValue}`}
</title>
</path>
);

View file

@ -195,7 +195,8 @@ export default class DataGripByAuthor {
const daysWorkedLosses = workDays + (lazyDays > 0 ? lazyDays : 0);
const percentWork = workDays * 100 / daysWorkedLosses;
const isStaff = daysWorkedLosses < 20 || (percentWork < 15);
const isBot = (/[^a-z]bot[^a-z]/gim).test(dot.author);
const isStaff = daysWorkedLosses < 20 || (percentWork < 15) || isBot;
const authorInfo = {
...dot,

View file

@ -21,7 +21,7 @@ export default class DataGripByPR {
if (!commit.prId) return;
const statistic = this.pr.get(commit.prId);
if (statistic) {
console.log('PR error');
console.log('Parsing error. PR already exist.');
} else {
this.#addCommitByPR(commit);
}

View file

@ -3,7 +3,7 @@ import IHashMap from 'ts/interfaces/HashMap';
import { getTypeAndScope, getTask, getTaskNumber } from './getTypeAndScope';
import getInfoFromNameAndEmail from './getCompany';
import { getGithubPrInfo } from './getMergeInfo';
import { getGithubPrInfo, getGitlabPrInfo } from './getMergeInfo';
import getCountryByTimeZone from './getCountryByTimeZone';
const MASTER_BRANCH = {
@ -148,13 +148,12 @@ export default function getCommitInfo(
} else if (isGitlabPR) {
commitType = COMMIT_TYPE.PR_GITLAB;
[, branch, toBranch ] = message
.replace(/'/gim, '')
.replace(/(Merge\sbranch\s)|(\sinto\s)/gim, ',')
.split(',');
[branch, toBranch] = getGitlabPrInfo(message);
if (toBranch && MASTER_BRANCH[toBranch]) {
task = getTask(branch) || `#${getTaskNumber(branch)}`;
prId = task;
task = getTask(branch);
taskNumber = getTaskNumber(branch);
prId = `#${taskNumber}-${Math.random()}`;
if (!task && taskNumber) task = `#${taskNumber}`;
}
}
taskNumber = getTaskNumber(task);

View file

@ -1,9 +1,10 @@
// "Merge pull request #3 in repository from TASK-123-add-profile to master"
// "Merge pull request #3 from facebook/compiler into master"
// "Merge pull request #3 from facebook/compiler"
/* "Merge pull request #3 in repository from TASK-123-add-profile to master"
* "Merge pull request #3 from facebook/compiler into master"
* "Merge pull request #3 from facebook/compiler"
*/
export function getGithubPrInfo(text: string) {
const json = (text || '')
.replace(/"/gim, '')
.replace(/["']+/gim, '')
.replace('#', '#": "')
.replace(' in ', '", "in": "')
.replace(' from ', '", "from": "')
@ -12,3 +13,12 @@ export function getGithubPrInfo(text: string) {
const data = JSON.parse(`{"${json}"}`);
return [data['Merge pull request #'], data.in, data.from, data.to];
}
/* "Merge branch 'J123456' into 'develop'" */
export function getGitlabPrInfo(text: string) {
const prefix = text.substring(14, text.length - 1);
const index = prefix.indexOf("'");
const branch = prefix.substring(0, index);
const toBranch = prefix.substring(index + 8);
return [branch, toBranch];
}

View file

@ -16,6 +16,7 @@ interface IChangesProps {
}
function Changes({ statistic }: IChangesProps) {
const files = dataGripStore.fileGrip.files.list;
const maxData = statistic.changesByTimestampCounter.maxData;
const [selected, setSelected] = useState<any>(maxData);
@ -26,7 +27,8 @@ function Changes({ statistic }: IChangesProps) {
value: dot.addedAndChanges,
meta: dot,
}));
if (!dots?.length) return (<NothingFound />);
if (!dots?.length || !files?.length) return (<NothingFound />);
const [fullDay, shortDay] = getDateByTimestamp(maxData.timestamp);
const recommendations = [

View file

@ -32,6 +32,7 @@ function Countries({ response, updateSort, rowsForExcel, mode }: CompaniesProps)
updateSort={updateSort}
type={mode === 'print' ? 'cards' : undefined}
columnCount={mode === 'print' ? 3 : undefined}
fullScreenMode="countries"
>
<Column
isFixed

View file

@ -31,6 +31,7 @@ function Travel({ response, updateSort, rowsForExcel, mode }: TravelProps) {
updateSort={updateSort}
type={mode === 'print' ? 'cards' : undefined}
columnCount={mode === 'print' ? 3 : undefined}
fullScreenMode="travel"
>
<Column
isFixed

View file

@ -15,6 +15,7 @@ import Countries from './components/Countries';
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 Travel from './components/Travel';
@ -26,32 +27,45 @@ const Country = observer(({
const travel = authors.filter((dot: any) => dot?.country?.length)
.sort((a: any, b: any) => b?.country?.length - a?.country?.length);
const canShowByCountries = (!fullScreen.isOpen || fullScreen.mode === 'countries');
const canShowByTravel = (!fullScreen.isOpen || fullScreen.mode === 'travel') && travel.length;
if (!countryRows?.length) {
return mode !== 'print' ? (<NothingFound/>) : null;
}
return (
<>
<PageWrapper>
<Title title="page.team.country.byTimezone"/>
<TimeZoneMap authors={authors}/>
</PageWrapper>
<CountryCharts/>
<Title title="page.team.country.table.title"/>
<DataLoader
to="response"
loader={(pagination?: IPaginationRequest, sort?: ISort[]) => getFakeLoader({
content: countryRows, pagination, sort, mode,
})}
watch={`${mode}${dataGripStore.hash}`}
>
<Countries
mode={mode}
rowsForExcel={countryRows}
/>
<Pagination/>
</DataLoader>
{travel.length ? (
{!fullScreen.isOpen && (
<>
<PageWrapper>
<Title title="page.team.country.byTimezone"/>
<TimeZoneMap authors={authors}/>
</PageWrapper>
<CountryCharts/>
</>
)}
{canShowByCountries ? (
<>
<Title title="page.team.country.table.title"/>
<DataLoader
to="response"
loader={(pagination?: IPaginationRequest, sort?: ISort[]) => getFakeLoader({
content: countryRows, pagination, sort, mode,
})}
watch={`${mode}${dataGripStore.hash}`}
>
<Countries
mode={mode}
rowsForExcel={countryRows}
/>
<Pagination/>
</DataLoader>
</>
) : null}
{canShowByTravel ? (
<>
<Title title="page.team.country.travel.title"/>
<DataLoader

View file

@ -57,7 +57,6 @@ function View({ response, updateSort, rowsForExcel, mode }: CompaniesProps) {
.reverse()
.map((taskId: any) => dataGripStore.dataGrip.tasks.statisticByName.get(taskId))
.filter(v => v);
console.log(content);
return (
<Tasks // @ts-ignore
response={{ content }}

View file

@ -16,11 +16,45 @@ import PageWrapper from 'ts/components/Page/wrapper';
import Filters from './Filters';
import View from './View';
interface IFilters {
user: number;
company: number;
}
function getContentByFilters(content: any, filters: IFilters) {
if (filters.user) {
const author = dataGripStore.dataGrip.author.statistic?.[filters.user]?.author;
return author
? content.filter((task: any) => {
return task.author === author || task.authors?.[author];
})
: content;
}
if (filters.company) {
const employments = dataGripStore.dataGrip.company.statistic?.[filters.company].employments;
const employmentInCompany = new Map(employments.map((key: string) => [key, true]));
return employments?.length
? content.filter((task: any) => employmentInCompany.has(task.author))
: content;
}
return content;
}
const Tasks = observer(({
mode,
}: ICommonPageProps): React.ReactElement | null => {
const rows = dataGripStore.dataGrip.tasks.statistic;
const [filters, setFilters] = useState<any>({ user: 0, company: 0 });
const content = getContentByFilters(rows, filters);
const hash = [
mode,
dataGripStore.hash,
filters.user,
filters.company,
content.length,
].join('.');
if (!rows?.length) return mode !== 'print' ? (<NothingFound />) : null;
@ -36,9 +70,9 @@ const Tasks = observer(({
<DataLoader
to="response"
loader={(pagination?: IPaginationRequest, sort?: ISort[]) => getFakeLoader({
content: rows, pagination, sort, mode,
content, pagination, sort, mode,
})}
watch={`${mode}${dataGripStore.hash}`}
watch={hash}
>
<View
mode={mode}

View file

@ -126,7 +126,7 @@ export default `
§ 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.daysChart.item: companies
§ 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

View file

@ -1,28 +1,28 @@
export default `
§ page.settings.document.title: Display settings
§ page.settings.document.name: Page title
§ page.settings.document.language: Interface language
§ page.settings.document.depersonalize: Hide personal data
§ page.settings.links.title: Link prefixes
§ page.settings.links.task: For task numbers
§ page.settings.links.pr: For PR
§ page.settings.user.title: Individual settings
§ page.settings.user.notFound: No individual settings. Data for all employees are calculated based on common parameters.
§ page.settings.user.subTitle: Addendum to employment contract $1
§ page.settings.user.from: Start date
§ page.settings.mailmap: .mailmap example
§ page.settings.common.title: General salary data
§ page.settings.common.type.title: Project work type
§ page.settings.common.type.full: Full-time employment
§ page.settings.common.type.part: Project work
§ page.settings.common.salary: Monthly salary in USD (US dollar, $)
§ page.settings.common.currency: Currency for view
§ page.settings.common.workDaysInYear: Number of working days in a year
§ page.settings.common.vacationDaysInYear: Number of vacation days in a year
§ page.settings.common.workDaysInWeek: Workdays
§ page.settings.form.save: Save
§ page.settings.form.cancel: Cancel
§ page.settings.form.remove: Remove
§ page.settings.form.addEmployee: Add an employee
§ page.settings.form.addContract: Add an employment contract
§ page.settings.document.title: Anzeigeeinstellungen
§ page.settings.document.name: Seitentitel
§ page.settings.document.language: Sprache der benutzeroberfläche
§ page.settings.document.depersonalize: Persönliche daten verstecken
§ page.settings.links.title: Link-Präfixe
§ page.settings.links.task: Für Aufgabennummern
§ page.settings.links.pr: Für PR
§ page.settings.user.title: Individuelle einstellungen
§ page.settings.user.notFound: Keine individuellen Einstellungen. Daten für alle Mitarbeiter werden anhand allgemeiner Parameter berechnet.
§ page.settings.user.subTitle: Zusatz zum arbeitsvertrag . $1
§ page.settings.user.from: Startdatum
§ page.settings.mailmap: Beispiel .mailmap
§ page.settings.common.title: Allgemeine Gehaltsdaten
§ page.settings.common.type.title: Art der projektarbeit
§ page.settings.common.type.full: Vollzeitbeschäftigung
§ page.settings.common.type.part: Projektarbeit
§ page.settings.common.salary: Monatliches gehalt in USD (US-Dollar, $)
§ page.settings.common.currency: Währung zur ansicht
§ page.settings.common.workDaysInYear: Anzahl der arbeitstage im Jahr
§ page.settings.common.vacationDaysInYear: Anzahl der urlaubstage im Jahr
§ page.settings.common.workDaysInWeek: Arbeitstage
§ page.settings.form.save: Speichern
§ page.settings.form.cancel: Abbrechen
§ page.settings.form.remove: Entfernen
§ page.settings.form.addEmployee: Mitarbeiter hinzufügen
§ page.settings.form.addContract: Arbeitsvertrag hinzufügen
`;

View file

@ -126,7 +126,7 @@ export default `
§ 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.daysChart.item: companies
§ 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

View file

@ -126,7 +126,7 @@ export default `
§ 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.daysChart.item: companies
§ 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

View file

@ -126,7 +126,7 @@ export default `
§ 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.daysChart.item: companies
§ 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

View file

@ -126,7 +126,7 @@ export default `
§ 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.daysChart.item: companies
§ 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

View file

@ -126,7 +126,7 @@ export default `
§ 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.daysChart.item: companies
§ 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

View file

@ -126,7 +126,7 @@ export default `
§ page.team.company.employments.title: По количеству сотрудников
§ page.team.company.employments.item: сотрудников
§ page.team.company.daysChart.title: По длительности контракта
§ page.team.company.daysChart.item: дней
§ page.team.company.daysChart.item: компаний
§ page.team.company.active.yes: активна
§ page.team.company.active.no: контракт истёк
§ page.team.country.byTimezone: По времени последнего коммита

View file

@ -121,7 +121,7 @@ export default `
§ 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.daysChart.item: companies
§ 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