added english language
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import CloseIcon from "../icons/CloseIcon";
|
||||
|
||||
// ─── Input field component ───────────────────────────────────────────────────
|
||||
@@ -24,11 +25,12 @@ function ImageUpload({
|
||||
onFileChange,
|
||||
onRemove,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const MAX_SIZE_MB = 5;
|
||||
|
||||
return (
|
||||
<div className="f-m-group">
|
||||
<label className="f-m-label">Фото профиля</label>
|
||||
<label className="f-m-label">{t('profile.photoLabel')}</label>
|
||||
{displayImage ? (
|
||||
<div className="f-img-preview-wrap">
|
||||
<img src={displayImage} alt="profile" className="f-img-preview" />
|
||||
@@ -36,7 +38,7 @@ function ImageUpload({
|
||||
className="f-img-remove"
|
||||
onClick={onRemove}
|
||||
type="button"
|
||||
aria-label="Удалить фото"
|
||||
aria-label={t('profile.deletePhoto')}
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
@@ -51,20 +53,21 @@ function ImageUpload({
|
||||
className="f-img-input"
|
||||
disabled={compressing}
|
||||
/>
|
||||
{compressing ? <span>Обработка...</span> : <span>📷 Добавить фото</span>}
|
||||
{compressing ? <span>{t('profile.processing')}</span> : <span>{t('profile.addPhoto')}</span>}
|
||||
</label>
|
||||
)}
|
||||
{imgError && <p className="f-err-msg">{imgError}</p>}
|
||||
<p className="f-hint">JPEG / PNG / WebP · до {MAX_SIZE_MB} МБ</p>
|
||||
<p className="f-hint">{t('profile.photoHint', { maxSize: MAX_SIZE_MB })}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Login field ─────────────────────────────────────────────────────────────
|
||||
function LoginField({ form, errors, onChange }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<InputField
|
||||
label="Логин"
|
||||
label={t('profile.loginLabel')}
|
||||
placeholder="username"
|
||||
value={form.login}
|
||||
error={errors.login}
|
||||
@@ -75,18 +78,19 @@ function LoginField({ form, errors, onChange }) {
|
||||
|
||||
// ─── Name fields row ─────────────────────────────────────────────────────────
|
||||
function NameFieldsRow({ form, errors, onChange }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="f-m-row">
|
||||
<InputField
|
||||
label="Имя"
|
||||
placeholder="Иван"
|
||||
label={t('profile.nameLabel')}
|
||||
placeholder={t('profile.namePlaceholder')}
|
||||
value={form.firstName}
|
||||
error={errors.firstName}
|
||||
onChange={(e) => onChange("firstName", e.target.value)}
|
||||
/>
|
||||
<InputField
|
||||
label="Фамилия"
|
||||
placeholder="Иванов"
|
||||
label={t('profile.surnameLabel')}
|
||||
placeholder={t('profile.surnamePlaceholder')}
|
||||
value={form.lastName}
|
||||
error={errors.lastName}
|
||||
onChange={(e) => onChange("lastName", e.target.value)}
|
||||
@@ -97,10 +101,11 @@ function NameFieldsRow({ form, errors, onChange }) {
|
||||
|
||||
// ─── Contact fields ─────────────────────────────────────────────────────────
|
||||
function ContactFields({ form, errors, onChange, onPhoneChange }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<InputField
|
||||
label="Номер телефона"
|
||||
label={t('profile.phoneLabel')}
|
||||
type="tel"
|
||||
placeholder="+7 (___) ___-__-__"
|
||||
value={form.phone}
|
||||
@@ -108,7 +113,7 @@ function ContactFields({ form, errors, onChange, onPhoneChange }) {
|
||||
onChange={(e) => onPhoneChange(e.target.value)}
|
||||
/>
|
||||
<InputField
|
||||
label="Электронная почта"
|
||||
label={t('profile.emailLabel')}
|
||||
type="email"
|
||||
placeholder="example@email.com"
|
||||
value={form.email}
|
||||
@@ -121,6 +126,7 @@ function ContactFields({ form, errors, onChange, onPhoneChange }) {
|
||||
|
||||
// ─── Submit button ──────────────────────────────────────────────────────────
|
||||
function SubmitButton({ loading, onSubmit }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<button
|
||||
className="f-m-submit"
|
||||
@@ -129,7 +135,7 @@ function SubmitButton({ loading, onSubmit }) {
|
||||
>
|
||||
<div className="f-m-submit-row">
|
||||
{loading && <div className="f-m-spinner" />}
|
||||
Сохранить профиль
|
||||
{t('profile.save')}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
@@ -137,9 +143,10 @@ function SubmitButton({ loading, onSubmit }) {
|
||||
|
||||
// ─── Modal header ──────────────────────────────────────────────────────────
|
||||
function ModalHeader({ onClose }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="f-modal-head">
|
||||
<span className="f-modal-title">Редактирование профиля</span>
|
||||
<span className="f-modal-title">{t('profile.editTitle')}</span>
|
||||
<button className="f-close-btn" onClick={onClose}>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ─── Profile info component ─────────────────────────────────────────────────
|
||||
export default function ProfileInfo({ firstName, email }) {
|
||||
const displayName = firstName || "Профиль";
|
||||
const { t } = useTranslation();
|
||||
const displayName = firstName || t('profile.default');
|
||||
|
||||
return (
|
||||
<div className="f-profile-info">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { formatPhone } from "../../utils/formatPhone";
|
||||
import { useImageUpload } from "../../hooks/useImageUpload";
|
||||
import { useUpdateProfile } from "../../hooks/useUpdateProfile";
|
||||
@@ -14,6 +15,7 @@ import {
|
||||
} from "./ProfileFormFields";
|
||||
|
||||
export default function ProfileModal({ onClose, initialData = {} }) {
|
||||
const { t } = useTranslation();
|
||||
const { user, updateUser, logout } = useAuthContext();
|
||||
const { updateProfile, loading: saving, error: saveError } = useUpdateProfile();
|
||||
const [form, setForm] = useState(initializeFormState(initialData));
|
||||
@@ -111,7 +113,7 @@ export default function ProfileModal({ onClose, initialData = {} }) {
|
||||
className="f-modal-logout"
|
||||
onClick={() => { logout(); onClose(); }}
|
||||
>
|
||||
Выйти
|
||||
{t('profile.logout')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import i18n from '../../i18n';
|
||||
|
||||
// ─── Form validation logic ──────────────────────────────────────────────────
|
||||
export function validateProfileForm(form) {
|
||||
const errors = {};
|
||||
|
||||
if (!form.firstName.trim()) {
|
||||
errors.firstName = "Введите имя";
|
||||
errors.firstName = i18n.t('profile.validateNameRequired');
|
||||
}
|
||||
|
||||
if (!form.lastName.trim()) {
|
||||
errors.lastName = "Введите фамилию";
|
||||
errors.lastName = i18n.t('profile.validateSurnameRequired');
|
||||
}
|
||||
|
||||
if (form.email && !form.email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
|
||||
errors.email = "Некорректный email";
|
||||
errors.email = i18n.t('profile.validateEmailInvalid');
|
||||
}
|
||||
|
||||
if (form.phone && form.phone.replace(/\D/g, "").length < 11) {
|
||||
errors.phone = "Некорректный номер телефона";
|
||||
errors.phone = i18n.t('profile.validatePhoneInvalid');
|
||||
}
|
||||
|
||||
return errors;
|
||||
|
||||
Reference in New Issue
Block a user