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:
|
||||
cat = (await db.execute(select(Category).where(Category.slug == category))).scalar_one_or_none()
|
||||
if cat:
|
||||
base = base.where(Article.category_id == cat.id)
|
||||
# Поддержка нескольких колледжей: category="slug1,slug2,..."
|
||||
slugs = [s for s in (part.strip() for part in category.split(",")) if s]
|
||||
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:
|
||||
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 }) {
|
||||
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 (
|
||||
<div className="filter-bar">
|
||||
{/* Категории */}
|
||||
{/* Категории (можно выбрать несколько) */}
|
||||
<div className="filter-row">
|
||||
<button
|
||||
className={`chip ${!filters.category ? 'chip--active' : 'chip--ghost'}`}
|
||||
onClick={() => set('category')(null)}
|
||||
className={`chip ${selected.length === 0 ? 'chip--active' : 'chip--ghost'}`}
|
||||
onClick={() => set('category')([])}
|
||||
>Все</button>
|
||||
{categories.map(c => (
|
||||
<button
|
||||
key={c.id}
|
||||
className={`chip ${filters.category === c.slug ? 'chip--active' : 'chip--ghost'}`}
|
||||
onClick={() => set('category')(c.slug)}
|
||||
className={`chip ${selected.includes(c.slug) ? 'chip--active' : 'chip--ghost'}`}
|
||||
onClick={() => toggleCategory(c.slug)}
|
||||
>{c.name}</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function NewsFeed() {
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [loadingMore, setLoadingMore] = 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 busyRef = useRef(false)
|
||||
@@ -60,7 +60,7 @@ export default function NewsFeed() {
|
||||
|
||||
const buildParams = useCallback((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
|
||||
const df = presetToDateFrom(filters.datePreset)
|
||||
if (df) params.date_from = df
|
||||
|
||||
Reference in New Issue
Block a user