From 3b6b97447b1a0c1d9277fd4668766737d6880481 Mon Sep 17 00:00:00 2001 From: ivannoskov Date: Wed, 7 Jan 2026 14:17:43 +0300 Subject: [PATCH] feat: global platform update --- app/(auth)/login/page.tsx | 17 +- app/dashboard/[workspaceId]/page.tsx | 649 ++++++++++++++++---- app/dashboard/onboarding/page.tsx | 2 +- app/favicon.ico | Bin 25931 -> 4286 bytes app/layout.tsx | 2 +- components/layout/app-sidebar.tsx | 887 +++++++++++++++++++++------ components/nav-main.tsx | 23 +- components/nav-user.tsx | 57 +- lib/api/analytics.ts | 11 + lib/demo/api.ts | 9 + lib/demo/demo-aware-api.ts | 11 + lib/demo/mock-data.ts | 123 ++++ lib/types/api.ts | 49 ++ public/file.svg | 1 - public/globe.svg | 1 - public/logo.webp | Bin 0 -> 2940 bytes public/logo_full_dark.webp | Bin 0 -> 9618 bytes public/logo_full_white.webp | Bin 0 -> 10304 bytes public/logo_full_white_2.png | Bin 0 -> 31219 bytes public/next.svg | 1 - public/vercel.svg | 1 - public/window.svg | 1 - 22 files changed, 1536 insertions(+), 309 deletions(-) delete mode 100644 public/file.svg delete mode 100644 public/globe.svg create mode 100644 public/logo.webp create mode 100644 public/logo_full_dark.webp create mode 100644 public/logo_full_white.webp create mode 100644 public/logo_full_white_2.png delete mode 100644 public/next.svg delete mode 100644 public/vercel.svg delete mode 100644 public/window.svg diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index d25e623..9aef4d1 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -17,6 +17,7 @@ import { import { Button } from "@/components/ui/button"; import { AlertCircle, LogIn, Loader2 } from "lucide-react"; import { Alert, AlertDescription } from "@/components/ui/alert"; +import Image from "next/image"; export default function LoginPage() { const { loginMock, error: authError } = useAuth(); @@ -43,15 +44,19 @@ export default function LoginPage() { const displayError = error || authError; return ( -
+
-
- - TG - +
+ Smartpost
- TGEX + Smartpost Система управления рекламными закупками в Telegram diff --git a/app/dashboard/[workspaceId]/page.tsx b/app/dashboard/[workspaceId]/page.tsx index fd8809e..feef1b6 100644 --- a/app/dashboard/[workspaceId]/page.tsx +++ b/app/dashboard/[workspaceId]/page.tsx @@ -1,16 +1,46 @@ "use client"; // ============================================================================ -// Dashboard Home Page +// Dashboard Overview (Главная) // ============================================================================ -import { useEffect, useState } from "react"; -import { Loader2, ShoppingCart, Users, TrendingUp, BarChart3 } from "lucide-react"; +import { useEffect, useMemo, useState } from "react"; +import { format, subDays, startOfDay, endOfDay } from "date-fns"; +import { ru } from "date-fns/locale"; +import { + ArrowDown, + ArrowUp, + Calendar as CalendarIcon, + BarChart3, + BarChartHorizontal, + Gauge, + Loader2, + Sparkles, + TrendingUp, + Users, +} from "lucide-react"; +import { + Bar, + BarChart, + CartesianGrid, + Cell, + ReferenceLine, + XAxis, + YAxis, +} from "recharts"; +import { DateRange } from "react-day-picker"; import { DashboardHeader } from "@/components/layout/dashboard-header"; import { useWorkspace } from "@/components/providers/workspace-provider"; import { demoAwareAnalyticsApi } from "@/lib/demo"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import type { SpendingAnalytics } from "@/lib/types/api"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Button } from "@/components/ui/button"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { Calendar } from "@/components/ui/calendar"; +import { cn } from "@/lib/utils"; +import type { OverviewAnalytics } from "@/lib/types/api"; // ============================================================================ // Helpers @@ -23,24 +53,94 @@ const formatNumber = (value: number | null | undefined, showSign = false): strin return formatted; }; -const formatCurrency = (value: number | null | undefined): string => { +const formatCurrency = ( + value: number | null | undefined, + digits = 0 +): string => { if (value === null || value === undefined) return "—"; return new Intl.NumberFormat("ru-RU", { style: "currency", currency: "RUB", - minimumFractionDigits: 0, - maximumFractionDigits: 0, + minimumFractionDigits: digits, + maximumFractionDigits: digits, }).format(value); }; +const formatDateShort = (value: string): string => { + const [year, month, day] = value.split("-"); + if (!year || !month || !day) return value; + return `${day}.${month}`; +}; + +const DeltaPill: React.FC<{ delta?: number }> = ({ delta }) => { + if (delta === undefined || delta === null) return null; + const isPositive = delta > 0; + const isZero = delta === 0; + const color = isZero + ? "bg-muted text-muted-foreground" + : isPositive + ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-200" + : "bg-rose-100 text-rose-700 dark:bg-rose-900/40 dark:text-rose-200"; + const Icon = isZero ? Sparkles : isPositive ? ArrowUp : ArrowDown; + const sign = delta > 0 ? "+" : ""; + return ( + + + {sign} + {delta.toFixed(1)}% + + ); +}; + // ============================================================================ // Component // ============================================================================ export default function DashboardPage() { - const { currentWorkspace, currentProject, isLoading: workspaceLoading, projects } = useWorkspace(); - const [spending, setSpending] = useState(null); + const { + currentWorkspace, + isLoading: workspaceLoading, + selectedProjects, + projects, + getProjectFilterId, + } = useWorkspace(); + + const [overview, setOverview] = useState(null); const [loading, setLoading] = useState(true); + const [cpfMode, setCpfMode] = useState<"top" | "worst">("top"); + const [projectPage, setProjectPage] = useState(1); + const pageSize = 5; + const [dateRange, setDateRange] = useState(() => ({ + from: subDays(new Date(), 29), + to: new Date(), + })); + + const dateFromIso = useMemo(() => { + return dateRange?.from + ? startOfDay(dateRange.from).toISOString() + : undefined; + }, [dateRange?.from]); + + const dateToIso = useMemo(() => { + return dateRange?.to ? endOfDay(dateRange.to).toISOString() : undefined; + }, [dateRange?.to]); + + const dateRangeLabel = useMemo(() => { + if (dateRange?.from && dateRange?.to) { + const sameYear = + dateRange.from.getFullYear() === dateRange.to.getFullYear(); + const sameMonth = + sameYear && dateRange.from.getMonth() === dateRange.to.getMonth(); + const fromFormat = sameMonth ? "d MMM" : "d MMM yyyy"; + return `${format(dateRange.from, fromFormat, { locale: ru })} — ${format(dateRange.to, "d MMM yyyy", { locale: ru })}`; + } + if (dateRange?.from) { + return format(dateRange.from, "d MMM yyyy", { locale: ru }); + } + return "За всё время"; + }, [dateRange]); + + const projectFilterId = getProjectFilterId(); useEffect(() => { if (!currentWorkspace) { @@ -51,19 +151,109 @@ export default function DashboardPage() { const loadData = async () => { try { setLoading(true); - const data = await demoAwareAnalyticsApi.spending(currentWorkspace.id, { - project_id: currentProject?.id, + const data = await demoAwareAnalyticsApi.overview(currentWorkspace.id, { + project_id: projectFilterId, + date_from: dateFromIso, + date_to: dateToIso, }); - setSpending(data); + setOverview(data); + setProjectPage(1); } catch (err) { - console.error("Failed to load spending:", err); + console.error("Failed to load overview:", err); } finally { setLoading(false); } }; loadData(); - }, [currentWorkspace?.id, currentProject?.id]); + }, [currentWorkspace?.id, projectFilterId, dateFromIso, dateToIso]); + + const stats = useMemo( + () => [ + { + title: "Сумма закупов", + value: overview?.total_cost.value, + delta: overview?.total_cost.delta_percent, + format: (v: number | null | undefined) => formatCurrency(v, 0), + icon: BarChartHorizontal, + }, + { + title: "Общий охват", + value: overview?.total_reach.value, + delta: overview?.total_reach.delta_percent, + format: (v: number | null | undefined) => formatNumber(v), + icon: Users, + }, + { + title: "Всего размещений", + value: overview?.placements_count.value, + delta: overview?.placements_count.delta_percent, + format: (v: number | null | undefined) => formatNumber(v), + icon: Gauge, + }, + { + title: "Кол-во подписок", + value: overview?.subscriptions_count.value, + delta: overview?.subscriptions_count.delta_percent, + format: (v: number | null | undefined) => formatNumber(v), + icon: TrendingUp, + }, + { + title: "Средний CPM", + value: overview?.avg_cpm.value, + delta: overview?.avg_cpm.delta_percent, + format: (v: number | null | undefined) => formatCurrency(v, 2), + icon: BarChart3, + }, + { + title: "Средний CPF", + value: overview?.avg_cpf.value, + delta: overview?.avg_cpf.delta_percent, + format: (v: number | null | undefined) => formatCurrency(v, 2), + icon: Sparkles, + }, + ], + [overview] + ); + + const dailyStats = overview?.daily_stats ?? []; + + const subscriptionsChartData = useMemo( + () => + dailyStats.map((item) => ({ + date: item.date, + dateLabel: formatDateShort(item.date), + value: item.subscriptions_delta, + })), + [dailyStats] + ); + + const costChartData = useMemo( + () => + dailyStats.map((item) => ({ + date: item.date, + dateLabel: formatDateShort(item.date), + value: item.cost, + })), + [dailyStats] + ); + + const cpfChartData = useMemo( + () => + dailyStats.map((item) => ({ + date: item.date, + dateLabel: formatDateShort(item.date), + value: item.cpf, + })), + [dailyStats] + ); + + const projectSpending = overview?.project_spending ?? []; + const totalProjectPages = Math.max(1, Math.ceil(projectSpending.length / pageSize)); + const paginatedProjects = projectSpending.slice( + (projectPage - 1) * pageSize, + projectPage * pageSize + ); if (workspaceLoading) { return ( @@ -92,134 +282,378 @@ export default function DashboardPage() { ); } - // Calculate totals and averages - const totalCost = spending?.total_cost ?? 0; - const totalSubscriptions = spending?.total_subscriptions ?? 0; - const totalViews = spending?.total_views ?? 0; - const avgCps = totalSubscriptions > 0 ? totalCost / totalSubscriptions : null; - const avgCpm = totalViews > 0 ? (totalCost / totalViews) * 1000 : null; - return ( <>
- {/* Stats Cards */} -
- - - Всего потрачено - +
+
+

Статистика за период

+

{dateRangeLabel}

+
+ + + + + + setDateRange(range)} + /> + + +
+ + {/* Строка 1 — компактные показатели */} +
+ {stats.map((item) => ( + + +
+ + {item.title} + + +
+ +
+ + {loading ? ( + + ) : ( +
{item.format(item.value)}
+ )} +
+
+ ))} +
+ + {/* Строка 2 — три диаграммы */} +
+ + + Динамика подписчиков по дням + {loading ? ( - +
+ +
) : ( -
{formatCurrency(totalCost)}
+ + + + + + + + {subscriptionsChartData.map((entry) => ( + = 0 ? "#22c55e" : "#ef4444"} + /> + ))} + + payload?.[0]?.payload?.date || ""} + formatter={(value: number) => [formatNumber(value, true), "Δ подписок"]} + /> + } + /> + + )}
- - - Подписчиков - + + + Траты по дням + {loading ? ( - +
+ +
) : ( -
{formatNumber(totalSubscriptions)}
+ + + + + formatCurrency(value, 0)} + /> + + payload?.[0]?.payload?.date || ""} + formatter={(value: number) => [formatCurrency(value, 0), "Затраты"]} + /> + } + /> + + )}
- - - Средний CPS - + + + Стоимость подписчика (CPF) + {loading ? ( - +
+ +
) : ( -
{formatCurrency(avgCps)}
- )} -
-
- - - - Средний CPM - - - - {loading ? ( - - ) : ( -
{formatCurrency(avgCpm)}
+ + + + + formatCurrency(value, 0)} + /> + + payload?.[0]?.payload?.date || ""} + formatter={(value: number) => [formatCurrency(value, 2), "CPF"]} + /> + } + /> + + )}
- {/* Quick Actions */} -
- - - Быстрый старт - Начните с основных действий - - - - → Создать размещение - - - → Создать креатив - - - → Посмотреть аналитику - - + {/* Строка 3 — таблицы */} +
+ + setCpfMode(value as "top" | "worst")}> + +
+ Топ / Антитоп по CPF + + + Топ 5 + + + Антитоп 5 + + +
+
+ + {loading ? ( +
+ +
+ ) : ( + <> + + {overview?.top_channels_by_cpf?.length ? ( + + + + Канал + CPF + + + + {overview.top_channels_by_cpf.slice(0, 5).map((row) => ( + + +
{row.title}
+ {row.username && ( + + @{row.username} + + )} +
+ + {formatCurrency(row.cpf, 2)} + +
+ ))} +
+
+ ) : ( +

Нет данных

+ )} +
+ + {overview?.worst_channels_by_cpf?.length ? ( + + + + Канал + CPF + + + + {overview.worst_channels_by_cpf.slice(0, 5).map((row) => ( + + +
{row.title}
+ {row.username && ( + + @{row.username} + + )} +
+ + {formatCurrency(row.cpf, 2)} + +
+ ))} +
+
+ ) : ( +

Нет данных

+ )} +
+ + )} +
+
- - - Проекты - - {projects.length > 0 - ? `${projects.length} активных проектов` - : "Нет активных проектов"} - + + + Сумма закупов по проектам +
+ + + {projectPage}/{totalProjectPages} + + +
- {projects.length > 0 ? ( -
- {projects.slice(0, 5).map((project) => ( -
- {project.title} - {project.username && ( - @{project.username} - )} -
- ))} + {loading ? ( +
+
+ ) : paginatedProjects.length === 0 ? ( +

Нет данных

) : ( -

- Добавьте бота в канал через Telegram для создания проекта -

+ + + + Проект + Сумма + + + + {paginatedProjects.map((row) => ( + + +
{row.project_title}
+ {row.project_username && ( + @{row.project_username} + )} +
+ + {formatCurrency(row.total_cost, 0)} + +
+ ))} +
+
)} @@ -228,4 +662,3 @@ export default function DashboardPage() { ); } - diff --git a/app/dashboard/onboarding/page.tsx b/app/dashboard/onboarding/page.tsx index 50420fc..fe33773 100644 --- a/app/dashboard/onboarding/page.tsx +++ b/app/dashboard/onboarding/page.tsx @@ -55,7 +55,7 @@ export default function OnboardingPage() {
- Добро пожаловать в TGEX! + Добро пожаловать в Smartpost! Создайте свой первый воркспейс для начала работы diff --git a/app/favicon.ico b/app/favicon.ico index 718d6fea4835ec2d246af9800eddb7ffb276240c..7822e83d34b7b9a80105bb94c456708dbc104f93 100644 GIT binary patch literal 4286 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 LrJugUA?W`A8`#=m diff --git a/app/layout.tsx b/app/layout.tsx index a2efcbb..5444193 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -14,7 +14,7 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "TGEX - Управление рекламными закупками", + title: "Smartpost - Управление рекламными закупками", description: "Система для владельцев каналов для управления рекламными закупками в Telegram", }; diff --git a/components/layout/app-sidebar.tsx b/components/layout/app-sidebar.tsx index 444563f..64a8644 100644 --- a/components/layout/app-sidebar.tsx +++ b/components/layout/app-sidebar.tsx @@ -6,27 +6,39 @@ import * as React from "react"; import { useParams } from "next/navigation"; +import Link from "next/link"; import { BarChart3, + BookmarkCheck, Building2, - Check, - ChevronsUpDown, + CalendarClock, + ClipboardCheck, ClipboardList, - ExternalLink, + Filter, Folder, - HelpCircle, + Gauge, + KanbanSquare, LayoutDashboard, + LineChart, + LifeBuoy, + ListChecks, Megaphone, MessageCircle, - Plus, + MonitorPlay, + NotebookPen, + PlayCircle, Settings, ShoppingCart, + Sparkles, Tv, + TrendingUp, BookOpen, + ChevronRight, + X, type LucideIcon, } from "lucide-react"; -import { NavMain } from "@/components/nav-main"; +import { type NavGuideState, NavMain } from "@/components/nav-main"; import { NavUser } from "@/components/nav-user"; import { Sidebar, @@ -36,17 +48,25 @@ import { SidebarMenu, SidebarMenuButton, SidebarMenuItem, + SidebarMenuSub, + SidebarMenuSubButton, + SidebarMenuSubItem, SidebarRail, SidebarSeparator, SidebarGroup, SidebarGroupContent, + SidebarGroupLabel, + useSidebar, } from "@/components/ui/sidebar"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { @@ -62,7 +82,9 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useAuth } from "@/components/auth/auth-provider"; import { useWorkspace } from "@/components/providers/workspace-provider"; -import { buildRoute, BOT_USERNAME } from "@/lib/utils/constants"; +import { buildRoute } from "@/lib/utils/constants"; +import Image from "next/image"; +import { DEMO_WORKSPACE_ID } from "@/lib/demo"; // ============================================================================ // Types @@ -80,35 +102,211 @@ interface NavItem { }[]; } -interface FooterLink { +interface TrainingStep { + id: string; title: string; - url: string; + description: string; + navItemTitle: string; + expandedNavItems?: string[]; + icon?: LucideIcon; + sections?: { + title: string; + description: string; + icon?: LucideIcon; + points?: string[]; + }[]; + points?: string[]; +} + +interface HelpItem { + title: string; + description: string; icon: LucideIcon; + href?: string; + external?: boolean; + action?: () => void; +} + +interface SupportLink { + title: string; + icon: LucideIcon; + href: string; external?: boolean; } -// ============================================================================ -// Footer Links Configuration -// ============================================================================ - -const footerLinks: FooterLink[] = [ +const TRAINING_STEPS: TrainingStep[] = [ { - title: "Поддержка", - url: `https://t.me/${BOT_USERNAME}`, - icon: MessageCircle, - external: true, + id: "dashboard", + title: "Главная панель", + description: + "Это стартовая точка: отсюда видно общее состояние закупок, статус проектов и свежие уведомления. Сюда возвращаемся, чтобы понять, что делать дальше и какие задачи горят.", + navItemTitle: "Главная", + icon: LayoutDashboard, + sections: [ + { + title: "Сводка показателей", + description: + "Карточки расхода, охвата и эффективности обновляются в режиме близи real-time. Это быстрый способ понять, всё ли идёт по плану.", + icon: Gauge, + points: [ + "Подсветка указывает, где есть отклонения от плана или резкие изменения.", + "Нажмите на карточку, чтобы перейти к связанному отчёту или проекту.", + ], + }, + { + title: "Что происходит сейчас", + description: + "Лента событий показывает новые заявки, комментарии и дедлайны. Так команда не пропускает задачи.", + icon: CalendarClock, + points: [ + "Фильтр по типу событий помогает отделить, например, комментарии от обновлений статусов.", + "Кнопка «Создать» рядом ведёт к быстрому созданию проектов и размещений.", + ], + }, + ], }, { - title: "Информационный канал", - url: "https://t.me/tgex_news", - icon: Megaphone, - external: true, + id: "projects", + title: "Проекты", + description: + "Управляйте рекламными активностями по каждому продукту или клиенту и контролируйте статусы задач.", + navItemTitle: "Проекты", + icon: KanbanSquare, + sections: [ + { + title: "Карточки проектов", + description: + "Каждый проект знает свой бюджет, статус и ответственных. Это рабочее пространство конкретной кампании.", + icon: NotebookPen, + points: [ + "Новые проекты создаются через кнопку «Добавить» — укажите клиента, цель и дедлайн.", + "Меняйте статусы (подготовка, в работе, завершён) по мере продвижения кампании.", + ], + }, + { + title: "Фильтры и поиск", + description: + "Фильтруйте проекты по менеджерам или тегам, чтобы быстро найти нужное направление.", + icon: Filter, + points: [ + "Сохраняйте выбранный фильтр как «Избранный», чтобы открывать его в один клик.", + ], + }, + ], }, { - title: "База знаний", - url: "https://docs.tgex.io", - icon: BookOpen, - external: true, + id: "channels", + title: "Каталог каналов", + description: + "Ищите площадки по темам и метрикам, собирайте избранные каналы и сравнивайте их перед закупкой.", + navItemTitle: "Каталог каналов", + icon: BookmarkCheck, + sections: [ + { + title: "Поиск и фильтры", + description: + "Фильтруйте каналы по тематике, охвату и цене. Результаты можно сортировать и группировать.", + icon: Filter, + points: [ + "Подборка фильтров сохраняется автоматически, чтобы вернуться к ней позже.", + ], + }, + { + title: "Работа с подборками", + description: + "Отмечайте интересные площадки, добавляйте их в планы закупов и сравнивайте показатели.", + icon: ListChecks, + points: [ + "Одним кликом отправляйте канал в план или проект, чтобы не собирать всё заново.", + ], + }, + ], + }, + { + id: "purchase-plans", + title: "Планы закупов", + description: + "Это черновики кампаний. Собирайте каналы, фиксируйте ставки и согласовывайте бюджет до запуска.", + navItemTitle: "Планы закупов", + icon: ListChecks, + sections: [ + { + title: "Конструктор кампаний", + description: + "Добавляйте каналы, задавайте даты и ставки. Система сама подсчитает бюджет и прогноз охвата.", + icon: NotebookPen, + points: [ + "Используйте заметки, чтобы фиксировать договорённости с площадками.", + "Приглашайте коллег к плану — они увидят прогресс в реальном времени.", + ], + }, + { + title: "Согласование", + description: + "Когда план готов, его можно перевести в статус «Утверждён» и автоматически развернуть в размещения.", + icon: ClipboardCheck, + points: [ + "История изменений сохранится и поможет восстановить контекст договорённостей.", + ], + }, + ], + }, + { + id: "placements", + title: "Размещения", + description: + "Здесь живут все закупки: от черновиков до опубликованных постов. Следите за статусом и результатами.", + navItemTitle: "Размещения", + icon: ClipboardCheck, + sections: [ + { + title: "Линия жизни закупки", + description: + "Каждое размещение проходит статусы: черновик, согласовано, оплачено, опубликовано. Становится понятно, где застряли процессы.", + icon: CalendarClock, + points: [ + "Обновляйте статусы и дедлайны — таблица подсветит просроченные задачи.", + ], + }, + { + title: "Фактические результаты", + description: + "После выхода поста вносите реальные просмотры, CTR и стоимость — аналитика подхватит данные автоматически.", + icon: Gauge, + points: [ + "Добавляйте скриншоты и ссылки для архива — к ним всегда можно вернуться.", + ], + }, + ], + }, + { + id: "analytics", + title: "Аналитика", + description: + "Сводная аналитика показывает, что работает и куда уходят бюджеты. Не нужно собирать отчёты вручную.", + navItemTitle: "Аналитика", + expandedNavItems: ["Аналитика"], + icon: TrendingUp, + sections: [ + { + title: "Срезы данных", + description: + "Выбирайте отчёт по проектам, каналам или креативам, чтобы увидеть вклад каждой части.", + icon: LineChart, + points: [ + "Сравнивайте периоды и экспортируйте графики для презентаций.", + ], + }, + { + title: "Фильтры и сегменты", + description: + "Фильтр по менеджеру, периоду или типу кампании помогает отвечать на конкретные вопросы бизнеса.", + icon: Filter, + points: [ + "Сохранённые сегменты экономят время: достаточно выбрать нужный набор критериев.", + ], + }, + ], }, ]; @@ -128,97 +326,92 @@ export function AppSidebar({ ...props }: React.ComponentProps) { isAdmin, createWorkspace, } = useWorkspace(); + const { state: sidebarState } = useSidebar(); + const isSidebarCollapsed = sidebarState === "collapsed"; + const expandedLogoWidth = 182; + const expandedLogoHeight = 31; + const expandedLogoDimensions = React.useMemo( + () => ({ + height: expandedLogoHeight, + width: expandedLogoWidth, + minWidth: expandedLogoWidth, + }), + [expandedLogoHeight, expandedLogoWidth] + ); const [showCreateDialog, setShowCreateDialog] = React.useState(false); const [newWorkspaceName, setNewWorkspaceName] = React.useState(""); const [isCreating, setIsCreating] = React.useState(false); + const handleWorkspaceSelect = React.useCallback( + (workspaceId: string) => { + if (currentWorkspace?.id === workspaceId) return; + const workspace = workspaces.find((w) => w.id === workspaceId); + if (workspace) { + setCurrentWorkspace(workspace); + } + }, + [currentWorkspace?.id, workspaces, setCurrentWorkspace] + ); // Build navigation items based on permissions const navItems: NavItem[] = React.useMemo(() => { if (!workspaceId) return []; - const items: NavItem[] = [ - { - title: "Главная", - url: buildRoute.dashboard(workspaceId), - icon: LayoutDashboard, - isActive: true, - }, - ]; + const items: NavItem[] = []; - // Projects - always visible (contains instructions how to add projects via bot) + // 1. Главная + items.push({ + title: "Главная", + url: buildRoute.dashboard(workspaceId), + icon: LayoutDashboard, + isActive: true, + }); + + // 2. Проекты (всегда доступны) items.push({ title: "Проекты", url: buildRoute.projects(workspaceId), icon: Tv, }); - // Channels catalog (available to all) - items.push({ - title: "Каталог каналов", - url: buildRoute.channels(workspaceId), - icon: Building2, - }); - - // Purchase Plans (requires placements_read or admin) - if (hasPermission("placements_read")) { - items.push({ - title: "Планы закупок", - url: buildRoute.purchasePlans(workspaceId), - icon: ClipboardList, - requiredPermission: "placements_read", - }); - } - - // Creatives (requires placements_read or admin) + // 3. Креативы (только с доступом к размещениям) if (hasPermission("placements_read")) { items.push({ title: "Креативы", url: buildRoute.creatives(workspaceId), icon: Folder, requiredPermission: "placements_read", - items: [ - { - title: "Все креативы", - url: buildRoute.creatives(workspaceId), - }, - ...(hasPermission("placements_write") - ? [ - { - title: "Создать", - url: buildRoute.creativeNew(workspaceId), - }, - ] - : []), - ], }); } - // Placements (requires placements_read or admin) + // 4. Каталог каналов (всегда доступен) + items.push({ + title: "Каталог каналов", + url: buildRoute.channels(workspaceId), + icon: Building2, + }); + + // 5. Планы закупов (правка названия) + if (hasPermission("placements_read")) { + items.push({ + title: "Планы закупов", + url: buildRoute.purchasePlans(workspaceId), + icon: ClipboardList, + requiredPermission: "placements_read", + }); + } + + // 6. Размещения if (hasPermission("placements_read")) { items.push({ title: "Размещения", url: buildRoute.placements(workspaceId), icon: ShoppingCart, requiredPermission: "placements_read", - items: [ - { - title: "Все размещения", - url: buildRoute.placements(workspaceId), - }, - ...(hasPermission("placements_write") - ? [ - { - title: "Создать", - url: buildRoute.placementNew(workspaceId), - }, - ] - : []), - ], }); } - // Analytics (requires analytics_read or admin) + // 7. Аналитика — только два подраздела if (hasPermission("analytics_read")) { items.push({ title: "Аналитика", @@ -227,48 +420,178 @@ export function AppSidebar({ ...props }: React.ComponentProps) { requiredPermission: "analytics_read", items: [ { - title: "Обзор", + title: "По проектам", url: buildRoute.analytics(workspaceId), }, { - title: "Расходы", - url: buildRoute.analyticsSpending(workspaceId), - }, - { - title: "Каналы", - url: buildRoute.analyticsChannels(workspaceId), - }, - { - title: "Креативы", + title: "По креативам", url: buildRoute.analyticsCreatives(workspaceId), }, ], }); } - // Settings (admin only) + // 8. Доступы (без подразделов, админ) if (isAdmin) { items.push({ - title: "Настройки", - url: buildRoute.settings(workspaceId), + title: "Доступы", + url: buildRoute.members(workspaceId), icon: Settings, requiredPermission: "admin_full", - items: [ - { - title: "Участники", - url: buildRoute.members(workspaceId), - }, - { - title: "Приглашения", - url: buildRoute.invites(workspaceId), - }, - ], }); } return items; }, [workspaceId, hasPermission, isAdmin]); + const availableTrainingSteps = React.useMemo( + () => + TRAINING_STEPS.filter((step) => + navItems.some((item) => item.title === step.navItemTitle) + ), + [navItems] + ); + const trainingStepsCount = availableTrainingSteps.length; + const [isTrainingOpen, setIsTrainingOpen] = React.useState(false); + const [trainingStepIndex, setTrainingStepIndex] = React.useState(0); + const safeTrainingStepIndex = + trainingStepsCount > 0 + ? Math.min(trainingStepIndex, trainingStepsCount - 1) + : 0; + const currentTrainingStep = + trainingStepsCount > 0 + ? availableTrainingSteps[safeTrainingStepIndex] + : undefined; + + React.useEffect(() => { + if (trainingStepsCount === 0) { + setIsTrainingOpen(false); + setTrainingStepIndex(0); + return; + } + + if (trainingStepIndex > trainingStepsCount - 1) { + setTrainingStepIndex(0); + } + }, [trainingStepIndex, trainingStepsCount]); + + const openTraining = React.useCallback(() => { + if (!trainingStepsCount) return; + setTrainingStepIndex(0); + setIsTrainingOpen(true); + }, [trainingStepsCount]); + + const closeTraining = React.useCallback(() => { + setIsTrainingOpen(false); + }, []); + + const goToNextTrainingStep = React.useCallback(() => { + setTrainingStepIndex((prev) => + Math.min(prev + 1, Math.max(trainingStepsCount - 1, 0)) + ); + }, [trainingStepsCount]); + + const goToPrevTrainingStep = React.useCallback(() => { + setTrainingStepIndex((prev) => Math.max(prev - 1, 0)); + }, []); + + const navGuideState = React.useMemo(() => { + if (!isTrainingOpen || !currentTrainingStep) return undefined; + return { + highlightedItem: currentTrainingStep.navItemTitle, + expandedItems: currentTrainingStep.expandedNavItems, + }; + }, [isTrainingOpen, currentTrainingStep]); + const isTrainingVisible = isTrainingOpen && Boolean(currentTrainingStep); + + const supportLinks = React.useMemo( + () => [ + { + title: "Наш канал", + href: "https://t.me/tgex_news", + icon: Megaphone, + external: true, + }, + { + title: "Поддержка", + href: "https://t.me/smartpost_help_bot", + icon: MessageCircle, + external: true, + }, + { + title: "База знаний", + href: "https://docs.smartpost.ru", + icon: BookOpen, + external: true, + }, + ], + [] + ); + + const extraHelpItems = React.useMemo( + () => [ + { + title: "Демо дашборд", + description: "Посмотрите пример рабочего пространства со всеми разделами.", + href: buildRoute.dashboard(DEMO_WORKSPACE_ID), + icon: Sparkles, + }, + { + title: "Открыть обучение", + description: "Запустите интерактивный тур по основным разделам.", + action: openTraining, + icon: MonitorPlay, + }, + ], + [openTraining] + ); + + const renderExtraHelpItem = React.useCallback((item: HelpItem) => { + const content = ( + <> + +

{item.title}

+ + ); + + if (item.action) { + return ( + + ); + } + + if (item.href) { + if (item.external) { + return ( + + {content} + + ); + } + + return ( + + {content} + + ); + } + + return ( +
{content}
+ ); + }, []); + const handleCreateWorkspace = async () => { if (!newWorkspaceName.trim()) return; @@ -289,98 +612,149 @@ export function AppSidebar({ ...props }: React.ComponentProps) { <> - {/* Workspace Switcher */} - - - + {isSidebarCollapsed ? ( +
+ Smartpost +
+ ) : ( +
-
- -
-
- - {currentWorkspace?.name || "TGEX"} - - - {workspaces.length} воркспейс{workspaces.length === 1 ? "" : workspaces.length < 5 ? "а" : "ов"} - -
- - - - - - Воркспейсы - - {workspaces.map((workspace) => ( - setCurrentWorkspace(workspace)} - className="gap-2 p-2" - > -
- -
- {workspace.name} - {currentWorkspace?.id === workspace.id && ( - - )} -
- ))} - - setShowCreateDialog(true)} - className="gap-2 p-2" - > -
- -
- - Создать воркспейс - -
-
- + Smartpost + Smartpost +
+ )} + {/*
+ Smartpost + + Управление закупками + +
*/} +
- + - {/* Footer Links */} - + - {footerLinks.map((link) => ( + {supportLinks.map((link) => ( - - - - {link.title} - {link.external && ( - - )} - + + {link.external ? ( + + + {link.title} + + ) : ( + + + {link.title} + + )} ))} + + {isSidebarCollapsed ? ( + + + + +
+ + Помощь +
+ +
+
+ + {extraHelpItems.map((item) => ( + + {renderExtraHelpItem(item)} + + ))} + +
+
+ ) : ( + + + + +
+ + Помощь +
+ +
+
+ + + {extraHelpItems.map((item) => ( + + + {renderExtraHelpItem(item)} + + + ))} + + +
+
+ )}
@@ -399,12 +773,31 @@ export function AppSidebar({ ...props }: React.ComponentProps) { ? `https://ui-avatars.com/api/?name=${user.username}` : `https://ui-avatars.com/api/?name=User`, }} + workspaceSwitcher={{ + workspaces, + currentWorkspaceId: currentWorkspace?.id, + onSelect: (workspace) => handleWorkspaceSelect(workspace.id), + onCreate: () => setShowCreateDialog(true), + }} /> )}
+ {isTrainingVisible && ( +
+ {workspaceSwitcher && ( + <> + + + Воркспейсы + + {workspaceSwitcher.workspaces.length > 0 ? ( + workspaceSwitcher.workspaces.map((workspace) => ( + workspaceSwitcher.onSelect(workspace)} + className="gap-2 p-2 text-sm" + > +
+ +
+ {workspace.name} + {workspaceSwitcher.currentWorkspaceId === workspace.id && ( + + )} +
+ )) + ) : ( +
+ Нет доступных воркспейсов +
+ )} + {workspaceSwitcher.onCreate && ( + <> + + +
+ +
+ + Создать воркспейс + +
+ + )} + + )} diff --git a/lib/api/analytics.ts b/lib/api/analytics.ts index d08301c..c2b5833 100644 --- a/lib/api/analytics.ts +++ b/lib/api/analytics.ts @@ -11,6 +11,8 @@ import type { AnalyticsQueryParams, SpendingAnalyticsQueryParams, PaginatedResponse, + OverviewAnalytics, + OverviewAnalyticsQueryParams, } from "@/lib/types/api"; export const analyticsApi = { @@ -49,4 +51,13 @@ export const analyticsApi = { `/workspaces/${workspaceId}/analytics/spending`, { params } ), + + /** + * Overview analytics for dashboard + */ + overview: (workspaceId: string, params?: OverviewAnalyticsQueryParams) => + api.get( + `/workspaces/${workspaceId}/analytics/overview`, + { params } + ), }; diff --git a/lib/demo/api.ts b/lib/demo/api.ts index 630c117..51efa35 100644 --- a/lib/demo/api.ts +++ b/lib/demo/api.ts @@ -14,6 +14,7 @@ import type { SpendingAnalytics, CreativeAnalyticsItem, ChannelAnalyticsItem, + OverviewAnalytics, } from "@/lib/types/api"; import { demoWorkspace, @@ -26,6 +27,7 @@ import { demoSpendingAnalytics, demoCreativeAnalytics, demoChannelAnalytics, + demoOverviewAnalytics, } from "./mock-data"; import { DEMO_USER } from "./constants"; @@ -191,6 +193,13 @@ export const demoApi = { } return Promise.resolve(paginate(items)); }, + overview: (_params?: { + project_id?: string; + date_from?: string; + date_to?: string; + }): Promise => { + return Promise.resolve(demoOverviewAnalytics); + }, }, }; diff --git a/lib/demo/demo-aware-api.ts b/lib/demo/demo-aware-api.ts index 3a79280..61d4283 100644 --- a/lib/demo/demo-aware-api.ts +++ b/lib/demo/demo-aware-api.ts @@ -15,6 +15,7 @@ import { demoProjects, demoWorkspace, demoMember, + demoOverviewAnalytics, } from "./mock-data"; import type { PaginatedResponse, @@ -29,6 +30,7 @@ import type { Workspace, WorkspaceMember, DateGrouping, + OverviewAnalytics, } from "@/lib/types/api"; import { workspacesApi, @@ -305,5 +307,14 @@ export const demoAwareAnalyticsApi = { } return analyticsApi.channels(workspaceId, params); }, + overview: async ( + workspaceId: string, + params?: { project_id?: string; date_from?: string; date_to?: string } + ): Promise => { + if (isDemoWorkspace(workspaceId)) { + return demoOverviewAnalytics; + } + return analyticsApi.overview(workspaceId, params); + }, }; diff --git a/lib/demo/mock-data.ts b/lib/demo/mock-data.ts index c961796..c99cc01 100644 --- a/lib/demo/mock-data.ts +++ b/lib/demo/mock-data.ts @@ -14,6 +14,7 @@ import type { SpendingAnalytics, CreativeAnalyticsItem, ChannelAnalyticsItem, + OverviewAnalytics, } from "@/lib/types/api"; import { DEMO_WORKSPACE_ID, DEMO_USER } from "./constants"; @@ -581,3 +582,125 @@ export const demoChannelAnalytics: ChannelAnalyticsItem[] = [ }, ]; +export const demoOverviewAnalytics: OverviewAnalytics = { + total_cost: { value: 523400.5, delta_percent: 12.4 }, + total_reach: { value: 380000, delta_percent: -8.1 }, + placements_count: { value: 74, delta_percent: 5.7 }, + subscriptions_count: { value: 14250, delta_percent: 18.2 }, + avg_cpm: { value: 1376.06, delta_percent: 2.9 }, + avg_cpf: { value: 36.74, delta_percent: -5.4 }, + daily_stats: [ + { date: "2024-06-01", cost: 18450, subscriptions: 420, subscriptions_delta: -15, cpf: 43.93 }, + { date: "2024-06-02", cost: 21700, subscriptions: 505, subscriptions_delta: 85, cpf: 42.97 }, + { date: "2024-06-03", cost: 15800, subscriptions: 380, subscriptions_delta: -125, cpf: 41.58 }, + { date: "2024-06-04", cost: 26500, subscriptions: 640, subscriptions_delta: 260, cpf: 41.41 }, + { date: "2024-06-05", cost: 30200, subscriptions: 715, subscriptions_delta: 75, cpf: 42.24 }, + { date: "2024-06-06", cost: 18800, subscriptions: 455, subscriptions_delta: -260, cpf: 41.32 }, + { date: "2024-06-07", cost: 22400, subscriptions: 520, subscriptions_delta: 65, cpf: 43.08 }, + ], + top_channels_by_cpf: [ + { + channel_id: "chan-0005-0000-0000-000000000005", + title: "Финансовая грамотность", + username: "finance_edu", + cpf: 18.7, + total_cost: 44200, + subscriptions: 2365, + }, + { + channel_id: "chan-0003-0000-0000-000000000003", + title: "Программисты", + username: "programmers_hub", + cpf: 21.8, + total_cost: 38900, + subscriptions: 1785, + }, + { + channel_id: "chan-0001-0000-0000-000000000001", + title: "Технологии Будущего", + username: "tech_future", + cpf: 23.4, + total_cost: 35500, + subscriptions: 1517, + }, + { + channel_id: "chan-0002-0000-0000-000000000002", + title: "Инвестиции Pro", + username: "invest_pro", + cpf: 24.6, + total_cost: 33100, + subscriptions: 1346, + }, + { + channel_id: "chan-0006-0000-0000-000000000006", + title: "Startup Daily", + username: "startup_daily", + cpf: 25.2, + total_cost: 28800, + subscriptions: 1143, + }, + ], + worst_channels_by_cpf: [ + { + channel_id: "chan-0010-0000-0000-000000000010", + title: "Crypto Buzz", + username: "cryptobuzz", + cpf: 84.5, + total_cost: 9900, + subscriptions: 117, + }, + { + channel_id: "chan-0009-0000-0000-000000000009", + title: "Ads Unlimited", + username: "ads_unlimited", + cpf: 72.3, + total_cost: 8200, + subscriptions: 113, + }, + { + channel_id: "chan-0008-0000-0000-000000000008", + title: "News Mix", + username: "newsmix", + cpf: 66.2, + total_cost: 11400, + subscriptions: 172, + }, + { + channel_id: "chan-0007-0000-0000-000000000007", + title: "Random Talks", + username: "randomtalks", + cpf: 61.9, + total_cost: 13400, + subscriptions: 216, + }, + { + channel_id: "chan-0004-0000-0000-000000000004", + title: "Маркетинг Pro", + username: "marketing_pro", + cpf: 58.4, + total_cost: 12700, + subscriptions: 217, + }, + ], + project_spending: [ + { + project_id: "proj-0001-0000-0000-000000000001", + project_title: "Крипто Новости", + project_username: "crypto_news_demo", + total_cost: 244000, + }, + { + project_id: "proj-0002-0000-0000-000000000002", + project_title: "IT Вакансии", + project_username: "it_jobs_demo", + total_cost: 182400, + }, + { + project_id: "proj-0003-0000-0000-000000000003", + project_title: "Стартапы и Бизнес", + project_username: "startups_demo", + total_cost: 96900, + }, + ], +}; + diff --git a/lib/types/api.ts b/lib/types/api.ts index f55b934..7c7afcd 100644 --- a/lib/types/api.ts +++ b/lib/types/api.ts @@ -354,6 +354,55 @@ export interface SpendingAnalyticsQueryParams { grouping?: DateGrouping; } +// Overview Analytics +export interface OverviewMetric { + value: number; + delta_percent: number; +} + +export interface OverviewDailyStat { + date: string; // YYYY-MM-DD + cost: number; + subscriptions: number; + subscriptions_delta: number; + cpf: number; +} + +export interface OverviewChannelCpf { + channel_id: string; + title: string; + username: string | null; + cpf: number; + total_cost: number; + subscriptions: number; +} + +export interface OverviewProjectSpending { + project_id: string; + project_title: string; + project_username: string | null; + total_cost: number; +} + +export interface OverviewAnalytics { + total_cost: OverviewMetric; + total_reach: OverviewMetric; + placements_count: OverviewMetric; + subscriptions_count: OverviewMetric; + avg_cpm: OverviewMetric; + avg_cpf: OverviewMetric; + daily_stats: OverviewDailyStat[]; + top_channels_by_cpf: OverviewChannelCpf[]; + worst_channels_by_cpf: OverviewChannelCpf[]; + project_spending: OverviewProjectSpending[]; +} + +export interface OverviewAnalyticsQueryParams { + project_id?: string; + date_from?: string; + date_to?: string; +} + // ---------------------------------------------------------------------------- // Common Types // ---------------------------------------------------------------------------- diff --git a/public/file.svg b/public/file.svg deleted file mode 100644 index 004145c..0000000 --- a/public/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/globe.svg b/public/globe.svg deleted file mode 100644 index 567f17b..0000000 --- a/public/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/logo.webp b/public/logo.webp new file mode 100644 index 0000000000000000000000000000000000000000..78d84283868f045aa1406360e19ef1266b138b8d GIT binary patch literal 2940 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 J7LflF{s;3gb2$J2 literal 0 HcmV?d00001 diff --git a/public/logo_full_white.webp b/public/logo_full_white.webp new file mode 100644 index 0000000000000000000000000000000000000000..79a159027285b6d01a838ba18406d03405caac77 GIT binary patch literal 10304 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 literal 0 HcmV?d00001 diff --git a/public/logo_full_white_2.png b/public/logo_full_white_2.png new file mode 100644 index 0000000000000000000000000000000000000000..65da075477b6440acd6e14b3c60620ef5b8391d2 GIT binary patch literal 31219 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 literal 0 HcmV?d00001 diff --git a/public/next.svg b/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index 7705396..0000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/window.svg b/public/window.svg deleted file mode 100644 index b2b2a44..0000000 --- a/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file