VORKOUT-9 #6

Merged
vlad.dev merged 11 commits from VORKOUT-9 into master 2025-04-29 12:15:41 +05:00
10 changed files with 158 additions and 43 deletions
Showing only changes of commit 53729813ff - Show all commits

View File

@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>React App</title> <title>VORKOUT</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -1,6 +1,7 @@
import { Drawer } from 'antd'; import { Drawer } from 'antd';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Avatar, Typography } from 'antd'; import { Avatar, Typography } from 'antd';
import { useTranslation } from 'react-i18next';
interface ContentDrawerProps { interface ContentDrawerProps {
open: boolean; open: boolean;
@ -15,6 +16,7 @@ export default function ContentDrawer({
children, children,
type, type,
}: ContentDrawerProps) { }: ContentDrawerProps) {
const { t } = useTranslation();
const [width, setWidth] = useState<number | string>('30%'); const [width, setWidth] = useState<number | string>('30%');
const calculateWidths = () => { const calculateWidths = () => {
@ -123,7 +125,7 @@ export default function ContentDrawer({
fontSize: '20px', fontSize: '20px',
}} }}
> >
Новая учетная запись {t('newAccount')}
</div> </div>
<div <div
@ -133,6 +135,7 @@ export default function ContentDrawer({
height: '24px', height: '24px',
width: '24px', width: '24px',
}} }}
onClick={closeDrawer}
> >
<img <img
src="./icons/drawer/delete.svg" src="./icons/drawer/delete.svg"

View File

@ -10,6 +10,7 @@ import {
UploadProps, UploadProps,
} from 'antd'; } from 'antd';
import { useState } from 'react'; import { useState } from 'react';
import { useTranslation } from 'react-i18next';
const { Option } = Select; const { Option } = Select;
@ -24,6 +25,7 @@ const getBase64 = (file: FileType): Promise<string> =>
}); });
export default function UserCreate() { export default function UserCreate() {
const { t } = useTranslation();
const [previewOpen, setPreviewOpen] = useState(false); const [previewOpen, setPreviewOpen] = useState(false);
const [previewImage, setPreviewImage] = useState(''); const [previewImage, setPreviewImage] = useState('');
@ -64,7 +66,9 @@ export default function UserCreate() {
/> />
</div> </div>
<span style={{ fontSize: '14px', color: '#8c8c8c' }}>Выбрать фото</span> <span style={{ fontSize: '14px', color: '#8c8c8c' }}>
{t('selectPhoto')}
</span>
</div> </div>
); );
@ -135,54 +139,54 @@ export default function UserCreate() {
style={{ flex: 1, display: 'flex', flexDirection: 'column' }} style={{ flex: 1, display: 'flex', flexDirection: 'column' }}
> >
<Form.Item <Form.Item
label="Имя" label={t('name')}
name="name" name="name"
rules={[{ required: true, message: 'Введите имя' }]} rules={[{ required: true, message: t('nameMessage') }]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="Логин" label={t('login')}
name="login" name="login"
rules={[{ required: true, message: 'Введите логин' }]} rules={[{ required: true, message: t('loginMessage') }]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="Пароль" label={t('password')}
name="password" name="password"
rules={[{ required: true, message: 'Введите пароль' }]} rules={[{ required: true, message: t('passwordMessage') }]}
> >
<Input.Password /> <Input.Password />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="E-mail" label={t('email')}
name="email" name="email"
rules={[ rules={[
{ required: true, message: 'Введите имейл' }, { required: true, message: t('emailMessage') },
{ type: 'email', message: 'Некорректный имейл' }, { type: 'email', message: t('emailErrorMessage') },
]} ]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="Привязка" label={t('tenant')}
name="tenant" name="tenant"
rules={[{ required: true, message: 'Введите привязку' }]} rules={[{ required: true, message: t('tenantMessage') }]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="Роль" label={t('role')}
name="role" name="role"
rules={[{ required: true, message: 'Выберите роль' }]} rules={[{ required: true, message: t('roleMessage') }]}
> >
<Select placeholder="Выберите роль"> <Select placeholder={t('roleMessage')}>
<Option value="Директор магазина">Директор магазина</Option> <Option value="Директор магазина">Директор магазина</Option>
<Option value="Менеджер">Менеджер</Option> <Option value="Менеджер">Менеджер</Option>
<Option value="Кассир">Кассир</Option> <Option value="Кассир">Кассир</Option>
@ -190,11 +194,11 @@ export default function UserCreate() {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="Статус" label={t('status')}
name="status" name="status"
rules={[{ required: true, message: 'Выберите статус' }]} rules={[{ required: true, message: t('statusMessage') }]}
> >
<Select placeholder="Выберите статус"> <Select placeholder={t('statusMessage')}>
<Option value="ACTIVE">Активен</Option> <Option value="ACTIVE">Активен</Option>
<Option value="DISABLED">Неактивен</Option> <Option value="DISABLED">Неактивен</Option>
<Option value="BLOCKED">Заблокирован</Option> <Option value="BLOCKED">Заблокирован</Option>
@ -216,7 +220,7 @@ export default function UserCreate() {
alt="save" alt="save"
style={{ height: '18px', width: '18px' }} style={{ height: '18px', width: '18px' }}
/>{' '} />{' '}
Добавить аккаунт {t('addAccount')}
</Button> </Button>
</Form.Item> </Form.Item>
</Form> </Form>

View File

@ -1,8 +1,10 @@
import { Button, Form, Input, Select } from 'antd'; import { Button, Form, Input, Select } from 'antd';
import { useTranslation } from 'react-i18next';
const { Option } = Select; const { Option } = Select;
export default function UserEdit() { export default function UserEdit() {
const { t } = useTranslation();
return ( return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}> <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Form <Form
@ -21,54 +23,54 @@ export default function UserEdit() {
style={{ flex: 1, display: 'flex', flexDirection: 'column' }} style={{ flex: 1, display: 'flex', flexDirection: 'column' }}
> >
<Form.Item <Form.Item
label="Имя" label={t('name')}
name="name" name="name"
rules={[{ required: true, message: 'Введите имя' }]} rules={[{ required: true, message: t('nameMessage') }]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="Логин" label={t('login')}
name="login" name="login"
rules={[{ required: true, message: 'Введите логин' }]} rules={[{ required: true, message: t('loginMessage') }]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="Пароль (последний ввод)" label={t('password')}
name="password" name="password"
rules={[{ required: true, message: 'Введите пароль' }]} rules={[{ required: true, message: t('passwordMessage') }]}
> >
<Input.Password /> <Input.Password />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="E-mail" label={t('email')}
name="email" name="email"
rules={[ rules={[
{ required: true, message: 'Введите имейл' }, { required: true, message: t('emailMessage') },
{ type: 'email', message: 'Некорректный имейл' }, { type: 'email', message: t('emailErrorMessage') },
]} ]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="Привязка" label={t('tenant')}
name="tenant" name="tenant"
rules={[{ required: true, message: 'Введите привязку' }]} rules={[{ required: true, message: t('tenantMessage') }]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="Роль" label={t('role')}
name="role" name="role"
rules={[{ required: true, message: 'Выберите роль' }]} rules={[{ required: true, message: t('roleMessage') }]}
> >
<Select placeholder="Выберите роль"> <Select placeholder={t('roleMessage')}>
<Option value="Директор магазина">Директор магазина</Option> <Option value="Директор магазина">Директор магазина</Option>
<Option value="Менеджер">Менеджер</Option> <Option value="Менеджер">Менеджер</Option>
<Option value="Кассир">Кассир</Option> <Option value="Кассир">Кассир</Option>
@ -76,11 +78,11 @@ export default function UserEdit() {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="Статус" label={t('status')}
name="status" name="status"
rules={[{ required: true, message: 'Выберите статус' }]} rules={[{ required: true, message: t('statusMessage') }]}
> >
<Select placeholder="Выберите статус"> <Select placeholder={t('statusMessage')}>
<Option value="ACTIVE">Активен</Option> <Option value="ACTIVE">Активен</Option>
<Option value="DISABLED">Неактивен</Option> <Option value="DISABLED">Неактивен</Option>
<Option value="BLOCKED">Заблокирован</Option> <Option value="BLOCKED">Заблокирован</Option>
@ -102,7 +104,7 @@ export default function UserEdit() {
alt="save" alt="save"
style={{ height: '18px', width: '18px' }} style={{ height: '18px', width: '18px' }}
/>{' '} />{' '}
Сохранить изменения {t('save')}
</Button> </Button>
</Form.Item> </Form.Item>
</Form> </Form>

View File

@ -0,0 +1,24 @@
import './i18n';
import { ConfigProvider } from 'antd';
import { useTranslation } from 'react-i18next';
import { theme } from './customTheme';
import en from 'antd/locale/en_US';
import ru from 'antd/locale/ru_RU';
const antdLocales = {
en: en,
ru: ru,
};
export default function AppWrapper({ children }: any) {
const { i18n } = useTranslation();
const currentLang = i18n.language.split('-')[0] as 'en' | 'ru';
return (
<ConfigProvider locale={antdLocales[currentLang]} theme={theme}>
{children}
</ConfigProvider>
);
}

74
client/src/config/i18n.ts Normal file
View File

@ -0,0 +1,74 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'en',
supportedLngs: ['en', 'ru'],
interpolation: { escapeValue: false },
resources: {
en: {
translation: {
accounts: 'Accounts',
processDiagrams: 'Process diagrams',
runningProcesses: 'Running processes',
settings: 'Settings',
eventsList: 'Events list',
configuration: 'Configuration',
selectPhoto: 'Select photo',
name: 'Name',
login: 'Login',
password: 'Password',
email: 'Email',
tenant: 'Tenant',
role: 'Role',
status: 'Status',
nameMessage: 'Enter name',
loginMessage: 'Enter login',
passwordMessage: 'Enter password',
emailMessage: 'Enter email',
emailErrorMessage: 'Incorrect email',
tenantMessage: 'Enter tenant',
roleMessage: 'Choose role',
statusMessage: 'Choose status',
addAccount: 'Add account',
save: 'Save changes',
newAccount: 'New account',
},
},
ru: {
translation: {
accounts: 'Учетные записи',
processDiagrams: 'Схемы процессов',
runningProcesses: 'Запущенные процессы',
settings: 'Настройки',
eventsList: 'Справочкин событий',
configuration: 'Конфигурация',
selectPhoto: 'Выбрать фото',
name: 'Имя',
login: 'Логин',
password: 'Пароль',
email: 'Имейл',
tenant: 'Привязка',
role: 'Роль',
status: 'Статус',
nameMessage: 'Введите имя',
loginMessage: 'Введите логин',
passwordMessage: 'Введите пароль',
emailMessage: 'Введите имейл',
emailErrorMessage: 'Некорректный имейл',
tenantMessage: 'Введите привязку',
roleMessage: 'Выберите роль',
statusMessage: 'Выберите статус',
addAccount: 'Добавить аккаунт',
save: 'Сохранить изменения',
newAccount: 'Новая учетная запись',
},
},
},
});
export default i18n;

View File

@ -1,9 +1,11 @@
import { useTranslation } from 'react-i18next';
import Header from '../components/Header'; import Header from '../components/Header';
export default function ConfigurationPage() { export default function ConfigurationPage() {
const { t } = useTranslation();
return ( return (
<> <>
<Header title="Конфигурация" /> <Header title={t('configuration')} />
</> </>
); );
} }

View File

@ -1,9 +1,11 @@
import { useTranslation } from 'react-i18next';
import Header from '../components/Header'; import Header from '../components/Header';
export default function EventsListPage() { export default function EventsListPage() {
const { t } = useTranslation();
return ( return (
<> <>
<Header title="Справочник событий" /> <Header title={t('eventsList')} />
</> </>
); );
} }

View File

@ -1,9 +1,11 @@
import { useTranslation } from 'react-i18next';
import Header from '../components/Header'; import Header from '../components/Header';
export default function ProcessDiagramPage() { export default function ProcessDiagramPage() {
const { t } = useTranslation();
return ( return (
<> <>
<Header title="Схемы процессов" /> <Header title={t('processDiagrams')} />
</> </>
); );
} }

View File

@ -1,9 +1,11 @@
import { useTranslation } from 'react-i18next';
import Header from '../components/Header'; import Header from '../components/Header';
export default function RunningProcessesPage() { export default function RunningProcessesPage() {
const { t } = useTranslation();
return ( return (
<> <>
<Header title="Запущенные процессы" /> <Header title={t('runningProcesses')} />
</> </>
); );
} }