import { useCallback, useState, useRef } from "react"; import PinIcon from "./icons/PinIcon"; import CloseIcon from "./icons/CloseIcon"; import { EMPTY_FORM, CATEGORIES } from "../config/feedConstants"; import { useCreatePost } from "../hooks/useCreatePost"; import { useUpdatePost } from "../hooks/useUpdatePost"; import { API_URL } from "../config/constants"; const MAX_SIZE_MB = 5; const ACCEPT = "image/jpeg,image/png,image/webp"; function compressImage(file, maxW = 1200, quality = 0.82) { return new Promise((resolve, reject) => { const img = new Image(); const url = URL.createObjectURL(file); img.onload = () => { URL.revokeObjectURL(url); const scale = Math.min(1, maxW / img.width); const canvas = document.createElement("canvas"); canvas.width = Math.round(img.width * scale); canvas.height = Math.round(img.height * scale); canvas.getContext("2d").drawImage(img, 0, 0, canvas.width, canvas.height); canvas.toBlob( blob => blob ? resolve(new File([blob], file.name, { type: "image/jpeg" })) : reject(new Error("Blob error")), "image/jpeg", quality ); }; img.onerror = reject; img.src = url; }); } const formatPhone = (val) => { const digits = val.replace(/\D/g, ""); const local = digits.startsWith("7") ? digits.slice(1) : digits; let result = "+7"; if (local.length > 0) result += " (" + local.slice(0, 3); if (local.length >= 3) result += ") " + local.slice(3, 6); if (local.length >= 6) result += "-" + local.slice(6, 8); if (local.length >= 8) result += "-" + local.slice(8, 10); return result; }; export default function PostModal({ onClose, onSuccess, initialData }) { const isEdit = !!initialData; const [form, setForm] = useState(isEdit ? { type: initialData.type || "loss", title: initialData.title || "", description: initialData.description || "", category: initialData.category || "", date: initialData.date || "", phone: initialData.phone || "", } : EMPTY_FORM); const [errors, setErrors] = useState({}); const [imageFile, setImageFile] = useState(null); // при редактировании показываем существующую картинку const existingImage = isEdit && initialData.images?.[0] ? `${API_URL}${initialData.images[0]}` : null; const [preview, setPreview] = useState(null); const [imgRemoved, setImgRemoved] = useState(false); // пользователь удалил старую const [imgError, setImgError] = useState(""); const [compressing, setCompressing] = useState(false); const fileInputRef = useRef(null); const { createAd, loading: creating, error: createError } = useCreatePost(); const { updateAd, loading: updating, error: updateError } = useUpdatePost(); const loading = creating || updating || compressing; const apiError = createError || updateError; const set = useCallback((k, v) => { setForm(p => ({ ...p, [k]: v })); setErrors(p => ({ ...p, [k]: "" })); }, []); const handleFileChange = async (e) => { const file = e.target.files?.[0]; if (!file) return; setImgError(""); if (!file.type.startsWith("image/")) { setImgError("Только изображения (JPEG, PNG, WebP)"); return; } if (file.size > MAX_SIZE_MB * 1024 * 1024) { setImgError(`Файл слишком большой — максимум ${MAX_SIZE_MB} МБ`); return; } setCompressing(true); try { const compressed = await compressImage(file); setImageFile(compressed); setPreview(URL.createObjectURL(compressed)); setImgRemoved(false); } catch { setImgError("Не удалось обработать изображение"); } finally { setCompressing(false); } }; const removeImage = () => { setImageFile(null); if (preview) URL.revokeObjectURL(preview); setPreview(null); setImgRemoved(true); setImgError(""); if (fileInputRef.current) fileInputRef.current.value = ""; }; // что показывать в блоке превью const displayImage = preview || (!imgRemoved ? existingImage : null); const validate = () => { const e = {}; if (!form.title.trim()) e.title = "Введите название"; if (!form.description.trim()) e.description = "Введите описание"; if (!form.category) e.category = "Выберите категорию"; if (!form.phone.trim()) e.phone = "Введите номер телефона"; setErrors(e); return !Object.keys(e).length; }; const handleSubmit = async () => { if (!validate()) return; const data = new FormData(); data.append("type", form.type); data.append("title", form.title.trim()); data.append("description", form.description.trim()); data.append("category", form.category); data.append("phone", form.phone.trim()); if (form.date) data.append("date", form.date); if (imageFile) data.append("image", imageFile); const result = isEdit ? await updateAd(initialData.id, data) : await createAd(data); if (result) { onSuccess(result); onClose(); } }; return (
e.target === e.currentTarget && onClose()}>
{isEdit ? "Редактировать объявление" : "Новое объявление"}
{/* тип */}
{/* название */}
set("title", e.target.value)} /> {errors.title &&

{errors.title}

}
{/* описание */}