feat: мультивыбор колледжей в фильтре публичной ленты
Параметр category в /news принимает несколько slug'ов через запятую (category_id IN ...), обратно совместим с одиночным slug. FilterBar: чипы колледжей стали переключателями (можно отметить несколько), filters.category теперь массив. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -62,9 +62,13 @@ async def list_news(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if category:
|
if category:
|
||||||
cat = (await db.execute(select(Category).where(Category.slug == category))).scalar_one_or_none()
|
# Поддержка нескольких колледжей: category="slug1,slug2,..."
|
||||||
if cat:
|
slugs = [s for s in (part.strip() for part in category.split(",")) if s]
|
||||||
base = base.where(Article.category_id == cat.id)
|
cat_ids = (
|
||||||
|
await db.execute(select(Category.id).where(Category.slug.in_(slugs)))
|
||||||
|
).scalars().all()
|
||||||
|
if cat_ids:
|
||||||
|
base = base.where(Article.category_id.in_(cat_ids))
|
||||||
else:
|
else:
|
||||||
return {"total": 0, "offset": offset, "limit": limit, "has_more": False, "items": []}
|
return {"total": 0, "offset": offset, "limit": limit, "has_more": False, "items": []}
|
||||||
|
|
||||||
|
|||||||
@@ -11,19 +11,28 @@ const DATE_PRESETS = [
|
|||||||
export default function FilterBar({ categories, tags, filters, onChange }) {
|
export default function FilterBar({ categories, tags, filters, onChange }) {
|
||||||
const set = key => val => onChange({ ...filters, [key]: val })
|
const set = key => val => onChange({ ...filters, [key]: val })
|
||||||
|
|
||||||
|
// Мультивыбор колледжей: filters.category — массив slug'ов
|
||||||
|
const selected = filters.category ?? []
|
||||||
|
const toggleCategory = slug => {
|
||||||
|
const next = selected.includes(slug)
|
||||||
|
? selected.filter(s => s !== slug)
|
||||||
|
: [...selected, slug]
|
||||||
|
set('category')(next)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="filter-bar">
|
<div className="filter-bar">
|
||||||
{/* Категории */}
|
{/* Категории (можно выбрать несколько) */}
|
||||||
<div className="filter-row">
|
<div className="filter-row">
|
||||||
<button
|
<button
|
||||||
className={`chip ${!filters.category ? 'chip--active' : 'chip--ghost'}`}
|
className={`chip ${selected.length === 0 ? 'chip--active' : 'chip--ghost'}`}
|
||||||
onClick={() => set('category')(null)}
|
onClick={() => set('category')([])}
|
||||||
>Все</button>
|
>Все</button>
|
||||||
{categories.map(c => (
|
{categories.map(c => (
|
||||||
<button
|
<button
|
||||||
key={c.id}
|
key={c.id}
|
||||||
className={`chip ${filters.category === c.slug ? 'chip--active' : 'chip--ghost'}`}
|
className={`chip ${selected.includes(c.slug) ? 'chip--active' : 'chip--ghost'}`}
|
||||||
onClick={() => set('category')(c.slug)}
|
onClick={() => toggleCategory(c.slug)}
|
||||||
>{c.name}</button>
|
>{c.name}</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default function NewsFeed() {
|
|||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [loadingMore, setLoadingMore] = useState(false)
|
const [loadingMore, setLoadingMore] = useState(false)
|
||||||
const [hasMore, setHasMore] = useState(false)
|
const [hasMore, setHasMore] = useState(false)
|
||||||
const [filters, setFilters] = useState({ category: null, tag: null, datePreset: '' })
|
const [filters, setFilters] = useState({ category: [], tag: null, datePreset: '' })
|
||||||
|
|
||||||
const offsetRef = useRef(0)
|
const offsetRef = useRef(0)
|
||||||
const busyRef = useRef(false)
|
const busyRef = useRef(false)
|
||||||
@@ -60,7 +60,7 @@ export default function NewsFeed() {
|
|||||||
|
|
||||||
const buildParams = useCallback((offset) => {
|
const buildParams = useCallback((offset) => {
|
||||||
const params = { limit: PAGE_SIZE, offset }
|
const params = { limit: PAGE_SIZE, offset }
|
||||||
if (filters.category) params.category = filters.category
|
if (filters.category?.length) params.category = filters.category.join(',')
|
||||||
if (filters.tag) params.tag = filters.tag
|
if (filters.tag) params.tag = filters.tag
|
||||||
const df = presetToDateFrom(filters.datePreset)
|
const df = presetToDateFrom(filters.datePreset)
|
||||||
if (df) params.date_from = df
|
if (df) params.date_from = df
|
||||||
|
|||||||
Reference in New Issue
Block a user