fixed code
This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
import ProfileAvatar from "./ProfileAvatar";
|
||||
import ProfileInfo from "./ProfileInfo";
|
||||
|
||||
export default function Profile({ data = {}, onEdit }) {
|
||||
return (
|
||||
<div className="f-profile" onClick={onEdit}>
|
||||
<ProfileAvatar avatar={data.avatar} name={`${data.firstName || ""} ${data.lastName || ""}`} />
|
||||
<ProfileInfo firstName={data.firstName} email={data.email} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import CloseIcon from "../icons/CloseIcon";
|
||||
import { MAX_SIZE_MB as MAX_PHOTO_SIZE_MB } from "../../hooks/useImageUpload";
|
||||
|
||||
// ─── Input field component ───────────────────────────────────────────────────
|
||||
function InputField({ label, error, ...props }) {
|
||||
return (
|
||||
<div className="f-m-group">
|
||||
<label className="f-m-label">{label}</label>
|
||||
<input
|
||||
className={`f-m-field${error ? " err" : ""}`}
|
||||
{...props}
|
||||
/>
|
||||
{error && <p className="f-err-msg">{error}</p>}
|
||||
</div>
|
||||
<div className="f-m-group">
|
||||
<label className="f-m-label">{label}</label>
|
||||
<input
|
||||
className={`f-m-field${error ? " err" : ""}`}
|
||||
{...props}
|
||||
/>
|
||||
{error && <p className="f-err-msg">{error}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,39 +26,38 @@ function ImageUpload({
|
||||
onRemove,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const MAX_SIZE_MB = 5;
|
||||
|
||||
return (
|
||||
<div className="f-m-group">
|
||||
<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" />
|
||||
<button
|
||||
className="f-img-remove"
|
||||
onClick={onRemove}
|
||||
type="button"
|
||||
aria-label={t('profile.deletePhoto')}
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<label className="f-img-upload-label" aria-disabled={compressing}>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept="image/jpeg,image/png,image/webp"
|
||||
onChange={onFileChange}
|
||||
className="f-img-input"
|
||||
disabled={compressing}
|
||||
/>
|
||||
{compressing ? <span>{t('profile.processing')}</span> : <span>{t('profile.addPhoto')}</span>}
|
||||
</label>
|
||||
)}
|
||||
{imgError && <p className="f-err-msg">{imgError}</p>}
|
||||
<p className="f-hint">{t('profile.photoHint', { maxSize: MAX_SIZE_MB })}</p>
|
||||
</div>
|
||||
<div className="f-m-group">
|
||||
<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" />
|
||||
<button
|
||||
className="f-img-remove"
|
||||
onClick={onRemove}
|
||||
type="button"
|
||||
aria-label={t('profile.deletePhoto')}
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<label className="f-img-upload-label">
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept="image/jpeg,image/png,image/webp"
|
||||
onChange={onFileChange}
|
||||
className="f-img-input"
|
||||
disabled={compressing}
|
||||
/>
|
||||
{compressing ? <span>{t('profile.processing')}</span> : <span>{t('profile.addPhoto')}</span>}
|
||||
</label>
|
||||
)}
|
||||
{imgError && <p className="f-err-msg">{imgError}</p>}
|
||||
<p className="f-hint">{t('profile.photoHint', { maxSize: MAX_PHOTO_SIZE_MB })}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -66,13 +65,13 @@ function ImageUpload({
|
||||
function LoginField({ form, errors, onChange }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<InputField
|
||||
label={t('profile.loginLabel')}
|
||||
placeholder="username"
|
||||
value={form.login}
|
||||
error={errors.login}
|
||||
onChange={(e) => onChange("login", e.target.value)}
|
||||
/>
|
||||
<InputField
|
||||
label={t('profile.loginLabel')}
|
||||
placeholder="username"
|
||||
value={form.login}
|
||||
error={errors.login}
|
||||
onChange={(e) => onChange("login", e.target.value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -80,22 +79,22 @@ function LoginField({ form, errors, onChange }) {
|
||||
function NameFieldsRow({ form, errors, onChange }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="f-m-row">
|
||||
<InputField
|
||||
label={t('profile.nameLabel')}
|
||||
placeholder={t('profile.namePlaceholder')}
|
||||
value={form.firstName}
|
||||
error={errors.firstName}
|
||||
onChange={(e) => onChange("firstName", e.target.value)}
|
||||
/>
|
||||
<InputField
|
||||
label={t('profile.surnameLabel')}
|
||||
placeholder={t('profile.surnamePlaceholder')}
|
||||
value={form.lastName}
|
||||
error={errors.lastName}
|
||||
onChange={(e) => onChange("lastName", e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="f-m-row">
|
||||
<InputField
|
||||
label={t('profile.nameLabel')}
|
||||
placeholder={t('profile.namePlaceholder')}
|
||||
value={form.firstName}
|
||||
error={errors.firstName}
|
||||
onChange={(e) => onChange("firstName", e.target.value)}
|
||||
/>
|
||||
<InputField
|
||||
label={t('profile.surnameLabel')}
|
||||
placeholder={t('profile.surnamePlaceholder')}
|
||||
value={form.lastName}
|
||||
error={errors.lastName}
|
||||
onChange={(e) => onChange("lastName", e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -103,24 +102,24 @@ function NameFieldsRow({ form, errors, onChange }) {
|
||||
function ContactFields({ form, errors, onChange, onPhoneChange }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<InputField
|
||||
label={t('profile.phoneLabel')}
|
||||
type="tel"
|
||||
placeholder="+7 (___) ___-__-__"
|
||||
value={form.phone}
|
||||
error={errors.phone}
|
||||
onChange={(e) => onPhoneChange(e.target.value)}
|
||||
/>
|
||||
<InputField
|
||||
label={t('profile.emailLabel')}
|
||||
type="email"
|
||||
placeholder="example@email.com"
|
||||
value={form.email}
|
||||
error={errors.email}
|
||||
onChange={(e) => onChange("email", e.target.value)}
|
||||
/>
|
||||
</>
|
||||
<>
|
||||
<InputField
|
||||
label={t('profile.phoneLabel')}
|
||||
type="tel"
|
||||
placeholder="+7 (___) ___-__-__"
|
||||
value={form.phone}
|
||||
error={errors.phone}
|
||||
onChange={(e) => onPhoneChange(e.target.value)}
|
||||
/>
|
||||
<InputField
|
||||
label={t('profile.emailLabel')}
|
||||
type="email"
|
||||
placeholder="example@email.com"
|
||||
value={form.email}
|
||||
error={errors.email}
|
||||
onChange={(e) => onChange("email", e.target.value)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -128,16 +127,17 @@ function ContactFields({ form, errors, onChange, onPhoneChange }) {
|
||||
function SubmitButton({ loading, onSubmit }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<button
|
||||
className="f-m-submit"
|
||||
onClick={onSubmit}
|
||||
disabled={loading}
|
||||
>
|
||||
<div className="f-m-submit-row">
|
||||
{loading && <div className="f-m-spinner" />}
|
||||
{t('profile.save')}
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
className="f-m-submit"
|
||||
type="submit"
|
||||
onClick={onSubmit}
|
||||
disabled={loading}
|
||||
>
|
||||
<div className="f-m-submit-row">
|
||||
{loading && <div className="f-m-spinner" />}
|
||||
{t('profile.save')}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -145,13 +145,13 @@ function SubmitButton({ loading, onSubmit }) {
|
||||
function ModalHeader({ onClose }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="f-modal-head">
|
||||
<span className="f-modal-title">{t('profile.editTitle')}</span>
|
||||
<button className="f-close-btn" onClick={onClose}>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
<div className="f-modal-head">
|
||||
<span className="f-modal-title">{t('profile.editTitle')}</span>
|
||||
<button className="f-close-btn" type="button" onClick={onClose}>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { InputField, ImageUpload, LoginField, NameFieldsRow, ContactFields, SubmitButton, ModalHeader };
|
||||
export { InputField, ImageUpload, LoginField, NameFieldsRow, ContactFields, SubmitButton, ModalHeader };
|
||||
@@ -1,14 +0,0 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ─── Profile info component ─────────────────────────────────────────────────
|
||||
export default function ProfileInfo({ firstName, email }) {
|
||||
const { t } = useTranslation();
|
||||
const displayName = firstName || t('profile.default');
|
||||
|
||||
return (
|
||||
<div className="f-profile-info">
|
||||
<div className="f-profile-name">{displayName}</div>
|
||||
{email && <div className="f-profile-email">{email}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -36,26 +36,21 @@ export default function ProfileModal({ onClose, initialData = {} }) {
|
||||
|
||||
const currentImage = images[0];
|
||||
const displayImage = currentImage?.preview ?? null;
|
||||
const imageFile = currentImage?.file ?? null;
|
||||
|
||||
// ─── Field change handler ───────────────────────────────────────────────
|
||||
const handleFieldChange = useCallback((key, value) => {
|
||||
setForm(prev => ({ ...prev, [key]: value }));
|
||||
setErrors(prev => ({ ...prev, [key]: "" }));
|
||||
}, []);
|
||||
|
||||
// ─── Remove image handler (wraps removeImage with the only image id) ─────
|
||||
const handleRemoveImage = useCallback(() => {
|
||||
if (currentImage?.id) removeImage(currentImage.id);
|
||||
}, [currentImage, removeImage]);
|
||||
|
||||
// ─── Phone field change with formatting ──────────────────────────────────
|
||||
const handlePhoneChange = useCallback((value) => {
|
||||
const formatted = formatPhone(value);
|
||||
handleFieldChange("phone", formatted);
|
||||
}, [handleFieldChange]);
|
||||
|
||||
// ─── Submit handler ─────────────────────────────────────────────────────
|
||||
const handleSubmit = async () => {
|
||||
const validationErrors = validateProfileForm(form);
|
||||
if (Object.keys(validationErrors).length) {
|
||||
@@ -77,57 +72,62 @@ export default function ProfileModal({ onClose, initialData = {} }) {
|
||||
const updatedUser = await updateProfile(user?.id, profileData);
|
||||
|
||||
if (updatedUser) {
|
||||
// Обновляем данные пользователя в контексте и localStorage
|
||||
updateUser(updatedUser);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="f-modal-bg" onClick={e => e.target === e.currentTarget && onClose()}>
|
||||
<div className="f-modal">
|
||||
<ModalHeader onClose={onClose} />
|
||||
|
||||
<ImageUpload
|
||||
displayImage={displayImage}
|
||||
imgError={imgError}
|
||||
compressing={compressing}
|
||||
fileInputRef={fileInputRef}
|
||||
onFileChange={handleFileChange}
|
||||
onRemove={handleRemoveImage}
|
||||
/>
|
||||
|
||||
<LoginField
|
||||
form={form}
|
||||
errors={errors}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
|
||||
<NameFieldsRow
|
||||
form={form}
|
||||
errors={errors}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
|
||||
<ContactFields
|
||||
form={form}
|
||||
errors={errors}
|
||||
onChange={handleFieldChange}
|
||||
onPhoneChange={handlePhoneChange}
|
||||
/>
|
||||
|
||||
{saveError && <p className="f-err-msg" style={{ marginBottom: 10 }}>{saveError}</p>}
|
||||
|
||||
<SubmitButton loading={compressing || saving} onSubmit={handleSubmit} />
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="f-modal-logout"
|
||||
onClick={() => { logout(); onClose(); }}
|
||||
<div
|
||||
className="f-modal-bg"
|
||||
onClick={e => e.target === e.currentTarget && !saving && onClose()}
|
||||
>
|
||||
<form
|
||||
className="f-modal"
|
||||
onSubmit={(e) => { e.preventDefault(); handleSubmit(); }}
|
||||
>
|
||||
{t('profile.logout')}
|
||||
</button>
|
||||
<ModalHeader onClose={onClose} />
|
||||
|
||||
<ImageUpload
|
||||
displayImage={displayImage}
|
||||
imgError={imgError}
|
||||
compressing={compressing}
|
||||
fileInputRef={fileInputRef}
|
||||
onFileChange={handleFileChange}
|
||||
onRemove={handleRemoveImage}
|
||||
/>
|
||||
|
||||
<LoginField
|
||||
form={form}
|
||||
errors={errors}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
|
||||
<NameFieldsRow
|
||||
form={form}
|
||||
errors={errors}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
|
||||
<ContactFields
|
||||
form={form}
|
||||
errors={errors}
|
||||
onChange={handleFieldChange}
|
||||
onPhoneChange={handlePhoneChange}
|
||||
/>
|
||||
|
||||
{saveError && <p className="f-err-msg" style={{ marginBottom: 10 }}>{saveError}</p>}
|
||||
|
||||
<SubmitButton loading={compressing || saving} onSubmit={handleSubmit} />
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="f-modal-logout"
|
||||
onClick={() => { logout(); onClose(); }}
|
||||
>
|
||||
{t('profile.logout')}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,2 @@
|
||||
export { default as Profile } from "./Profile";
|
||||
export { default as ProfileModal } from "./ProfileModal";
|
||||
export { default as ProfileAvatar } from "./ProfileAvatar";
|
||||
export { default as ProfileInfo } from "./ProfileInfo";
|
||||
export { validateProfileForm, initializeFormState } from "./profileValidation";
|
||||
|
||||
Reference in New Issue
Block a user