diff --git a/src/components/MainFeed.jsx b/src/components/MainFeed.jsx index 5ed0374..23f28c9 100644 --- a/src/components/MainFeed.jsx +++ b/src/components/MainFeed.jsx @@ -8,6 +8,7 @@ import CreateUpdatePost from "./CreateUpdatePost"; import MenuIcon from "./icons/MenuIcon"; import { ProfileModal } from "./profile"; import AuthModal from "./AuthModal"; +import RulesModal from "./RulesModal"; import { useAuthContext } from "../context/AuthContext"; /** @@ -32,6 +33,8 @@ export default function MainFeed() { const [profileModalOpen, setProfileModalOpen] = useState(false); // Открыто ли модальное окно аутентификации (login / register) const [authModalOpen, setAuthModalOpen] = useState(false); + // Открыто ли модальное окно правил площадки + const [rulesModalOpen, setRulesModalOpen] = useState(false); // Массив объявлений из API (основная лента) const [ads, setAds] = useState([]); // Объявления текущего пользователя (загружаются отдельно по userId) @@ -45,13 +48,26 @@ export default function MainFeed() { // Данные профиля — из контекста авторизации (или пустой объект для гостя) const profileData = useMemo(() => { if (!user) return {}; + + // Если бэк присылает имя одним полем "name" — разбиваем на firstName/lastName + // Если бэк присылает отдельные поля firstName/lastName — используем их напрямую + const nameParts = (user.name || "").trim().split(/\s+/); + const firstName = user.firstName || nameParts[0] || ""; + const lastName = user.lastName || nameParts.slice(1).join(" ") || ""; + + // Аватар может быть относительным путём (/uploads/avatars/...) — добавляем API_URL + const rawAvatar = user.avatar || null; + const avatar = rawAvatar && rawAvatar.startsWith("/") + ? `${API_URL}${rawAvatar}` + : rawAvatar; + return { login: user.login || "", - firstName: user.name || user.firstName || "", - lastName: user.lastName || "", + firstName, + lastName, email: user.email || "", phone: user.phone || "", - avatar: user.avatar || null, + avatar, id: user.id, }; }, [user]); @@ -230,9 +246,8 @@ export default function MainFeed() { ad={ad} liked={liked.has(ad.id)} onLike={toggleLike} - onEdit={handleEdit} - // На вкладке «Мои объявления» все посты принадлежат пользователю - isOwner={activeNav === "Мои объявления" || (isAuthenticated && ad.userId === user?.id)} + onEdit={activeNav === "Мои объявления" ? handleEdit : undefined} + isOwner={activeNav === "Мои объявления"} /> )); }; @@ -253,6 +268,7 @@ export default function MainFeed() { isAuthenticated={isAuthenticated} onLogout={logout} onLoginClick={() => setAuthModalOpen(true)} + onRulesClick={() => setRulesModalOpen(true)} />
@@ -334,6 +350,11 @@ export default function MainFeed() { onClose={() => setAuthModalOpen(false)} /> )} + + {/* Модальное окно правил площадки */} + {rulesModalOpen && ( + setRulesModalOpen(false)} /> + )}
); } diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 17184b7..3a313c2 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -13,6 +13,7 @@ export default function Sidebar({ isAuthenticated, onLogout, onLoginClick, + onRulesClick, }) { return ( <> @@ -48,11 +49,20 @@ export default function Sidebar({
{NAV_BOTTOM.map(({ icon, label }) => { const Ic = NavIcons[icon]; + const handleBottomClick = () => { + if (label === "Правила площадки") { + onRulesClick?.(); + onClose(); + return; + } + onNavClick(label); + onClose(); + }; return (
{ onNavClick(label); onClose(); }} + onClick={handleBottomClick} > {label}
diff --git a/src/components/icons/NavIcons.jsx b/src/components/icons/NavIcons.jsx index e16b378..69af650 100644 --- a/src/components/icons/NavIcons.jsx +++ b/src/components/icons/NavIcons.jsx @@ -36,4 +36,13 @@ export const NavIcons = { ), + rules: () => ( + + + + + + + + ), }; \ No newline at end of file diff --git a/src/components/profile/ProfileModal.jsx b/src/components/profile/ProfileModal.jsx index 1dd230f..1c1ed2c 100644 --- a/src/components/profile/ProfileModal.jsx +++ b/src/components/profile/ProfileModal.jsx @@ -52,11 +52,12 @@ export default function ProfileModal({ onClose, initialData = {} }) { } const profileData = { - name: form.firstName.trim(), - login: form.login.trim(), - phone: form.phone.trim(), - email: form.email.trim(), - avatar: imageFile || existingImageUrl, + firstName: form.firstName.trim(), + lastName: form.lastName.trim(), + login: form.login.trim(), + phone: form.phone.trim(), + email: form.email.trim(), + avatar: imageFile || existingImageUrl, }; const updatedUser = await updateProfile(user?.id, profileData); diff --git a/src/config/feedConstants.js b/src/config/feedConstants.js index e5a1f92..71371e0 100644 --- a/src/config/feedConstants.js +++ b/src/config/feedConstants.js @@ -9,7 +9,7 @@ export const NAV_ITEMS = [ export const NAV_BOTTOM = [ { icon: "user", label: "Профиль" }, - { icon: "settings", label: "Настройки" }, + { icon: "rules", label: "Правила площадки" }, ]; export const CATEGORIES = [ diff --git a/src/styles/card.css b/src/styles/card.css index d239848..1b1aa8b 100644 --- a/src/styles/card.css +++ b/src/styles/card.css @@ -8,17 +8,18 @@ .f-card:hover { border-color: rgba(45,224,200,.3); transform: translateY(-2px); } .f-card-inner { - display: flex; flex-direction: row; min-height: 160px; + display: flex; flex-direction: row; min-height: 180px; } /* Image Panel */ .f-card-img { width: 180px; min-width: 180px; + align-self: stretch; /* растягивается на всю высоту карточки */ background: linear-gradient(135deg, #1a3a4a 0%, #0f2235 100%); display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; flex-shrink: 0; } -.f-card-img img { width: 100%; height: 100%; object-fit: cover; display: block; } +.f-card-img img { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; position: absolute; inset: 0; } .f-card-emoji { font-size: 3rem; opacity: .22; } /* Badges */ @@ -103,7 +104,7 @@ .f-card-img { width: 100%; min-width: unset; - height: 160px; + height: 180px; } /* Убираем кнопку карты на мобилке — занимает много места */ diff --git a/src/styles/modal.css b/src/styles/modal.css index 989771f..ed3614b 100644 --- a/src/styles/modal.css +++ b/src/styles/modal.css @@ -77,8 +77,8 @@ .f-img-input { display: none; } /* Image Preview */ -.f-img-preview-wrap { position: relative; display: inline-block; width: 100%; } -.f-img-preview { width: 100%; max-height: 220px; object-fit: cover; border-radius: 12px; display: block; } +.f-img-preview-wrap { position: relative; display: block; width: 100%; } +.f-img-preview { width: 100%; height: 200px; object-fit: cover; object-position: center; border-radius: 12px; display: block; } .f-img-remove { position: absolute; top: 8px; right: 8px; background: rgba(0,0,0,0.55); border: none; border-radius: 50%;