diff --git a/public/index.html b/public/index.html index e10f6b6..03c0301 100644 --- a/public/index.html +++ b/public/index.html @@ -42,7 +42,7 @@ - + diff --git a/src/ts/components/Console/index.tsx b/src/ts/components/Console/index.tsx index 8b587c0..16db12a 100644 --- a/src/ts/components/Console/index.tsx +++ b/src/ts/components/Console/index.tsx @@ -41,10 +41,10 @@ function Console({ className, textForCopy, children }: IConsoleProps) { className={`${style.console_copy}`} onClick={() => { copyInBuffer(textForCopy); - notificationsStore.show('Текст скопирован'); + notificationsStore.show(localization.get('uiKit.console.notification')); }} > - {localization.get('uiKit.console')} + {localization.get('uiKit.console.button')} ); diff --git a/src/ts/components/DataView/index.tsx b/src/ts/components/DataView/index.tsx index e0561b7..56db1ca 100644 --- a/src/ts/components/DataView/index.tsx +++ b/src/ts/components/DataView/index.tsx @@ -5,6 +5,7 @@ import ISort from 'ts/interfaces/Sort'; import Table from 'ts/components/Table'; import Cards from 'ts/components/Cards'; import { downloadCsv } from 'ts/helpers/File'; +import viewSettings from 'ts/store/ViewSettings'; import style from './index.module.scss'; import PageWrapper from '../Page/wrapper'; @@ -32,7 +33,10 @@ function DataView({ children, }: IDataViewProps): React.ReactElement | null { const urlParams = useParams(); - const [localType, setType] = useState(type || 'table'); + const defaultType = viewSettings.getItem(urlParams, 'table'); + console.log(defaultType); + const [localType, setType] = useState(type || defaultType); + console.log(localType); if (!rows || !rows.length) return null; @@ -64,7 +68,9 @@ function DataView({ src={icon} className={style.data_view_icon} onClick={() => { - setType(localType === 'table' ? 'cards' : 'table'); + const newType = localType === 'table' ? 'cards' : 'table'; + setType(newType); + viewSettings.setItem(urlParams, newType, 'table'); }} /> @@ -99,7 +105,7 @@ function DataView({ DataView.defaultProps = { rows: [], sort: [], - type: 'table', + type: undefined, columnCount: undefined, updateSort: () => { }, diff --git a/src/ts/components/Description/index.module.scss b/src/ts/components/Description/index.module.scss index 6c4c90f..cef19ee 100644 --- a/src/ts/components/Description/index.module.scss +++ b/src/ts/components/Description/index.module.scss @@ -31,6 +31,19 @@ margin-bottom: 4px; } + &_link { + text-decoration: underline; + color: var(--color-button); + + &:hover { + text-decoration: none; + } + + &:visited { + color: var(--color-button); + } + } + &_list { position: relative; padding: 0 0 0 22px; diff --git a/src/ts/components/Description/index.tsx b/src/ts/components/Description/index.tsx index 9d92f1d..4c9af97 100644 --- a/src/ts/components/Description/index.tsx +++ b/src/ts/components/Description/index.tsx @@ -6,9 +6,10 @@ import cssStyle from './index.module.scss'; interface ICommonProps { text: string; style: any; + className?: string; } -function getTextWithLink(text: string) { +function getTextWithLink(text: string, className?: string) { const parts = (text || '') .split(/(\[[^\]]+\])/gim) .map((value: string) => { @@ -18,7 +19,10 @@ function getTextWithLink(text: string) { + rel="noreferrer" + className={className || ''} + to={link} + > {title} ); @@ -26,43 +30,43 @@ function getTextWithLink(text: string) { return (<>{parts}) ; } -function getTextWithStyle(text: string) { +function getTextWithStyle(text: string, className?: string) { const parts = (text || '') .split('*') .map((value: string, index: number) => (index % 2 - ? ({getTextWithLink(value)}) - : ({getTextWithLink(value)}) + ? ({getTextWithLink(value, className)}) + : ({getTextWithLink(value, className)}) )); return (<>{parts}) ; } -function List({ text, style }: ICommonProps) { +function List({ text, style, className }: ICommonProps) { return (

- {getTextWithStyle(text)} + {getTextWithStyle(text, className)}

); } -function Title({ text, style }: ICommonProps) { +function Title({ text, style, className }: ICommonProps) { return (
- {getTextWithStyle(text)} + {getTextWithStyle(text, className)}
); } -function SimpleText({ text, style }: ICommonProps) { +function SimpleText({ text, style, className }: ICommonProps) { return (

{getTextWithStyle(text)}

@@ -72,9 +76,10 @@ function SimpleText({ text, style }: ICommonProps) { interface IDescriptionProps { text?: string | string[]; style?: any; + className?: string; } -function Description({ text, style }: IDescriptionProps) { +function Description({ text, style, className }: IDescriptionProps) { const paragraphs = !Array.isArray(text) ? (text || '').trim().split(/\n+/gm) : text; @@ -82,29 +87,35 @@ function Description({ text, style }: IDescriptionProps) { const items = paragraphs.map((paragraph) => { const prefix = paragraph.substring(0, 2); const mainText = paragraph.substring(2); + if (prefix === '- ') { return ( ); } + if (prefix === '# ') { return ( ); } + return ( <SimpleText key={mainText} text={paragraph} style={style} + className={className} /> ); }); diff --git a/src/ts/helpers/Title.ts b/src/ts/helpers/Title.ts index f083b8b..fb3a9ee 100644 --- a/src/ts/helpers/Title.ts +++ b/src/ts/helpers/Title.ts @@ -1,3 +1,5 @@ +import localization from './Localization'; + function getFormattedType(dataGrip: any): string { const popularType = dataGrip.extension.statistic?.[0] || {}; const extension = popularType?.extension || ''; @@ -45,7 +47,7 @@ function getFormattedType(dataGrip: any): string { export default function getTitle(dataGrip: any, commits: any) { if (!commits.length) { - return 'Git Statistics'; + return localization.get('common.title'); } const type = getFormattedType(dataGrip) || ''; diff --git a/src/ts/pages/Settings/components/Common.tsx b/src/ts/pages/Settings/components/Common.tsx index 192cb5f..d87bd41 100644 --- a/src/ts/pages/Settings/components/Common.tsx +++ b/src/ts/pages/Settings/components/Common.tsx @@ -19,10 +19,10 @@ const Common = observer((): React.ReactElement | null => { <InputString title="page.settings.document.name" value={title} - placeholder="Git Statistics" + placeholder={localization.get('common.title')} onChange={(value: string) => { setTitle(value); - document.title = value || 'Git Statistics'; + document.title = value || localization.get('common.title'); applicationHasCustom.title = true; }} /> diff --git a/src/ts/pages/Welcome/index.tsx b/src/ts/pages/Welcome/index.tsx index 7112105..b5df0c8 100644 --- a/src/ts/pages/Welcome/index.tsx +++ b/src/ts/pages/Welcome/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import { Link } from 'react-router-dom'; import Console from 'ts/components/Console'; +import Description from 'ts/components/Description'; import { getStringFromFileList, getStringsForParser, @@ -14,27 +14,14 @@ import style from './styles/index.module.scss'; function WarningInfo() { return ( <h4 className={style.welcome_warning}> - <p> - {'Сервис '} - <span className={style.welcome_warning_bold}>НЕ ХРАНИТ</span> - {' и '} - <span className={style.welcome_warning_bold}>НЕ ПЕРЕДАЁТ</span> - {' ваши данные. Все расчёты выполняются локально в вашем браузере прямо на вашей машине.'} - </p> - <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> + <Description + text={localization.get('page.welcome.warning1')} + className={style.welcome_warning_text} + /> + <Description + text={localization.get('page.welcome.warning2')} + className={style.welcome_warning_text} + /> </h4> ); } @@ -54,16 +41,10 @@ function Welcome() { className={style.welcome_console} textForCopy={command} /> - <p className={style.welcome_description}> - {localization.get('page.welcome.description1')} - <Link - className={`${style.welcome_link}`} - target="_blank" - to="https://git-scm.com/docs/gitmailmap"> - .mailmap - </Link> - {localization.get('page.welcome.description2')} - </p> + <Description + text={localization.get('page.welcome.description')} + className={`${style.welcome_description}`} + /> <h2 className={style.welcome_last_title}> {localization.get('page.welcome.step2') === 'page.welcome.step2' ? '' diff --git a/src/ts/pages/Welcome/styles/index.module.scss b/src/ts/pages/Welcome/styles/index.module.scss index 0988383..83f7f5a 100644 --- a/src/ts/pages/Welcome/styles/index.module.scss +++ b/src/ts/pages/Welcome/styles/index.module.scss @@ -23,34 +23,23 @@ } &_warning { - font-weight: 100; - font-size: var(--font-s); width: 100%; margin: 0 auto; padding: 6px; box-sizing: border-box; - - line-height: 1.5; - text-align: center; border-bottom: 3px solid red; background-color: #FCDADA; - &_bold { - font-weight: bold; - color: red; + &_text { + text-align: center; + color: #B50404; } &_link { - text-decoration: underline; color: var(--color-button); - - &:hover { - text-decoration: none; - } } } - &_link, &_description { font-size: var(--font-xs); @@ -66,12 +55,6 @@ color: #878FA1; } - &_link { - display: inline; - margin: 16px 4px 0 4px; - text-decoration: underline; - } - &_first_title, &_last_title { font-size: 42px; diff --git a/src/ts/store/ViewSettings.ts b/src/ts/store/ViewSettings.ts new file mode 100644 index 0000000..be02d7d --- /dev/null +++ b/src/ts/store/ViewSettings.ts @@ -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; diff --git a/src/ts/translations/en/common.ts b/src/ts/translations/en/common.ts index 2858ac5..3aed18e 100644 --- a/src/ts/translations/en/common.ts +++ b/src/ts/translations/en/common.ts @@ -1,5 +1,6 @@ export default ` -§ uiKit.console: Copy +§ uiKit.console.button: Copy +§ uiKit.console.notification: Text was copied § uiKit.dataLoader.page: Page § uiKit.dataLoader.size: Displayed § 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. 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.notifications.save: The changes have been saved § common.notifications.setting: The settings have been saved diff --git a/src/ts/translations/en/pages.ts b/src/ts/translations/en/pages.ts index 7a453e8..83e5c9a 100644 --- a/src/ts/translations/en/pages.ts +++ b/src/ts/translations/en/pages.ts @@ -1,11 +1,9 @@ export default ` § 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.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.description2: [Create a .mailmap file|https://git-scm.com/docs/gitmailmap] in the root of the project to consolidate employee statistics. -§ 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.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.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.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.description: the most popular word. Occurs $1 times. diff --git a/src/ts/translations/ru/common.ts b/src/ts/translations/ru/common.ts index 04eabdc..923847e 100644 --- a/src/ts/translations/ru/common.ts +++ b/src/ts/translations/ru/common.ts @@ -1,5 +1,6 @@ export default ` -§ uiKit.console: Копировать +§ uiKit.console.button: Копировать +§ uiKit.console.notification: Текст скопирован § uiKit.dataLoader.page: Страница § uiKit.dataLoader.size: Отображается по § uiKit.dataLoader.from: из @@ -22,6 +23,7 @@ export default ` Поэтому система не рассчитывает для него ряд показателей. Если это ошибка и данного сотрудника нужно рассчитать как обычного, перейдите в раздел «Настройки» и измените его тип. +§ common.title: Git статистика § common.filters: Фильтры § common.notifications.save: Изменения сохранены § common.notifications.setting: Настройки сохранены diff --git a/src/ts/translations/ru/pages.ts b/src/ts/translations/ru/pages.ts index 1f08674..f22f4ef 100644 --- a/src/ts/translations/ru/pages.ts +++ b/src/ts/translations/ru/pages.ts @@ -2,8 +2,6 @@ export default ` § page.welcome.step1: Выполните команду в корне вашего проекта § page.welcome.step3: Перетащите § 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.warning1: Сервис *НЕ ХРАНИТ* и *НЕ ПЕРЕДАЁТ* ваши данные. Все расчёты выполняются локально в вашем браузере прямо на вашей машине. § page.welcome.warning2: Сервис *НЕ СОБИРАЕТ СТАТИСТИКУ* по проектам. Вы можете отключить интернет, проверить трафик и даже собрать локальный билд из [исходников|https://github.com/bakhirev/assayo].