MMM-2022 lol(kek): 4eburek

This commit is contained in:
bakhirev 2023-12-03 13:18:16 +03:00
parent a22abc0dc5
commit ffe71996f1
14 changed files with 149 additions and 84 deletions

View file

@ -42,7 +42,7 @@
<meta name="copyright" content="(c) Bakhirev Aleksei"> <meta name="copyright" content="(c) Bakhirev Aleksei">
<meta http-equiv="Reply-to" content="alexey-bakhirev@yandex.ru"> <meta http-equiv="Reply-to" content="alexey-bakhirev@yandex.ru">
<meta name="application-name" content="Git Statistics"> <meta name="application-name" content="Git statistics">
<meta name="msapplication-tooltip" content="Simple and fast report on Git commit history."> <meta name="msapplication-tooltip" content="Simple and fast report on Git commit history.">
<meta property="og:title" content="Git Statistics"> <meta property="og:title" content="Git Statistics">
<meta property="og:description" content="Simple and fast report on Git commit history."> <meta property="og:description" content="Simple and fast report on Git commit history.">

View file

@ -41,10 +41,10 @@ function Console({ className, textForCopy, children }: IConsoleProps) {
className={`${style.console_copy}`} className={`${style.console_copy}`}
onClick={() => { onClick={() => {
copyInBuffer(textForCopy); copyInBuffer(textForCopy);
notificationsStore.show('Текст скопирован'); notificationsStore.show(localization.get('uiKit.console.notification'));
}} }}
> >
{localization.get('uiKit.console')} {localization.get('uiKit.console.button')}
</Button> </Button>
</div> </div>
); );

View file

@ -5,6 +5,7 @@ import ISort from 'ts/interfaces/Sort';
import Table from 'ts/components/Table'; import Table from 'ts/components/Table';
import Cards from 'ts/components/Cards'; import Cards from 'ts/components/Cards';
import { downloadCsv } from 'ts/helpers/File'; import { downloadCsv } from 'ts/helpers/File';
import viewSettings from 'ts/store/ViewSettings';
import style from './index.module.scss'; import style from './index.module.scss';
import PageWrapper from '../Page/wrapper'; import PageWrapper from '../Page/wrapper';
@ -32,7 +33,10 @@ function DataView({
children, children,
}: IDataViewProps): React.ReactElement | null { }: IDataViewProps): React.ReactElement | null {
const urlParams = useParams<any>(); const urlParams = useParams<any>();
const [localType, setType] = useState<string>(type || 'table'); const defaultType = viewSettings.getItem(urlParams, 'table');
console.log(defaultType);
const [localType, setType] = useState<string>(type || defaultType);
console.log(localType);
if (!rows || !rows.length) return null; if (!rows || !rows.length) return null;
@ -64,7 +68,9 @@ function DataView({
src={icon} src={icon}
className={style.data_view_icon} className={style.data_view_icon}
onClick={() => { onClick={() => {
setType(localType === 'table' ? 'cards' : 'table'); const newType = localType === 'table' ? 'cards' : 'table';
setType(newType);
viewSettings.setItem(urlParams, newType, 'table');
}} }}
/> />
</div> </div>
@ -99,7 +105,7 @@ function DataView({
DataView.defaultProps = { DataView.defaultProps = {
rows: [], rows: [],
sort: [], sort: [],
type: 'table', type: undefined,
columnCount: undefined, columnCount: undefined,
updateSort: () => { updateSort: () => {
}, },

View file

@ -31,6 +31,19 @@
margin-bottom: 4px; margin-bottom: 4px;
} }
&_link {
text-decoration: underline;
color: var(--color-button);
&:hover {
text-decoration: none;
}
&:visited {
color: var(--color-button);
}
}
&_list { &_list {
position: relative; position: relative;
padding: 0 0 0 22px; padding: 0 0 0 22px;

View file

@ -6,9 +6,10 @@ import cssStyle from './index.module.scss';
interface ICommonProps { interface ICommonProps {
text: string; text: string;
style: any; style: any;
className?: string;
} }
function getTextWithLink(text: string) { function getTextWithLink(text: string, className?: string) {
const parts = (text || '') const parts = (text || '')
.split(/(\[[^\]]+\])/gim) .split(/(\[[^\]]+\])/gim)
.map((value: string) => { .map((value: string) => {
@ -18,7 +19,10 @@ function getTextWithLink(text: string) {
<Link <Link
key={value} key={value}
target="_blank" target="_blank"
to={link}> rel="noreferrer"
className={className || ''}
to={link}
>
{title} {title}
</Link> </Link>
); );
@ -26,43 +30,43 @@ function getTextWithLink(text: string) {
return (<>{parts}</>) ; return (<>{parts}</>) ;
} }
function getTextWithStyle(text: string) { function getTextWithStyle(text: string, className?: string) {
const parts = (text || '') const parts = (text || '')
.split('*') .split('*')
.map((value: string, index: number) => (index % 2 .map((value: string, index: number) => (index % 2
? (<b key={value}>{getTextWithLink(value)}</b>) ? (<b key={value}>{getTextWithLink(value, className)}</b>)
: (<span key={value}>{getTextWithLink(value)}</span>) : (<span key={value}>{getTextWithLink(value, className)}</span>)
)); ));
return (<>{parts}</>) ; return (<>{parts}</>) ;
} }
function List({ text, style }: ICommonProps) { function List({ text, style, className }: ICommonProps) {
return ( return (
<p <p
className={cssStyle.description_list}
style={style || {}} style={style || {}}
className={`${cssStyle.description_list} ${className || ''}`}
> >
{getTextWithStyle(text)} {getTextWithStyle(text, className)}
</p> </p>
); );
} }
function Title({ text, style }: ICommonProps) { function Title({ text, style, className }: ICommonProps) {
return ( return (
<h6 <h6
className={cssStyle.description_title}
style={style || {}} style={style || {}}
className={`${cssStyle.description_title} ${className || ''}`}
> >
{getTextWithStyle(text)} {getTextWithStyle(text, className)}
</h6> </h6>
); );
} }
function SimpleText({ text, style }: ICommonProps) { function SimpleText({ text, style, className }: ICommonProps) {
return ( return (
<p <p
className={cssStyle.description_text}
style={style || {}} style={style || {}}
className={`${cssStyle.description_text} ${className || ''}`}
> >
{getTextWithStyle(text)} {getTextWithStyle(text)}
</p> </p>
@ -72,9 +76,10 @@ function SimpleText({ text, style }: ICommonProps) {
interface IDescriptionProps { interface IDescriptionProps {
text?: string | string[]; text?: string | string[];
style?: any; style?: any;
className?: string;
} }
function Description({ text, style }: IDescriptionProps) { function Description({ text, style, className }: IDescriptionProps) {
const paragraphs = !Array.isArray(text) const paragraphs = !Array.isArray(text)
? (text || '').trim().split(/\n+/gm) ? (text || '').trim().split(/\n+/gm)
: text; : text;
@ -82,29 +87,35 @@ function Description({ text, style }: IDescriptionProps) {
const items = paragraphs.map((paragraph) => { const items = paragraphs.map((paragraph) => {
const prefix = paragraph.substring(0, 2); const prefix = paragraph.substring(0, 2);
const mainText = paragraph.substring(2); const mainText = paragraph.substring(2);
if (prefix === '- ') { if (prefix === '- ') {
return ( return (
<List <List
key={mainText} key={mainText}
text={mainText} text={mainText}
style={style} style={style}
className={className}
/> />
); );
} }
if (prefix === '# ') { if (prefix === '# ') {
return ( return (
<Title <Title
key={mainText} key={mainText}
text={mainText} text={mainText}
style={style} style={style}
className={className}
/> />
); );
} }
return ( return (
<SimpleText <SimpleText
key={mainText} key={mainText}
text={paragraph} text={paragraph}
style={style} style={style}
className={className}
/> />
); );
}); });

View file

@ -1,3 +1,5 @@
import localization from './Localization';
function getFormattedType(dataGrip: any): string { function getFormattedType(dataGrip: any): string {
const popularType = dataGrip.extension.statistic?.[0] || {}; const popularType = dataGrip.extension.statistic?.[0] || {};
const extension = popularType?.extension || ''; const extension = popularType?.extension || '';
@ -45,7 +47,7 @@ function getFormattedType(dataGrip: any): string {
export default function getTitle(dataGrip: any, commits: any) { export default function getTitle(dataGrip: any, commits: any) {
if (!commits.length) { if (!commits.length) {
return 'Git Statistics'; return localization.get('common.title');
} }
const type = getFormattedType(dataGrip) || ''; const type = getFormattedType(dataGrip) || '';

View file

@ -19,10 +19,10 @@ const Common = observer((): React.ReactElement | null => {
<InputString <InputString
title="page.settings.document.name" title="page.settings.document.name"
value={title} value={title}
placeholder="Git Statistics" placeholder={localization.get('common.title')}
onChange={(value: string) => { onChange={(value: string) => {
setTitle(value); setTitle(value);
document.title = value || 'Git Statistics'; document.title = value || localization.get('common.title');
applicationHasCustom.title = true; applicationHasCustom.title = true;
}} }}
/> />

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router-dom';
import Console from 'ts/components/Console'; import Console from 'ts/components/Console';
import Description from 'ts/components/Description';
import { import {
getStringFromFileList, getStringFromFileList,
getStringsForParser, getStringsForParser,
@ -14,27 +14,14 @@ import style from './styles/index.module.scss';
function WarningInfo() { function WarningInfo() {
return ( return (
<h4 className={style.welcome_warning}> <h4 className={style.welcome_warning}>
<p> <Description
{'Сервис '} text={localization.get('page.welcome.warning1')}
<span className={style.welcome_warning_bold}>НЕ ХРАНИТ</span> className={style.welcome_warning_text}
{' и '} />
<span className={style.welcome_warning_bold}>НЕ ПЕРЕДАЁТ</span> <Description
{' ваши данные. Все расчёты выполняются локально в вашем браузере прямо на вашей машине.'} text={localization.get('page.welcome.warning2')}
</p> className={style.welcome_warning_text}
<p> />
{'Сервис '}
<span className={style.welcome_warning_bold}>НЕ СОБИРАЕТ СТАТИСТИКУ</span>
{' по проектам. Вы можете отключить интернет, проверить трафик и даже собрать локальный билд из '}
<a
href='https://github.com/bakhirev/assayo'
target="_blank"
rel="noreferrer"
className={style.welcome_warning_link}
>
исходников
</a>
{'.'}
</p>
</h4> </h4>
); );
} }
@ -54,16 +41,10 @@ function Welcome() {
className={style.welcome_console} className={style.welcome_console}
textForCopy={command} textForCopy={command}
/> />
<p className={style.welcome_description}> <Description
{localization.get('page.welcome.description1')} text={localization.get('page.welcome.description')}
<Link className={`${style.welcome_description}`}
className={`${style.welcome_link}`} />
target="_blank"
to="https://git-scm.com/docs/gitmailmap">
.mailmap
</Link>
{localization.get('page.welcome.description2')}
</p>
<h2 className={style.welcome_last_title}> <h2 className={style.welcome_last_title}>
{localization.get('page.welcome.step2') === 'page.welcome.step2' {localization.get('page.welcome.step2') === 'page.welcome.step2'
? '' ? ''

View file

@ -23,34 +23,23 @@
} }
&_warning { &_warning {
font-weight: 100;
font-size: var(--font-s);
width: 100%; width: 100%;
margin: 0 auto; margin: 0 auto;
padding: 6px; padding: 6px;
box-sizing: border-box; box-sizing: border-box;
line-height: 1.5;
text-align: center;
border-bottom: 3px solid red; border-bottom: 3px solid red;
background-color: #FCDADA; background-color: #FCDADA;
&_bold { &_text {
font-weight: bold; text-align: center;
color: red; color: #B50404;
} }
&_link { &_link {
text-decoration: underline;
color: var(--color-button); color: var(--color-button);
&:hover {
text-decoration: none;
}
} }
} }
&_link,
&_description { &_description {
font-size: var(--font-xs); font-size: var(--font-xs);
@ -66,12 +55,6 @@
color: #878FA1; color: #878FA1;
} }
&_link {
display: inline;
margin: 16px 4px 0 4px;
text-decoration: underline;
}
&_first_title, &_first_title,
&_last_title { &_last_title {
font-size: 42px; font-size: 42px;

View file

@ -0,0 +1,69 @@
import { observable, action, makeObservable } from 'mobx';
class ViewSettingsStore {
key: string = 'view_settings';
version: number = 1;
settings: any = {};
constructor() {
this.load();
makeObservable(this, {
settings: observable,
load: action,
setItem: action,
});
}
load() {
const settings = JSON.parse(localStorage.getItem(this.key) || '{}') || {};
if (settings.version === this.version) {
this.settings = settings.settings;
}
}
save() {
if (Object.keys(this.settings).length === 0) {
localStorage.removeItem(this.key);
return;
}
localStorage.setItem(this.key, JSON.stringify({
version: this.version,
settings: this.settings,
}));
}
#getPath(path: any): string {
if (!path) return '';
if (Array.isArray(path)) {
return path.join('.');
} else if (typeof path === 'object') {
return [path.type, path.page].join('.');
}
return path;
}
setItem(path: any, value: any, defaultValue?: any) {
const formattedPath = this.#getPath(path);
if (!formattedPath) return;
if (!value || value === defaultValue) {
delete this.settings[formattedPath];
} else {
this.settings[formattedPath] = value;
}
this.save();
}
getItem(path: any, defaultValue?: any): any {
const formattedPath = this.#getPath(path);
return this.settings?.[formattedPath] || defaultValue;
}
}
const viewSettings = new ViewSettingsStore();
export default viewSettings;

View file

@ -1,5 +1,6 @@
export default ` export default `
§ uiKit.console: Copy § uiKit.console.button: Copy
§ uiKit.console.notification: Text was copied
§ uiKit.dataLoader.page: Page § uiKit.dataLoader.page: Page
§ uiKit.dataLoader.size: Displayed § uiKit.dataLoader.size: Displayed
§ uiKit.dataLoader.from: out of § uiKit.dataLoader.from: out of
@ -22,6 +23,7 @@ The work of employees with such status on this project can be neglected as their
Therefore, the system does not calculate a number of indicators for him. Therefore, the system does not calculate a number of indicators for him.
If this is an error and this employee needs to be calculated as usual, go to the Settings section and change his type. If this is an error and this employee needs to be calculated as usual, go to the Settings section and change his type.
§ common.title: Git statistics
§ common.filters: Filters § common.filters: Filters
§ common.notifications.save: The changes have been saved § common.notifications.save: The changes have been saved
§ common.notifications.setting: The settings have been saved § common.notifications.setting: The settings have been saved

View file

@ -1,11 +1,9 @@
export default ` export default `
§ page.welcome.step1: Execute the command in the root of your project. § page.welcome.step1: Execute the command in the root of your project.
§ page.welcome.step3: Drag and Drop. § page.welcome.step3: Drag and drop
§ page.welcome.step4: the log.txt file onto this page. § page.welcome.step4: the log.txt file onto this page.
§ page.welcome.description1: Git will create a log.txt file. It contains data for report generation. Or use git shortlog -s -n -e if you don't need a report. Create a file. § page.welcome.description: Git will create a log.txt file. It contains data for report generation. Or use git shortlog -s -n -e if you don't need a report. Create a [.mailmap|https://git-scm.com/docs/gitmailmap] file in the root of the project to consolidate employee statistics.
§ page.welcome.description2: [Create a .mailmap file|https://git-scm.com/docs/gitmailmap] in the root of the project to consolidate employee statistics. § page.welcome.warning1: The service *DOES NOT SAVE* and *DOES NOT TRANSFER* your data. All calculations are performed locally in your browser on your machine.
§ page.welcome.description: Git will create a log.txt file. It contains data for report generation. Or use git shortlog -s -n -e if you don't need a report. Create a [.mailmap file|https://git-scm.com/docs/gitmailmap] in the root of the project to consolidate employee statistics.
§ page.welcome.warning1: The service *DOES NOT STORE* and *DOES NOT TRANSFER* your data. All calculations are performed locally in your browser on your machine.
§ page.welcome.warning2: The service *DOES NOT COLLECT STATISTICS* on projects. You can disconnect the internet, check traffic, and even build a local version from the [source|https://github.com/bakhirev/assayo]. § page.welcome.warning2: The service *DOES NOT COLLECT STATISTICS* on projects. You can disconnect the internet, check traffic, and even build a local version from the [source|https://github.com/bakhirev/assayo].
§ page.common.words.title: Word Statistics. § page.common.words.title: Word Statistics.
§ page.common.words.description: the most popular word. Occurs $1 times. § page.common.words.description: the most popular word. Occurs $1 times.

View file

@ -1,5 +1,6 @@
export default ` export default `
§ uiKit.console: Копировать § uiKit.console.button: Копировать
§ uiKit.console.notification: Текст скопирован
§ uiKit.dataLoader.page: Страница § uiKit.dataLoader.page: Страница
§ uiKit.dataLoader.size: Отображается по § uiKit.dataLoader.size: Отображается по
§ uiKit.dataLoader.from: из § uiKit.dataLoader.from: из
@ -22,6 +23,7 @@ export default `
Поэтому система не рассчитывает для него ряд показателей. Поэтому система не рассчитывает для него ряд показателей.
Если это ошибка и данного сотрудника нужно рассчитать как обычного, перейдите в раздел «Настройки» и измените его тип. Если это ошибка и данного сотрудника нужно рассчитать как обычного, перейдите в раздел «Настройки» и измените его тип.
§ common.title: Git статистика
§ common.filters: Фильтры § common.filters: Фильтры
§ common.notifications.save: Изменения сохранены § common.notifications.save: Изменения сохранены
§ common.notifications.setting: Настройки сохранены § common.notifications.setting: Настройки сохранены

View file

@ -2,8 +2,6 @@ export default `
§ page.welcome.step1: Выполните команду в корне вашего проекта § page.welcome.step1: Выполните команду в корне вашего проекта
§ page.welcome.step3: Перетащите § page.welcome.step3: Перетащите
§ page.welcome.step4: файл log.txt на эту страницу § page.welcome.step4: файл log.txt на эту страницу
§ page.welcome.description1: Git создаст файл log.txt. Он содержит данные для построения отчёта. Или git shortlog -s -n -e если отчёт вам не нужен. Создайте файл
§ page.welcome.description2: [.mailmap|https://git-scm.com/docs/gitmailmap] в корне проекта, чтобы объединить статистику по сотрудникам.
§ page.welcome.description: Git создаст файл log.txt. Он содержит данные для построения отчёта. Или git shortlog -s -n -e если отчёт вам не нужен. Создайте файл [.mailmap|https://git-scm.com/docs/gitmailmap] в корне проекта, чтобы объединить статистику по сотрудникам. § page.welcome.description: Git создаст файл log.txt. Он содержит данные для построения отчёта. Или git shortlog -s -n -e если отчёт вам не нужен. Создайте файл [.mailmap|https://git-scm.com/docs/gitmailmap] в корне проекта, чтобы объединить статистику по сотрудникам.
§ page.welcome.warning1: Сервис *НЕ ХРАНИТ* и *НЕ ПЕРЕДАЁТ* ваши данные. Все расчёты выполняются локально в вашем браузере прямо на вашей машине. § page.welcome.warning1: Сервис *НЕ ХРАНИТ* и *НЕ ПЕРЕДАЁТ* ваши данные. Все расчёты выполняются локально в вашем браузере прямо на вашей машине.
§ page.welcome.warning2: Сервис *НЕ СОБИРАЕТ СТАТИСТИКУ* по проектам. Вы можете отключить интернет, проверить трафик и даже собрать локальный билд из [исходников|https://github.com/bakhirev/assayo]. § page.welcome.warning2: Сервис *НЕ СОБИРАЕТ СТАТИСТИКУ* по проектам. Вы можете отключить интернет, проверить трафик и даже собрать локальный билд из [исходников|https://github.com/bakhirev/assayo].