Merge pull request #33 from SlimusMinus/fix-post_responce
fixed post create response
This commit is contained in:
@@ -54,6 +54,9 @@ export default function CreateUpdatePost({ onClose, onSuccess, initialData }) {
|
||||
// Единый хук для создания и обновления
|
||||
const { save, loading: saving, error: apiError } = usePostMutation();
|
||||
|
||||
// Состояние для модалки-результата (успех / блокировка контента)
|
||||
const [resultModal, setResultModal] = useState(null); // null | "success" | "blocked"
|
||||
|
||||
// Внутри компонента, рядом с другими хуками
|
||||
const initialCoords = isEdit && initialData.latitude && initialData.longitude
|
||||
? { latitude: initialData.latitude, longitude: initialData.longitude }
|
||||
@@ -124,14 +127,22 @@ export default function CreateUpdatePost({ onClose, onSuccess, initialData }) {
|
||||
|
||||
// save(id, data): id = null → POST (создание), id = string → PUT (обновление)
|
||||
const result = await save(isEdit ? initialData.id : null, data);
|
||||
if (result) {
|
||||
onSuccess(result);
|
||||
onClose();
|
||||
if (!result) {
|
||||
// Ошибка уже записана в apiError, либо null если неизвестная
|
||||
return;
|
||||
}
|
||||
if (result.blocked) {
|
||||
setResultModal("blocked");
|
||||
return;
|
||||
}
|
||||
if (result.data) {
|
||||
setResultModal("success");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
// Клик по фону (не по самой модалке) закрывает её
|
||||
<>
|
||||
{/* Клик по фону (не по самой модалке) закрывает её */}
|
||||
<div className="f-modal-bg" onClick={e => e.target === e.currentTarget && onClose()}>
|
||||
<div className="f-modal">
|
||||
|
||||
@@ -309,5 +320,45 @@ export default function CreateUpdatePost({ onClose, onSuccess, initialData }) {
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Модалка-результат после отправки ── */}
|
||||
{resultModal && (
|
||||
<div className="f-modal-bg" onClick={e => {
|
||||
if (e.target === e.currentTarget) {
|
||||
if (resultModal === "success") {
|
||||
onSuccess?.();
|
||||
}
|
||||
onClose();
|
||||
}
|
||||
}}>
|
||||
<div className="f-modal f-modal-result">
|
||||
<div className={`f-result-icon ${resultModal === "blocked" ? "blocked" : "success"}`}>
|
||||
{resultModal === "blocked" ? "✕" : "✓"}
|
||||
</div>
|
||||
<h3 className="f-result-title">
|
||||
{resultModal === "blocked"
|
||||
? t('post.blockedTitle')
|
||||
: t('post.successTitle')}
|
||||
</h3>
|
||||
<p className="f-result-text">
|
||||
{resultModal === "blocked"
|
||||
? t('post.blockedText')
|
||||
: t('post.successText')}
|
||||
</p>
|
||||
<button
|
||||
className={`f-result-btn ${resultModal === "blocked" ? "err" : "ok"}`}
|
||||
onClick={() => {
|
||||
if (resultModal === "success") {
|
||||
onSuccess?.();
|
||||
}
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t('post.resultClose')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import headphonesIcon from '../assets/headphones.png';
|
||||
import watchIcon from '../assets/watch.png';
|
||||
import glassesIcon from '../assets/glasses.png';
|
||||
|
||||
export const API_URL = process.env.REACT_APP_API_URL || "http://192.168.1.68:8080";
|
||||
export const API_URL = process.env.REACT_APP_API_URL || "http://localhost:8080";
|
||||
|
||||
export function getImageUrl(filename) {
|
||||
if (!filename) return null;
|
||||
|
||||
@@ -11,11 +11,10 @@ import { API_URL } from "../config/authConstants";
|
||||
*
|
||||
* Автоматически добавляет заголовок Authorization с JWT-токеном.
|
||||
*
|
||||
* @returns {{
|
||||
* save: (id: string|null, data: FormData) => Promise<object|null>,
|
||||
* loading: boolean,
|
||||
* error: string,
|
||||
* }}
|
||||
* save() возвращает:
|
||||
* { data } — успешный ответ сервера
|
||||
* { blocked: true } — контент заблокирован модерацией
|
||||
* null — серверная ошибка (свойство error содержит текст)
|
||||
*/
|
||||
export function usePostMutation() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -24,7 +23,7 @@ export function usePostMutation() {
|
||||
/**
|
||||
* @param {string|null} id — id объявления при обновлении, null при создании
|
||||
* @param {FormData} data — данные формы
|
||||
* @returns {object|null} — ответ сервера или null при ошибке
|
||||
* @returns {Promise<{data:object}|{blocked:boolean, reason:string}|null>}
|
||||
*/
|
||||
const save = async (id, data) => {
|
||||
setLoading(true);
|
||||
@@ -45,8 +44,18 @@ export function usePostMutation() {
|
||||
throw new Error("Требуется авторизация. Войдите в аккаунт.");
|
||||
}
|
||||
|
||||
if (!res.ok) throw new Error(`Ошибка сервера: ${res.status}`);
|
||||
return await res.json();
|
||||
if (!res.ok) {
|
||||
let body;
|
||||
try { body = await res.json(); } catch { body = null; }
|
||||
|
||||
if (body?.error === "CONTENT_BLOCKED") {
|
||||
return { blocked: true, reason: body.reason || "" };
|
||||
}
|
||||
|
||||
throw new Error(body?.message || `Ошибка сервера: ${res.status}`);
|
||||
}
|
||||
|
||||
return { data: await res.json() };
|
||||
} catch (e) {
|
||||
setError(e.message || "Неизвестная ошибка");
|
||||
return null;
|
||||
|
||||
@@ -252,3 +252,54 @@
|
||||
padding: 20px 16px 32px; /* 32px снизу — отступ от home indicator */
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Result Modal (success / blocked) ── */
|
||||
.f-modal-result {
|
||||
text-align: center;
|
||||
padding: 36px 28px 28px;
|
||||
max-width: 380px;
|
||||
}
|
||||
.f-result-icon {
|
||||
width: 56px; height: 56px;
|
||||
border-radius: 50%;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
margin: 0 auto 16px;
|
||||
font-size: 1.5rem; font-weight: 900;
|
||||
}
|
||||
.f-result-icon.success {
|
||||
background: rgba(var(--teal-rgb), .15);
|
||||
color: var(--teal);
|
||||
}
|
||||
.f-result-icon.blocked {
|
||||
background: rgba(255, 107, 107, .15);
|
||||
color: #ff6b6b;
|
||||
}
|
||||
.f-result-title {
|
||||
margin: 0 0 8px;
|
||||
font-size: 1.05rem; font-weight: 800;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.f-result-text {
|
||||
margin: 0 0 22px;
|
||||
font-size: .85rem; line-height: 1.5;
|
||||
color: var(--muted);
|
||||
}
|
||||
.f-result-btn {
|
||||
width: 100%; border: none; border-radius: 12px;
|
||||
padding: 13px; font-family: var(--font);
|
||||
font-size: .9rem; font-weight: 800; cursor: pointer;
|
||||
transition: opacity .15s, transform .15s;
|
||||
}
|
||||
.f-result-btn:hover { opacity: .88; transform: translateY(-1px); }
|
||||
.f-result-btn:active { transform: translateY(0); }
|
||||
.f-result-btn.ok {
|
||||
background: linear-gradient(90deg, var(--teal2), var(--teal), var(--teal2));
|
||||
background-size: 200% 100%;
|
||||
color: #082320;
|
||||
animation: shimmer 3s linear infinite;
|
||||
}
|
||||
.f-result-btn.err {
|
||||
background: rgba(255, 107, 107, .12);
|
||||
border: 1.5px solid rgba(255, 107, 107, .35);
|
||||
color: #ff6b6b;
|
||||
}
|
||||
Reference in New Issue
Block a user