mirror of
https://github.com/bakhirev/assayo.git
synced 2024-11-16 16:21:41 +00:00
update
This commit is contained in:
parent
2e29d4ede2
commit
5d890b4848
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
|
||||
import Banner from 'ts/components/Banner';
|
||||
|
||||
import style from './index.module.scss';
|
||||
import style from '../index.module.scss';
|
||||
|
||||
interface ICardWithBannerProps {
|
||||
long?: boolean;
|
36
src/ts/components/CardWithIcon/components/Scoring.tsx
Normal file
36
src/ts/components/CardWithIcon/components/Scoring.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import style from '../index.module.scss';
|
||||
|
||||
export interface IScoringProps {
|
||||
title?: string;
|
||||
value?: number;
|
||||
total?: number;
|
||||
}
|
||||
|
||||
function Scoring({
|
||||
title,
|
||||
value,
|
||||
total,
|
||||
}: IScoringProps): React.ReactElement | null {
|
||||
const { t } = useTranslation();
|
||||
if (!value) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
title={t(title || 'page.person.scoring.toolbar')}
|
||||
className={style.card_with_icon_scoring}
|
||||
>
|
||||
{`${value} / ${total || value}`}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Scoring.defaultProps = {
|
||||
title: undefined,
|
||||
value: undefined,
|
||||
total: undefined,
|
||||
};
|
||||
|
||||
export default Scoring;
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
.card_with_icon,
|
||||
.card_with_icon_long {
|
||||
position: relative;
|
||||
|
||||
display: inline-block;
|
||||
width: calc(50% - 12px);
|
||||
min-height: 270px;
|
||||
|
@ -11,8 +13,9 @@
|
|||
vertical-align: top;
|
||||
text-decoration: none;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
|
||||
border-radius: 8px;
|
||||
border-radius: var(--border-radius-m);
|
||||
border: 1px solid var(--color-border);
|
||||
background-color: var(--color-white);
|
||||
|
||||
|
@ -40,7 +43,8 @@
|
|||
}
|
||||
|
||||
&_title,
|
||||
&_description {
|
||||
&_description,
|
||||
&_scoring {
|
||||
font-weight: 100;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
|
@ -57,7 +61,8 @@
|
|||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
&_description {
|
||||
&_description,
|
||||
&_scoring {
|
||||
font-size: var(--font-xs);
|
||||
line-height: 16px;
|
||||
color: var(--color-grey);
|
||||
|
@ -68,6 +73,22 @@
|
|||
padding: 0;
|
||||
line-height: 270px;
|
||||
}
|
||||
|
||||
&_scoring {
|
||||
position: absolute;
|
||||
bottom: -11px;
|
||||
left: 30%;
|
||||
right: 30%;
|
||||
|
||||
display: inline-block;
|
||||
padding: var(--space-xxxs) var(--space-xs);
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
|
||||
border-radius: var(--border-radius-s);
|
||||
border: 1px solid var(--color-border);
|
||||
background-color: var(--color-white);
|
||||
}
|
||||
}
|
||||
|
||||
.card_with_icon_long {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { IScoringProps } from './components/Scoring';
|
||||
import Scoring from './components/Scoring';
|
||||
import style from './index.module.scss';
|
||||
|
||||
interface ICardWithIconProps {
|
||||
|
@ -11,6 +13,7 @@ interface ICardWithIconProps {
|
|||
color?: string;
|
||||
icon?: string;
|
||||
long?: boolean;
|
||||
scoring?: IScoringProps;
|
||||
}
|
||||
|
||||
function CardWithIcon({
|
||||
|
@ -21,8 +24,10 @@ function CardWithIcon({
|
|||
color,
|
||||
icon,
|
||||
long = false,
|
||||
scoring,
|
||||
}: ICardWithIconProps): React.ReactElement | null {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!value && value !== 0) return null;
|
||||
|
||||
return (
|
||||
|
@ -48,6 +53,11 @@ function CardWithIcon({
|
|||
<figcaption className={style.card_with_icon_description}>
|
||||
{t(description || '')}
|
||||
</figcaption>
|
||||
<Scoring
|
||||
title={scoring?.title}
|
||||
value={scoring?.value}
|
||||
total={scoring?.total}
|
||||
/>
|
||||
</figure>
|
||||
);
|
||||
}
|
||||
|
@ -58,6 +68,7 @@ CardWithIcon.defaultProps = {
|
|||
color: undefined,
|
||||
icon: undefined,
|
||||
long: false,
|
||||
scoring: undefined,
|
||||
};
|
||||
|
||||
export default CardWithIcon;
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import UiKitSelectOption from './Option';
|
||||
import IOption from '../interfaces/Option';
|
||||
|
||||
import style from '../styles/index.module.scss';
|
||||
|
||||
interface UiKitSelectListProps {
|
||||
value: any;
|
||||
options: any;
|
||||
options: IOption[];
|
||||
search?: string;
|
||||
keyCode?: string;
|
||||
setKeyCode: Function;
|
||||
|
@ -26,8 +27,10 @@ function UiKitSelectList({
|
|||
const [selectedIndex, setSelectedIndex] = useState<number>(-1);
|
||||
|
||||
console.log(value);
|
||||
const searchResult = options
|
||||
?.filter((option: any) => option.title.indexOf(search) !== -1);
|
||||
const searchText = search ? search.toLowerCase() : '';
|
||||
const searchResult = searchText
|
||||
? options?.filter((option: any) => option?._textForSearch?.indexOf(searchText) !== -1)
|
||||
: options;
|
||||
|
||||
useEffect(() => {
|
||||
if (!keyCode) return;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import React from 'react';
|
||||
|
||||
import IOption from '../interfaces/Option';
|
||||
import style from '../styles/index.module.scss';
|
||||
|
||||
interface UiKitSelectValueProps {
|
||||
value: any;
|
||||
options: any;
|
||||
options: IOption[];
|
||||
className?: string;
|
||||
onClick: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import IOption from '../interfaces/Option';
|
||||
|
||||
function getStringFromObject(value: any) {
|
||||
return value?.title
|
||||
|| value?.name
|
||||
|
@ -23,8 +25,9 @@ function getValue(
|
|||
formatter: (a: any, i?: number) => string,
|
||||
) {
|
||||
const type = typeof value;
|
||||
if (type === 'boolean') return value ? 'yes' : 'no';
|
||||
if (type === 'number' || type === 'string') return value;
|
||||
if (type === 'boolean') return value ? 'true' : 'false';
|
||||
if (type === 'number') return `${value}`;
|
||||
if (type === 'string') return value;
|
||||
if (!value) return '';
|
||||
|
||||
return Array.isArray(value)
|
||||
|
@ -40,10 +43,12 @@ export function getId(value: any, index: number) {
|
|||
return getValue(value, (v: any) => getIdFromObject(v, index));
|
||||
}
|
||||
|
||||
export function getOption(value: any, index: number) {
|
||||
export function getOption(value: any, index: number): IOption {
|
||||
const title = getTitle(value);
|
||||
return {
|
||||
id: getId(value, index),
|
||||
title: getTitle(value),
|
||||
title,
|
||||
_textForSearch: title.toLowerCase(),
|
||||
source: value,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import IOption from './interfaces/Option';
|
||||
import UiKitSelectValue from './components/Value';
|
||||
import UiKitSelectSearch from './components/Search';
|
||||
import UiKitSelectList from './components/List';
|
||||
|
@ -25,7 +26,7 @@ function UiKitSelect({
|
|||
const [search, setSearch] = useState<string>('');
|
||||
const [keyCode, setKeyCode] = useState<string>('');
|
||||
|
||||
const formattedOptions = useMemo(() => options?.map(getOption) || [], [options]);
|
||||
const formattedOptions: IOption[] = useMemo(() => options?.map(getOption) || [], [options]);
|
||||
const formattedValue = useMemo(() => {
|
||||
const selectedOption = options.find((option: any) => option.id === value);
|
||||
return getTitle(selectedOption) || getTitle(value);
|
||||
|
|
6
src/ts/components/CustomSelect/interfaces/Option.ts
Normal file
6
src/ts/components/CustomSelect/interfaces/Option.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export default interface IOption {
|
||||
id: string | number | boolean;
|
||||
title: string;
|
||||
_textForSearch: string;
|
||||
source: any;
|
||||
}
|
61
src/ts/helpers/DataGrip/components/scoring.ts
Normal file
61
src/ts/helpers/DataGrip/components/scoring.ts
Normal file
|
@ -0,0 +1,61 @@
|
|||
import IHashMap from 'ts/interfaces/HashMap';
|
||||
|
||||
const PROPERTIES = [
|
||||
{ property: 'daysWorked', sort: 1 },
|
||||
{ property: 'daysLosses', sort: -1 },
|
||||
{ property: 'commits', sort: 1 },
|
||||
{ property: 'daysForTask', sort: -1 },
|
||||
{ property: 'tasks', sort: 1 },
|
||||
{ property: 'moneyAll', sort: 1 },
|
||||
{ property: 'moneyWorked', sort: 1 },
|
||||
{ property: 'moneyLosses', sort: -1 },
|
||||
{ property: 'weekendPayment', sort: -1 },
|
||||
];
|
||||
|
||||
export default class DataGripByScoring {
|
||||
total: IHashMap<number> = {};
|
||||
|
||||
statisticByName: IHashMap<any> = {};
|
||||
|
||||
constructor() {
|
||||
this.clear();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.total = {};
|
||||
this.statisticByName = {};
|
||||
}
|
||||
|
||||
updateTotalInfo(dataGripByAuthor: any) {
|
||||
const list = [...dataGripByAuthor.statistic];
|
||||
|
||||
list.forEach((user: any) => {
|
||||
this.statisticByName[user.author] = {};
|
||||
});
|
||||
|
||||
PROPERTIES.forEach((config: any) => {
|
||||
const values = list.map((user: any) => {
|
||||
const value = user[config.property] || 0;
|
||||
return Array.isArray(value)
|
||||
? value?.length
|
||||
: value;
|
||||
});
|
||||
|
||||
const uniqValues = Array.from(new Set(values));
|
||||
const places = uniqValues
|
||||
.sort((a:number, b:number) => (b - a) * config.sort)
|
||||
.map((v, i) => [v, i + 1]);
|
||||
const refValuePlace = Object.fromEntries(places);
|
||||
|
||||
list.forEach((user: any) => {
|
||||
const userValue = user[config.property];
|
||||
const userFormattedValue = Array.isArray(userValue)
|
||||
? userValue?.length
|
||||
: userValue;
|
||||
this.statisticByName[user.author][config.property] = refValuePlace[userFormattedValue];
|
||||
});
|
||||
|
||||
this.total[config.property] = uniqValues.length;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import DataGripByGet from './components/get';
|
|||
import DataGripByPR from './components/pr';
|
||||
import DataGripByTasks from './components/tasks';
|
||||
import DataGripByRelease from './components/release';
|
||||
import DataGripByScoring from './components/scoring';
|
||||
|
||||
class DataGrip {
|
||||
firstLastCommit: any = new MinMaxCounter();
|
||||
|
@ -39,6 +40,8 @@ class DataGrip {
|
|||
|
||||
release: any = new DataGripByRelease();
|
||||
|
||||
scoring: any = new DataGripByScoring();
|
||||
|
||||
clear() {
|
||||
this.firstLastCommit.clear();
|
||||
this.author.clear();
|
||||
|
@ -52,6 +55,7 @@ class DataGrip {
|
|||
this.pr.clear();
|
||||
this.tasks.clear();
|
||||
this.release.clear();
|
||||
this.scoring.clear();
|
||||
}
|
||||
|
||||
addCommit(commit: ICommit | ISystemCommit) {
|
||||
|
@ -81,6 +85,7 @@ class DataGrip {
|
|||
this.pr.updateTotalInfo(this.author);
|
||||
this.tasks.updateTotalInfo(this.pr);
|
||||
this.release.updateTotalInfo();
|
||||
this.scoring.updateTotalInfo(this.author);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ import IPersonCommonProps from '../interfaces/CommonProps';
|
|||
|
||||
const Money = observer(({ user }: IPersonCommonProps): React.ReactElement => {
|
||||
const statistic = user;
|
||||
const scoringTotal = dataGripStore.dataGrip.scoring.total;
|
||||
const scoring = dataGripStore.dataGrip.scoring.statisticByName[user.author];
|
||||
const byTimestamp = dataGripStore.dataGrip.timestamp.statisticByAuthor[statistic.author];
|
||||
const taskNumber = statistic.tasks.length;
|
||||
|
||||
|
@ -36,24 +38,40 @@ const Money = observer(({ user }: IPersonCommonProps): React.ReactElement => {
|
|||
icon="./assets/cards/money_total.png"
|
||||
title="page.person.money.moneyAll.title"
|
||||
description="page.person.money.moneyAll.description"
|
||||
scoring={{
|
||||
value: scoring.moneyAll,
|
||||
total: scoringTotal.moneyAll,
|
||||
}}
|
||||
/>
|
||||
<CardWithIcon
|
||||
value={getShortMoney(statistic.moneyWorked)}
|
||||
icon="./assets/cards/money_work.png"
|
||||
title="page.person.money.moneyWorked.title"
|
||||
description="page.person.money.moneyWorked.description"
|
||||
scoring={{
|
||||
value: scoring.moneyWorked,
|
||||
total: scoringTotal.moneyWorked,
|
||||
}}
|
||||
/>
|
||||
<CardWithIcon
|
||||
value={getShortMoney(statistic.moneyLosses)}
|
||||
icon="./assets/cards/money_lazy.png"
|
||||
title="page.person.money.moneyLosses.title"
|
||||
description="page.person.money.moneyLosses.description"
|
||||
scoring={{
|
||||
value: scoring.moneyLosses,
|
||||
total: scoringTotal.moneyLosses,
|
||||
}}
|
||||
/>
|
||||
<CardWithIcon
|
||||
value={getShortMoney(byTimestamp.weekendPayment)}
|
||||
icon="./assets/cards/money_holidays.png"
|
||||
title="page.team.total.weekendPayment.title"
|
||||
description="page.team.total.weekendPayment.description"
|
||||
scoring={{
|
||||
value: scoring.weekendPayment,
|
||||
total: scoringTotal.weekendPayment,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</PageColumn>
|
||||
|
|
|
@ -4,7 +4,7 @@ import { observer } from 'mobx-react-lite';
|
|||
import { getShortNumber } from 'ts/helpers/formatter';
|
||||
|
||||
import CardWithIcon from 'ts/components/CardWithIcon';
|
||||
import CardWithBanner from 'ts/components/CardWithIcon/Banner';
|
||||
import CardWithBanner from 'ts/components/CardWithIcon/components/Banner';
|
||||
import NothingFound from 'ts/components/NothingFound';
|
||||
import IsStaff from 'ts/components/NothingFound/components/IsStaff';
|
||||
import PageWrapper from 'ts/components/Page/wrapper';
|
||||
|
|
|
@ -6,7 +6,7 @@ import achievementByAuthor from 'ts/helpers/achievement/byCompetition';
|
|||
import ACHIEVEMENT_TYPE from 'ts/helpers/achievement/constants/type';
|
||||
|
||||
import CardWithIcon from 'ts/components/CardWithIcon';
|
||||
import CardWithBanner from 'ts/components/CardWithIcon/Banner';
|
||||
import CardWithBanner from 'ts/components/CardWithIcon/components/Banner';
|
||||
import Achievements from 'ts/components/Achievement';
|
||||
import Description from 'ts/components/Description';
|
||||
import PageWrapper from 'ts/components/Page/wrapper';
|
||||
|
@ -37,6 +37,8 @@ function AchievementBlock({ title, achievements }: IAchievementBlockProps) {
|
|||
const Total = observer(({ user }: IPersonCommonProps): React.ReactElement => {
|
||||
const { t } = useTranslation();
|
||||
const statistic = user;
|
||||
const scoringTotal = dataGripStore.dataGrip.scoring.total;
|
||||
const scoring = dataGripStore.dataGrip.scoring.statisticByName[user.author];
|
||||
const commitsWithGet = dataGripStore.dataGrip.get.getsByAuthor[user.author];
|
||||
const taskNumber = statistic.tasks.length;
|
||||
const achievements = achievementByAuthor.authors[statistic.author];
|
||||
|
@ -51,24 +53,40 @@ const Total = observer(({ user }: IPersonCommonProps): React.ReactElement => {
|
|||
icon="./assets/cards/work_days.png"
|
||||
title="page.person.total.daysWorked.title"
|
||||
description="page.person.total.daysWorked.description"
|
||||
scoring={{
|
||||
value: scoring.daysWorked,
|
||||
total: scoringTotal.daysWorked,
|
||||
}}
|
||||
/>
|
||||
<CardWithIcon
|
||||
value={taskNumber ? taskNumber : null}
|
||||
icon="./assets/cards/tasks.png"
|
||||
title="page.person.total.tasks.title"
|
||||
description="page.person.total.tasks.description"
|
||||
scoring={{
|
||||
value: scoring.tasks,
|
||||
total: scoringTotal.tasks,
|
||||
}}
|
||||
/>
|
||||
<CardWithIcon
|
||||
value={statistic.daysLosses}
|
||||
icon="./assets/cards/lazy.png"
|
||||
title="page.team.total.daysLosses.title"
|
||||
description="page.team.total.daysLosses.description"
|
||||
scoring={{
|
||||
value: scoring.daysLosses,
|
||||
total: scoringTotal.daysLosses,
|
||||
}}
|
||||
/>
|
||||
<CardWithIcon
|
||||
value={statistic.commits}
|
||||
icon="./assets/cards/commits.png"
|
||||
title="page.team.total.commits.title"
|
||||
description="page.team.total.commits.description"
|
||||
scoring={{
|
||||
value: scoring.commits,
|
||||
total: scoringTotal.commits,
|
||||
}}
|
||||
/>
|
||||
<CardWithBanner long />
|
||||
</div>
|
||||
|
|
|
@ -181,6 +181,7 @@ export default `
|
|||
§ page.person.total.daysWorked.description: Учтены только дни, в которые делались коммиты
|
||||
§ page.person.total.tasks.title: задач
|
||||
§ page.person.total.tasks.description: Если коммиты правильно подписаны
|
||||
§ page.person.scoring.toolbar: Позиция по этой метрике, относительно других сотрудников
|
||||
§ page.person.character.title: Персонаж
|
||||
§ page.person.achievement.title: Достижения
|
||||
§ page.person.achievement.positive: Позитивные
|
||||
|
|
Loading…
Reference in a new issue