import type { PlacementStatus, PlacementPostStatus, CostType, PlacementType, InviteLinkType } from "@/lib/types/api"; export interface ColumnConfig { id: string; label: string; group: string; editable: boolean; partialEdit?: boolean; width?: string; required?: boolean; } export const COLUMN_GROUPS = [ { id: "basic", label: "Основная информация" }, { id: "content", label: "Контент" }, { id: "finance", label: "Финансы" }, { id: "dates", label: "Даты и время" }, { id: "metrics", label: "Метрики" }, { id: "calculated", label: "Расчетные показатели" }, { id: "technical", label: "Технические детали" }, ] as const; export const ALL_COLUMNS: ColumnConfig[] = [ // Basic { id: "checkbox", label: "Чекбокс", group: "basic", editable: false, required: true }, { id: "number", label: "Номер", group: "basic", editable: false, required: true }, { id: "status_deal", label: "Статус сделки", group: "basic", editable: true, partialEdit: true }, { id: "status_post", label: "Статус поста", group: "basic", editable: true, partialEdit: true }, // Content { id: "creative", label: "Креатив", group: "content", editable: true }, { id: "invite_link", label: "Пригл. ссылка", group: "content", editable: false }, { id: "post_link", label: "Ссылка на пост", group: "content", editable: false }, // Finance { id: "cost", label: "Стоимость", group: "finance", editable: true, partialEdit: true }, { id: "cost_before", label: "Стоимость до торга", group: "finance", editable: true }, { id: "cost_format", label: "Формат оплаты", group: "finance", editable: true, partialEdit: true }, { id: "placement_type", label: "Тип закупа", group: "finance", editable: true }, { id: "discount", label: "% скидки", group: "finance", editable: false }, { id: "payment_date", label: "Дата оплаты", group: "finance", editable: true }, // Dates { id: "planned_date", label: "Плановая дата", group: "dates", editable: true }, { id: "actual_date", label: "Фактическая дата", group: "dates", editable: false }, { id: "format", label: "Плановый формат", group: "dates", editable: true }, { id: "time_top", label: "Время в топе", group: "dates", editable: false }, { id: "time_in_feed", label: "Время в ленте", group: "dates", editable: false }, { id: "link_created", label: "Дата ссылки", group: "dates", editable: false }, { id: "post_deleted", label: "Дата удаления", group: "dates", editable: false }, // Metrics { id: "subscriptions", label: "Всего подписок", group: "metrics", editable: false }, { id: "views", label: "Просмотры", group: "metrics", editable: false }, // Calculated { id: "cpf", label: "CPF", group: "calculated", editable: false }, { id: "cpm", label: "CPM", group: "calculated", editable: true, partialEdit: true }, { id: "conversion_24h", label: "Конверсия 24ч", group: "calculated", editable: false }, { id: "conversion_48h", label: "Конверсия 48ч", group: "calculated", editable: false }, { id: "conversion_total", label: "Конверсия общ.", group: "calculated", editable: false }, { id: "total_unsubs", label: "Всего отписок", group: "calculated", editable: false }, { id: "unsub_percent", label: "% отписок", group: "calculated", editable: false }, { id: "total_active", label: "Итого подписок", group: "calculated", editable: false }, // Technical { id: "invite_type", label: "Тип ссылки", group: "technical", editable: true }, { id: "comment", label: "Комментарий", group: "technical", editable: true }, ] as const; // Default visible columns export const DEFAULT_VISIBLE_COLUMNS = new Set([ "checkbox", "number", "status_deal", "status_post", "creative", "invite_link", "cost", "cost_before", "cost_format", "planned_date", "format", "subscriptions", "views", "cpf", "cpm", "comment", ]); // Status lists export const PLACEMENT_DEAL_STATUSES: PlacementStatus[] = [ "Без статуса", "Написать", "Ждём ответа", "Согласование условий", "Оплатить", "Оплачено", "Отмена", "Не подходит цена", "Неактуально", "Не отвечает", ]; export const PLACEMENT_POST_STATUSES: PlacementPostStatus[] = [ "Без статуса", "Отправить пост", "Согласование поста", "Ожидание отложки", "Запланирован", "Пост вышел", "Размещение отработало - пост удалён", "Размещение отработало - пост не удалён", "Проверить - пост удалён раньше срока", "Проверить - пост не вышел", "Размещение отработало", ]; export const COST_FORMATS: CostType[] = ["fixed", "cpm"]; export const PLACEMENT_TYPES: PlacementType[] = ["self_promo", "standard"]; export const INVITE_TYPES: InviteLinkType[] = ["public", "approval"]; // Manual post statuses (user can select before post is published) export const MANUAL_POST_STATUSES: Set = new Set([ "Без статуса", "Отправить пост", "Согласование поста", "Ожидание отложки", "Запланирован", ]); // Automatic post statuses (set by backend) export const AUTOMATIC_POST_STATUSES: Set = new Set([ "Пост вышел", "Размещение отработало - пост удалён", "Размещение отработало - пост не удалён", "Проверить - пост удалён раньше срока", "Проверить - пост не вышел", "Размещение отработало", ]); // Helper functions export const canEditPostStatus = (currentStatus: PlacementPostStatus): boolean => { return MANUAL_POST_STATUSES.has(currentStatus); }; export const isAutomaticPostStatus = (status: PlacementPostStatus): boolean => { return AUTOMATIC_POST_STATUSES.has(status); }; // Status icon and color helpers import { CircleHelp, FileText, Clock, DollarSign, CheckCircle2, XCircle, AlertTriangle, Send, Calendar, } from "lucide-react"; const StatusIconMap: Record = { "Без статуса": CircleHelp, "Написать": FileText, "Ждём ответа": Clock, "Согласование условий": FileText, "Оплатить": DollarSign, "Оплачено": CheckCircle2, "Отмена": XCircle, "Не подходит цена": AlertTriangle, "Неактуально": XCircle, "Не отвечает": AlertTriangle, }; const PostStatusIconMap: Record = { "Без статуса": CircleHelp, "Отправить пост": Send, "Согласование поста": FileText, "Ожидание отложки": Clock, "Запланирован": Calendar, "Пост вышел": CheckCircle2, "Размещение отработало - пост удалён": CheckCircle2, "Размещение отработало - пост не удалён": CheckCircle2, "Проверить - пост удалён раньше срока": AlertTriangle, "Проверить - пост не вышел": Clock, "Размещение отработало": CheckCircle2, }; const StatusColorMap: Record = { "Без статуса": "bg-gray-100 text-gray-700 border-gray-200 dark:bg-gray-900/30 dark:text-gray-400 dark:border-gray-800", "Написать": "bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-900/30 dark:text-blue-400 dark:border-blue-800", "Ждём ответа": "bg-amber-100 text-amber-700 border-amber-200 dark:bg-amber-900/30 dark:text-amber-400 dark:border-amber-800", "Согласование условий": "bg-purple-100 text-purple-700 border-purple-200 dark:bg-purple-900/30 dark:text-purple-400 dark:border-purple-800", "Оплатить": "bg-green-100 text-green-700 border-green-200 dark:bg-green-900/30 dark:text-green-400 dark:border-green-800", "Оплачено": "bg-emerald-100 text-emerald-700 border-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400 dark:border-emerald-800", "Отмена": "bg-red-100 text-red-700 border-red-200 dark:bg-red-900/30 dark:text-red-400 dark:border-red-800", "Не подходит цена": "bg-orange-100 text-orange-700 border-orange-200 dark:bg-orange-900/30 dark:text-orange-400 dark:border-orange-800", "Неактуально": "bg-gray-100 text-gray-700 border-gray-200 dark:bg-gray-900/30 dark:text-gray-400 dark:border-gray-800", "Не отвечает": "bg-red-100 text-red-700 border-red-200 dark:bg-red-900/30 dark:text-red-400 dark:border-red-800", }; const PostStatusColorMap: Record = { "Без статуса": "bg-gray-100 text-gray-700 border-gray-200 dark:bg-gray-900/30 dark:text-gray-400 dark:border-gray-800", "Отправить пост": "bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-900/30 dark:text-blue-400 dark:border-blue-800", "Согласование поста": "bg-purple-100 text-purple-700 border-purple-200 dark:bg-purple-900/30 dark:text-purple-400 dark:border-purple-800", "Ожидание отложки": "bg-amber-100 text-amber-700 border-amber-200 dark:bg-amber-900/30 dark:text-amber-400 dark:border-amber-800", "Запланирован": "bg-cyan-100 text-cyan-700 border-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-400 dark:border-cyan-800", "Пост вышел": "bg-emerald-100 text-emerald-700 border-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400 dark:border-emerald-800", "Размещение отработало - пост удалён": "bg-emerald-100 text-emerald-700 border-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400 dark:border-emerald-800", "Размещение отработало - пост не удалён": "bg-emerald-100 text-emerald-700 border-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400 dark:border-emerald-800", "Проверить - пост удалён раньше срока": "bg-orange-100 text-orange-700 border-orange-200 dark:bg-orange-900/30 dark:text-orange-400 dark:border-orange-800", "Проверить - пост не вышел": "bg-red-100 text-red-700 border-red-200 dark:bg-red-900/30 dark:text-red-400 dark:border-red-800", "Размещение отработало": "bg-emerald-100 text-emerald-700 border-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400 dark:border-emerald-800", }; export const getPlacementStatusIcon = (status: PlacementStatus): any => { return StatusIconMap[status] || CircleHelp; }; export const getPlacementStatusColor = (status: PlacementStatus): string => { return StatusColorMap[status] || StatusColorMap["Без статуса"]; }; export const getPlacementPostStatusIcon = (status: PlacementPostStatus): any => { return PostStatusIconMap[status] || CircleHelp; }; export const getPlacementPostStatusColor = (status: PlacementPostStatus): string => { return PostStatusColorMap[status] || PostStatusColorMap["Без статуса"]; };