"use client"; import { useState } from "react"; import { Loader2, Filter, ArrowUpDown, X, Check, ArrowUp, ArrowDown } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Checkbox } from "@/components/ui/checkbox"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Separator } from "@/components/ui/separator"; import { cn } from "@/lib/utils"; import type { Placement, PlacementStatus } from "@/lib/types/api"; type FilterCondition = "contains" | "not_contains" | "equals"; interface FilterField { key: string; label: string; type: "text" | "number" | "date"; } interface SortField { key: string; label: string; } interface SortOption { field: string; direction: "asc" | "desc"; } interface FiltersModalProps { open: boolean; onOpenChange: (open: boolean) => void; onApply: (filters: PlacementFilters) => void; existingPlacements: Placement[]; initialFilters: PlacementFilters; } export interface PlacementFilters { channelFilters: { name: { value: string; condition: FilterCondition }; username: { value: string; condition: FilterCondition }; subscribersMin: number | null; subscribersMax: number | null; }; placementFilters: { statuses: PlacementStatus[]; costMin: number | null; costMax: number | null; dateFrom: string | null; dateTo: string | null; hasPlacement: boolean | null; }; sort: SortOption | null; } const CHANNEL_FILTER_FIELDS: FilterField[] = [ { key: "name", label: "Название канала", type: "text" }, { key: "username", label: "Username", type: "text" }, ]; const SORT_FIELDS: SortField[] = [ { key: "title", label: "Название канала" }, { key: "subscribers", label: "Подписчики" }, { key: "cost", label: "Стоимость" }, { key: "date", label: "Дата размещения" }, { key: "cpm", label: "CPM" }, { key: "cpf", label: "CPF" }, ]; const CONDITION_OPTIONS: { value: FilterCondition; label: string }[] = [ { value: "contains", label: "содержит" }, { value: "not_contains", label: "не содержит" }, { value: "equals", label: "равно" }, ]; const STATUS_OPTIONS: PlacementStatus[] = [ "Без статуса", "Написать", "Ждём ответа", "Согласование условий", "Оплатить", "Оплачено", "Отмена", "Не подходит цена", "Неактуально", "Не отвечает", ]; const CONDITION_LABELS: Record = { contains: "содержит", not_contains: "не содержит", equals: "равно", }; export function FiltersModal({ open, onOpenChange, onApply, existingPlacements, initialFilters, }: FiltersModalProps) { const [filters, setFilters] = useState(initialFilters); const [activeTab, setActiveTab] = useState<"channels" | "placements" | "sort">("channels"); const handleApply = () => { onApply(filters); onOpenChange(false); }; const handleReset = () => { const defaultFilters: PlacementFilters = { channelFilters: { name: { value: "", condition: "contains" }, username: { value: "", condition: "contains" }, subscribersMin: null, subscribersMax: null, }, placementFilters: { statuses: [], costMin: null, costMax: null, dateFrom: null, dateTo: null, hasPlacement: null, }, sort: null, }; setFilters(defaultFilters); }; const toggleStatus = (status: PlacementStatus) => { const currentStatuses = filters.placementFilters.statuses; const newStatuses = currentStatuses.includes(status) ? currentStatuses.filter((s) => s !== status) : [...currentStatuses, status]; setFilters({ ...filters, placementFilters: { ...filters.placementFilters, statuses: newStatuses }, }); }; const updateChannelFilter = ( key: "name" | "username", updates: { value?: string; condition?: FilterCondition } ) => { setFilters({ ...filters, channelFilters: { ...filters.channelFilters, [key]: { ...filters.channelFilters[key], ...updates }, }, }); }; const hasActiveFilters = filters.channelFilters.name.value || filters.channelFilters.username.value || filters.channelFilters.subscribersMin !== null || filters.channelFilters.subscribersMax !== null || filters.placementFilters.statuses.length > 0 || filters.placementFilters.costMin !== null || filters.placementFilters.costMax !== null || filters.placementFilters.dateFrom || filters.placementFilters.dateTo || filters.sort; return ( Фильтры и сортировка Настройте фильтры для каналов и размещений
{activeTab === "channels" && (
{CHANNEL_FILTER_FIELDS.map((field) => (
updateChannelFilter(field.key as "name" | "username", { value: e.target.value }) } />
))}
setFilters({ ...filters, channelFilters: { ...filters.channelFilters, subscribersMin: e.target.value ? parseInt(e.target.value) : null, }, }) } />
setFilters({ ...filters, channelFilters: { ...filters.channelFilters, subscribersMax: e.target.value ? parseInt(e.target.value) : null, }, }) } />
)} {activeTab === "placements" && (
{STATUS_OPTIONS.map((status) => ( ))}
setFilters({ ...filters, placementFilters: { ...filters.placementFilters, costMin: e.target.value ? parseFloat(e.target.value) : null, }, }) } />
setFilters({ ...filters, placementFilters: { ...filters.placementFilters, costMax: e.target.value ? parseFloat(e.target.value) : null, }, }) } />
setFilters({ ...filters, placementFilters: { ...filters.placementFilters, dateFrom: e.target.value || null, }, }) } />
setFilters({ ...filters, placementFilters: { ...filters.placementFilters, dateTo: e.target.value || null, }, }) } />
)} {activeTab === "sort" && (
{filters.sort && (
Текущая сортировка: {SORT_FIELDS.find((f) => f.key === filters.sort?.field)?.label || filters.sort?.field} {filters.sort.direction === "asc" ? "↑" : "↓"} ( {filters.sort.direction === "asc" ? "A-Z" : "Z-A"})
)}
)}
); }