@inspirer-dev/crm-dashboard 1.0.89 → 1.0.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,11 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import React, { useState, useMemo, useCallback, useRef, useEffect, memo } from "react";
3
- import { Box, Flex, Field, TextInput, MultiSelect, MultiSelectOption, Button, DatePicker, Loader, Main } from "@strapi/design-system";
4
- import { Cross, CaretUp, CaretDown, Download, GridFour, Clock } from "@strapi/icons";
3
+ import { Box, Flex, Field, TextInput, MultiSelect, MultiSelectOption, Button, DatePicker, Loader, Typography, Badge, Modal, Checkbox, Textarea, Main } from "@strapi/design-system";
4
+ import { Cross, CaretUp, CaretDown, Download, GridFour, Clock, Message } from "@strapi/icons";
5
5
  import { useTheme } from "styled-components";
6
- import { QueryClient, QueryClientProvider, useQuery, keepPreviousData } from "@tanstack/react-query";
6
+ import { QueryClient, QueryClientProvider, useQuery, keepPreviousData, useQueryClient, useMutation } from "@tanstack/react-query";
7
7
  import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
8
+ import { useFetchClient } from "@strapi/strapi/admin";
8
9
  import { ResponsiveContainer, AreaChart, CartesianGrid, XAxis, YAxis, Tooltip, Area, PieChart, Pie, Cell } from "recharts";
9
10
  const queryClient = new QueryClient({
10
11
  defaultOptions: {
@@ -29,7 +30,10 @@ const crmKeys = {
29
30
  antiSpamLogsWithFilters: (filters, page) => [...crmKeys.antiSpamLogs(), "list", { filters, page }],
30
31
  campaigns: () => [...crmKeys.all, "campaigns"],
31
32
  segments: () => [...crmKeys.all, "segments"],
32
- templates: () => [...crmKeys.all, "templates"]
33
+ templates: () => [...crmKeys.all, "templates"],
34
+ manualPushTemplates: () => [...crmKeys.all, "manual-pushes", "templates"],
35
+ manualPushHistory: () => [...crmKeys.all, "manual-pushes", "history"],
36
+ manualPushStats: (manualPushId) => [...crmKeys.all, "manual-pushes", "stats", manualPushId]
33
37
  };
34
38
  const QueryProvider = ({ children }) => {
35
39
  return /* @__PURE__ */ jsxs(QueryClientProvider, { client: queryClient, children: [
@@ -271,6 +275,72 @@ const useABTestData = (filters) => {
271
275
  staleTime: 2 * 60 * 1e3
272
276
  });
273
277
  };
278
+ const PLUGIN_BASE = "/crm-dashboard";
279
+ const extractBackendError = (data) => {
280
+ if (!data) return "Unknown error";
281
+ if (data.backend?.message) return data.backend.message;
282
+ if (typeof data.backend === "string") return data.backend;
283
+ if (data.error) return data.error;
284
+ return JSON.stringify(data);
285
+ };
286
+ const useManualPushTemplates = () => {
287
+ const { get } = useFetchClient();
288
+ return useQuery({
289
+ queryKey: crmKeys.manualPushTemplates(),
290
+ queryFn: async () => {
291
+ const { data } = await get(`${PLUGIN_BASE}/manual-pushes/templates`);
292
+ if (data?.error) throw new Error(data.error);
293
+ return data?.data ?? [];
294
+ },
295
+ staleTime: 60 * 1e3
296
+ });
297
+ };
298
+ const useManualPushHistory = () => {
299
+ const { get } = useFetchClient();
300
+ return useQuery({
301
+ queryKey: crmKeys.manualPushHistory(),
302
+ queryFn: async () => {
303
+ const { data } = await get(`${PLUGIN_BASE}/manual-pushes/history`, {
304
+ params: { limit: 50 }
305
+ });
306
+ if (Array.isArray(data)) return data;
307
+ if (data?.data && Array.isArray(data.data)) return data.data;
308
+ return [];
309
+ },
310
+ staleTime: 15 * 1e3,
311
+ refetchInterval: 10 * 1e3
312
+ });
313
+ };
314
+ const useDispatchManualPush = () => {
315
+ const { post } = useFetchClient();
316
+ const queryClient2 = useQueryClient();
317
+ return useMutation({
318
+ mutationFn: async (payload) => {
319
+ const { data } = await post(`${PLUGIN_BASE}/manual-pushes/dispatch`, payload);
320
+ if (data?.error) throw new Error(extractBackendError(data));
321
+ return data;
322
+ },
323
+ onSuccess: (_data, variables) => {
324
+ if (!variables.dryRun) {
325
+ queryClient2.invalidateQueries({ queryKey: crmKeys.manualPushHistory() });
326
+ }
327
+ }
328
+ });
329
+ };
330
+ const useDispatchTestManualPush = () => {
331
+ const { post } = useFetchClient();
332
+ const queryClient2 = useQueryClient();
333
+ return useMutation({
334
+ mutationFn: async (payload) => {
335
+ const { data } = await post(`${PLUGIN_BASE}/manual-pushes/dispatch-test`, payload);
336
+ if (data?.error) throw new Error(extractBackendError(data));
337
+ return data;
338
+ },
339
+ onSuccess: () => {
340
+ queryClient2.invalidateQueries({ queryKey: crmKeys.manualPushHistory() });
341
+ }
342
+ });
343
+ };
274
344
  function useStagedFilters(initialState) {
275
345
  const [draft, setDraftState] = useState(initialState);
276
346
  const [applied, setApplied] = useState(initialState);
@@ -3097,7 +3167,401 @@ const StatsView = () => {
3097
3167
  ] });
3098
3168
  };
3099
3169
  const StatsView$1 = memo(StatsView);
3100
- const homePageStyles = ".dashboard-container {\n --crm-bg-primary: #ffffff;\n --crm-bg-secondary: #f6f6f9;\n --crm-bg-tertiary: #eaeaef;\n --crm-text-primary: #32324d;\n --crm-text-secondary: #666687;\n --crm-text-muted: #8e8ea9;\n --crm-border: #dcdce4;\n --crm-border-light: #eaeaef;\n --crm-shadow: rgba(0, 0, 0, 0.08);\n --crm-shadow-strong: rgba(0, 0, 0, 0.12);\n --crm-accent: #4945ff;\n --crm-accent-light: #d9d8ff;\n --crm-success: #059669;\n --crm-success-light: #d1fae5;\n --crm-warning: #d97706;\n --crm-warning-light: #fef3c7;\n --crm-danger: #dc2626;\n --crm-danger-light: #fee2e2;\n --crm-header-gradient: linear-gradient(135deg, #4945ff 0%, #7b79ff 100%);\n --crm-chart-grid: #e0e0e0;\n --crm-chart-text: #666687;\n --crm-table-header-bg: #f6f6f9;\n --crm-table-row-hover: #f0f0ff;\n --crm-table-row-stripe: #fafafc;\n --tab-icon-stats-gradient: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%);\n --tab-icon-logs-gradient: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);\n --tab-icon-antispam-gradient: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);\n\n min-height: 100vh;\n background: var(--crm-bg-secondary);\n}\n\n.dashboard-container.theme-dark {\n --crm-bg-primary: #212134;\n --crm-bg-secondary: #181826;\n --crm-bg-tertiary: #2a2a42;\n --crm-text-primary: #ffffff;\n --crm-text-secondary: #a5a5ba;\n --crm-text-muted: #7b7b98;\n --crm-border: #3d3d5c;\n --crm-border-light: #2a2a42;\n --crm-shadow: rgba(0, 0, 0, 0.3);\n --crm-shadow-strong: rgba(0, 0, 0, 0.4);\n --crm-accent: #a5a3ff;\n --crm-accent-light: rgba(123, 121, 255, 0.2);\n --crm-success: #34d399;\n --crm-success-light: rgba(16, 185, 129, 0.2);\n --crm-warning: #fbbf24;\n --crm-warning-light: rgba(245, 158, 11, 0.2);\n --crm-danger: #f87171;\n --crm-danger-light: rgba(239, 68, 68, 0.2);\n --crm-header-gradient: linear-gradient(135deg, #2a2a5c 0%, #3d3d80 100%);\n --crm-chart-grid: #3d3d5c;\n --crm-chart-text: #a5a5ba;\n --crm-table-header-bg: #2a2a42;\n --crm-table-row-hover: #2a2a50;\n --crm-table-row-stripe: #1e1e32;\n --tab-icon-stats-gradient: linear-gradient(135deg, rgba(123, 121, 255, 0.2) 0%, rgba(99, 102, 241, 0.3) 100%);\n --tab-icon-logs-gradient: linear-gradient(135deg, rgba(16, 185, 129, 0.2) 0%, rgba(52, 211, 153, 0.3) 100%);\n --tab-icon-antispam-gradient: linear-gradient(135deg, rgba(245, 158, 11, 0.2) 0%, rgba(251, 191, 36, 0.3) 100%);\n}\n\n.dashboard-header {\n background: var(--crm-header-gradient);\n padding: 24px 32px;\n border-radius: 0 0 16px 16px;\n margin-bottom: 24px;\n box-shadow: 0 4px 24px var(--crm-shadow-strong);\n}\n\n.dashboard-header h1 {\n color: white;\n font-size: 28px;\n font-weight: 700;\n margin: 0 0 4px 0;\n}\n\n.dashboard-header p {\n color: rgba(255, 255, 255, 0.85);\n font-size: 14px;\n margin: 0;\n}\n\n.tab-navigation {\n display: flex;\n gap: 12px;\n padding: 0 24px;\n margin-bottom: 16px;\n}\n\n.tab-card {\n flex: 1;\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px 20px;\n background: var(--crm-bg-primary);\n border: 2px solid transparent;\n border-radius: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 2px 8px var(--crm-shadow);\n position: relative;\n overflow: hidden;\n}\n\n.tab-card::before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 4px;\n background: transparent;\n transition: background 0.2s ease;\n}\n\n.tab-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 8px 24px var(--crm-shadow-strong);\n}\n\n.tab-card.active {\n border-color: var(--tab-color, var(--crm-accent));\n box-shadow: 0 4px 16px var(--crm-shadow-strong);\n}\n\n.tab-card.active::before {\n background: var(--tab-color, var(--crm-accent));\n}\n\n.tab-icon {\n width: 44px;\n height: 44px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n transition: transform 0.2s ease;\n}\n\n.tab-card:hover .tab-icon {\n transform: scale(1.05);\n}\n\n.tab-icon.stats {\n background: var(--tab-icon-stats-gradient);\n color: var(--crm-accent);\n}\n\n.tab-icon.logs {\n background: var(--tab-icon-logs-gradient);\n color: var(--crm-success);\n}\n\n.tab-icon.antispam {\n background: var(--tab-icon-antispam-gradient);\n color: var(--crm-warning);\n}\n\n.tab-content-wrapper {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n}\n\n.tab-label {\n font-size: 15px;\n font-weight: 600;\n color: var(--crm-text-primary);\n margin: 0;\n}\n\n.tab-description {\n font-size: 12px;\n color: var(--crm-text-muted);\n margin: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tab-badge {\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 28px;\n height: 28px;\n padding: 0 10px;\n border-radius: 14px;\n font-size: 13px;\n font-weight: 600;\n flex-shrink: 0;\n transition: all 0.2s ease;\n}\n\n.tab-badge.stats {\n background: var(--crm-accent-light);\n color: var(--crm-accent);\n}\n\n.tab-badge.logs {\n background: var(--crm-success-light);\n color: var(--crm-success);\n}\n\n.tab-badge.antispam {\n background: var(--crm-warning-light);\n color: var(--crm-warning);\n}\n\n.tab-card.active .tab-badge.stats {\n background: var(--crm-accent);\n color: white;\n}\n\n.tab-card.active .tab-badge.logs {\n background: var(--crm-success);\n color: white;\n}\n\n.tab-card.active .tab-badge.antispam {\n background: var(--crm-warning);\n color: white;\n}\n\n.tab-content {\n margin: 0 24px;\n padding: 24px;\n background: var(--crm-bg-primary);\n border-radius: 16px;\n box-shadow: 0 4px 16px var(--crm-shadow);\n}\n\n.loading-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 64px;\n gap: 16px;\n color: var(--crm-text-muted);\n}\n\n.loading-spinner {\n width: 40px;\n height: 40px;\n border: 3px solid var(--crm-border);\n border-top-color: var(--crm-accent);\n border-radius: 50%;\n animation: spin 0.8s linear infinite;\n}\n\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n/* Stats Cards */\n.stats-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n}\n\n.stat-card {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 20px;\n border: 1px solid var(--crm-border-light);\n box-shadow: 0 2px 8px var(--crm-shadow);\n transition: all 0.2s ease;\n}\n\n.stat-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 16px var(--crm-shadow-strong);\n}\n\n.stat-card-header {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 12px;\n}\n\n.stat-card-icon {\n width: 40px;\n height: 40px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.stat-card-icon.primary {\n background: var(--crm-accent-light);\n color: var(--crm-accent);\n}\n\n.stat-card-icon.success {\n background: var(--crm-success-light);\n color: var(--crm-success);\n}\n\n.stat-card-icon.warning {\n background: var(--crm-warning-light);\n color: var(--crm-warning);\n}\n\n.stat-card-icon.danger {\n background: var(--crm-danger-light);\n color: var(--crm-danger);\n}\n\n.stat-card-value {\n font-size: 28px;\n font-weight: 700;\n color: var(--crm-text-primary);\n line-height: 1.2;\n}\n\n.stat-card-label {\n font-size: 13px;\n color: var(--crm-text-muted);\n margin-top: 4px;\n}\n\n.stat-card-trend {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 12px;\n margin-top: 8px;\n}\n\n.stat-card-trend.up {\n background: var(--crm-success-light);\n color: var(--crm-success);\n}\n\n.stat-card-trend.down {\n background: var(--crm-danger-light);\n color: var(--crm-danger);\n}\n\n.stat-card-trend.neutral {\n background: var(--crm-bg-tertiary);\n color: var(--crm-text-muted);\n}\n\n.export-dropdown {\n position: relative;\n display: inline-block;\n}\n\n.export-dropdown-menu {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n background: var(--crm-bg-primary);\n border: 1px solid var(--crm-border);\n border-radius: 8px;\n box-shadow: 0 4px 16px var(--crm-shadow-strong);\n min-width: 140px;\n z-index: 100;\n overflow: hidden;\n}\n\n.export-dropdown-item {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 10px 14px;\n border: none;\n background: none;\n font-size: 13px;\n color: var(--crm-text-primary);\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.export-dropdown-item:hover {\n background: var(--crm-bg-tertiary);\n}\n\n.export-dropdown-item svg {\n width: 16px;\n height: 16px;\n color: var(--crm-text-muted);\n}\n\n.granularity-select {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.granularity-select label {\n font-size: 12px;\n color: var(--crm-text-muted);\n white-space: nowrap;\n}\n\n.granularity-pills {\n display: flex;\n gap: 2px;\n background: var(--crm-bg-tertiary);\n border-radius: 6px;\n padding: 2px;\n}\n\n.granularity-pill {\n padding: 4px 10px;\n font-size: 11px;\n font-weight: 600;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.15s ease;\n background: transparent;\n color: var(--crm-text-muted);\n}\n\n.granularity-pill.active {\n background: var(--crm-accent);\n color: white;\n}\n\n.granularity-pill:hover:not(.active) {\n background: var(--crm-bg-secondary);\n color: var(--crm-text-primary);\n}\n\n.compare-toggle {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: var(--crm-text-secondary);\n cursor: pointer;\n}\n\n.compare-toggle input {\n width: 16px;\n height: 16px;\n accent-color: var(--crm-accent);\n}\n\n.compare-toggle:hover {\n color: var(--crm-text-primary);\n}\n\n.metrics-toggle {\n display: flex;\n gap: 12px;\n margin-top: 12px;\n padding-top: 12px;\n border-top: 1px solid var(--crm-border-light);\n}\n\n.metrics-toggle label {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--crm-text-secondary);\n cursor: pointer;\n}\n\n.metrics-toggle input {\n width: 14px;\n height: 14px;\n}\n\n.metrics-toggle label:hover {\n color: var(--crm-text-primary);\n}\n\n/* Chart Container */\n.chart-container {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 24px;\n border: 1px solid var(--crm-border-light);\n box-shadow: 0 2px 8px var(--crm-shadow);\n}\n\n.chart-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n}\n\n.chart-title {\n font-size: 16px;\n font-weight: 600;\n color: var(--crm-text-primary);\n}\n\n.chart-subtitle {\n font-size: 13px;\n color: var(--crm-text-muted);\n margin-top: 2px;\n}\n\n/* Table Styles */\n.crm-table-wrapper {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n border: 1px solid var(--crm-border-light);\n box-shadow: 0 2px 8px var(--crm-shadow);\n overflow: hidden;\n}\n\n.crm-table {\n width: 100%;\n border-collapse: collapse;\n}\n\n.crm-table thead {\n background: var(--crm-table-header-bg);\n}\n\n.crm-table th {\n padding: 14px 16px;\n text-align: left;\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--crm-text-secondary);\n border-bottom: 1px solid var(--crm-border);\n}\n\n.crm-table td {\n padding: 14px 16px;\n font-size: 14px;\n color: var(--crm-text-primary);\n border-bottom: 1px solid var(--crm-border-light);\n}\n\n.crm-table tbody tr {\n transition: background-color 0.15s ease;\n}\n\n.crm-table tbody tr:hover {\n background: var(--crm-table-row-hover);\n}\n\n.crm-table tbody tr:nth-child(even) {\n background: var(--crm-table-row-stripe);\n}\n\n.crm-table tbody tr:nth-child(even):hover {\n background: var(--crm-table-row-hover);\n}\n\n.crm-table tbody tr:last-child td {\n border-bottom: none;\n}\n\n/* Status Badge */\n.status-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.status-badge::before {\n content: '';\n width: 6px;\n height: 6px;\n border-radius: 50%;\n}\n\n.status-badge.sent {\n background: var(--crm-success-light);\n color: var(--crm-success);\n}\n\n.status-badge.sent::before {\n background: var(--crm-success);\n}\n\n.status-badge.planned {\n background: var(--crm-warning-light);\n color: var(--crm-warning);\n}\n\n.status-badge.planned::before {\n background: var(--crm-warning);\n}\n\n.status-badge.failed {\n background: var(--crm-danger-light);\n color: var(--crm-danger);\n}\n\n.status-badge.failed::before {\n background: var(--crm-danger);\n}\n\n.status-badge.cancelled {\n background: var(--crm-bg-tertiary);\n color: var(--crm-text-muted);\n}\n\n.status-badge.cancelled::before {\n background: var(--crm-text-muted);\n}\n\n.status-badge.warning {\n background: var(--crm-warning-light);\n color: var(--crm-warning);\n}\n\n.status-badge.warning::before {\n background: var(--crm-warning);\n}\n\n/* Pagination */\n.pagination {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 0;\n}\n\n.pagination-info {\n font-size: 13px;\n color: var(--crm-text-muted);\n}\n\n.pagination-controls {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.pagination-btn {\n padding: 8px 16px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n background: var(--crm-bg-primary);\n border: 1px solid var(--crm-border);\n color: var(--crm-text-primary);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.pagination-btn:hover:not(:disabled) {\n background: var(--crm-accent-light);\n border-color: var(--crm-accent);\n color: var(--crm-accent);\n}\n\n.pagination-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.pagination-current {\n padding: 8px 12px;\n font-size: 13px;\n font-weight: 600;\n color: var(--crm-text-primary);\n}\n\n/* Empty State */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 24px;\n text-align: center;\n}\n\n.empty-state-icon {\n width: 64px;\n height: 64px;\n border-radius: 16px;\n background: var(--crm-bg-tertiary);\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 16px;\n color: var(--crm-text-muted);\n}\n\n.empty-state-title {\n font-size: 16px;\n font-weight: 600;\n color: var(--crm-text-primary);\n margin-bottom: 4px;\n}\n\n.empty-state-description {\n font-size: 14px;\n color: var(--crm-text-muted);\n}\n\n/* Section Header in table wrapper */\n.crm-table-wrapper .section-header,\n.crm-table-wrapper > div:first-child {\n padding: 16px 20px;\n}\n\n/* Funnel grid responsive */\n.funnel-grid {\n display: grid;\n grid-template-columns: repeat(5, 1fr);\n gap: 12px;\n}\n\n@media (max-width: 1200px) {\n .stats-grid {\n grid-template-columns: repeat(3, 1fr) !important;\n }\n\n .funnel-grid {\n grid-template-columns: repeat(3, 1fr);\n }\n}\n\n@media (max-width: 1024px) {\n .tab-navigation {\n flex-direction: column;\n gap: 8px;\n padding: 0 16px;\n }\n\n .tab-card {\n padding: 14px 16px;\n }\n\n .tab-description {\n display: none;\n }\n\n .tab-content {\n margin: 0 16px;\n border-radius: 12px;\n }\n\n .stats-grid {\n grid-template-columns: repeat(2, 1fr) !important;\n }\n\n .funnel-grid {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .chart-container {\n padding: 16px;\n }\n}\n\n@media (max-width: 768px) {\n .stats-grid {\n grid-template-columns: repeat(2, 1fr) !important;\n }\n\n .crm-table th,\n .crm-table td {\n padding: 10px 12px;\n font-size: 13px;\n }\n}\n\n@media (max-width: 640px) {\n .dashboard-header {\n padding: 20px;\n border-radius: 0 0 12px 12px;\n }\n\n .dashboard-header h1 {\n font-size: 22px;\n }\n\n .tab-icon {\n width: 36px;\n height: 36px;\n }\n\n .tab-label {\n font-size: 14px;\n }\n\n .stats-grid {\n grid-template-columns: 1fr !important;\n }\n\n .funnel-grid {\n grid-template-columns: 1fr;\n }\n\n .stat-card-value {\n font-size: 24px;\n }\n\n .chart-container {\n padding: 12px;\n }\n}\n\n/* Today Widget */\n.today-widget {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 20px;\n border: 1px solid var(--crm-border-light);\n box-shadow: 0 4px 16px var(--crm-shadow);\n}\n\n.today-widget.loading {\n min-height: 160px;\n display: flex;\n flex-direction: column;\n}\n\n.today-widget-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n}\n\n.today-widget-title {\n font-size: 13px;\n font-weight: 700;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n color: var(--crm-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.live-dot {\n width: 8px;\n height: 8px;\n background: var(--crm-success);\n border-radius: 50%;\n animation: pulse 2s ease-in-out infinite;\n box-shadow: 0 0 8px var(--crm-success);\n}\n\n@keyframes pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.7; transform: scale(1.2); }\n}\n\n.today-widget-comparison {\n font-size: 11px;\n color: var(--crm-text-muted);\n background: var(--crm-bg-tertiary);\n padding: 4px 10px;\n border-radius: 12px;\n}\n\n.today-widget-metrics {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 10px;\n margin-bottom: 16px;\n}\n\n.today-metric {\n text-align: center;\n padding: 12px 8px;\n background: var(--crm-bg-secondary);\n border-radius: 10px;\n border: 1px solid var(--crm-border-light);\n transition: border-color 0.2s ease, background-color 0.2s ease;\n}\n\n.today-metric:hover {\n border-color: var(--crm-border);\n background: var(--crm-bg-tertiary);\n}\n\n.today-metric-value {\n font-size: 22px;\n font-weight: 700;\n line-height: 1.2;\n color: var(--crm-text-primary);\n}\n\n.today-metric-label {\n font-size: 11px;\n color: var(--crm-text-muted);\n margin-top: 4px;\n font-weight: 500;\n}\n\n.trend-indicator {\n display: inline-flex;\n align-items: center;\n font-size: 11px;\n font-weight: 600;\n margin-top: 6px;\n padding: 3px 8px;\n border-radius: 10px;\n}\n\n.trend-indicator.up {\n background: var(--crm-success-light);\n color: var(--crm-success);\n}\n\n.trend-indicator.down {\n background: var(--crm-danger-light);\n color: var(--crm-danger);\n}\n\n.trend-neutral {\n display: inline-flex;\n font-size: 11px;\n font-weight: 600;\n margin-top: 6px;\n padding: 3px 8px;\n border-radius: 10px;\n background: var(--crm-bg-tertiary);\n color: var(--crm-text-muted);\n}\n\n.today-widget-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 12px;\n border-top: 1px solid var(--crm-border-light);\n font-size: 12px;\n}\n\n.today-rates {\n display: flex;\n gap: 16px;\n}\n\n.today-rates span {\n color: var(--crm-text-muted);\n font-weight: 500;\n}\n\n.today-rates span strong {\n color: var(--crm-text-primary);\n font-weight: 700;\n}\n\n.today-updated {\n color: var(--crm-text-muted);\n font-size: 11px;\n}\n\n.today-widget-loading {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n color: var(--crm-text-muted);\n}\n\n/* Cohort Heat Map */\n.cohort-heatmap {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 24px;\n border: 1px solid var(--crm-border-light);\n}\n\n.cohort-table-wrapper {\n overflow-x: auto;\n margin: 16px 0;\n}\n\n.cohort-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n}\n\n.cohort-header-cell {\n padding: 10px 8px;\n text-align: center;\n font-weight: 600;\n color: var(--crm-text-secondary);\n background: var(--crm-bg-secondary);\n border-bottom: 1px solid var(--crm-border);\n}\n\n.cohort-period-header {\n min-width: 60px;\n}\n\n.cohort-date-cell {\n padding: 10px 12px;\n font-weight: 600;\n color: var(--crm-text-primary);\n background: var(--crm-bg-secondary);\n white-space: nowrap;\n}\n\n.cohort-users-cell {\n padding: 10px 12px;\n text-align: right;\n color: var(--crm-text-secondary);\n background: var(--crm-bg-secondary);\n}\n\n.cohort-cell {\n padding: 10px 8px;\n text-align: center;\n font-weight: 600;\n font-size: 11px;\n transition: all 0.15s ease;\n cursor: default;\n}\n\n.cohort-cell:hover {\n filter: brightness(1.1);\n z-index: 1;\n position: relative;\n}\n\n.cohort-legend {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n font-size: 11px;\n color: var(--crm-text-muted);\n margin-top: 12px;\n}\n\n.cohort-legend-gradient {\n width: 120px;\n height: 8px;\n border-radius: 4px;\n background: linear-gradient(90deg, rgba(239, 68, 68, 0.6) 0%, rgba(245, 158, 11, 0.6) 50%, rgba(16, 185, 129, 0.8) 100%);\n}\n\n.cohort-loading {\n text-align: center;\n padding: 32px;\n color: var(--crm-text-muted);\n}\n\n/* A/B Tests */\n.ab-tests-section {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 24px;\n border: 1px solid var(--crm-border-light);\n}\n\n.ab-tests-list {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.ab-test-card {\n background: var(--crm-bg-secondary);\n border-radius: 10px;\n padding: 20px;\n border: 1px solid var(--crm-border-light);\n}\n\n.ab-campaign-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n}\n\n.ab-test-name {\n font-size: 15px;\n font-weight: 600;\n color: var(--crm-text-primary);\n}\n\n.ab-block-count {\n font-size: 12px;\n font-weight: 500;\n color: var(--crm-text-muted);\n background: var(--crm-bg-tertiary);\n padding: 2px 10px;\n border-radius: 10px;\n}\n\n.ab-blocks-list {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.ab-message-block {\n background: var(--crm-bg-primary);\n border-radius: 8px;\n padding: 16px;\n border: 1px solid var(--crm-border-light);\n}\n\n.ab-block-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 12px;\n}\n\n.ab-block-name {\n font-size: 13px;\n font-weight: 600;\n color: var(--crm-text-secondary);\n}\n\n.ab-test-status {\n font-size: 12px;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.ab-test-variants {\n display: grid;\n grid-template-columns: 1fr auto 1fr;\n gap: 16px;\n align-items: center;\n}\n\n.ab-variant {\n background: var(--crm-bg-secondary);\n border-radius: 8px;\n padding: 16px;\n border: 2px solid transparent;\n transition: all 0.2s ease;\n}\n\n.ab-variant.winner {\n border-color: var(--crm-success);\n box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.1);\n}\n\n.ab-variant-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.ab-variant-name {\n font-weight: 600;\n color: var(--crm-text-primary);\n}\n\n.ab-variant-tag {\n font-size: 10px;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 10px;\n text-transform: uppercase;\n}\n\n.ab-variant-tag.control {\n background: var(--crm-bg-tertiary);\n color: var(--crm-text-muted);\n}\n\n.ab-variant-tag.treatment {\n background: var(--crm-accent-light);\n color: var(--crm-accent);\n}\n\n.ab-variant-stats {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 12px;\n}\n\n.ab-stat {\n text-align: center;\n}\n\n.ab-stat-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--crm-text-primary);\n}\n\n.ab-stat-label {\n font-size: 10px;\n color: var(--crm-text-muted);\n margin-top: 2px;\n}\n\n.ab-vs {\n font-size: 12px;\n font-weight: 700;\n color: var(--crm-text-muted);\n text-align: center;\n}\n\n.ab-test-analysis {\n display: flex;\n gap: 24px;\n margin-top: 16px;\n padding-top: 16px;\n border-top: 1px solid var(--crm-border-light);\n}\n\n.ab-analysis-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n}\n\n.ab-analysis-label {\n color: var(--crm-text-muted);\n}\n\n.ab-analysis-value {\n font-weight: 600;\n color: var(--crm-text-primary);\n}\n\n.ab-analysis-value.positive {\n color: var(--crm-success);\n}\n\n.ab-analysis-value.negative {\n color: var(--crm-danger);\n}\n\n.ab-analysis-value.confidence-high {\n color: var(--crm-success);\n}\n\n.ab-analysis-value.confidence-medium {\n color: var(--crm-success);\n}\n\n.ab-analysis-value.confidence-low {\n color: var(--crm-warning);\n}\n\n.ab-tests-loading {\n text-align: center;\n padding: 32px;\n color: var(--crm-text-muted);\n}\n\n/* Multi-variant A/B Tests */\n.ab-test-variants-multi {\n display: flex;\n align-items: stretch;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.ab-vs-small {\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n font-weight: 700;\n color: var(--crm-text-muted);\n padding: 0 4px;\n}\n\n.ab-variant-compact {\n flex: 1;\n min-width: 140px;\n max-width: 200px;\n background: var(--crm-bg-secondary);\n border-radius: 8px;\n padding: 12px;\n border: 2px solid transparent;\n transition: all 0.2s ease;\n}\n\n.ab-variant-compact.winner {\n border-color: var(--crm-success);\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1);\n}\n\n.ab-variant-compact .ab-variant-header {\n margin-bottom: 8px;\n flex-wrap: wrap;\n gap: 4px;\n}\n\n.ab-variant-compact .ab-variant-name {\n font-size: 13px;\n}\n\n.ab-variant-stats-compact {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.ab-stat-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: 12px;\n}\n\n.ab-stat-row .ab-stat-label {\n color: var(--crm-text-muted);\n font-size: 11px;\n}\n\n.ab-stat-row .ab-stat-value {\n font-weight: 600;\n color: var(--crm-text-primary);\n font-size: 12px;\n}\n\n.ab-variant-lift {\n margin-top: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--crm-border-light);\n text-align: center;\n font-size: 12px;\n font-weight: 600;\n}\n\n.ab-variant-lift .positive {\n color: var(--crm-success);\n}\n\n.ab-variant-lift .negative {\n color: var(--crm-danger);\n}\n\n.ab-test-analysis-compact {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 12px;\n padding-top: 12px;\n border-top: 1px solid var(--crm-border-light);\n font-size: 12px;\n}\n\n.ab-test-analysis-compact .ab-analysis-label {\n color: var(--crm-text-muted);\n}\n\n.ab-test-analysis-compact .ab-analysis-value {\n font-weight: 600;\n}\n\n@media (max-width: 640px) {\n .ab-test-variants-multi {\n flex-direction: column;\n }\n\n .ab-variant-compact {\n max-width: none;\n }\n\n .ab-vs-small {\n padding: 4px 0;\n }\n}\n\n/* Send Time Heat Map */\n.sendtime-heatmap {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 24px;\n border: 1px solid var(--crm-border-light);\n}\n\n.sendtime-best {\n text-align: right;\n}\n\n.sendtime-best-label {\n font-size: 11px;\n color: var(--crm-text-muted);\n}\n\n.sendtime-best-value {\n font-size: 16px;\n font-weight: 700;\n color: var(--crm-success);\n}\n\n.sendtime-best-rate {\n font-size: 12px;\n color: var(--crm-success);\n}\n\n.sendtime-grid-wrapper {\n overflow-x: auto;\n margin: 16px 0;\n}\n\n.sendtime-grid {\n display: grid;\n grid-template-columns: 60px repeat(7, 1fr);\n gap: 2px;\n min-width: 400px;\n}\n\n.sendtime-corner {\n background: transparent;\n}\n\n.sendtime-day-header {\n text-align: center;\n font-size: 11px;\n font-weight: 600;\n color: var(--crm-text-secondary);\n padding: 8px 4px;\n}\n\n.sendtime-hour-label {\n font-size: 10px;\n color: var(--crm-text-muted);\n display: flex;\n align-items: center;\n justify-content: flex-end;\n padding-right: 8px;\n}\n\n.sendtime-cell {\n aspect-ratio: 1;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: default;\n transition: all 0.15s ease;\n min-height: 32px;\n}\n\n.sendtime-cell:hover {\n filter: brightness(1.15);\n z-index: 1;\n}\n\n.sendtime-cell.best {\n box-shadow: 0 0 0 2px var(--crm-success);\n}\n\n.sendtime-cell-value {\n font-size: 9px;\n font-weight: 600;\n color: inherit;\n}\n\n.sendtime-legend {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n font-size: 11px;\n color: var(--crm-text-muted);\n margin-top: 12px;\n}\n\n.sendtime-legend-gradient {\n width: 120px;\n height: 8px;\n border-radius: 4px;\n background: linear-gradient(90deg, rgba(239, 68, 68, 0.6) 0%, rgba(245, 158, 11, 0.6) 50%, rgba(16, 185, 129, 0.8) 100%);\n}\n\n.sendtime-loading {\n text-align: center;\n padding: 32px;\n color: var(--crm-text-muted);\n}\n\n/* Segment Table */\n.segment-table-section {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 24px;\n border: 1px solid var(--crm-border-light);\n}\n\n.segment-cards {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 16px;\n}\n\n.segment-card {\n background: var(--crm-bg-secondary);\n border-radius: 10px;\n padding: 16px;\n border-left: 4px solid var(--crm-text-muted);\n transition: all 0.2s ease;\n}\n\n.segment-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px var(--crm-shadow);\n}\n\n.segment-card-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.segment-icon {\n font-size: 18px;\n}\n\n.segment-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--crm-text-primary);\n flex: 1;\n}\n\n.segment-users {\n font-size: 12px;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.segment-metrics {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.segment-metric {\n text-align: center;\n}\n\n.segment-metric-value {\n font-size: 16px;\n font-weight: 700;\n color: var(--crm-text-primary);\n}\n\n.segment-metric-label {\n font-size: 10px;\n color: var(--crm-text-muted);\n}\n\n.segment-bar-wrapper {\n height: 4px;\n background: var(--crm-bg-tertiary);\n border-radius: 2px;\n overflow: hidden;\n}\n\n.segment-bar {\n height: 100%;\n border-radius: 2px;\n transition: width 0.3s ease;\n}\n\n.segment-loading {\n text-align: center;\n padding: 32px;\n color: var(--crm-text-muted);\n}\n\n/* Goal Tracker */\n.goal-tracker {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 20px;\n border: 1px solid var(--crm-border-light);\n}\n\n.goal-tracker-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 16px;\n}\n\n.goal-edit-btn {\n background: transparent;\n border: none;\n font-size: 16px;\n cursor: pointer;\n padding: 4px 8px;\n border-radius: 6px;\n transition: background 0.15s ease;\n}\n\n.goal-edit-btn:hover {\n background: var(--crm-bg-tertiary);\n}\n\n.goal-items {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.goal-item {\n padding: 12px;\n background: var(--crm-bg-secondary);\n border-radius: 8px;\n}\n\n.goal-item-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 8px;\n}\n\n.goal-icon {\n font-size: 16px;\n}\n\n.goal-name {\n font-size: 13px;\n font-weight: 600;\n color: var(--crm-text-primary);\n flex: 1;\n}\n\n.goal-progress-badge {\n font-size: 11px;\n font-weight: 700;\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.goal-bar-wrapper {\n height: 6px;\n background: var(--crm-bg-tertiary);\n border-radius: 3px;\n overflow: hidden;\n margin-bottom: 8px;\n}\n\n.goal-bar {\n height: 100%;\n border-radius: 3px;\n transition: width 0.3s ease;\n}\n\n.goal-values {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n}\n\n.goal-current {\n font-weight: 600;\n color: var(--crm-text-primary);\n}\n\n.goal-separator {\n color: var(--crm-text-muted);\n}\n\n.goal-target {\n color: var(--crm-text-secondary);\n}\n\n.goal-target-input {\n width: 80px;\n padding: 2px 6px;\n border: 1px solid var(--crm-border);\n border-radius: 4px;\n font-size: 12px;\n background: var(--crm-bg-primary);\n color: var(--crm-text-primary);\n}\n\n.goal-target-input:focus {\n outline: none;\n border-color: var(--crm-accent);\n}\n\n/* Enhanced Stats Layout */\n.stats-header-row {\n display: grid;\n grid-template-columns: 320px 1fr;\n gap: 16px;\n margin-bottom: 24px;\n}\n\n@media (max-width: 1024px) {\n .stats-header-row {\n grid-template-columns: 1fr;\n }\n\n .ab-test-variants {\n grid-template-columns: 1fr;\n }\n\n .ab-vs {\n padding: 8px 0;\n }\n\n .ab-test-analysis {\n flex-wrap: wrap;\n gap: 12px;\n }\n}\n\n@media (max-width: 768px) {\n .segment-metrics {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n";
3170
+ const stripHtml = (html) => html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<[^>]+>/g, "").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").trim();
3171
+ const truncate = (text, n) => text.length > n ? `${text.slice(0, n)}…` : text;
3172
+ const formatDateTime = (iso) => new Date(iso).toLocaleString("ru-RU", {
3173
+ day: "2-digit",
3174
+ month: "2-digit",
3175
+ year: "numeric",
3176
+ hour: "2-digit",
3177
+ minute: "2-digit"
3178
+ });
3179
+ const parseUserIdList = (raw) => {
3180
+ if (!raw.trim()) return [];
3181
+ return raw.split(/[\s,]+/).map((t) => t.trim()).filter(Boolean).map((t) => Number(t)).filter((n) => Number.isInteger(n) && n > 0);
3182
+ };
3183
+ const ManualPushesView = () => {
3184
+ const { data: templates = [], isLoading, error, refetch } = useManualPushTemplates();
3185
+ const { data: history = [], isLoading: historyLoading } = useManualPushHistory();
3186
+ const [modalState, setModalState] = useState(null);
3187
+ const sortedTemplates = useMemo(
3188
+ () => [...templates].sort(
3189
+ (a, b) => a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0
3190
+ ),
3191
+ [templates]
3192
+ );
3193
+ return /* @__PURE__ */ jsxs(Box, { padding: 4, children: [
3194
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", paddingBottom: 4, children: [
3195
+ /* @__PURE__ */ jsxs("div", { children: [
3196
+ /* @__PURE__ */ jsx(Typography, { variant: "beta", children: "Ручные рассылки" }),
3197
+ /* @__PURE__ */ jsx("div", { style: { color: "var(--crm-text-secondary)", fontSize: 13, marginTop: 4 }, children: "Опубликованные в Strapi шаблоны Manual Push. Отправка не происходит автоматически — используйте кнопки ниже." })
3198
+ ] }),
3199
+ /* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: () => refetch(), children: "Обновить" })
3200
+ ] }),
3201
+ isLoading && /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: "Загрузка шаблонов…" }) }),
3202
+ error && /* @__PURE__ */ jsx(
3203
+ Box,
3204
+ {
3205
+ padding: 4,
3206
+ background: "danger100",
3207
+ hasRadius: true,
3208
+ style: { border: "1px solid var(--crm-danger)" },
3209
+ children: /* @__PURE__ */ jsxs(Typography, { textColor: "danger700", children: [
3210
+ "Не удалось загрузить шаблоны: ",
3211
+ error.message
3212
+ ] })
3213
+ }
3214
+ ),
3215
+ !isLoading && !error && sortedTemplates.length === 0 && /* @__PURE__ */ jsx(Box, { padding: 6, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: "Шаблоны не найдены. Создайте запись в Content Manager → Manual Push и опубликуйте её." }) }),
3216
+ /* @__PURE__ */ jsx(
3217
+ "div",
3218
+ {
3219
+ style: {
3220
+ display: "grid",
3221
+ gridTemplateColumns: "repeat(auto-fill, minmax(360px, 1fr))",
3222
+ gap: 16
3223
+ },
3224
+ children: sortedTemplates.map((t) => /* @__PURE__ */ jsx(
3225
+ TemplateCard,
3226
+ {
3227
+ template: t,
3228
+ onSendTest: () => setModalState({ template: t, mode: "test" }),
3229
+ onDispatch: () => setModalState({ template: t, mode: "segment" })
3230
+ },
3231
+ t.documentId
3232
+ ))
3233
+ }
3234
+ ),
3235
+ /* @__PURE__ */ jsxs(Box, { paddingTop: 8, children: [
3236
+ /* @__PURE__ */ jsx(Typography, { variant: "beta", children: "Последние отправки" }),
3237
+ /* @__PURE__ */ jsx(Box, { paddingTop: 3, children: historyLoading ? /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsx(Loader, { children: "Загрузка истории…" }) }) : history.length === 0 ? /* @__PURE__ */ jsx(Box, { padding: 4, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: "Пока ни одной отправки." }) }) : /* @__PURE__ */ jsx(HistoryTable, { history, templates }) })
3238
+ ] }),
3239
+ modalState && /* @__PURE__ */ jsx(DispatchModal, { state: modalState, onClose: () => setModalState(null) })
3240
+ ] });
3241
+ };
3242
+ const TemplateCard = ({ template, onSendTest, onDispatch }) => {
3243
+ const preview = truncate(stripHtml(template.body || ""), 220);
3244
+ const hasTestUsers = template.testUserIds.length > 0;
3245
+ return /* @__PURE__ */ jsxs(
3246
+ Box,
3247
+ {
3248
+ background: "neutral0",
3249
+ hasRadius: true,
3250
+ padding: 4,
3251
+ shadow: "tableShadow",
3252
+ style: { display: "flex", flexDirection: "column", gap: 12 },
3253
+ children: [
3254
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", alignItems: "flex-start", gap: 2, children: /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
3255
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", ellipsis: true, children: template.name }),
3256
+ /* @__PURE__ */ jsxs(Flex, { gap: 1, paddingTop: 1, children: [
3257
+ template.locales.map((l) => /* @__PURE__ */ jsx(Badge, { backgroundColor: "primary200", textColor: "primary600", children: l.toUpperCase() }, l)),
3258
+ template.image && /* @__PURE__ */ jsx(Badge, { backgroundColor: "secondary200", textColor: "secondary700", children: "IMG" }),
3259
+ template.buttonUrl && /* @__PURE__ */ jsx(Badge, { backgroundColor: "alternative200", textColor: "alternative700", children: "BTN" })
3260
+ ] })
3261
+ ] }) }),
3262
+ template.image?.url && /* @__PURE__ */ jsx(
3263
+ "div",
3264
+ {
3265
+ style: {
3266
+ background: "var(--crm-bg-tertiary)",
3267
+ borderRadius: 8,
3268
+ overflow: "hidden",
3269
+ maxHeight: 120,
3270
+ display: "flex",
3271
+ justifyContent: "center",
3272
+ alignItems: "center"
3273
+ },
3274
+ children: /* @__PURE__ */ jsx(
3275
+ "img",
3276
+ {
3277
+ src: template.image.url,
3278
+ alt: template.image.alternativeText || template.name,
3279
+ style: { maxWidth: "100%", maxHeight: 120, objectFit: "cover" }
3280
+ }
3281
+ )
3282
+ }
3283
+ ),
3284
+ /* @__PURE__ */ jsx(
3285
+ "div",
3286
+ {
3287
+ style: {
3288
+ color: "var(--crm-text-secondary)",
3289
+ fontSize: 13,
3290
+ whiteSpace: "pre-wrap",
3291
+ lineHeight: 1.4,
3292
+ minHeight: 40
3293
+ },
3294
+ children: preview || /* @__PURE__ */ jsx("em", { style: { opacity: 0.6 }, children: "Тело шаблона пустое" })
3295
+ }
3296
+ ),
3297
+ template.buttonLabel && template.buttonUrl && /* @__PURE__ */ jsxs("div", { style: { fontSize: 12, color: "var(--crm-text-muted)" }, children: [
3298
+ "Кнопка: ",
3299
+ /* @__PURE__ */ jsx("strong", { children: template.buttonLabel }),
3300
+ " → ",
3301
+ truncate(template.buttonUrl, 48)
3302
+ ] }),
3303
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 12, color: "var(--crm-text-muted)" }, children: [
3304
+ "testUserIds: ",
3305
+ hasTestUsers ? template.testUserIds.join(", ") : "—"
3306
+ ] }),
3307
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, justifyContent: "flex-end", paddingTop: 1, children: [
3308
+ /* @__PURE__ */ jsx(
3309
+ Button,
3310
+ {
3311
+ variant: "tertiary",
3312
+ disabled: !hasTestUsers,
3313
+ onClick: onSendTest,
3314
+ title: hasTestUsers ? "" : "Заполните testUserIds в Strapi",
3315
+ children: "Тест-отправка"
3316
+ }
3317
+ ),
3318
+ /* @__PURE__ */ jsx(Button, { variant: "default", onClick: onDispatch, children: "Отправить…" })
3319
+ ] })
3320
+ ]
3321
+ }
3322
+ );
3323
+ };
3324
+ const HistoryTable = ({ history, templates }) => {
3325
+ const nameByDocId = useMemo(() => {
3326
+ const map = /* @__PURE__ */ new Map();
3327
+ templates.forEach((t) => map.set(t.documentId, t.name));
3328
+ return map;
3329
+ }, [templates]);
3330
+ return /* @__PURE__ */ jsx(Box, { background: "neutral0", hasRadius: true, shadow: "tableShadow", style: { overflow: "hidden" }, children: /* @__PURE__ */ jsxs("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: 13 }, children: [
3331
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { style: { background: "var(--crm-table-header-bg)", textAlign: "left" }, children: [
3332
+ /* @__PURE__ */ jsx("th", { style: { padding: "10px 12px" }, children: "Отправлено" }),
3333
+ /* @__PURE__ */ jsx("th", { style: { padding: "10px 12px" }, children: "Шаблон" }),
3334
+ /* @__PURE__ */ jsx("th", { style: { padding: "10px 12px", textAlign: "right" }, children: "В очереди" }),
3335
+ /* @__PURE__ */ jsx("th", { style: { padding: "10px 12px", textAlign: "right" }, children: "Отпр." }),
3336
+ /* @__PURE__ */ jsx("th", { style: { padding: "10px 12px", textAlign: "right" }, children: "Ошибки" }),
3337
+ /* @__PURE__ */ jsx("th", { style: { padding: "10px 12px", textAlign: "right" }, children: "Отмен." }),
3338
+ /* @__PURE__ */ jsx("th", { style: { padding: "10px 12px" }, children: "Кем" })
3339
+ ] }) }),
3340
+ /* @__PURE__ */ jsx("tbody", { children: history.map((row) => /* @__PURE__ */ jsxs("tr", { style: { borderTop: "1px solid var(--crm-border-light)" }, children: [
3341
+ /* @__PURE__ */ jsx("td", { style: { padding: "10px 12px", whiteSpace: "nowrap" }, children: formatDateTime(row.dispatchedAt) }),
3342
+ /* @__PURE__ */ jsx("td", { style: { padding: "10px 12px" }, children: nameByDocId.get(row.strapiDocumentId) || row.strapiDocumentId }),
3343
+ /* @__PURE__ */ jsx("td", { style: { padding: "10px 12px", textAlign: "right" }, children: row.totalQueued }),
3344
+ /* @__PURE__ */ jsx(
3345
+ "td",
3346
+ {
3347
+ style: {
3348
+ padding: "10px 12px",
3349
+ textAlign: "right",
3350
+ color: "var(--crm-success)"
3351
+ },
3352
+ children: row.counts.sent
3353
+ }
3354
+ ),
3355
+ /* @__PURE__ */ jsx(
3356
+ "td",
3357
+ {
3358
+ style: {
3359
+ padding: "10px 12px",
3360
+ textAlign: "right",
3361
+ color: "var(--crm-danger)"
3362
+ },
3363
+ children: row.counts.failed
3364
+ }
3365
+ ),
3366
+ /* @__PURE__ */ jsx("td", { style: { padding: "10px 12px", textAlign: "right" }, children: row.counts.cancelled }),
3367
+ /* @__PURE__ */ jsx(
3368
+ "td",
3369
+ {
3370
+ style: {
3371
+ padding: "10px 12px",
3372
+ color: "var(--crm-text-muted)",
3373
+ whiteSpace: "nowrap"
3374
+ },
3375
+ children: row.dispatchedBy || "—"
3376
+ }
3377
+ )
3378
+ ] }, row.manualPushId)) })
3379
+ ] }) });
3380
+ };
3381
+ const DispatchModal = ({ state, onClose }) => {
3382
+ const { template, mode } = state;
3383
+ const isTest = mode === "test";
3384
+ const dispatchMutation = useDispatchManualPush();
3385
+ const dispatchTestMutation = useDispatchTestManualPush();
3386
+ const [dispatchedBy, setDispatchedBy] = useState("");
3387
+ const [includeRu, setIncludeRu] = useState(false);
3388
+ const [includeEn, setIncludeEn] = useState(false);
3389
+ const [userIdsRaw, setUserIdsRaw] = useState("");
3390
+ const [excludeOptedOut, setExcludeOptedOut] = useState(true);
3391
+ const [dryRunResult, setDryRunResult] = useState(null);
3392
+ const [confirmed, setConfirmed] = useState(false);
3393
+ const reset = () => {
3394
+ setDispatchedBy("");
3395
+ setIncludeRu(false);
3396
+ setIncludeEn(false);
3397
+ setUserIdsRaw("");
3398
+ setExcludeOptedOut(true);
3399
+ setDryRunResult(null);
3400
+ setConfirmed(false);
3401
+ };
3402
+ const close = () => {
3403
+ reset();
3404
+ onClose();
3405
+ };
3406
+ const buildPayload = (dryRun) => {
3407
+ const userIds = parseUserIdList(userIdsRaw);
3408
+ const languages = [];
3409
+ if (includeRu) languages.push("ru");
3410
+ if (includeEn) languages.push("en");
3411
+ return {
3412
+ strapiDocumentId: template.documentId,
3413
+ segment: {
3414
+ ...userIds.length > 0 ? { userIds } : {},
3415
+ ...languages.length > 0 ? { languages } : {},
3416
+ excludeOptedOut
3417
+ },
3418
+ dryRun,
3419
+ ...dispatchedBy ? { dispatchedBy } : {}
3420
+ };
3421
+ };
3422
+ const onDryRun = async () => {
3423
+ setDryRunResult(null);
3424
+ try {
3425
+ const res = await dispatchMutation.mutateAsync(buildPayload(true));
3426
+ setDryRunResult(res.totalQueued);
3427
+ } catch {
3428
+ }
3429
+ };
3430
+ const onConfirm = async () => {
3431
+ if (isTest) {
3432
+ try {
3433
+ await dispatchTestMutation.mutateAsync({
3434
+ strapiDocumentId: template.documentId,
3435
+ ...dispatchedBy ? { dispatchedBy } : {}
3436
+ });
3437
+ close();
3438
+ } catch {
3439
+ }
3440
+ return;
3441
+ }
3442
+ if (!confirmed) {
3443
+ setConfirmed(true);
3444
+ return;
3445
+ }
3446
+ try {
3447
+ await dispatchMutation.mutateAsync(buildPayload(false));
3448
+ close();
3449
+ } catch {
3450
+ }
3451
+ };
3452
+ const mutation = isTest ? dispatchTestMutation : dispatchMutation;
3453
+ const err = mutation.error;
3454
+ return /* @__PURE__ */ jsx(Modal.Root, { open: true, onOpenChange: (open) => !open && close(), children: /* @__PURE__ */ jsxs(Modal.Content, { style: { width: 640, maxWidth: "95vw" }, children: [
3455
+ /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsxs(Modal.Title, { children: [
3456
+ isTest ? "Тест-отправка" : "Отправка",
3457
+ ": ",
3458
+ template.name
3459
+ ] }) }),
3460
+ /* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, alignItems: "stretch", children: [
3461
+ isTest ? /* @__PURE__ */ jsxs(Box, { padding: 3, background: "neutral100", hasRadius: true, children: [
3462
+ /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
3463
+ "Будет отправлено пользователям из ",
3464
+ /* @__PURE__ */ jsx("code", { children: "testUserIds" }),
3465
+ ":"
3466
+ ] }),
3467
+ /* @__PURE__ */ jsx("div", { style: { marginTop: 8, fontFamily: "monospace", fontSize: 13 }, children: template.testUserIds.join(", ") }),
3468
+ /* @__PURE__ */ jsx("div", { style: { marginTop: 8, fontSize: 12, color: "var(--crm-text-muted)" }, children: "Anti-spam, opt-out и priority конфликты для тест-отправки пропускаются." })
3469
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3470
+ /* @__PURE__ */ jsxs(Field.Root, { children: [
3471
+ /* @__PURE__ */ jsx(Field.Label, { children: "Языки (опц.)" }),
3472
+ /* @__PURE__ */ jsxs(Flex, { gap: 4, paddingTop: 1, children: [
3473
+ /* @__PURE__ */ jsx(
3474
+ Checkbox,
3475
+ {
3476
+ checked: includeRu,
3477
+ onCheckedChange: (v) => setIncludeRu(Boolean(v)),
3478
+ children: "Русский"
3479
+ }
3480
+ ),
3481
+ /* @__PURE__ */ jsx(
3482
+ Checkbox,
3483
+ {
3484
+ checked: includeEn,
3485
+ onCheckedChange: (v) => setIncludeEn(Boolean(v)),
3486
+ children: "English"
3487
+ }
3488
+ )
3489
+ ] }),
3490
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 12, color: "var(--crm-text-muted)", marginTop: 4 }, children: [
3491
+ "Пусто = все языки. Фильтр по ",
3492
+ /* @__PURE__ */ jsx("code", { children: "languageCode" }),
3493
+ " Telegram-аккаунта."
3494
+ ] })
3495
+ ] }),
3496
+ /* @__PURE__ */ jsxs(Field.Root, { children: [
3497
+ /* @__PURE__ */ jsx(Field.Label, { children: "userIds (опц.)" }),
3498
+ /* @__PURE__ */ jsx(
3499
+ Textarea,
3500
+ {
3501
+ value: userIdsRaw,
3502
+ onChange: (e) => setUserIdsRaw(e.target.value),
3503
+ placeholder: "42, 101, 202 (или с переносами)"
3504
+ }
3505
+ ),
3506
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: "var(--crm-text-muted)", marginTop: 4 }, children: "Прямая адресация — если заполнено, все остальные фильтры (кроме языков) игнорируются." })
3507
+ ] }),
3508
+ /* @__PURE__ */ jsx(
3509
+ Checkbox,
3510
+ {
3511
+ checked: excludeOptedOut,
3512
+ onCheckedChange: (v) => setExcludeOptedOut(Boolean(v)),
3513
+ children: "Исключить отписавшихся (opted_out)"
3514
+ }
3515
+ )
3516
+ ] }),
3517
+ /* @__PURE__ */ jsxs(Field.Root, { children: [
3518
+ /* @__PURE__ */ jsx(Field.Label, { children: "dispatchedBy (опц.)" }),
3519
+ /* @__PURE__ */ jsx(
3520
+ TextInput,
3521
+ {
3522
+ value: dispatchedBy,
3523
+ onChange: (e) => setDispatchedBy(e.target.value),
3524
+ placeholder: "ваш email или ник"
3525
+ }
3526
+ )
3527
+ ] }),
3528
+ !isTest && dryRunResult !== null && /* @__PURE__ */ jsx(Box, { padding: 3, background: "primary100", hasRadius: true, children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
3529
+ "Подойдёт пользователей: ",
3530
+ /* @__PURE__ */ jsx("strong", { children: dryRunResult })
3531
+ ] }) }),
3532
+ confirmed && !isTest && /* @__PURE__ */ jsx(Box, { padding: 3, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsxs(Typography, { textColor: "warning700", children: [
3533
+ "Подтвердите: будет отправлено ",
3534
+ dryRunResult ?? "?",
3535
+ " сообщений. Повторное нажатие «Отправить» запустит рассылку."
3536
+ ] }) }),
3537
+ err && /* @__PURE__ */ jsx(Box, { padding: 3, background: "danger100", hasRadius: true, children: /* @__PURE__ */ jsx(Typography, { textColor: "danger700", children: err.message }) })
3538
+ ] }) }),
3539
+ /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3540
+ /* @__PURE__ */ jsx(Modal.Close, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: close, children: "Отмена" }) }),
3541
+ !isTest && /* @__PURE__ */ jsx(
3542
+ Button,
3543
+ {
3544
+ variant: "secondary",
3545
+ onClick: onDryRun,
3546
+ loading: dispatchMutation.isPending && dryRunResult === null,
3547
+ disabled: dispatchMutation.isPending,
3548
+ children: "Оценить (dry run)"
3549
+ }
3550
+ ),
3551
+ /* @__PURE__ */ jsx(
3552
+ Button,
3553
+ {
3554
+ variant: confirmed && !isTest ? "danger-light" : "default",
3555
+ loading: mutation.isPending,
3556
+ onClick: onConfirm,
3557
+ disabled: mutation.isPending,
3558
+ children: isTest ? "Отправить тест" : confirmed ? "Подтвердить отправку" : "Отправить"
3559
+ }
3560
+ )
3561
+ ] })
3562
+ ] }) });
3563
+ };
3564
+ const homePageStyles = ".dashboard-container {\n --crm-bg-primary: #ffffff;\n --crm-bg-secondary: #f6f6f9;\n --crm-bg-tertiary: #eaeaef;\n --crm-text-primary: #32324d;\n --crm-text-secondary: #666687;\n --crm-text-muted: #8e8ea9;\n --crm-border: #dcdce4;\n --crm-border-light: #eaeaef;\n --crm-shadow: rgba(0, 0, 0, 0.08);\n --crm-shadow-strong: rgba(0, 0, 0, 0.12);\n --crm-accent: #4945ff;\n --crm-accent-light: #d9d8ff;\n --crm-success: #059669;\n --crm-success-light: #d1fae5;\n --crm-warning: #d97706;\n --crm-warning-light: #fef3c7;\n --crm-danger: #dc2626;\n --crm-danger-light: #fee2e2;\n --crm-header-gradient: linear-gradient(135deg, #4945ff 0%, #7b79ff 100%);\n --crm-chart-grid: #e0e0e0;\n --crm-chart-text: #666687;\n --crm-table-header-bg: #f6f6f9;\n --crm-table-row-hover: #f0f0ff;\n --crm-table-row-stripe: #fafafc;\n --tab-icon-stats-gradient: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%);\n --tab-icon-logs-gradient: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);\n --tab-icon-antispam-gradient: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);\n --tab-icon-manual-pushes-gradient: linear-gradient(135deg, #e0e7ff 0%, #b4b1ff 100%);\n\n min-height: 100vh;\n background: var(--crm-bg-secondary);\n}\n\n.dashboard-container.theme-dark {\n --crm-bg-primary: #212134;\n --crm-bg-secondary: #181826;\n --crm-bg-tertiary: #2a2a42;\n --crm-text-primary: #ffffff;\n --crm-text-secondary: #a5a5ba;\n --crm-text-muted: #7b7b98;\n --crm-border: #3d3d5c;\n --crm-border-light: #2a2a42;\n --crm-shadow: rgba(0, 0, 0, 0.3);\n --crm-shadow-strong: rgba(0, 0, 0, 0.4);\n --crm-accent: #a5a3ff;\n --crm-accent-light: rgba(123, 121, 255, 0.2);\n --crm-success: #34d399;\n --crm-success-light: rgba(16, 185, 129, 0.2);\n --crm-warning: #fbbf24;\n --crm-warning-light: rgba(245, 158, 11, 0.2);\n --crm-danger: #f87171;\n --crm-danger-light: rgba(239, 68, 68, 0.2);\n --crm-header-gradient: linear-gradient(135deg, #2a2a5c 0%, #3d3d80 100%);\n --crm-chart-grid: #3d3d5c;\n --crm-chart-text: #a5a5ba;\n --crm-table-header-bg: #2a2a42;\n --crm-table-row-hover: #2a2a50;\n --crm-table-row-stripe: #1e1e32;\n --tab-icon-stats-gradient: linear-gradient(135deg, rgba(123, 121, 255, 0.2) 0%, rgba(99, 102, 241, 0.3) 100%);\n --tab-icon-logs-gradient: linear-gradient(135deg, rgba(16, 185, 129, 0.2) 0%, rgba(52, 211, 153, 0.3) 100%);\n --tab-icon-antispam-gradient: linear-gradient(135deg, rgba(245, 158, 11, 0.2) 0%, rgba(251, 191, 36, 0.3) 100%);\n --tab-icon-manual-pushes-gradient: linear-gradient(135deg, rgba(123, 121, 255, 0.2) 0%, rgba(165, 163, 255, 0.3) 100%);\n}\n\n.dashboard-header {\n background: var(--crm-header-gradient);\n padding: 24px 32px;\n border-radius: 0 0 16px 16px;\n margin-bottom: 24px;\n box-shadow: 0 4px 24px var(--crm-shadow-strong);\n}\n\n.dashboard-header h1 {\n color: white;\n font-size: 28px;\n font-weight: 700;\n margin: 0 0 4px 0;\n}\n\n.dashboard-header p {\n color: rgba(255, 255, 255, 0.85);\n font-size: 14px;\n margin: 0;\n}\n\n.tab-navigation {\n display: flex;\n gap: 12px;\n padding: 0 24px;\n margin-bottom: 16px;\n}\n\n.tab-card {\n flex: 1;\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px 20px;\n background: var(--crm-bg-primary);\n border: 2px solid transparent;\n border-radius: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 2px 8px var(--crm-shadow);\n position: relative;\n overflow: hidden;\n}\n\n.tab-card::before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 4px;\n background: transparent;\n transition: background 0.2s ease;\n}\n\n.tab-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 8px 24px var(--crm-shadow-strong);\n}\n\n.tab-card.active {\n border-color: var(--tab-color, var(--crm-accent));\n box-shadow: 0 4px 16px var(--crm-shadow-strong);\n}\n\n.tab-card.active::before {\n background: var(--tab-color, var(--crm-accent));\n}\n\n.tab-icon {\n width: 44px;\n height: 44px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n transition: transform 0.2s ease;\n}\n\n.tab-card:hover .tab-icon {\n transform: scale(1.05);\n}\n\n.tab-icon.stats {\n background: var(--tab-icon-stats-gradient);\n color: var(--crm-accent);\n}\n\n.tab-icon.logs {\n background: var(--tab-icon-logs-gradient);\n color: var(--crm-success);\n}\n\n.tab-icon.antispam {\n background: var(--tab-icon-antispam-gradient);\n color: var(--crm-warning);\n}\n\n.tab-icon.manual-pushes {\n background: var(--tab-icon-manual-pushes-gradient);\n color: var(--crm-accent);\n}\n\n.tab-content-wrapper {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n}\n\n.tab-label {\n font-size: 15px;\n font-weight: 600;\n color: var(--crm-text-primary);\n margin: 0;\n}\n\n.tab-description {\n font-size: 12px;\n color: var(--crm-text-muted);\n margin: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tab-badge {\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 28px;\n height: 28px;\n padding: 0 10px;\n border-radius: 14px;\n font-size: 13px;\n font-weight: 600;\n flex-shrink: 0;\n transition: all 0.2s ease;\n}\n\n.tab-badge.stats {\n background: var(--crm-accent-light);\n color: var(--crm-accent);\n}\n\n.tab-badge.logs {\n background: var(--crm-success-light);\n color: var(--crm-success);\n}\n\n.tab-badge.antispam {\n background: var(--crm-warning-light);\n color: var(--crm-warning);\n}\n\n.tab-badge.manual-pushes {\n background: var(--crm-accent-light);\n color: var(--crm-accent);\n}\n\n.tab-card.active .tab-badge.stats {\n background: var(--crm-accent);\n color: white;\n}\n\n.tab-card.active .tab-badge.logs {\n background: var(--crm-success);\n color: white;\n}\n\n.tab-card.active .tab-badge.antispam {\n background: var(--crm-warning);\n color: white;\n}\n\n.tab-card.active .tab-badge.manual-pushes {\n background: var(--crm-accent);\n color: white;\n}\n\n.tab-content {\n margin: 0 24px;\n padding: 24px;\n background: var(--crm-bg-primary);\n border-radius: 16px;\n box-shadow: 0 4px 16px var(--crm-shadow);\n}\n\n.loading-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 64px;\n gap: 16px;\n color: var(--crm-text-muted);\n}\n\n.loading-spinner {\n width: 40px;\n height: 40px;\n border: 3px solid var(--crm-border);\n border-top-color: var(--crm-accent);\n border-radius: 50%;\n animation: spin 0.8s linear infinite;\n}\n\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n/* Stats Cards */\n.stats-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n}\n\n.stat-card {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 20px;\n border: 1px solid var(--crm-border-light);\n box-shadow: 0 2px 8px var(--crm-shadow);\n transition: all 0.2s ease;\n}\n\n.stat-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 16px var(--crm-shadow-strong);\n}\n\n.stat-card-header {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 12px;\n}\n\n.stat-card-icon {\n width: 40px;\n height: 40px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.stat-card-icon.primary {\n background: var(--crm-accent-light);\n color: var(--crm-accent);\n}\n\n.stat-card-icon.success {\n background: var(--crm-success-light);\n color: var(--crm-success);\n}\n\n.stat-card-icon.warning {\n background: var(--crm-warning-light);\n color: var(--crm-warning);\n}\n\n.stat-card-icon.danger {\n background: var(--crm-danger-light);\n color: var(--crm-danger);\n}\n\n.stat-card-value {\n font-size: 28px;\n font-weight: 700;\n color: var(--crm-text-primary);\n line-height: 1.2;\n}\n\n.stat-card-label {\n font-size: 13px;\n color: var(--crm-text-muted);\n margin-top: 4px;\n}\n\n.stat-card-trend {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 12px;\n margin-top: 8px;\n}\n\n.stat-card-trend.up {\n background: var(--crm-success-light);\n color: var(--crm-success);\n}\n\n.stat-card-trend.down {\n background: var(--crm-danger-light);\n color: var(--crm-danger);\n}\n\n.stat-card-trend.neutral {\n background: var(--crm-bg-tertiary);\n color: var(--crm-text-muted);\n}\n\n.export-dropdown {\n position: relative;\n display: inline-block;\n}\n\n.export-dropdown-menu {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n background: var(--crm-bg-primary);\n border: 1px solid var(--crm-border);\n border-radius: 8px;\n box-shadow: 0 4px 16px var(--crm-shadow-strong);\n min-width: 140px;\n z-index: 100;\n overflow: hidden;\n}\n\n.export-dropdown-item {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 10px 14px;\n border: none;\n background: none;\n font-size: 13px;\n color: var(--crm-text-primary);\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.export-dropdown-item:hover {\n background: var(--crm-bg-tertiary);\n}\n\n.export-dropdown-item svg {\n width: 16px;\n height: 16px;\n color: var(--crm-text-muted);\n}\n\n.granularity-select {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.granularity-select label {\n font-size: 12px;\n color: var(--crm-text-muted);\n white-space: nowrap;\n}\n\n.granularity-pills {\n display: flex;\n gap: 2px;\n background: var(--crm-bg-tertiary);\n border-radius: 6px;\n padding: 2px;\n}\n\n.granularity-pill {\n padding: 4px 10px;\n font-size: 11px;\n font-weight: 600;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.15s ease;\n background: transparent;\n color: var(--crm-text-muted);\n}\n\n.granularity-pill.active {\n background: var(--crm-accent);\n color: white;\n}\n\n.granularity-pill:hover:not(.active) {\n background: var(--crm-bg-secondary);\n color: var(--crm-text-primary);\n}\n\n.compare-toggle {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: var(--crm-text-secondary);\n cursor: pointer;\n}\n\n.compare-toggle input {\n width: 16px;\n height: 16px;\n accent-color: var(--crm-accent);\n}\n\n.compare-toggle:hover {\n color: var(--crm-text-primary);\n}\n\n.metrics-toggle {\n display: flex;\n gap: 12px;\n margin-top: 12px;\n padding-top: 12px;\n border-top: 1px solid var(--crm-border-light);\n}\n\n.metrics-toggle label {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--crm-text-secondary);\n cursor: pointer;\n}\n\n.metrics-toggle input {\n width: 14px;\n height: 14px;\n}\n\n.metrics-toggle label:hover {\n color: var(--crm-text-primary);\n}\n\n/* Chart Container */\n.chart-container {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 24px;\n border: 1px solid var(--crm-border-light);\n box-shadow: 0 2px 8px var(--crm-shadow);\n}\n\n.chart-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n}\n\n.chart-title {\n font-size: 16px;\n font-weight: 600;\n color: var(--crm-text-primary);\n}\n\n.chart-subtitle {\n font-size: 13px;\n color: var(--crm-text-muted);\n margin-top: 2px;\n}\n\n/* Table Styles */\n.crm-table-wrapper {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n border: 1px solid var(--crm-border-light);\n box-shadow: 0 2px 8px var(--crm-shadow);\n overflow: hidden;\n}\n\n.crm-table {\n width: 100%;\n border-collapse: collapse;\n}\n\n.crm-table thead {\n background: var(--crm-table-header-bg);\n}\n\n.crm-table th {\n padding: 14px 16px;\n text-align: left;\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--crm-text-secondary);\n border-bottom: 1px solid var(--crm-border);\n}\n\n.crm-table td {\n padding: 14px 16px;\n font-size: 14px;\n color: var(--crm-text-primary);\n border-bottom: 1px solid var(--crm-border-light);\n}\n\n.crm-table tbody tr {\n transition: background-color 0.15s ease;\n}\n\n.crm-table tbody tr:hover {\n background: var(--crm-table-row-hover);\n}\n\n.crm-table tbody tr:nth-child(even) {\n background: var(--crm-table-row-stripe);\n}\n\n.crm-table tbody tr:nth-child(even):hover {\n background: var(--crm-table-row-hover);\n}\n\n.crm-table tbody tr:last-child td {\n border-bottom: none;\n}\n\n/* Status Badge */\n.status-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.status-badge::before {\n content: '';\n width: 6px;\n height: 6px;\n border-radius: 50%;\n}\n\n.status-badge.sent {\n background: var(--crm-success-light);\n color: var(--crm-success);\n}\n\n.status-badge.sent::before {\n background: var(--crm-success);\n}\n\n.status-badge.planned {\n background: var(--crm-warning-light);\n color: var(--crm-warning);\n}\n\n.status-badge.planned::before {\n background: var(--crm-warning);\n}\n\n.status-badge.failed {\n background: var(--crm-danger-light);\n color: var(--crm-danger);\n}\n\n.status-badge.failed::before {\n background: var(--crm-danger);\n}\n\n.status-badge.cancelled {\n background: var(--crm-bg-tertiary);\n color: var(--crm-text-muted);\n}\n\n.status-badge.cancelled::before {\n background: var(--crm-text-muted);\n}\n\n.status-badge.warning {\n background: var(--crm-warning-light);\n color: var(--crm-warning);\n}\n\n.status-badge.warning::before {\n background: var(--crm-warning);\n}\n\n/* Pagination */\n.pagination {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 0;\n}\n\n.pagination-info {\n font-size: 13px;\n color: var(--crm-text-muted);\n}\n\n.pagination-controls {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.pagination-btn {\n padding: 8px 16px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n background: var(--crm-bg-primary);\n border: 1px solid var(--crm-border);\n color: var(--crm-text-primary);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.pagination-btn:hover:not(:disabled) {\n background: var(--crm-accent-light);\n border-color: var(--crm-accent);\n color: var(--crm-accent);\n}\n\n.pagination-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.pagination-current {\n padding: 8px 12px;\n font-size: 13px;\n font-weight: 600;\n color: var(--crm-text-primary);\n}\n\n/* Empty State */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 24px;\n text-align: center;\n}\n\n.empty-state-icon {\n width: 64px;\n height: 64px;\n border-radius: 16px;\n background: var(--crm-bg-tertiary);\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 16px;\n color: var(--crm-text-muted);\n}\n\n.empty-state-title {\n font-size: 16px;\n font-weight: 600;\n color: var(--crm-text-primary);\n margin-bottom: 4px;\n}\n\n.empty-state-description {\n font-size: 14px;\n color: var(--crm-text-muted);\n}\n\n/* Section Header in table wrapper */\n.crm-table-wrapper .section-header,\n.crm-table-wrapper > div:first-child {\n padding: 16px 20px;\n}\n\n/* Funnel grid responsive */\n.funnel-grid {\n display: grid;\n grid-template-columns: repeat(5, 1fr);\n gap: 12px;\n}\n\n@media (max-width: 1200px) {\n .stats-grid {\n grid-template-columns: repeat(3, 1fr) !important;\n }\n\n .funnel-grid {\n grid-template-columns: repeat(3, 1fr);\n }\n}\n\n@media (max-width: 1024px) {\n .tab-navigation {\n flex-direction: column;\n gap: 8px;\n padding: 0 16px;\n }\n\n .tab-card {\n padding: 14px 16px;\n }\n\n .tab-description {\n display: none;\n }\n\n .tab-content {\n margin: 0 16px;\n border-radius: 12px;\n }\n\n .stats-grid {\n grid-template-columns: repeat(2, 1fr) !important;\n }\n\n .funnel-grid {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .chart-container {\n padding: 16px;\n }\n}\n\n@media (max-width: 768px) {\n .stats-grid {\n grid-template-columns: repeat(2, 1fr) !important;\n }\n\n .crm-table th,\n .crm-table td {\n padding: 10px 12px;\n font-size: 13px;\n }\n}\n\n@media (max-width: 640px) {\n .dashboard-header {\n padding: 20px;\n border-radius: 0 0 12px 12px;\n }\n\n .dashboard-header h1 {\n font-size: 22px;\n }\n\n .tab-icon {\n width: 36px;\n height: 36px;\n }\n\n .tab-label {\n font-size: 14px;\n }\n\n .stats-grid {\n grid-template-columns: 1fr !important;\n }\n\n .funnel-grid {\n grid-template-columns: 1fr;\n }\n\n .stat-card-value {\n font-size: 24px;\n }\n\n .chart-container {\n padding: 12px;\n }\n}\n\n/* Today Widget */\n.today-widget {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 20px;\n border: 1px solid var(--crm-border-light);\n box-shadow: 0 4px 16px var(--crm-shadow);\n}\n\n.today-widget.loading {\n min-height: 160px;\n display: flex;\n flex-direction: column;\n}\n\n.today-widget-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n}\n\n.today-widget-title {\n font-size: 13px;\n font-weight: 700;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n color: var(--crm-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.live-dot {\n width: 8px;\n height: 8px;\n background: var(--crm-success);\n border-radius: 50%;\n animation: pulse 2s ease-in-out infinite;\n box-shadow: 0 0 8px var(--crm-success);\n}\n\n@keyframes pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.7; transform: scale(1.2); }\n}\n\n.today-widget-comparison {\n font-size: 11px;\n color: var(--crm-text-muted);\n background: var(--crm-bg-tertiary);\n padding: 4px 10px;\n border-radius: 12px;\n}\n\n.today-widget-metrics {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 10px;\n margin-bottom: 16px;\n}\n\n.today-metric {\n text-align: center;\n padding: 12px 8px;\n background: var(--crm-bg-secondary);\n border-radius: 10px;\n border: 1px solid var(--crm-border-light);\n transition: border-color 0.2s ease, background-color 0.2s ease;\n}\n\n.today-metric:hover {\n border-color: var(--crm-border);\n background: var(--crm-bg-tertiary);\n}\n\n.today-metric-value {\n font-size: 22px;\n font-weight: 700;\n line-height: 1.2;\n color: var(--crm-text-primary);\n}\n\n.today-metric-label {\n font-size: 11px;\n color: var(--crm-text-muted);\n margin-top: 4px;\n font-weight: 500;\n}\n\n.trend-indicator {\n display: inline-flex;\n align-items: center;\n font-size: 11px;\n font-weight: 600;\n margin-top: 6px;\n padding: 3px 8px;\n border-radius: 10px;\n}\n\n.trend-indicator.up {\n background: var(--crm-success-light);\n color: var(--crm-success);\n}\n\n.trend-indicator.down {\n background: var(--crm-danger-light);\n color: var(--crm-danger);\n}\n\n.trend-neutral {\n display: inline-flex;\n font-size: 11px;\n font-weight: 600;\n margin-top: 6px;\n padding: 3px 8px;\n border-radius: 10px;\n background: var(--crm-bg-tertiary);\n color: var(--crm-text-muted);\n}\n\n.today-widget-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 12px;\n border-top: 1px solid var(--crm-border-light);\n font-size: 12px;\n}\n\n.today-rates {\n display: flex;\n gap: 16px;\n}\n\n.today-rates span {\n color: var(--crm-text-muted);\n font-weight: 500;\n}\n\n.today-rates span strong {\n color: var(--crm-text-primary);\n font-weight: 700;\n}\n\n.today-updated {\n color: var(--crm-text-muted);\n font-size: 11px;\n}\n\n.today-widget-loading {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n color: var(--crm-text-muted);\n}\n\n/* Cohort Heat Map */\n.cohort-heatmap {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 24px;\n border: 1px solid var(--crm-border-light);\n}\n\n.cohort-table-wrapper {\n overflow-x: auto;\n margin: 16px 0;\n}\n\n.cohort-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n}\n\n.cohort-header-cell {\n padding: 10px 8px;\n text-align: center;\n font-weight: 600;\n color: var(--crm-text-secondary);\n background: var(--crm-bg-secondary);\n border-bottom: 1px solid var(--crm-border);\n}\n\n.cohort-period-header {\n min-width: 60px;\n}\n\n.cohort-date-cell {\n padding: 10px 12px;\n font-weight: 600;\n color: var(--crm-text-primary);\n background: var(--crm-bg-secondary);\n white-space: nowrap;\n}\n\n.cohort-users-cell {\n padding: 10px 12px;\n text-align: right;\n color: var(--crm-text-secondary);\n background: var(--crm-bg-secondary);\n}\n\n.cohort-cell {\n padding: 10px 8px;\n text-align: center;\n font-weight: 600;\n font-size: 11px;\n transition: all 0.15s ease;\n cursor: default;\n}\n\n.cohort-cell:hover {\n filter: brightness(1.1);\n z-index: 1;\n position: relative;\n}\n\n.cohort-legend {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n font-size: 11px;\n color: var(--crm-text-muted);\n margin-top: 12px;\n}\n\n.cohort-legend-gradient {\n width: 120px;\n height: 8px;\n border-radius: 4px;\n background: linear-gradient(90deg, rgba(239, 68, 68, 0.6) 0%, rgba(245, 158, 11, 0.6) 50%, rgba(16, 185, 129, 0.8) 100%);\n}\n\n.cohort-loading {\n text-align: center;\n padding: 32px;\n color: var(--crm-text-muted);\n}\n\n/* A/B Tests */\n.ab-tests-section {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 24px;\n border: 1px solid var(--crm-border-light);\n}\n\n.ab-tests-list {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.ab-test-card {\n background: var(--crm-bg-secondary);\n border-radius: 10px;\n padding: 20px;\n border: 1px solid var(--crm-border-light);\n}\n\n.ab-campaign-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n}\n\n.ab-test-name {\n font-size: 15px;\n font-weight: 600;\n color: var(--crm-text-primary);\n}\n\n.ab-block-count {\n font-size: 12px;\n font-weight: 500;\n color: var(--crm-text-muted);\n background: var(--crm-bg-tertiary);\n padding: 2px 10px;\n border-radius: 10px;\n}\n\n.ab-blocks-list {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.ab-message-block {\n background: var(--crm-bg-primary);\n border-radius: 8px;\n padding: 16px;\n border: 1px solid var(--crm-border-light);\n}\n\n.ab-block-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 12px;\n}\n\n.ab-block-name {\n font-size: 13px;\n font-weight: 600;\n color: var(--crm-text-secondary);\n}\n\n.ab-test-status {\n font-size: 12px;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.ab-test-variants {\n display: grid;\n grid-template-columns: 1fr auto 1fr;\n gap: 16px;\n align-items: center;\n}\n\n.ab-variant {\n background: var(--crm-bg-secondary);\n border-radius: 8px;\n padding: 16px;\n border: 2px solid transparent;\n transition: all 0.2s ease;\n}\n\n.ab-variant.winner {\n border-color: var(--crm-success);\n box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.1);\n}\n\n.ab-variant-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.ab-variant-name {\n font-weight: 600;\n color: var(--crm-text-primary);\n}\n\n.ab-variant-tag {\n font-size: 10px;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 10px;\n text-transform: uppercase;\n}\n\n.ab-variant-tag.control {\n background: var(--crm-bg-tertiary);\n color: var(--crm-text-muted);\n}\n\n.ab-variant-tag.treatment {\n background: var(--crm-accent-light);\n color: var(--crm-accent);\n}\n\n.ab-variant-stats {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 12px;\n}\n\n.ab-stat {\n text-align: center;\n}\n\n.ab-stat-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--crm-text-primary);\n}\n\n.ab-stat-label {\n font-size: 10px;\n color: var(--crm-text-muted);\n margin-top: 2px;\n}\n\n.ab-vs {\n font-size: 12px;\n font-weight: 700;\n color: var(--crm-text-muted);\n text-align: center;\n}\n\n.ab-test-analysis {\n display: flex;\n gap: 24px;\n margin-top: 16px;\n padding-top: 16px;\n border-top: 1px solid var(--crm-border-light);\n}\n\n.ab-analysis-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n}\n\n.ab-analysis-label {\n color: var(--crm-text-muted);\n}\n\n.ab-analysis-value {\n font-weight: 600;\n color: var(--crm-text-primary);\n}\n\n.ab-analysis-value.positive {\n color: var(--crm-success);\n}\n\n.ab-analysis-value.negative {\n color: var(--crm-danger);\n}\n\n.ab-analysis-value.confidence-high {\n color: var(--crm-success);\n}\n\n.ab-analysis-value.confidence-medium {\n color: var(--crm-success);\n}\n\n.ab-analysis-value.confidence-low {\n color: var(--crm-warning);\n}\n\n.ab-tests-loading {\n text-align: center;\n padding: 32px;\n color: var(--crm-text-muted);\n}\n\n/* Multi-variant A/B Tests */\n.ab-test-variants-multi {\n display: flex;\n align-items: stretch;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.ab-vs-small {\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n font-weight: 700;\n color: var(--crm-text-muted);\n padding: 0 4px;\n}\n\n.ab-variant-compact {\n flex: 1;\n min-width: 140px;\n max-width: 200px;\n background: var(--crm-bg-secondary);\n border-radius: 8px;\n padding: 12px;\n border: 2px solid transparent;\n transition: all 0.2s ease;\n}\n\n.ab-variant-compact.winner {\n border-color: var(--crm-success);\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1);\n}\n\n.ab-variant-compact .ab-variant-header {\n margin-bottom: 8px;\n flex-wrap: wrap;\n gap: 4px;\n}\n\n.ab-variant-compact .ab-variant-name {\n font-size: 13px;\n}\n\n.ab-variant-stats-compact {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.ab-stat-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: 12px;\n}\n\n.ab-stat-row .ab-stat-label {\n color: var(--crm-text-muted);\n font-size: 11px;\n}\n\n.ab-stat-row .ab-stat-value {\n font-weight: 600;\n color: var(--crm-text-primary);\n font-size: 12px;\n}\n\n.ab-variant-lift {\n margin-top: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--crm-border-light);\n text-align: center;\n font-size: 12px;\n font-weight: 600;\n}\n\n.ab-variant-lift .positive {\n color: var(--crm-success);\n}\n\n.ab-variant-lift .negative {\n color: var(--crm-danger);\n}\n\n.ab-test-analysis-compact {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 12px;\n padding-top: 12px;\n border-top: 1px solid var(--crm-border-light);\n font-size: 12px;\n}\n\n.ab-test-analysis-compact .ab-analysis-label {\n color: var(--crm-text-muted);\n}\n\n.ab-test-analysis-compact .ab-analysis-value {\n font-weight: 600;\n}\n\n@media (max-width: 640px) {\n .ab-test-variants-multi {\n flex-direction: column;\n }\n\n .ab-variant-compact {\n max-width: none;\n }\n\n .ab-vs-small {\n padding: 4px 0;\n }\n}\n\n/* Send Time Heat Map */\n.sendtime-heatmap {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 24px;\n border: 1px solid var(--crm-border-light);\n}\n\n.sendtime-best {\n text-align: right;\n}\n\n.sendtime-best-label {\n font-size: 11px;\n color: var(--crm-text-muted);\n}\n\n.sendtime-best-value {\n font-size: 16px;\n font-weight: 700;\n color: var(--crm-success);\n}\n\n.sendtime-best-rate {\n font-size: 12px;\n color: var(--crm-success);\n}\n\n.sendtime-grid-wrapper {\n overflow-x: auto;\n margin: 16px 0;\n}\n\n.sendtime-grid {\n display: grid;\n grid-template-columns: 60px repeat(7, 1fr);\n gap: 2px;\n min-width: 400px;\n}\n\n.sendtime-corner {\n background: transparent;\n}\n\n.sendtime-day-header {\n text-align: center;\n font-size: 11px;\n font-weight: 600;\n color: var(--crm-text-secondary);\n padding: 8px 4px;\n}\n\n.sendtime-hour-label {\n font-size: 10px;\n color: var(--crm-text-muted);\n display: flex;\n align-items: center;\n justify-content: flex-end;\n padding-right: 8px;\n}\n\n.sendtime-cell {\n aspect-ratio: 1;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: default;\n transition: all 0.15s ease;\n min-height: 32px;\n}\n\n.sendtime-cell:hover {\n filter: brightness(1.15);\n z-index: 1;\n}\n\n.sendtime-cell.best {\n box-shadow: 0 0 0 2px var(--crm-success);\n}\n\n.sendtime-cell-value {\n font-size: 9px;\n font-weight: 600;\n color: inherit;\n}\n\n.sendtime-legend {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n font-size: 11px;\n color: var(--crm-text-muted);\n margin-top: 12px;\n}\n\n.sendtime-legend-gradient {\n width: 120px;\n height: 8px;\n border-radius: 4px;\n background: linear-gradient(90deg, rgba(239, 68, 68, 0.6) 0%, rgba(245, 158, 11, 0.6) 50%, rgba(16, 185, 129, 0.8) 100%);\n}\n\n.sendtime-loading {\n text-align: center;\n padding: 32px;\n color: var(--crm-text-muted);\n}\n\n/* Segment Table */\n.segment-table-section {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 24px;\n border: 1px solid var(--crm-border-light);\n}\n\n.segment-cards {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 16px;\n}\n\n.segment-card {\n background: var(--crm-bg-secondary);\n border-radius: 10px;\n padding: 16px;\n border-left: 4px solid var(--crm-text-muted);\n transition: all 0.2s ease;\n}\n\n.segment-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px var(--crm-shadow);\n}\n\n.segment-card-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.segment-icon {\n font-size: 18px;\n}\n\n.segment-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--crm-text-primary);\n flex: 1;\n}\n\n.segment-users {\n font-size: 12px;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.segment-metrics {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.segment-metric {\n text-align: center;\n}\n\n.segment-metric-value {\n font-size: 16px;\n font-weight: 700;\n color: var(--crm-text-primary);\n}\n\n.segment-metric-label {\n font-size: 10px;\n color: var(--crm-text-muted);\n}\n\n.segment-bar-wrapper {\n height: 4px;\n background: var(--crm-bg-tertiary);\n border-radius: 2px;\n overflow: hidden;\n}\n\n.segment-bar {\n height: 100%;\n border-radius: 2px;\n transition: width 0.3s ease;\n}\n\n.segment-loading {\n text-align: center;\n padding: 32px;\n color: var(--crm-text-muted);\n}\n\n/* Goal Tracker */\n.goal-tracker {\n background: var(--crm-bg-primary);\n border-radius: 12px;\n padding: 20px;\n border: 1px solid var(--crm-border-light);\n}\n\n.goal-tracker-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 16px;\n}\n\n.goal-edit-btn {\n background: transparent;\n border: none;\n font-size: 16px;\n cursor: pointer;\n padding: 4px 8px;\n border-radius: 6px;\n transition: background 0.15s ease;\n}\n\n.goal-edit-btn:hover {\n background: var(--crm-bg-tertiary);\n}\n\n.goal-items {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.goal-item {\n padding: 12px;\n background: var(--crm-bg-secondary);\n border-radius: 8px;\n}\n\n.goal-item-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 8px;\n}\n\n.goal-icon {\n font-size: 16px;\n}\n\n.goal-name {\n font-size: 13px;\n font-weight: 600;\n color: var(--crm-text-primary);\n flex: 1;\n}\n\n.goal-progress-badge {\n font-size: 11px;\n font-weight: 700;\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.goal-bar-wrapper {\n height: 6px;\n background: var(--crm-bg-tertiary);\n border-radius: 3px;\n overflow: hidden;\n margin-bottom: 8px;\n}\n\n.goal-bar {\n height: 100%;\n border-radius: 3px;\n transition: width 0.3s ease;\n}\n\n.goal-values {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n}\n\n.goal-current {\n font-weight: 600;\n color: var(--crm-text-primary);\n}\n\n.goal-separator {\n color: var(--crm-text-muted);\n}\n\n.goal-target {\n color: var(--crm-text-secondary);\n}\n\n.goal-target-input {\n width: 80px;\n padding: 2px 6px;\n border: 1px solid var(--crm-border);\n border-radius: 4px;\n font-size: 12px;\n background: var(--crm-bg-primary);\n color: var(--crm-text-primary);\n}\n\n.goal-target-input:focus {\n outline: none;\n border-color: var(--crm-accent);\n}\n\n/* Enhanced Stats Layout */\n.stats-header-row {\n display: grid;\n grid-template-columns: 320px 1fr;\n gap: 16px;\n margin-bottom: 24px;\n}\n\n@media (max-width: 1024px) {\n .stats-header-row {\n grid-template-columns: 1fr;\n }\n\n .ab-test-variants {\n grid-template-columns: 1fr;\n }\n\n .ab-vs {\n padding: 8px 0;\n }\n\n .ab-test-analysis {\n flex-wrap: wrap;\n gap: 12px;\n }\n}\n\n@media (max-width: 768px) {\n .segment-metrics {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n";
3101
3565
  const filterBarStyles = "/* Filter Bar Container */\n.filter-bar {\n position: sticky;\n top: 0;\n z-index: 10;\n transition: box-shadow 0.2s ease, transform 0.2s ease;\n border-radius: 12px;\n}\n\n.filter-bar.scrolled {\n box-shadow: 0 4px 20px var(--crm-shadow-strong, rgba(0, 0, 0, 0.12));\n}\n\n/* Date Presets Button Group */\n.date-presets-group {\n display: inline-flex;\n gap: 0;\n background: var(--crm-bg-tertiary, #eaeaef);\n border-radius: 8px;\n padding: 3px;\n}\n\n.date-preset-btn {\n padding: 6px 10px;\n font-size: 12px;\n font-weight: 500;\n border-radius: 6px;\n background: transparent;\n border: none;\n cursor: pointer;\n transition: all 0.15s ease;\n color: var(--crm-text-secondary, #666687);\n white-space: nowrap;\n}\n\n.date-preset-btn:hover {\n background: var(--crm-bg-primary, #ffffff);\n color: var(--crm-text-primary, #32324d);\n}\n\n.date-preset-btn.active {\n background: var(--crm-accent, #4945ff);\n color: white;\n box-shadow: 0 2px 4px rgba(73, 69, 255, 0.25);\n}\n\n/* Active Filters */\n.active-filters {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.active-filters-label {\n font-size: 13px;\n font-weight: 500;\n color: var(--crm-text-muted, #8e8ea9);\n}\n\n.active-filters-chips {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n/* Filter Chips */\n.filter-chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n background: var(--crm-accent-light, #d9d8ff);\n border: 1px solid transparent;\n border-radius: 20px;\n cursor: pointer;\n transition: all 0.15s ease;\n font-family: inherit;\n}\n\n.filter-chip:hover {\n background: var(--crm-accent, #4945ff);\n transform: translateY(-1px);\n}\n\n.filter-chip:hover .filter-chip-label,\n.filter-chip:hover .filter-chip-value {\n color: white;\n}\n\n.filter-chip:hover .filter-chip-remove {\n opacity: 1;\n color: white;\n}\n\n.filter-chip-label {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n color: var(--crm-accent, #4945ff);\n transition: color 0.15s ease;\n}\n\n.filter-chip-value {\n font-size: 13px;\n font-weight: 500;\n color: var(--crm-text-primary, #32324d);\n max-width: 150px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n transition: color 0.15s ease;\n}\n\n.filter-chip-remove {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: rgba(0, 0, 0, 0.1);\n opacity: 0.6;\n transition: all 0.15s ease;\n color: var(--crm-text-primary, #32324d);\n}\n\n.filter-chip:hover .filter-chip-remove {\n background: rgba(255, 255, 255, 0.2);\n}\n\n/* Clear All Button */\n.filter-chip.filter-chip-clear {\n background: var(--crm-bg-tertiary, #eaeaef);\n color: var(--crm-text-secondary, #666687);\n font-size: 12px;\n font-weight: 500;\n padding: 6px 12px;\n}\n\n.filter-chip.filter-chip-clear:hover {\n background: var(--crm-danger, #dc2626);\n color: white;\n}\n\n/* Filter Section Styling */\n.filter-section {\n display: flex;\n align-items: flex-end;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.filter-section-divider {\n width: 1px;\n height: 32px;\n background: var(--crm-border-light, #eaeaef);\n margin: 0 4px;\n}\n\n/* Filter Actions */\n.filter-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-left: auto;\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n .date-presets-group {\n width: 100%;\n justify-content: center;\n }\n\n .date-preset-btn {\n flex: 1;\n text-align: center;\n padding: 8px 6px;\n }\n\n .active-filters {\n flex-direction: column;\n align-items: flex-start;\n }\n\n .active-filters-chips {\n width: 100%;\n }\n\n .filter-chip {\n flex: 1;\n justify-content: center;\n min-width: 0;\n }\n\n .filter-chip-value {\n max-width: 100px;\n }\n}\n\n/* Animation for chip removal */\n@keyframes chipFadeIn {\n from {\n opacity: 0;\n transform: scale(0.9) translateY(-4px);\n }\n to {\n opacity: 1;\n transform: scale(1) translateY(0);\n }\n}\n\n.filter-chip {\n animation: chipFadeIn 0.2s ease;\n}\n\n/* Advanced Filters Toggle */\n.advanced-toggle {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 6px 12px;\n font-size: 13px;\n font-weight: 500;\n color: var(--crm-text-secondary, #666687);\n background: transparent;\n border: 1px dashed var(--crm-border, #dcdce4);\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.advanced-toggle:hover {\n border-color: var(--crm-accent, #4945ff);\n color: var(--crm-accent, #4945ff);\n background: var(--crm-accent-light, #d9d8ff);\n}\n\n.advanced-toggle.active {\n border-style: solid;\n border-color: var(--crm-accent, #4945ff);\n color: var(--crm-accent, #4945ff);\n background: var(--crm-accent-light, #d9d8ff);\n}\n\n.advanced-toggle-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n font-size: 11px;\n font-weight: 600;\n background: var(--crm-accent, #4945ff);\n color: white;\n border-radius: 9px;\n}\n\n/* Advanced Section */\n.advanced-section {\n margin-top: 16px;\n padding-top: 16px;\n border-top: 1px solid var(--crm-border-light, #eaeaef);\n animation: slideDown 0.2s ease;\n}\n\n@keyframes slideDown {\n from {\n opacity: 0;\n transform: translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Apply Button Indicator */\n.apply-indicator {\n display: inline-block;\n width: 8px;\n height: 8px;\n background: var(--crm-warning, #d97706);\n border-radius: 50%;\n margin-left: 6px;\n animation: pulse 1.5s ease-in-out infinite;\n}\n\n@keyframes pulse {\n 0%, 100% {\n opacity: 1;\n transform: scale(1);\n }\n 50% {\n opacity: 0.6;\n transform: scale(0.85);\n }\n}\n";
3102
3566
  const useIsDarkTheme = () => {
3103
3567
  const theme = useTheme();
@@ -3124,6 +3588,13 @@ const tabs = [
3124
3588
  description: "Заблокированные сообщения",
3125
3589
  icon: /* @__PURE__ */ jsx(Cross, { width: 20, height: 20 }),
3126
3590
  color: "#d97706"
3591
+ },
3592
+ {
3593
+ id: "manual-pushes",
3594
+ label: "Ручные рассылки",
3595
+ description: "Шаблоны Manual Push и ручная отправка",
3596
+ icon: /* @__PURE__ */ jsx(Message, { width: 20, height: 20 }),
3597
+ color: "#7b79ff"
3127
3598
  }
3128
3599
  ];
3129
3600
  const formatNumber = (num) => {
@@ -3145,11 +3616,12 @@ const DashboardContent = () => {
3145
3616
  );
3146
3617
  const { data: stats, isLoading } = useDashboardStats(defaultFilters);
3147
3618
  const badgeCounts = useMemo(() => {
3148
- if (!stats) return { stats: 0, logs: 0, antispam: 0 };
3619
+ if (!stats) return { stats: 0, logs: 0, antispam: 0, "manual-pushes": 0 };
3149
3620
  return {
3150
3621
  stats: stats.totals?.sent ?? 0,
3151
3622
  logs: stats.funnel?.sent ?? 0,
3152
- antispam: stats.funnel?.blocked_total ?? 0
3623
+ antispam: stats.funnel?.blocked_total ?? 0,
3624
+ "manual-pushes": 0
3153
3625
  };
3154
3626
  }, [stats]);
3155
3627
  const renderTabContent = () => {
@@ -3160,6 +3632,8 @@ const DashboardContent = () => {
3160
3632
  return /* @__PURE__ */ jsx(LogsTable, {});
3161
3633
  case "antispam":
3162
3634
  return /* @__PURE__ */ jsx(AntiSpamLogsTable$1, {});
3635
+ case "manual-pushes":
3636
+ return /* @__PURE__ */ jsx(ManualPushesView, {});
3163
3637
  default:
3164
3638
  return null;
3165
3639
  }