update
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 230 B After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 230 B After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 9.9 KiB |
|
@ -16,9 +16,12 @@ function Block({
|
||||||
const src = {
|
const src = {
|
||||||
home: `./assets/games/home${getRandom(2)}.png`,
|
home: `./assets/games/home${getRandom(2)}.png`,
|
||||||
road: './assets/games/road.png',
|
road: './assets/games/road.png',
|
||||||
green: './assets/games/green.png',
|
|
||||||
}[type || ''] || defaultSprite;
|
}[type || ''] || defaultSprite;
|
||||||
|
|
||||||
|
if (type === 'green') {
|
||||||
|
return (<div className={className.join(' ')} />);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
className={className.join(' ')}
|
className={className.join(' ')}
|
||||||
|
|
|
@ -42,7 +42,6 @@ function CityMap({
|
||||||
style={{
|
style={{
|
||||||
padding: `${paddingTop * cellSize}px ${paddingLeft * cellSize}px`,
|
padding: `${paddingTop * cellSize}px ${paddingLeft * cellSize}px`,
|
||||||
maxWidth: 24 * cellSize,
|
maxWidth: 24 * cellSize,
|
||||||
backgroundImage: 'url(./assets/games/green.png)',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={style.city_builder}>
|
<div className={style.city_builder}>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import IHashMap from 'ts/interfaces/HashMap';
|
import IHashMap from 'ts/interfaces/HashMap';
|
||||||
import UiKitButton from 'ts/components/UiKit/components/Button';
|
|
||||||
import Description from 'ts/components/Description';
|
import Description from 'ts/components/Description';
|
||||||
import ShowSymbol from 'ts/components/ShowSymbol';
|
import ShowSymbol from 'ts/components/ShowSymbol';
|
||||||
import { shuffle } from 'ts/helpers/random';
|
import { shuffle } from 'ts/helpers/random';
|
||||||
|
@ -54,19 +53,22 @@ function CityBuilder({
|
||||||
length={20}
|
length={20}
|
||||||
/>
|
/>
|
||||||
<Description
|
<Description
|
||||||
|
className={style.city_builder_description}
|
||||||
text={`Сейчас в проекте есть ${selected.value || 0} файлов созданных этим пользователем. Это примерно ${percent}% от всех файлов в проекте.`}
|
text={`Сейчас в проекте есть ${selected.value || 0} файлов созданных этим пользователем. Это примерно ${percent}% от всех файлов в проекте.`}
|
||||||
/>
|
/>
|
||||||
<div className={style.city_builder_control}>
|
<div className={style.city_builder_control}>
|
||||||
<UiKitButton
|
<button
|
||||||
disabled={!selectedIndex}
|
disabled={!selectedIndex}
|
||||||
className={style.city_builder_control_prev}
|
className={style.city_builder_control_prev}
|
||||||
|
style={{ backgroundImage: 'url(./assets/menu/arrow_left.svg)' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedIndex(selectedIndex - 1);
|
setSelectedIndex(selectedIndex - 1);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<UiKitButton
|
<button
|
||||||
disabled={selectedIndex === lastIndex}
|
disabled={selectedIndex === lastIndex}
|
||||||
className={style.city_builder_control_next}
|
className={style.city_builder_control_next}
|
||||||
|
style={{ backgroundImage: 'url(./assets/menu/arrow_right.svg)' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedIndex(selectedIndex + 1);
|
setSelectedIndex(selectedIndex + 1);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: #6CCB70;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&_block {
|
&_block {
|
||||||
|
@ -29,7 +28,6 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
border: none;
|
border: none;
|
||||||
background-color: var(--color-13);
|
|
||||||
|
|
||||||
background-repeat: repeat;
|
background-repeat: repeat;
|
||||||
background-size: auto 100%;
|
background-size: auto 100%;
|
||||||
|
|
|
@ -1,18 +1,29 @@
|
||||||
@import 'src/styles/variables';
|
@import 'src/styles/variables';
|
||||||
|
|
||||||
|
.city_builder_description {
|
||||||
|
margin: var(--space-m) auto;
|
||||||
|
}
|
||||||
|
|
||||||
.city_builder_control {
|
.city_builder_control {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0 0 var(--space-xxl);
|
margin: var(--space-xxl) auto;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background-color: var(--color-13);
|
background-color: var(--color-13);
|
||||||
|
|
||||||
|
background-size: auto;
|
||||||
|
background-repeat: repeat;
|
||||||
|
background-position: top left;
|
||||||
|
|
||||||
&_prev,
|
&_prev,
|
||||||
&_next {
|
&_next {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
||||||
display: block;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
width: 30%;
|
width: 30%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -20,7 +31,15 @@
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border: none;
|
border: none;
|
||||||
color: transparent;
|
color: transparent;
|
||||||
|
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
background-size: auto 30%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&_prev {
|
&_prev {
|
||||||
|
|
|
@ -2,48 +2,48 @@ import IHashMap from 'ts/interfaces/HashMap';
|
||||||
import { IDirtyFile } from 'ts/interfaces/FileInfo';
|
import { IDirtyFile } from 'ts/interfaces/FileInfo';
|
||||||
|
|
||||||
export default class FileGripByAuthor {
|
export default class FileGripByAuthor {
|
||||||
addedFilesByAuthor: IHashMap<number> = {};
|
statisticByName: IHashMap<any> = {};
|
||||||
|
|
||||||
addedWithoutRemoveFilesByAuthor: IHashMap<number> = {};
|
|
||||||
|
|
||||||
removedFilesByAuthor: IHashMap<number> = {};
|
|
||||||
|
|
||||||
totalAddedFiles: number = 0;
|
totalAddedFiles: number = 0;
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.addedFilesByAuthor = {};
|
this.statisticByName = {};
|
||||||
this.addedWithoutRemoveFilesByAuthor = {};
|
|
||||||
this.removedFilesByAuthor = {};
|
|
||||||
this.totalAddedFiles = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addFile(file: IDirtyFile) {
|
addFile(file: IDirtyFile) {
|
||||||
const create = file?.firstCommit?.author || '';
|
const firstAuthor = file?.firstCommit?.author || '';
|
||||||
const remove = file?.lastCommit?.author || '';
|
const lastAuthor = file?.lastCommit?.author || '';
|
||||||
|
|
||||||
if (!(create || remove) || file?.name?.[0] === '.') return;
|
if (!(firstAuthor || lastAuthor) || file?.name?.[0] === '.') return;
|
||||||
|
|
||||||
this.#addNewAuthor(create);
|
this.#addCommitByAuthor(firstAuthor);
|
||||||
this.#addNewAuthor(remove);
|
this.#addCommitByAuthor(lastAuthor);
|
||||||
|
this.#updateCommitByAuthor(file, firstAuthor, lastAuthor);
|
||||||
|
}
|
||||||
|
|
||||||
this.addedWithoutRemoveFilesByAuthor[create] += 1;
|
#addCommitByAuthor(author: string) {
|
||||||
if (file.action !== 'D') {
|
if (this.statisticByName[author]) return;
|
||||||
this.addedFilesByAuthor[create] += 1;
|
this.statisticByName[author] = {
|
||||||
|
addedFiles: 0,
|
||||||
|
removedFiles: 0,
|
||||||
|
addedWithoutRemoveFiles: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateCommitByAuthor(file: IDirtyFile, firstAuthor: string, lastAuthor: string) {
|
||||||
|
const createStatistic = this.statisticByName[firstAuthor];
|
||||||
|
const removeStatistic = this.statisticByName[lastAuthor];
|
||||||
|
|
||||||
|
createStatistic.addedWithoutRemoveFiles += 1;
|
||||||
|
if (file.action === 'D') {
|
||||||
|
removeStatistic.removedFiles += 1;
|
||||||
} else {
|
} else {
|
||||||
this.removedFilesByAuthor[remove] += 1;
|
createStatistic.addedFiles += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#addNewAuthor(author: string) {
|
|
||||||
const value = this.addedFilesByAuthor[author];
|
|
||||||
if (value || value === 0) return;
|
|
||||||
this.addedFilesByAuthor[author] = 0;
|
|
||||||
this.addedWithoutRemoveFilesByAuthor[author] = 0;
|
|
||||||
this.removedFilesByAuthor[author] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTotalInfo() {
|
updateTotalInfo() {
|
||||||
this.totalAddedFiles = Object.values(this.addedFilesByAuthor)
|
this.totalAddedFiles = Object.values(this.statisticByName)
|
||||||
.reduce((sum: number, value: number) => sum + value, 0);
|
.reduce((sum: number, stat: any) => sum + stat.addedFiles, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,36 +51,36 @@ const SettingForm = observer((response: any): React.ReactElement | null => {
|
||||||
<PageWrapper>
|
<PageWrapper>
|
||||||
<PageColumn>
|
<PageColumn>
|
||||||
<Common />
|
<Common />
|
||||||
<Prefixes />
|
|
||||||
<Salary />
|
<Salary />
|
||||||
</PageColumn>
|
</PageColumn>
|
||||||
<PageColumn>
|
<PageColumn>
|
||||||
<Title title="page.settings.user.title"/>
|
<Prefixes />
|
||||||
{employees.length > 0 ? (
|
<Title title="page.settings.mailmap"/>
|
||||||
users
|
<MailMap />
|
||||||
) : (
|
|
||||||
<NothingFound message="page.settings.user.notFound" />
|
|
||||||
)}
|
|
||||||
{authors.length && (
|
|
||||||
<div className={style.buttons_footer}>
|
|
||||||
<UiKitButtonMenu
|
|
||||||
options={authors}
|
|
||||||
onClick={(user: any) => {
|
|
||||||
formStore.updateState('employees', [
|
|
||||||
...employees,
|
|
||||||
getNewEmployeesSettings(user?.title, formStore.state, selectedNames?.length),
|
|
||||||
]);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{localization.get('page.settings.form.addEmployee')}
|
|
||||||
</UiKitButtonMenu>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</PageColumn>
|
</PageColumn>
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
<PageWrapper>
|
<PageWrapper>
|
||||||
<Title title="page.settings.mailmap"/>
|
<Title title="page.settings.user.title"/>
|
||||||
<MailMap />
|
{employees.length > 0 ? (
|
||||||
|
users
|
||||||
|
) : (
|
||||||
|
<NothingFound message="page.settings.user.notFound" />
|
||||||
|
)}
|
||||||
|
{authors.length && (
|
||||||
|
<div className={style.buttons_footer}>
|
||||||
|
<UiKitButtonMenu
|
||||||
|
options={authors}
|
||||||
|
onClick={(user: any) => {
|
||||||
|
formStore.updateState('employees', [
|
||||||
|
...employees,
|
||||||
|
getNewEmployeesSettings(user?.title, formStore.state, selectedNames?.length),
|
||||||
|
]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{localization.get('page.settings.form.addEmployee')}
|
||||||
|
</UiKitButtonMenu>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,36 +3,8 @@ import { observer } from 'mobx-react-lite';
|
||||||
|
|
||||||
import DataLoader from 'ts/components/DataLoader';
|
import DataLoader from 'ts/components/DataLoader';
|
||||||
import SettingForm from './components/Form';
|
import SettingForm from './components/Form';
|
||||||
// import settingsStore from 'ts/store/Settings';
|
|
||||||
import settingsApi from 'ts/api/settings';
|
import settingsApi from 'ts/api/settings';
|
||||||
import formStore from './store/Form';
|
import formStore from './store/Form';
|
||||||
//
|
|
||||||
// function getDeepCopy(state: any) {
|
|
||||||
// const {
|
|
||||||
// from,
|
|
||||||
// to,
|
|
||||||
// minCommits,
|
|
||||||
// isFullTime,
|
|
||||||
// defaultSalary,
|
|
||||||
// defaultWorkDays,
|
|
||||||
// holidaysInYear,
|
|
||||||
// currency,
|
|
||||||
// salary,
|
|
||||||
// workDays,
|
|
||||||
// } = state;
|
|
||||||
// return {
|
|
||||||
// from,
|
|
||||||
// to,
|
|
||||||
// minCommits,
|
|
||||||
// isFullTime,
|
|
||||||
// defaultSalary,
|
|
||||||
// defaultWorkDays,
|
|
||||||
// holidaysInYear,
|
|
||||||
// currency,
|
|
||||||
// salary: { ...salary },
|
|
||||||
// workDays: { ...workDays },
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
const SettingPage = observer((): React.ReactElement | null => {
|
const SettingPage = observer((): React.ReactElement | null => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -15,7 +15,12 @@ import getOptions from 'ts/components/LineChart/helpers/getOptions';
|
||||||
import { ColumnTypesEnum } from 'ts/components/Table/interfaces/Column';
|
import { ColumnTypesEnum } from 'ts/components/Table/interfaces/Column';
|
||||||
|
|
||||||
const TeamBuilding = observer((): React.ReactElement => {
|
const TeamBuilding = observer((): React.ReactElement => {
|
||||||
const filesByAuthor = dataGripStore.fileGrip.author?.addedFilesByAuthor;
|
const filesByAuthor = dataGripStore.fileGrip.author?.statisticByName || {};
|
||||||
|
const addedFilesByAuthor = Object.entries(filesByAuthor)
|
||||||
|
.reduce((acc: any, item: any) => {
|
||||||
|
acc[item[0]] = item[1].addedFiles;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
const tracksAuth = dataGripStore.dataGrip.author.statistic
|
const tracksAuth = dataGripStore.dataGrip.author.statistic
|
||||||
.filter((item: any) => !item.isStaff);
|
.filter((item: any) => !item.isStaff);
|
||||||
|
@ -69,7 +74,7 @@ const TeamBuilding = observer((): React.ReactElement => {
|
||||||
</DataView>
|
</DataView>
|
||||||
|
|
||||||
<Title title="Количество созданных файлов, если бы это был город"/>
|
<Title title="Количество созданных файлов, если бы это был город"/>
|
||||||
<CityBuilder valuesByTitle={filesByAuthor} />
|
<CityBuilder valuesByTitle={addedFilesByAuthor} />
|
||||||
|
|
||||||
<Title title="Количество созданных папок"/>
|
<Title title="Количество созданных папок"/>
|
||||||
<DataView rows={maxMessageLength}>
|
<DataView rows={maxMessageLength}>
|
||||||
|
|