added rules on ReFound
This commit is contained in:
@@ -8,6 +8,7 @@ import CreateUpdatePost from "./CreateUpdatePost";
|
|||||||
import MenuIcon from "./icons/MenuIcon";
|
import MenuIcon from "./icons/MenuIcon";
|
||||||
import { ProfileModal } from "./profile";
|
import { ProfileModal } from "./profile";
|
||||||
import AuthModal from "./AuthModal";
|
import AuthModal from "./AuthModal";
|
||||||
|
import RulesModal from "./RulesModal";
|
||||||
import { useAuthContext } from "../context/AuthContext";
|
import { useAuthContext } from "../context/AuthContext";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,6 +33,8 @@ export default function MainFeed() {
|
|||||||
const [profileModalOpen, setProfileModalOpen] = useState(false);
|
const [profileModalOpen, setProfileModalOpen] = useState(false);
|
||||||
// Открыто ли модальное окно аутентификации (login / register)
|
// Открыто ли модальное окно аутентификации (login / register)
|
||||||
const [authModalOpen, setAuthModalOpen] = useState(false);
|
const [authModalOpen, setAuthModalOpen] = useState(false);
|
||||||
|
// Открыто ли модальное окно правил площадки
|
||||||
|
const [rulesModalOpen, setRulesModalOpen] = useState(false);
|
||||||
// Массив объявлений из API (основная лента)
|
// Массив объявлений из API (основная лента)
|
||||||
const [ads, setAds] = useState([]);
|
const [ads, setAds] = useState([]);
|
||||||
// Объявления текущего пользователя (загружаются отдельно по userId)
|
// Объявления текущего пользователя (загружаются отдельно по userId)
|
||||||
@@ -45,13 +48,26 @@ export default function MainFeed() {
|
|||||||
// Данные профиля — из контекста авторизации (или пустой объект для гостя)
|
// Данные профиля — из контекста авторизации (или пустой объект для гостя)
|
||||||
const profileData = useMemo(() => {
|
const profileData = useMemo(() => {
|
||||||
if (!user) return {};
|
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 {
|
return {
|
||||||
login: user.login || "",
|
login: user.login || "",
|
||||||
firstName: user.name || user.firstName || "",
|
firstName,
|
||||||
lastName: user.lastName || "",
|
lastName,
|
||||||
email: user.email || "",
|
email: user.email || "",
|
||||||
phone: user.phone || "",
|
phone: user.phone || "",
|
||||||
avatar: user.avatar || null,
|
avatar,
|
||||||
id: user.id,
|
id: user.id,
|
||||||
};
|
};
|
||||||
}, [user]);
|
}, [user]);
|
||||||
@@ -230,9 +246,8 @@ export default function MainFeed() {
|
|||||||
ad={ad}
|
ad={ad}
|
||||||
liked={liked.has(ad.id)}
|
liked={liked.has(ad.id)}
|
||||||
onLike={toggleLike}
|
onLike={toggleLike}
|
||||||
onEdit={handleEdit}
|
onEdit={activeNav === "Мои объявления" ? handleEdit : undefined}
|
||||||
// На вкладке «Мои объявления» все посты принадлежат пользователю
|
isOwner={activeNav === "Мои объявления"}
|
||||||
isOwner={activeNav === "Мои объявления" || (isAuthenticated && ad.userId === user?.id)}
|
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
@@ -253,6 +268,7 @@ export default function MainFeed() {
|
|||||||
isAuthenticated={isAuthenticated}
|
isAuthenticated={isAuthenticated}
|
||||||
onLogout={logout}
|
onLogout={logout}
|
||||||
onLoginClick={() => setAuthModalOpen(true)}
|
onLoginClick={() => setAuthModalOpen(true)}
|
||||||
|
onRulesClick={() => setRulesModalOpen(true)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="f-main">
|
<div className="f-main">
|
||||||
@@ -334,6 +350,11 @@ export default function MainFeed() {
|
|||||||
onClose={() => setAuthModalOpen(false)}
|
onClose={() => setAuthModalOpen(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Модальное окно правил площадки */}
|
||||||
|
{rulesModalOpen && (
|
||||||
|
<RulesModal onClose={() => setRulesModalOpen(false)} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export default function Sidebar({
|
|||||||
isAuthenticated,
|
isAuthenticated,
|
||||||
onLogout,
|
onLogout,
|
||||||
onLoginClick,
|
onLoginClick,
|
||||||
|
onRulesClick,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -48,11 +49,20 @@ export default function Sidebar({
|
|||||||
<div className="f-sb-bottom">
|
<div className="f-sb-bottom">
|
||||||
{NAV_BOTTOM.map(({ icon, label }) => {
|
{NAV_BOTTOM.map(({ icon, label }) => {
|
||||||
const Ic = NavIcons[icon];
|
const Ic = NavIcons[icon];
|
||||||
|
const handleBottomClick = () => {
|
||||||
|
if (label === "Правила площадки") {
|
||||||
|
onRulesClick?.();
|
||||||
|
onClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onNavClick(label);
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={label}
|
key={label}
|
||||||
className={`f-sb-item${activeNav === label ? " active" : ""}`}
|
className={`f-sb-item${activeNav === label ? " active" : ""}`}
|
||||||
onClick={() => { onNavClick(label); onClose(); }}
|
onClick={handleBottomClick}
|
||||||
>
|
>
|
||||||
<Ic /> {label}
|
<Ic /> {label}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,4 +36,13 @@ export const NavIcons = {
|
|||||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
|
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
|
rules: () => (
|
||||||
|
<svg width={20} height={20} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||||
|
<polyline points="14 2 14 8 20 8" />
|
||||||
|
<line x1="8" y1="13" x2="16" y2="13" />
|
||||||
|
<line x1="8" y1="17" x2="13" y2="17" />
|
||||||
|
<line x1="8" y1="9" x2="10" y2="9" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
};
|
};
|
||||||
@@ -52,11 +52,12 @@ export default function ProfileModal({ onClose, initialData = {} }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const profileData = {
|
const profileData = {
|
||||||
name: form.firstName.trim(),
|
firstName: form.firstName.trim(),
|
||||||
login: form.login.trim(),
|
lastName: form.lastName.trim(),
|
||||||
phone: form.phone.trim(),
|
login: form.login.trim(),
|
||||||
email: form.email.trim(),
|
phone: form.phone.trim(),
|
||||||
avatar: imageFile || existingImageUrl,
|
email: form.email.trim(),
|
||||||
|
avatar: imageFile || existingImageUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
const updatedUser = await updateProfile(user?.id, profileData);
|
const updatedUser = await updateProfile(user?.id, profileData);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const NAV_ITEMS = [
|
|||||||
|
|
||||||
export const NAV_BOTTOM = [
|
export const NAV_BOTTOM = [
|
||||||
{ icon: "user", label: "Профиль" },
|
{ icon: "user", label: "Профиль" },
|
||||||
{ icon: "settings", label: "Настройки" },
|
{ icon: "rules", label: "Правила площадки" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const CATEGORIES = [
|
export const CATEGORIES = [
|
||||||
|
|||||||
@@ -8,17 +8,18 @@
|
|||||||
.f-card:hover { border-color: rgba(45,224,200,.3); transform: translateY(-2px); }
|
.f-card:hover { border-color: rgba(45,224,200,.3); transform: translateY(-2px); }
|
||||||
|
|
||||||
.f-card-inner {
|
.f-card-inner {
|
||||||
display: flex; flex-direction: row; min-height: 160px;
|
display: flex; flex-direction: row; min-height: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Image Panel */
|
/* Image Panel */
|
||||||
.f-card-img {
|
.f-card-img {
|
||||||
width: 180px; min-width: 180px;
|
width: 180px; min-width: 180px;
|
||||||
|
align-self: stretch; /* растягивается на всю высоту карточки */
|
||||||
background: linear-gradient(135deg, #1a3a4a 0%, #0f2235 100%);
|
background: linear-gradient(135deg, #1a3a4a 0%, #0f2235 100%);
|
||||||
display: flex; align-items: center; justify-content: center;
|
display: flex; align-items: center; justify-content: center;
|
||||||
position: relative; overflow: hidden; flex-shrink: 0;
|
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; }
|
.f-card-emoji { font-size: 3rem; opacity: .22; }
|
||||||
|
|
||||||
/* Badges */
|
/* Badges */
|
||||||
@@ -103,7 +104,7 @@
|
|||||||
.f-card-img {
|
.f-card-img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: unset;
|
min-width: unset;
|
||||||
height: 160px;
|
height: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Убираем кнопку карты на мобилке — занимает много места */
|
/* Убираем кнопку карты на мобилке — занимает много места */
|
||||||
|
|||||||
@@ -77,8 +77,8 @@
|
|||||||
.f-img-input { display: none; }
|
.f-img-input { display: none; }
|
||||||
|
|
||||||
/* Image Preview */
|
/* Image Preview */
|
||||||
.f-img-preview-wrap { position: relative; display: inline-block; width: 100%; }
|
.f-img-preview-wrap { position: relative; display: block; width: 100%; }
|
||||||
.f-img-preview { width: 100%; max-height: 220px; object-fit: cover; border-radius: 12px; display: block; }
|
.f-img-preview { width: 100%; height: 200px; object-fit: cover; object-position: center; border-radius: 12px; display: block; }
|
||||||
.f-img-remove {
|
.f-img-remove {
|
||||||
position: absolute; top: 8px; right: 8px;
|
position: absolute; top: 8px; right: 8px;
|
||||||
background: rgba(0,0,0,0.55); border: none; border-radius: 50%;
|
background: rgba(0,0,0,0.55); border: none; border-radius: 50%;
|
||||||
|
|||||||
Reference in New Issue
Block a user