This commit is contained in:
Бахирев 2024-03-15 16:32:34 +03:00
parent a4b269f1ca
commit d53ae9b3e3
67 changed files with 613 additions and 41510 deletions

View file

@ -1,17 +1,17 @@
{
"files": {
"main.css": "./static/css/main.44209276.css",
"main.js": "./static/js/main.5a210281.js",
"main.css": "./static/css/main.8e65d99e.css",
"main.js": "./static/js/main.cb604897.js",
"static/media/car.png": "./static/media/car.b8dd8738e37fe866285f.png",
"index.html": "./index.html",
"static/media/warning.svg": "./static/media/warning.e39a87773603f3ab157f.svg",
"static/media/info.svg": "./static/media/info.954631f6b19e3fe9c495.svg",
"static/media/alert.svg": "./static/media/alert.41e2b99c481139c13074.svg",
"main.44209276.css.map": "./static/css/main.44209276.css.map",
"main.5a210281.js.map": "./static/js/main.5a210281.js.map"
"main.8e65d99e.css.map": "./static/css/main.8e65d99e.css.map",
"main.cb604897.js.map": "./static/js/main.cb604897.js.map"
},
"entrypoints": [
"static/css/main.44209276.css",
"static/js/main.5a210281.js"
"static/css/main.8e65d99e.css",
"static/js/main.cb604897.js"
]
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" fill="#84858D"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h280v80H200Zm440-160-55-58 102-102H360v-80h327L585-622l55-58 200 200-200 200Z"/></svg>

After

Width:  |  Height:  |  Size: 273 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" fill="#84858D"><path d="M240-40q-33 0-56.5-23.5T160-120v-440q0-33 23.5-56.5T240-640h120v80H240v440h480v-440H600v-80h120q33 0 56.5 23.5T800-560v440q0 33-23.5 56.5T720-40H240Zm200-280v-447l-64 64-56-57 160-160 160 160-56 57-64-64v447h-80Z"/></svg>

After

Width:  |  Height:  |  Size: 333 B

View file

@ -1 +1 @@
<!doctype html><html><head><meta name="viewport" content="width=device-width,height=device-height,initial-scale=1,user-scalable=no,maximum-scale=1"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta http-equiv="Cache-Control" content="no-cache"><meta http-equiv="cleartype" content="on"><meta name="HandheldFriendly" content="True"><meta name="format-detection" content="telephone=no"><meta name="format-detection" content="address=no"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><script type="text/javascript">var report=[]</script><script src="/log.txt"></script><script src="./log.txt"></script><script src="../log.txt"></script><script src="./log-0.txt"></script><script src="./log-1.txt"></script><script src="./log-2.txt"></script><script src="./log-3.txt"></script><script src="./log-4.txt"></script><script src="./log-5.txt"></script><script src="./log-6.txt"></script><script src="./report/log-0.txt"></script><script src="./report/log-1.txt"></script><script src="./report/log-2.txt"></script><script src="./report/log-3.txt"></script><script src="./report/log-4.txt"></script><script src="./report/log-5.txt"></script><script src="./report/log-6.txt"></script><link rel="icon" href="./favicon.svg"/><link rel="apple-touch-icon" href="./logo192.png"/><link rel="manifest" href="./manifest.json"/><title>Git Statistics</title><meta name="description" content="Simple and fast report on git commit history."><meta name="keywords" content="git, statistics, audit, history, log, monitoring, employee control"><meta name="author" content="Bakhirev Aleksei"><meta name="copyright" content="(c) Bakhirev Aleksei"><meta http-equiv="Reply-to" content="alexey-bakhirev@yandex.ru"><meta name="application-name" content="Git statistics"><meta name="msapplication-tooltip" content="Simple and fast report on Git commit history."><meta property="og:title" content="Git Statistics"><meta property="og:description" content="Simple and fast report on Git commit history."><meta property="og:image" content="http://assayo.jp/assets/seo/custom_icon_256.png"><meta property="og:site_name" content="Assayo"><meta property="og:url" content="http://assayo.jp/"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="Git Statistics"><meta name="twitter:description" content="Simple and fast report on Git commit history."><meta name="twitter:creator" content="Bakhirev Aleksei"><meta name="twitter:image:src" content="http://assayo.jp/assets/seo/custom_icon_256.png"><meta name="twitter:domain" content="assayo.jp"><meta name="twitter:site" content="assayo.jp"><meta itemprop="name" content="Git Statistics"><meta itemprop="description" content="Simple and fast report on Git commit history."><meta itemprop="image" content="http://assayo.jp/assets/seo/custom_icon_256.png"><script defer="defer" src="./static/js/main.5a210281.js"></script><link href="./static/css/main.44209276.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html><head><meta name="viewport" content="width=device-width,height=device-height,initial-scale=1,user-scalable=no,maximum-scale=1"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta http-equiv="Cache-Control" content="no-cache"><meta http-equiv="cleartype" content="on"><meta name="HandheldFriendly" content="True"><meta name="format-detection" content="telephone=no"><meta name="format-detection" content="address=no"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="theme-color" content="white"/><script type="text/javascript">var report=[]</script><script src="/log.txt"></script><script src="./log.txt"></script><script src="../log.txt"></script><script src="./log-0.txt"></script><script src="./log-1.txt"></script><script src="./log-2.txt"></script><script src="./log-3.txt"></script><script src="./log-4.txt"></script><script src="./log-5.txt"></script><script src="./log-6.txt"></script><script src="./report/log-0.txt"></script><script src="./report/log-1.txt"></script><script src="./report/log-2.txt"></script><script src="./report/log-3.txt"></script><script src="./report/log-4.txt"></script><script src="./report/log-5.txt"></script><script src="./report/log-6.txt"></script><link rel="icon" href="./favicon.svg"/><link rel="apple-touch-icon" href="./logo192.png"/><link rel="manifest" href="./manifest.json"/><title>Git Statistics</title><meta name="description" content="Simple and fast report on git commit history."><meta name="keywords" content="git, statistics, audit, history, log, monitoring, employee control"><meta name="author" content="Bakhirev Aleksei"><meta name="copyright" content="(c) Bakhirev Aleksei"><meta http-equiv="Reply-to" content="alexey-bakhirev@yandex.ru"><meta name="application-name" content="Git statistics"><meta name="msapplication-tooltip" content="Simple and fast report on Git commit history."><meta property="og:title" content="Git Statistics"><meta property="og:description" content="Simple and fast report on Git commit history."><meta property="og:image" content="http://assayo.jp/assets/seo/custom_icon_256.png"><meta property="og:site_name" content="Assayo"><meta property="og:url" content="http://assayo.jp/"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="Git Statistics"><meta name="twitter:description" content="Simple and fast report on Git commit history."><meta name="twitter:creator" content="Bakhirev Aleksei"><meta name="twitter:image:src" content="http://assayo.jp/assets/seo/custom_icon_256.png"><meta name="twitter:domain" content="assayo.jp"><meta name="twitter:site" content="assayo.jp"><meta itemprop="name" content="Git Statistics"><meta itemprop="description" content="Simple and fast report on Git commit history."><meta itemprop="image" content="http://assayo.jp/assets/seo/custom_icon_256.png"><script defer="defer" src="./static/js/main.cb604897.js"></script><link href="./static/css/main.8e65d99e.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 309 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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,5 +1,3 @@
/*! sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
/*! xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/**

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" fill="#84858D"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h280v80H200Zm440-160-55-58 102-102H360v-80h327L585-622l55-58 200 200-200 200Z"/></svg>

After

Width:  |  Height:  |  Size: 273 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" fill="#84858D"><path d="M240-40q-33 0-56.5-23.5T160-120v-440q0-33 23.5-56.5T240-640h120v80H240v440h480v-440H600v-80h120q33 0 56.5 23.5T800-560v440q0 33-23.5 56.5T720-40H240Zm200-280v-447l-64 64-56-57 160-160 160 160-56 57-64-64v447h-80Z"/></svg>

After

Width:  |  Height:  |  Size: 333 B

View file

@ -9,7 +9,8 @@
<meta name="format-detection" content="telephone=no">
<meta name="format-detection" content="address=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="theme-color" content="white"/>
<script type="text/javascript">
var report = [];

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 309 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" fill="#84858D"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h280v80H200Zm440-160-55-58 102-102H360v-80h327L585-622l55-58 200 200-200 200Z"/></svg>

After

Width:  |  Height:  |  Size: 273 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" fill="#84858D"><path d="M240-40q-33 0-56.5-23.5T160-120v-440q0-33 23.5-56.5T240-640h120v80H240v440h480v-440H600v-80h120q33 0 56.5 23.5T800-560v440q0 33-23.5 56.5T720-40H240Zm200-280v-447l-64 64-56-57 160-160 160 160-56 57-64-64v447h-80Z"/></svg>

After

Width:  |  Height:  |  Size: 333 B

View file

@ -3,9 +3,16 @@ import { HashRouter } from 'react-router-dom';
import { render } from 'react-dom';
import localization from 'ts/helpers/Localization';
import ru from 'ts/translations/ru/index';
import en from 'ts/translations/en/index';
import './ts/helpers/i18n';
import de from 'ts/translations/de';
import en from 'ts/translations/en';
import es from 'ts/translations/es';
import fr from 'ts/translations/fr';
import ja from 'ts/translations/ja';
import pt from 'ts/translations/pt';
import ru from 'ts/translations/ru';
import zh from 'ts/translations/zh';
import initializationI18n from './ts/helpers/i18n';
import Authorization from 'ts/pages/Authorization';
import userSettings from 'ts/store/UserSettings';
@ -23,8 +30,14 @@ if (module.hot) {
module.hot.accept();
}
localization.parse('de', de);
localization.parse('en', en);
localization.parse('es', es);
localization.parse('fr', fr);
localization.parse('ja', ja);
localization.parse('pt', pt);
localization.parse('ru', ru);
localization.parse('zh', zh);
function renderReactApplication() {
// @ts-ignore
@ -45,5 +58,8 @@ function renderReactApplication() {
}
userSettings.loadUserSettings().then(() => {
applyUrlCommands(renderReactApplication);
applyUrlCommands((parameters: any) => {
initializationI18n(parameters.lang || parameters.language);
renderReactApplication();
});
});

View file

@ -67,6 +67,8 @@ hr {
}
input, select, td {
padding: 0;
margin: 0;
vertical-align: middle
}

View file

@ -44,7 +44,11 @@ function Recommendations({
return (
<>
<Title title={title}/>
<div className={className}>
<div
className={className}
onTouchStart={(event) => event.stopPropagation()}
onMouseDown={(event) => event.stopPropagation()}
>
{cards}
</div>
</>

View file

@ -45,6 +45,8 @@ function Table({
<div
ref={refTable}
className={`${style.table_wrapper} scroll_x`}
onTouchStart={(event) => event.stopPropagation()}
onMouseDown={(event) => event.stopPropagation()}
>
<div className={`${style.table}`}>
<Header

View file

@ -38,6 +38,8 @@ function Tempo({
ref={ref}
style={customStyle}
className={`${style.tempo_wrapper} scroll_x`}
onTouchStart={(event) => event.stopPropagation()}
onMouseDown={(event) => event.stopPropagation()}
>
<div className={style.tempo}>
{columns}

View file

@ -1,4 +1,5 @@
import React, { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
import Wrapper, { IUiKitWrapperProps } from './Wrapper';
import style from '../styles/index.module.scss';
@ -20,6 +21,7 @@ function UiKitInputNumber({
placeholder,
onChange,
}: IUiKitSelectProps) {
const { t } = useTranslation();
return (
<Wrapper
title={title}
@ -31,7 +33,7 @@ function UiKitInputNumber({
<input
type="number"
value={value}
placeholder={placeholder}
placeholder={placeholder ? t(placeholder) : ''}
className={style.ui_kit_common}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
if (onChange) onChange(parseInt(event.target.value, 10) || 0);

View file

@ -2,6 +2,7 @@ import React, { ChangeEvent } from 'react';
import Wrapper, { IUiKitWrapperProps } from './Wrapper';
import style from '../styles/index.module.scss';
import styleSelect from '../styles/select.module.scss';
interface IUiKitSelectProps extends IUiKitWrapperProps {
value: any;
@ -46,7 +47,7 @@ function UiKitSelect({
className={className}
>
<select
className={`${style.ui_kit_common} ${style.ui_kit_select}`}
className={`${style.ui_kit_common} ${styleSelect.ui_kit_select} ${className || ''}`}
onChange={(event: ChangeEvent<HTMLSelectElement>) => {
const selectedValue = event.target.value;
const formattedValue = selectedValue !== 'null'

View file

@ -4,10 +4,13 @@ import { IUiKitWrapperProps } from './Wrapper';
import UiKitButton from './Button';
import UiKitSelect from './Select';
import style from '../styles/select.module.scss';
interface IUiKitSelectWithButtonsProps extends IUiKitWrapperProps {
className?: string;
value: any;
options: any[];
reverse: boolean;
onChange: Function;
}
@ -15,17 +18,25 @@ function UiKitSelectWithButtons({
className,
value,
options,
reverse,
onChange,
}: IUiKitSelectWithButtonsProps) {
let index = options.map((item: any) => item.id).indexOf(value);
if (index === -1) index = 0;
const disabledPrev = index <= 0;
const disabledNext = index >= (options.length - 1);
const newPrevValue = options[index - 1]?.id;
const newNextValue = options[index + 1]?.id;
return (
<>
<div className={`${style.ui_kit_select_with_buttons_wrapper} ${className || ''}`}>
<UiKitButton
mode="second"
disabled={index <= 0}
className={style.ui_kit_select_with_buttons_left}
disabled={reverse ? disabledNext : disabledPrev}
onClick={() => {
onChange(options[index - 1]?.id);
onChange(reverse ? newNextValue : newPrevValue);
}}
>
«
@ -33,23 +44,24 @@ function UiKitSelectWithButtons({
<UiKitSelect
value={value}
options={options}
className={className}
onChange={onChange}
/>
<UiKitButton
mode="second"
disabled={index >= (options.length - 1)}
className={style.ui_kit_select_with_buttons_right}
disabled={reverse ? disabledPrev : disabledNext}
onClick={() => {
onChange(options[index + 1]?.id);
onChange(reverse ? newPrevValue : newNextValue);
}}
>
»
</UiKitButton>
</>
</div>
);
}
UiKitSelectWithButtons.defaultProps = {
reverse: false,
className: '',
};

View file

@ -1,7 +1,7 @@
import React, { ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import style from '../styles/index.module.scss';
import style from '../styles/wrapper.module.scss';
export interface IUiKitWrapperProps {
title?: string;

View file

@ -22,53 +22,6 @@
background-color: #FFFFFF;
}
.ui_kit_wrapper + .ui_kit_wrapper {
vertical-align: top;
margin-top: var(--space-l);
}
.ui_kit_title,
.ui_kit_description,
.ui_kit_help,
.ui_kit_error,
.button {
font-weight: 100;
font-size: var(--font-xs);
display: block;
padding: 0;
margin: 0 auto;
text-align: left;
line-height: 1.3;
text-decoration: none;
vertical-align: bottom;
color: var(--color-grey);
}
.ui_kit_title {
font-weight: bold;
color: var(--color-black);
}
.ui_kit_title,
.ui_kit_description {
margin-bottom: 6px;
}
.ui_kit_help,
.ui_kit_error {
margin-top: 6px;
}
.ui_kit_error {
color: var(--color-12);
}
.ui_kit_select {
padding: 0 var(--space-xs) 0 var(--space-l);
}
.ui_kit_dialog {
position: absolute;
bottom: 48px;

View file

@ -0,0 +1,27 @@
@import 'src/styles/variables';
.ui_kit_select {
padding: 0 var(--space-xs) 0 var(--space-l);
}
.ui_kit_select_with_buttons {
&_wrapper {
position: relative;
padding: 0 50px;
text-align: center;
}
&_left {
position: absolute;
top: 0;
left: 0;
display: inline-block;
}
&_right {
position: absolute;
top: 0;
right: 0;
display: inline-block;
}
}

View file

@ -0,0 +1,43 @@
@import 'src/styles/variables';
.ui_kit_wrapper + .ui_kit_wrapper {
vertical-align: top;
margin-top: var(--space-l);
}
.ui_kit_title,
.ui_kit_description,
.ui_kit_help,
.ui_kit_error {
font-weight: 100;
font-size: var(--font-xs);
display: block;
padding: 0;
margin: 0 auto;
text-align: left;
line-height: 1.3;
text-decoration: none;
vertical-align: bottom;
color: var(--color-grey);
}
.ui_kit_title {
font-weight: bold;
color: var(--color-black);
}
.ui_kit_title,
.ui_kit_description {
margin-bottom: 6px;
}
.ui_kit_help,
.ui_kit_error {
margin-top: 6px;
}
.ui_kit_error {
color: var(--color-12);
}

View file

@ -58,8 +58,8 @@ export default function applyUrlCommands(callback: Function) {
const jsUrl = parameters.dump || parameters.log;
if (jsUrl) {
loadJsDump(jsUrl, callback);
loadJsDump(jsUrl, () => callback(parameters));
} else {
callback();
callback(parameters);
}
}

View file

@ -30,11 +30,26 @@ export function getDayPrefix(index:number) {
][index];
}
function getLangPrefix() {
// @ts-ignore
const code = window?.localization?.language || 'ru';
return {
ru: 'ru-RU',
en: 'en-EN',
zh: 'zh-ZH',
es: 'es-ES',
fr: 'fr-FR',
pt: 'pt-PT',
de: 'de-DE',
ja: 'ja-JA',
}[code] || 'ru-RU';
}
export function getDateByTimestamp(timestamp: string) {
const date = new Date(timestamp);
const day = date.getDay() - 1;
return [
date.toLocaleString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' }),
date.toLocaleString(getLangPrefix(), { day: 'numeric', month: 'long', year: 'numeric' }),
getDayName(day < 0 ? 6 : day),
];
}
@ -50,7 +65,7 @@ export function getClearHTML(text: string) {
export function getDate(timestamp: string) {
if (!timestamp) return '';
const date = new Date(timestamp);
return date.toLocaleString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' });
return date.toLocaleString(getLangPrefix(), { day: 'numeric', month: 'long', year: 'numeric' });
}
export function getDateForExcel(timestamp: string) {
@ -62,7 +77,7 @@ export function getDateForExcel(timestamp: string) {
export function getShortDate(timestamp: string) {
if (!timestamp) return '';
const date = new Date(timestamp);
return date.toLocaleString('ru-RU', { day: 'numeric', month: 'long' });
return date.toLocaleString(getLangPrefix(), { day: 'numeric', month: 'long' });
}
export function getShortTime(timestamp: string) {
@ -72,7 +87,7 @@ export function getShortTime(timestamp: string) {
}
export function getMoney(value: number, options?: any) {
return (value || 0).toLocaleString('ru-RU', {
return (value || 0).toLocaleString(getLangPrefix(), {
style: 'currency',
currency: settingsStore?.currency || 'USD',
currencyDisplay: 'symbol',

View file

@ -1,8 +1,14 @@
import i18next from 'i18next';
import { initReactI18next } from 'react-i18next';
import ru from '../translations/ru';
import de from '../translations/de';
import en from '../translations/en';
import es from '../translations/es';
import fr from '../translations/fr';
import ja from '../translations/ja';
import pt from '../translations/pt';
import ru from '../translations/ru';
import zh from '../translations/zh';
function getJsonFromString(text: string) {
return text
@ -27,14 +33,22 @@ function getTranslationWrapper(translation: string) {
};
}
export default function initializationI18n(defaultLanguage?: string) {
i18next.use(initReactI18next).init({
lng: 'ru', // if you're using a language detector, do not define the lng option
lng: defaultLanguage || 'ru', // if you're using a language detector, do not define the lng option
debug: false,
resources: {
ru: getTranslationWrapper(ru),
de: getTranslationWrapper(de),
en: getTranslationWrapper(en),
es: getTranslationWrapper(es),
fr: getTranslationWrapper(fr),
ja: getTranslationWrapper(ja),
pt: getTranslationWrapper(pt),
ru: getTranslationWrapper(ru),
zh: getTranslationWrapper(zh),
},
// if you see an error like: "Argument of type 'DefaultTFuncReturn' is not assignable to parameter of type xyz"
// set returnNull to false (and also in the i18next.d.ts options)
// returnNull: false,
});
}

View file

@ -4,6 +4,7 @@ import { useParams } from 'react-router-dom';
import isMobile from 'ts/helpers/isMobile';
import { TEAM, PERSON } from '../helpers/menu';
import PageSwiper from './Swiper';
import style from '../styles/slider.module.scss';
interface ISectionSliderProps {
@ -56,6 +57,11 @@ function MobileView({ getViewById }: ISectionSliderProps) {
if (!currentView.length) {
return getViewById(page);
return (
<PageSwiper>
{getViewById(page)}
</PageSwiper>
);
}
const [viewName, className] = currentView;

View file

@ -0,0 +1,132 @@
import React, { ReactNode, useEffect, useRef, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { TEAM, PERSON } from '../helpers/menu';
function getXByEvent(event: any) {
return event.screenX || event?.touches?.[0]?.screenX || 0;
}
function getYByEvent(event: any) {
return event.screenY || event?.touches?.[0]?.screenY || 0;
}
function getLeftWithLimits(moveEvent: any) {
if (moveEvent?.top > 15
|| moveEvent?.top < -15) return 0;
const size = Math.abs(moveEvent?.left || 0);
const direction = size / (moveEvent?.left || 0);
return size > 20
? (Math.min(size, 40) * direction)
: 0;
}
function addSwipeEvents(
element: any,
urlParameters: any,
setMoveEvent: Function,
navigate: Function,
) {
let top = 0;
let left = 0;
let firstPositionX = 0;
let firstPositionY = 0;
let overflowX: any = undefined;
function onStart(event: any) {
setMoveEvent({ top: 0, left: 0 });
top = 0;
left = 0;
firstPositionY = getYByEvent(event);
firstPositionX = getXByEvent(event);
overflowX = document.body.style.overflowX;
document.body.style.overflowX = 'hidden';
}
function onMove(event: any) {
if (!firstPositionX) return;
top = getYByEvent(event) - firstPositionY;
left = getXByEvent(event) - firstPositionX;
setMoveEvent({ top, left });
}
function onEnd() {
if (!firstPositionX) return;
const { type, page, userId } = urlParameters;
const list = type === 'team' ? TEAM : PERSON;
const pages = list.map((item) => item.id).filter((value) => value);
const index = pages.indexOf(page || 'total');
const getUrl = type === 'team'
? (newPage?: string) => `/team/${newPage || 'total'}`
: (newPage?: string) => `/person/${newPage || 'total'}/${userId || '0'}`;
const hasSmallShiftByY = Math.abs(top) < 15;
if (left > 30 && index > 0 && hasSmallShiftByY) {
navigate(getUrl(pages[index - 1]));
}
const hasNext = index >= 0 && index < (pages.length - 1);
if (left < -30 && hasNext && hasSmallShiftByY) {
navigate(getUrl(pages[index + 1]));
}
document.body.style.overflowX = overflowX;
setMoveEvent(null);
}
function onCancel() {
if (!firstPositionX) return;
top = 0;
left = 0;
document.body.style.overflowX = overflowX;
setMoveEvent(null);
}
// const events = ['mousedown', 'mousemove', 'mouseup', 'mouseleave'];
const events = ['touchstart', 'touchmove', 'touchend', 'touchcancel'];
element.addEventListener(events[0], onStart);
element.addEventListener(events[1], onMove);
element.addEventListener(events[2], onEnd);
element.addEventListener(events[3], onCancel);
return () => {
element.removeEventListener(events[0], onStart);
element.removeEventListener(events[1], onMove);
element.removeEventListener(events[2], onEnd);
element.removeEventListener(events[3], onCancel);
};
}
interface IPageSwiperProps {
children: ReactNode;
}
export default function PageSwiper({ children }: IPageSwiperProps) {
const { type, page, userId } = useParams<any>();
const navigate = useNavigate();
const ref = useRef() as React.MutableRefObject<HTMLDivElement>;
const [moveEvent, setMoveEvent] = useState<any>(null);
const left = getLeftWithLimits(moveEvent);
useEffect(() => {
const element = ref?.current;
if (!element) return;
return addSwipeEvents(
element,
{ type, page, userId },
setMoveEvent,
navigate,
);
}, []);
return (
<div
ref={ref}
style={{ position: 'relative', left }}
>
{children}
</div>
);
}

View file

@ -29,8 +29,8 @@ function getMenu(navigate: Function): any[] {
},
{
id: 'print',
title: 'sidebar.buttons.print',
icon: './assets/menu/print.svg',
title: 'sidebar.buttons.share',
icon: './assets/menu/share.svg',
onClick() {
navigator.share({
title: localization.get('common.title'),
@ -41,8 +41,8 @@ function getMenu(navigate: Function): any[] {
},
{
id: 'settings',
title: 'sidebar.buttons.settings',
icon: './assets/menu/setting.svg',
title: 'sidebar.buttons.logout',
icon: './assets/menu/logout.svg',
onClick() {
confirm.open({
title: 'Вы уверены что хотите выйти?',

View file

@ -3,8 +3,10 @@ import { useNavigate, useLocation } from 'react-router-dom';
import { observer } from 'mobx-react-lite';
import { useTranslation } from 'react-i18next';
import Select from 'ts/components/UiKit/components/Select';
import Buttons from 'ts/pages/Settings/components/Buttons';
import settingsForm from 'ts/pages/Settings/store/Form';
import localization from 'ts/helpers/Localization';
import Title from './Title';
import Filters from './Filters';
@ -12,9 +14,9 @@ import printStore from '../../store/Print';
import style from '../../styles/header.module.scss';
const Header = observer((): React.ReactElement | null => {
const { t, i18n } = useTranslation();
const navigate = useNavigate();
const location = useLocation();
const { t } = useTranslation();
return (
<header className={style.header}>
@ -24,6 +26,24 @@ const Header = observer((): React.ReactElement | null => {
) : (
<>
<Filters/>
<Select
className={style.header_lang}
value={localization.language}
options={[
{ id: 'ru', title: 'RU' },
{ id: 'en', title: 'EN' },
{ id: 'zh', title: 'ZH' },
{ id: 'es', title: 'ES' },
{ id: 'fr', title: 'FR' },
{ id: 'pt', title: 'PT' },
{ id: 'de', title: 'DE' },
{ id: 'ja', title: 'JA' },
]}
onChange={(item: any, id: string) => {
localization.language = id;
i18n.changeLanguage(id);
}}
/>
<img
title={t('sidebar.buttons.print')}
className={style.header_print}

View file

@ -42,13 +42,15 @@
border-radius: var(--border-radius-m);
--temp-color: var(--color-grey);
&:hover,
&:first-child:hover,
&:nth-child(4n+2):hover,
&_selected {
background-color: #35353F;
--temp-color: white;
}
&:active {
&:first-child:active,
&:nth-child(4n+2):active {
background-color: #45454F;
--temp-color: white;
}

View file

@ -18,18 +18,31 @@
}
&_print,
&_lang,
&_setting {
display: inline-block;
width: 24px;
height: 24px;
padding: 0;
margin: 4px 24px 0 0;
cursor: pointer;
user-select: none;
vertical-align: top;
}
&_setting {
margin: 4px 0 0;
}
&_print {
margin-right: 24px;
margin: 4px 24px 0 0;
}
&_lang {
width: 50px;
height: 32px;
padding: 0;
margin: 0 24px 0 0;
}
&_with_tab {

View file

@ -18,7 +18,7 @@
display: block;
height: 100px;
width: 100%;
background-color: var(--color-black);
background-color: white;
}
&_main,
@ -34,6 +34,7 @@
&_main_mobile {
width: calc(100vw - 240px);
padding: 24px 24px 82px;
}
}

View file

@ -13,6 +13,8 @@ const Month = observer(({ user }: IPersonCommonProps): React.ReactElement => {
const max = statistic.commitsByTimestampCounter.max;
return (
<>
<br/>
<PageWrapper template="table">
<YearChart
showEvents={false}
@ -21,6 +23,7 @@ const Month = observer(({ user }: IPersonCommonProps): React.ReactElement => {
wordDays={statistic.allCommitsByTimestamp}
/>
</PageWrapper>
</>
);
});

View file

@ -1,11 +1,9 @@
import React, { useState } from 'react';
import React from 'react';
import { observer } from 'mobx-react-lite';
import { IPagination } from 'ts/interfaces/Pagination';
import dataGripStore from 'ts/store/DataGrip';
import { getShortDateRange } from 'ts/helpers/formatter';
import UiKitButton from 'ts/components/UiKit/components/Button';
import PageWrapper from 'ts/components/Page/wrapper';
import DataLoader from 'ts/components/DataLoader';
import Pagination from 'ts/components/DataLoader/components/Pagination';
@ -13,9 +11,6 @@ import getFakeLoader from 'ts/components/DataLoader/helpers/formatter';
import NothingFound from 'ts/components/NothingFound';
import TempoChart from 'ts/components/Tempo';
import uiKitStyle from 'ts/components/UiKit/styles/index.module.scss';
import style from 'ts/pages/Team/styles/filters.module.scss';
import IPersonCommonProps from '../interfaces/CommonProps';
interface ITempoViewProps {
@ -41,58 +36,24 @@ function getPartOfData(filters: any, rows: any[]) {
return rows.filter((row: any) => (row.week === filters.week)).slice(0, 7);
}
const Tempo = observer(({ user }: IPersonCommonProps): React.ReactElement => {
const author = user;
const rows = dataGripStore.dataGrip.timestamp.statisticByAuthor[author.author]?.allCommitsByTimestamp || [];
const firstIndex = rows.length - 1;
const firstPoint = rows[firstIndex];
const [week, setWeek] = useState<number>(firstPoint.week);
const Tempo = observer(({ user, filters }: IPersonCommonProps): React.ReactElement => {
const rows = dataGripStore.dataGrip.timestamp.statistic.allCommitsByTimestamp || [];
if (!rows?.length) return (<NothingFound />);
const partOfData = getPartOfData({ week, user: author.author }, rows);
const firstWeekDay = partOfData[0];
const lastWeekDay = partOfData[partOfData.length - 1];
const partOfData = getPartOfData({ week: filters.week, user: user.author }, rows);
if (!partOfData?.length) return (<NothingFound />);
return (
<>
<PageWrapper>
<div className={style.tempo_filters}>
<UiKitButton
mode="second"
disabled={week === 1}
onClick={() => {
setWeek(week - 1);
}}
>
«
</UiKitButton>
<div className={`${uiKitStyle.ui_kit_common} ${style.tempo_filters_date_range}`}>
{getShortDateRange({
from: firstWeekDay.timestamp,
to: lastWeekDay.timestamp,
})}
</div>
<UiKitButton
mode="second"
disabled={week === firstPoint.week}
onClick={() => {
setWeek(week + 1);
}}
>
»
</UiKitButton>
</div>
</PageWrapper>
<br/>
<PageWrapper template="table">
<DataLoader
to="response"
loader={() => getFakeLoader({ content: partOfData })}
watch={week}
watch={JSON.stringify(filters)}
>
<TempoView user={author.author}/>
<TempoView user={user.author}/>
<Pagination />
</DataLoader>
</PageWrapper>

View file

@ -1,14 +1,21 @@
import React from 'react';
import React, { useMemo } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { observer } from 'mobx-react-lite';
import dataGripStore from 'ts/store/DataGrip';
import UiKitSelect from 'ts/components/UiKit/components/Select';
import UiKitButton from 'ts/components/UiKit/components/Button';
import SelectWithButtons from 'ts/components/UiKit/components/SelectWithButtons';
import style from 'ts/pages/Team/styles/filters.module.scss';
import { getFormattedWeeks } from 'ts/pages/Team/components/TempoFilters';
import style from '../styles/index.module.scss';
interface IUserSelectProps {
filters: any,
onChange: Function,
}
const UserSelect = observer((): React.ReactElement => {
const UserSelect = observer(({
filters,
onChange,
}: IUserSelectProps): React.ReactElement => {
const { type, page, userId } = useParams<any>();
const navigate = useNavigate();
@ -16,34 +23,32 @@ const UserSelect = observer((): React.ReactElement => {
const authors = dataGripStore.dataGrip.author.list;
const options = authors.map((title: string, id: number) => ({ id, title }));
const rows = dataGripStore.dataGrip.timestamp.statistic.allCommitsByTimestamp || [];
const weeks = useMemo(() => getFormattedWeeks(rows), [rows]);
return (
<div className={style.user_select}>
<UiKitButton
mode="second"
disabled={formattedUserId <= 0}
onClick={() => {
navigate(`/${type}/${page}/${formattedUserId - 1}`);
}}
>
«
</UiKitButton>
<UiKitSelect
<div className={style.table_filters}>
<SelectWithButtons
title="page.team.tree.filters.author"
value={formattedUserId}
className={style.table_filters_item}
options={options}
className={style.user_name}
onChange={(newUserId: string) => {
onChange={(newUserId: number) => {
navigate(`/${type}/${page}/${newUserId}`);
}}
/>
<UiKitButton
mode="second"
disabled={formattedUserId >= (authors.length - 1)}
onClick={() => {
navigate(`/${type}/${page}/${formattedUserId + 1}`);
{page === 'day' ? (
<SelectWithButtons
reverse
title="page.team.tree.filters.author"
value={filters?.week || rows[rows.length - 1].week}
className={style.table_filters_item}
options={weeks.reverse()}
onChange={(week: number) => {
onChange({ ...filters, week });
}}
>
»
</UiKitButton>
/>
) : null}
</div>
);
});

View file

@ -132,6 +132,7 @@ const Week = observer(({
return (
<>
<br/>
<Recommendations
mode={mode}
recommendations={recommendations}

View file

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { observer } from 'mobx-react-lite';
import { useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
@ -26,7 +26,7 @@ interface IPersonProps {
userId?: string | number;
}
function getViewByIdByUser(user: any) {
function getViewByIdByUser(user: any, filters: any) {
return function getViewById(page?: string) {
const mode = printStore.processing ? 'print' : undefined;
if (page === 'total') return <Total user={user}/>;
@ -48,7 +48,12 @@ function getViewByIdByUser(user: any) {
/>
);
if (page === 'speed') return <Speed user={user}/>;
if (page === 'day') return <Tempo user={user}/>;
if (page === 'day') return (
<Tempo
user={user}
filters={filters}
/>
);
if (page === 'print') return <Print user={user}/>;
return <Total user={user}/>;
};
@ -59,16 +64,26 @@ const Person = observer(({
}: IPersonProps) => {
const { t } = useTranslation();
const { type, page, userId: userIdFromUrl } = useParams<any>();
const rows = dataGripStore.dataGrip.timestamp.statistic.allCommitsByTimestamp || [];
const defaultWeek = rows.length
? rows[rows.length - 1].week
: 0;
const [filters, setFilters] = useState<any>({ week: defaultWeek });
const user = dataGripStore.dataGrip.author.statistic[userId || userIdFromUrl || 0];
if (type !== 'person' || !user) return null;
const getViewById = getViewByIdByUser(user);
const getViewById = getViewByIdByUser(user, filters);
return (
<>
{page !== 'print' && (
<>
<Title title={t('common.filters')} />
<UserSelect />
<UserSelect
filters={filters}
onChange={setFilters}
/>
</>
)}
<SectionSlider getViewById={getViewById} />

View file

@ -1,3 +1,4 @@
export default interface IPersonCommonProps {
user: any;
filters?: any;
}

View file

@ -15,7 +15,6 @@ const Common = observer((): React.ReactElement | null => {
const [language, setLanguage] = useState<string>(localization.language);
useEffect(() => {
console.log(language);
i18n.changeLanguage(language);
}, [language]);
@ -39,6 +38,12 @@ const Common = observer((): React.ReactElement | null => {
options={[
{ id: 'ru', title: 'Русский' },
{ id: 'en', title: 'English' },
{ id: 'zh', title: '中文' },
{ id: 'es', title: 'Español' },
{ id: 'fr', title: 'Français' },
{ id: 'pt', title: 'Português' },
{ id: 'de', title: 'Deutsch' },
{ id: 'ja', title: '日本語' },
]}
onChange={(item: any, id: string) => {
localization.language = id;

View file

@ -42,8 +42,8 @@ export default function getEmptySettings(): IUserSetting {
return {
version: 1,
defaultSalary: {
value: 180000,
currency: 'RUB',
value: 3000,
currency: 'USD',
workDaysInYear: 247,
vacationDaysInYear: 28,
workDaysInWeek: [1, 1, 1, 1, 1, 0, 0],

View file

@ -10,7 +10,7 @@ class Settings {
workDaysInMonth: number = 22;
salaryInDay: number = 180000 / 22;
salaryInDay: number = 3000 / 22;
update(customSettings?: IUserSetting) {
this.customSettings = customSettings || getEmptySettings();

View file

@ -3,10 +3,7 @@ import { observer } from 'mobx-react-lite';
import { IPagination } from 'ts/interfaces/Pagination';
import dataGripStore from 'ts/store/DataGrip';
import { getShortDateRange } from 'ts/helpers/formatter';
import UiKitButton from 'ts/components/UiKit/components/Button';
import UiKitSelect from 'ts/components/UiKit/components/Select';
import PageWrapper from 'ts/components/Page/wrapper';
import DataLoader from 'ts/components/DataLoader';
import getFakeLoader from 'ts/components/DataLoader/helpers/formatter';
@ -14,8 +11,7 @@ import NothingFound from 'ts/components/NothingFound';
import TempoChart from 'ts/components/Tempo';
import Title from 'ts/components/Title';
import uiKitStyle from 'ts/components/UiKit/styles/index.module.scss';
import style from '../styles/filters.module.scss';
import TempoFilters from './TempoFilters';
interface ITempoViewProps {
order: string[];
@ -44,83 +40,38 @@ function getPartOfData(filters: any, rows: any[]) {
const Tempo = observer((): React.ReactElement => {
const rows = dataGripStore.dataGrip.timestamp.statistic.allCommitsByTimestamp || [];
const order = dataGripStore.dataGrip.author.list || [];
const users = dataGripStore.dataGrip.author.list || [];
const firstIndex = rows.length - 1;
const firstPoint = rows[firstIndex];
const [week, setWeek] = useState<number>(firstPoint.week);
const [user, setUser] = useState<string>('');
const [filters, setFilters] = useState<any>({ week: firstPoint.week });
const user = filters.user
? users[filters.user - 1]
: '';
if (!rows?.length) return (<NothingFound />);
const partOfData = getPartOfData({ week, user }, rows);
const firstWeekDay = partOfData[0];
const lastWeekDay = partOfData[partOfData.length - 1];
const partOfData = getPartOfData({ week: filters.week, user }, rows);
if (!partOfData?.length) return (<NothingFound />);
return (
<>
<Title title="common.filters" />
<PageWrapper>
<div className={style.tempo_filters}>
<UiKitButton
mode="second"
disabled={week === 1}
onClick={() => {
setWeek(week - 1);
}}
>
«
</UiKitButton>
<div className={`${uiKitStyle.ui_kit_common} ${style.tempo_filters_date_range}`}>
{getShortDateRange({
from: firstWeekDay.timestamp,
to: lastWeekDay.timestamp,
})}
</div>
<UiKitButton
mode="second"
disabled={week === firstPoint.week}
onClick={() => {
setWeek(week + 1);
}}
>
»
</UiKitButton>
<UiKitButton
mode="second"
onClick={() => {
setUser(order[order.indexOf(user) - 1]);
}}
>
«
</UiKitButton>
<UiKitSelect
className={style.tempo_filters_user}
value={user}
options={[ '', ...dataGripStore.dataGrip.author.list]}
onChange={(id: number, name: string) => {
setUser(name);
}}
<TempoFilters
filters={filters}
onChange={setFilters}
/>
<UiKitButton
mode="second"
onClick={() => {
setUser(order[order.indexOf(user) + 1]);
}}
>
»
</UiKitButton>
</div>
</PageWrapper>
<br/>
<PageWrapper template="table">
<DataLoader
to="response"
loader={() => getFakeLoader({ content: partOfData })}
watch={`${week}${user}`}
watch={JSON.stringify(filters)}
>
<TempoView
order={order}
order={users}
user={user}
/>
</DataLoader>

View file

@ -0,0 +1,83 @@
import React, { useMemo } from 'react';
import { observer } from 'mobx-react-lite';
import { useTranslation } from 'react-i18next';
import SelectWithButtons from 'ts/components/UiKit/components/SelectWithButtons';
import { getShortDateRange } from 'ts/helpers/formatter';
import dataGripStore from 'ts/store/DataGrip';
import style from '../styles/filters.module.scss';
export function getFormattedWeeks(rows: any[]) {
const groups = (rows || []).reduce((group: any, row: any) => {
if (!group[row.week]) group[row.week] = [];
group[row.week].push(row);
return group;
}, {});
return Object.entries(groups).map((item: any) => {
const firstDay = item[1][0];
const lastDay = item[1][(item[1].length - 1)];
return {
id: firstDay.week,
days: item[1],
title: getShortDateRange({
from: firstDay.timestamp,
to: lastDay.timestamp,
}),
};
});
}
function getFormattedUsers(rows: any[], t: Function) {
const options = rows.map((title: string, id: number) => ({ id: id + 1, title }));
options.unshift({ id: 0, title: t('page.team.tree.filters.all') });
return options;
}
interface ITempoFiltersProps {
filters: {
week?: number;
user?: number;
};
onChange: Function;
}
const TempoFilters = observer(({
filters,
onChange,
}: ITempoFiltersProps): React.ReactElement => {
const { t } = useTranslation();
const rows = dataGripStore.dataGrip.timestamp.statistic.allCommitsByTimestamp || [];
const weeks = useMemo(() => getFormattedWeeks(rows), [rows]);
const authors = dataGripStore.dataGrip.author.list;
const users = useMemo(() => getFormattedUsers(authors, t), [authors]);
return (
<div className={style.table_filters}>
<SelectWithButtons
title="page.team.tree.filters.author"
value={filters.user}
className={style.table_filters_item}
options={users}
onChange={(user: number) => {
onChange({ ...filters, user });
}}
/>
<SelectWithButtons
reverse
title="page.team.tree.filters.author"
value={filters.week || rows[rows.length - 1].week}
className={style.table_filters_item}
options={weeks.reverse()}
onChange={(week: number) => {
onChange({ ...filters, week });
}}
/>
</div>
);
});
export default TempoFilters;

View file

@ -3,7 +3,7 @@ import { observer } from 'mobx-react-lite';
import { useTranslation } from 'react-i18next';
import dataGripStore from 'ts/store/DataGrip';
import UiKitSelect from 'ts/components/UiKit/components/SelectWithButtons';
import SelectWithButtons from 'ts/components/UiKit/components/SelectWithButtons';
import UiKitInputNumber from 'ts/components/UiKit/components/InputNumber';
import treeStore from '../store/Tree';
@ -16,21 +16,21 @@ const TreeFilters = observer((): React.ReactElement => {
options.unshift({ id: 0, title: t('page.team.tree.filters.all') });
return (
<div>
<UiKitSelect
<div className={style.table_filters}>
<SelectWithButtons
title="page.team.tree.filters.author"
value={treeStore.authorId}
className={style.table_filters_item}
options={options}
className={style.tree_filters}
onChange={(authorId: number) => {
treeStore.updateFilter('authorId', authorId);
}}
/>
<UiKitInputNumber
title="page.team.tree.filters.commits"
placeholder="page.team.tree.filters.commits"
help="page.team.tree.filters.help"
value={treeStore.minCommits}
className={style.tree_filters}
value={treeStore.minCommits || ''}
className={style.table_filters_item}
onChange={(minCommits: number) => {
treeStore.updateFilter('minCommits', minCommits);
}}

View file

@ -1,25 +1,28 @@
@import 'src/styles/variables';
.tree_filters {
.table_filters {
&_item {
display: inline-block;
margin: 0 24px 24px 0;
width: 350px;
margin: 0 24px 0 0;
vertical-align: top;
}
.tempo_filters {
margin: 0 0 24px 0;
&_date_range {
width: 260px;
max-width: 260px;
margin: 0 var(--space-xs);
text-align: center;
&_item:first-child {
margin-left: 0;
}
}
&_user {
display: inline-block;
width: 260px;
max-width: 260px;
margin: 0 6px;
@media (max-width: 900px) {
.table_filters {
&_item {
display: block;
width: 100%;
margin: 0;
}
&_item:first-child {
margin-bottom: 24px;
}
}
}

View file

@ -43,13 +43,13 @@ class SettingsStore implements ISettingsStore {
isFullTime: boolean = true;
defaultSalary: number = 180000;
defaultSalary: number = 3000;
defaultWorkDays: number = 5;
holidaysInYear: number = 118 + 22; // праздники + выходные + отпуск
currency: string = 'RUB';
currency: string = 'USD';
salary: any = {};

View file

@ -3,6 +3,8 @@ export default `
§ sidebar.switch.person: Der Angestellte
§ sidebar.buttons.settings: Die Einstellungen
§ sidebar.buttons.print: Drucken
§ sidebar.buttons.share: Share
§ sidebar.buttons.logout: Logout
§ sidebar.filters.all: Die ganze Zeit
§ sidebar.filters.year: Jahr
§ sidebar.filters.halfYear: ein halbes Jahr

View file

@ -3,6 +3,8 @@ export default `
§ sidebar.switch.person: Employee
§ sidebar.buttons.settings: Settings
§ sidebar.buttons.print: Print
§ sidebar.buttons.share: Share
§ sidebar.buttons.logout: Logout
§ sidebar.filters.all: all time
§ sidebar.filters.year: year
§ sidebar.filters.halfYear: half year

View file

@ -3,6 +3,8 @@ export default `
§ sidebar.switch.person: Empleado
§ sidebar.buttons.settings: Ajustes
§ sidebar.buttons.print: Impresión
§ sidebar.buttons.share: Share
§ sidebar.buttons.logout: Logout
§ sidebar.filters.all: a todas horas
§ sidebar.filters.year: año
§ sidebar.filters.halfYear: medio año

View file

@ -3,6 +3,8 @@ export default `
§ sidebar.switch.person: Employé
§ sidebar.buttons.settings: Réglages
§ sidebar.buttons.print: Impression
§ sidebar.buttons.share: Share
§ sidebar.buttons.logout: Logout
§ sidebar.filters.all: à toute heure
§ sidebar.filters.year: année
§ sidebar.filters.halfYear: demi-année

View file

@ -3,12 +3,14 @@ export default `
§ sidebar.switch.person: 従業員
§ sidebar.buttons.settings: 設定
§ sidebar.buttons.print: 印刷
§ sidebar.buttons.share: Share
§ sidebar.buttons.logout: Logout
§ sidebar.filters.all: すべての時間
§ sidebar.filters.year:
§ sidebar.filters.halfYear: 半年
§ sidebar.filters.month:
§ sidebar.filters.week: 週間
§ sidebar.team.total: 般的な情報
§ sidebar.filters.week: 週間
§ sidebar.team.total: 般的な情報
§ sidebar.team.scope: モジュール
§ sidebar.team.author: スタッフ
§ sidebar.team.type: タスクの種類

View file

@ -3,6 +3,8 @@ export default `
§ sidebar.switch.person: Funcionário
§ sidebar.buttons.settings: Sintonização
§ sidebar.buttons.print: Impressão
§ sidebar.buttons.share: Share
§ sidebar.buttons.logout: Logout
§ sidebar.filters.all: o tempo todo
§ sidebar.filters.year: ano
§ sidebar.filters.halfYear: meio ano

View file

@ -3,6 +3,8 @@ export default `
§ sidebar.switch.person: Сотрудник
§ sidebar.buttons.settings: Настройки
§ sidebar.buttons.print: Печать
§ sidebar.buttons.share: Расшарить
§ sidebar.buttons.logout: Выйти
§ sidebar.filters.all: всё время
§ sidebar.filters.year: год
§ sidebar.filters.halfYear: пол года

View file

@ -3,12 +3,14 @@ export default `
§ sidebar.switch.person: 雇员
§ sidebar.buttons.settings: 设置
§ sidebar.buttons.print: 印刷业
§ sidebar.buttons.share: Share
§ sidebar.buttons.logout: Logout
§ sidebar.filters.all: 一直
§ sidebar.filters.year: 年份
§ sidebar.filters.halfYear: 半年
§ sidebar.filters.month: 月份
§ sidebar.filters.week:
§ sidebar.team.total: 般资料
§ sidebar.filters.week:
§ sidebar.team.total: 般资料
§ sidebar.team.scope: 功能
§ sidebar.team.author: 员工
§ sidebar.team.type: 任务类型
@ -26,7 +28,7 @@ export default `
§ sidebar.team.words: 流行语
§ sidebar.team.top: 测验
§ sidebar.team.settings: 设置
§ sidebar.person.total: 般资料
§ sidebar.person.total: 般资料
§ sidebar.person.money: 工作的成本
§ sidebar.person.speed: 速度
§ sidebar.person.day: 白天