feat: some fixes

This commit is contained in:
ivannoskov
2026-02-02 11:17:50 +03:00
parent f46a3265c6
commit 74ef1108e1
10 changed files with 945 additions and 262 deletions

View File

@@ -5,7 +5,7 @@
// ============================================================================
import { useEffect, useState, useMemo, useCallback } from "react";
import { useParams } from "next/navigation";
import { useParams, useSearchParams } from "next/navigation";
import Link from "next/link";
import {
Loader2,
@@ -22,6 +22,7 @@ import {
FileSpreadsheet,
FileText,
Info,
X,
} from "lucide-react";
import { DashboardHeader } from "@/components/layout/dashboard-header";
import { useWorkspace } from "@/components/providers/workspace-provider";
@@ -254,6 +255,7 @@ const downloadBlob = (blob: Blob, filename: string) => {
export default function PlacementsPage() {
const params = useParams();
const searchParams = useSearchParams();
const workspaceId = params?.workspaceId as string;
const { selectedProjects, getProjectFilterId, allProjectsSelected, hasPermission } = useWorkspace();
const projectFilterId = getProjectFilterId();
@@ -264,6 +266,7 @@ export default function PlacementsPage() {
const [includeArchived, setIncludeArchived] = useState(false);
const [sortField, setSortField] = useState<SortField | null>(null);
const [sortOrder, setSortOrder] = useState<SortOrder>(null);
const [creativeFilterId, setCreativeFilterId] = useState<string | null>(searchParams.get("creative_id"));
// Aggregation settings
const [aggCost, setAggCost] = useState<AggFunc>("sum");
@@ -274,6 +277,11 @@ export default function PlacementsPage() {
const canWrite = hasPermission("placements_write");
useEffect(() => {
const creativeId = searchParams.get("creative_id");
setCreativeFilterId(creativeId);
}, [searchParams]);
useEffect(() => {
loadPlacements();
}, [workspaceId, projectFilterId, includeArchived]);
@@ -321,11 +329,15 @@ export default function PlacementsPage() {
// Filter and sort
const filteredPlacements = useMemo(() => {
return placements
.filter(
(p) =>
.filter((p) => {
const matchesSearch =
p.channel.title.toLowerCase().includes(search.toLowerCase()) ||
(p.creative_id && p.creative_id.toString().toLowerCase().includes(search.toLowerCase()))
)
(p.creative_id && p.creative_id.toString().toLowerCase().includes(search.toLowerCase()));
const matchesCreative = !creativeFilterId || p.creative_id === creativeFilterId;
return matchesSearch && matchesCreative;
})
.sort((a, b) => {
if (!sortField || !sortOrder) return 0;
@@ -460,11 +472,24 @@ export default function PlacementsPage() {
<div className="flex flex-1 flex-col gap-4 p-4 pt-0 mt-4">
<div className="flex items-center justify-between">
<div>
<div className="space-y-1">
<h1 className="text-2xl font-bold tracking-tight">Размещения</h1>
<p className="text-muted-foreground">
{filteredPlacements.length} размещений
</p>
<div className="flex items-center gap-2">
<p className="text-muted-foreground">
{filteredPlacements.length} размещений
</p>
{creativeFilterId && (
<Badge variant="secondary" className="gap-1">
Креатив
<Link
href={`/dashboard/${workspaceId}/placements`}
className="hover:text-destructive"
>
<X className="h-3 w-3" />
</Link>
</Badge>
)}
</div>
</div>
<div className="flex items-center gap-2">
{/* Export Button */}