@inspirer-dev/crm-dashboard 1.0.90 → 1.0.92

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.
@@ -7,6 +7,7 @@ const icons = require("@strapi/icons");
7
7
  const styledComponents = require("styled-components");
8
8
  const reactQuery = require("@tanstack/react-query");
9
9
  const reactQueryDevtools = require("@tanstack/react-query-devtools");
10
+ const admin = require("@strapi/strapi/admin");
10
11
  const recharts = require("recharts");
11
12
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
12
13
  const React__default = /* @__PURE__ */ _interopDefault(React);
@@ -33,7 +34,10 @@ const crmKeys = {
33
34
  antiSpamLogsWithFilters: (filters, page) => [...crmKeys.antiSpamLogs(), "list", { filters, page }],
34
35
  campaigns: () => [...crmKeys.all, "campaigns"],
35
36
  segments: () => [...crmKeys.all, "segments"],
36
- templates: () => [...crmKeys.all, "templates"]
37
+ templates: () => [...crmKeys.all, "templates"],
38
+ manualPushTemplates: () => [...crmKeys.all, "manual-pushes", "templates"],
39
+ manualPushHistory: () => [...crmKeys.all, "manual-pushes", "history"],
40
+ manualPushStats: (manualPushId) => [...crmKeys.all, "manual-pushes", "stats", manualPushId]
37
41
  };
38
42
  const QueryProvider = ({ children }) => {
39
43
  return /* @__PURE__ */ jsxRuntime.jsxs(reactQuery.QueryClientProvider, { client: queryClient, children: [
@@ -275,6 +279,72 @@ const useABTestData = (filters) => {
275
279
  staleTime: 2 * 60 * 1e3
276
280
  });
277
281
  };
282
+ const PLUGIN_BASE = "/crm-dashboard";
283
+ const extractBackendError = (data) => {
284
+ if (!data) return "Unknown error";
285
+ if (data.backend?.message) return data.backend.message;
286
+ if (typeof data.backend === "string") return data.backend;
287
+ if (data.error) return data.error;
288
+ return JSON.stringify(data);
289
+ };
290
+ const useManualPushTemplates = () => {
291
+ const { get } = admin.useFetchClient();
292
+ return reactQuery.useQuery({
293
+ queryKey: crmKeys.manualPushTemplates(),
294
+ queryFn: async () => {
295
+ const { data } = await get(`${PLUGIN_BASE}/manual-pushes/templates`);
296
+ if (data?.error) throw new Error(data.error);
297
+ return data?.data ?? [];
298
+ },
299
+ staleTime: 60 * 1e3
300
+ });
301
+ };
302
+ const useManualPushHistory = () => {
303
+ const { get } = admin.useFetchClient();
304
+ return reactQuery.useQuery({
305
+ queryKey: crmKeys.manualPushHistory(),
306
+ queryFn: async () => {
307
+ const { data } = await get(`${PLUGIN_BASE}/manual-pushes/history`, {
308
+ params: { limit: 50 }
309
+ });
310
+ if (Array.isArray(data)) return data;
311
+ if (data?.data && Array.isArray(data.data)) return data.data;
312
+ return [];
313
+ },
314
+ staleTime: 15 * 1e3,
315
+ refetchInterval: 10 * 1e3
316
+ });
317
+ };
318
+ const useDispatchManualPush = () => {
319
+ const { post } = admin.useFetchClient();
320
+ const queryClient2 = reactQuery.useQueryClient();
321
+ return reactQuery.useMutation({
322
+ mutationFn: async (payload) => {
323
+ const { data } = await post(`${PLUGIN_BASE}/manual-pushes/dispatch`, payload);
324
+ if (data?.error) throw new Error(extractBackendError(data));
325
+ return data;
326
+ },
327
+ onSuccess: (_data, variables) => {
328
+ if (!variables.dryRun) {
329
+ queryClient2.invalidateQueries({ queryKey: crmKeys.manualPushHistory() });
330
+ }
331
+ }
332
+ });
333
+ };
334
+ const useDispatchTestManualPush = () => {
335
+ const { post } = admin.useFetchClient();
336
+ const queryClient2 = reactQuery.useQueryClient();
337
+ return reactQuery.useMutation({
338
+ mutationFn: async (payload) => {
339
+ const { data } = await post(`${PLUGIN_BASE}/manual-pushes/dispatch-test`, payload);
340
+ if (data?.error) throw new Error(extractBackendError(data));
341
+ return data;
342
+ },
343
+ onSuccess: () => {
344
+ queryClient2.invalidateQueries({ queryKey: crmKeys.manualPushHistory() });
345
+ }
346
+ });
347
+ };
278
348
  function useStagedFilters(initialState) {
279
349
  const [draft, setDraftState] = React.useState(initialState);
280
350
  const [applied, setApplied] = React.useState(initialState);
@@ -3101,7 +3171,401 @@ const StatsView = () => {
3101
3171
  ] });
3102
3172
  };
3103
3173
  const StatsView$1 = React.memo(StatsView);
3104
- 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";
3174
+ 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();
3175
+ const truncate = (text, n) => text.length > n ? `${text.slice(0, n)}…` : text;
3176
+ const formatDateTime = (iso) => new Date(iso).toLocaleString("ru-RU", {
3177
+ day: "2-digit",
3178
+ month: "2-digit",
3179
+ year: "numeric",
3180
+ hour: "2-digit",
3181
+ minute: "2-digit"
3182
+ });
3183
+ const parseUserIdList = (raw) => {
3184
+ if (!raw.trim()) return [];
3185
+ return raw.split(/[\s,]+/).map((t) => t.trim()).filter(Boolean).map((t) => Number(t)).filter((n) => Number.isInteger(n) && n > 0);
3186
+ };
3187
+ const ManualPushesView = () => {
3188
+ const { data: templates = [], isLoading, error, refetch } = useManualPushTemplates();
3189
+ const { data: history = [], isLoading: historyLoading } = useManualPushHistory();
3190
+ const [modalState, setModalState] = React.useState(null);
3191
+ const sortedTemplates = React.useMemo(
3192
+ () => [...templates].sort(
3193
+ (a, b) => a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0
3194
+ ),
3195
+ [templates]
3196
+ );
3197
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 4, children: [
3198
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", paddingBottom: 4, children: [
3199
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3200
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", children: "Ручные рассылки" }),
3201
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--crm-text-secondary)", fontSize: 13, marginTop: 4 }, children: "Опубликованные в Strapi шаблоны Manual Push. Отправка не происходит автоматически — используйте кнопки ниже." })
3202
+ ] }),
3203
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: () => refetch(), children: "Обновить" })
3204
+ ] }),
3205
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Загрузка шаблонов…" }) }),
3206
+ error && /* @__PURE__ */ jsxRuntime.jsx(
3207
+ designSystem.Box,
3208
+ {
3209
+ padding: 4,
3210
+ background: "danger100",
3211
+ hasRadius: true,
3212
+ style: { border: "1px solid var(--crm-danger)" },
3213
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { textColor: "danger700", children: [
3214
+ "Не удалось загрузить шаблоны: ",
3215
+ error.message
3216
+ ] })
3217
+ }
3218
+ ),
3219
+ !isLoading && !error && sortedTemplates.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 6, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: "Шаблоны не найдены. Создайте запись в Content Manager → Manual Push и опубликуйте её." }) }),
3220
+ /* @__PURE__ */ jsxRuntime.jsx(
3221
+ "div",
3222
+ {
3223
+ style: {
3224
+ display: "grid",
3225
+ gridTemplateColumns: "repeat(auto-fill, minmax(360px, 1fr))",
3226
+ gap: 16
3227
+ },
3228
+ children: sortedTemplates.map((t) => /* @__PURE__ */ jsxRuntime.jsx(
3229
+ TemplateCard,
3230
+ {
3231
+ template: t,
3232
+ onSendTest: () => setModalState({ template: t, mode: "test" }),
3233
+ onDispatch: () => setModalState({ template: t, mode: "segment" })
3234
+ },
3235
+ t.documentId
3236
+ ))
3237
+ }
3238
+ ),
3239
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingTop: 8, children: [
3240
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", children: "Последние отправки" }),
3241
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 3, children: historyLoading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Загрузка истории…" }) }) : history.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, background: "neutral100", hasRadius: true, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: "Пока ни одной отправки." }) }) : /* @__PURE__ */ jsxRuntime.jsx(HistoryTable, { history, templates }) })
3242
+ ] }),
3243
+ modalState && /* @__PURE__ */ jsxRuntime.jsx(DispatchModal, { state: modalState, onClose: () => setModalState(null) })
3244
+ ] });
3245
+ };
3246
+ const TemplateCard = ({ template, onSendTest, onDispatch }) => {
3247
+ const preview = truncate(stripHtml(template.body || ""), 220);
3248
+ const hasTestUsers = template.testUserIds.length > 0;
3249
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3250
+ designSystem.Box,
3251
+ {
3252
+ background: "neutral0",
3253
+ hasRadius: true,
3254
+ padding: 4,
3255
+ shadow: "tableShadow",
3256
+ style: { display: "flex", flexDirection: "column", gap: 12 },
3257
+ children: [
3258
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "space-between", alignItems: "flex-start", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
3259
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", ellipsis: true, children: template.name }),
3260
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, paddingTop: 1, children: [
3261
+ template.locales.map((l) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary200", textColor: "primary600", children: l.toUpperCase() }, l)),
3262
+ template.image && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary200", textColor: "secondary700", children: "IMG" }),
3263
+ template.buttonUrl && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "alternative200", textColor: "alternative700", children: "BTN" })
3264
+ ] })
3265
+ ] }) }),
3266
+ template.image?.url && /* @__PURE__ */ jsxRuntime.jsx(
3267
+ "div",
3268
+ {
3269
+ style: {
3270
+ background: "var(--crm-bg-tertiary)",
3271
+ borderRadius: 8,
3272
+ overflow: "hidden",
3273
+ maxHeight: 120,
3274
+ display: "flex",
3275
+ justifyContent: "center",
3276
+ alignItems: "center"
3277
+ },
3278
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3279
+ "img",
3280
+ {
3281
+ src: template.image.url,
3282
+ alt: template.image.alternativeText || template.name,
3283
+ style: { maxWidth: "100%", maxHeight: 120, objectFit: "cover" }
3284
+ }
3285
+ )
3286
+ }
3287
+ ),
3288
+ /* @__PURE__ */ jsxRuntime.jsx(
3289
+ "div",
3290
+ {
3291
+ style: {
3292
+ color: "var(--crm-text-secondary)",
3293
+ fontSize: 13,
3294
+ whiteSpace: "pre-wrap",
3295
+ lineHeight: 1.4,
3296
+ minHeight: 40
3297
+ },
3298
+ children: preview || /* @__PURE__ */ jsxRuntime.jsx("em", { style: { opacity: 0.6 }, children: "Тело шаблона пустое" })
3299
+ }
3300
+ ),
3301
+ template.buttonLabel && template.buttonUrl && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 12, color: "var(--crm-text-muted)" }, children: [
3302
+ "Кнопка: ",
3303
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: template.buttonLabel }),
3304
+ " → ",
3305
+ truncate(template.buttonUrl, 48)
3306
+ ] }),
3307
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 12, color: "var(--crm-text-muted)" }, children: [
3308
+ "testUserIds: ",
3309
+ hasTestUsers ? template.testUserIds.join(", ") : "—"
3310
+ ] }),
3311
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, justifyContent: "flex-end", paddingTop: 1, children: [
3312
+ /* @__PURE__ */ jsxRuntime.jsx(
3313
+ designSystem.Button,
3314
+ {
3315
+ variant: "tertiary",
3316
+ disabled: !hasTestUsers,
3317
+ onClick: onSendTest,
3318
+ title: hasTestUsers ? "" : "Заполните testUserIds в Strapi",
3319
+ children: "Тест-отправка"
3320
+ }
3321
+ ),
3322
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "default", onClick: onDispatch, children: "Отправить…" })
3323
+ ] })
3324
+ ]
3325
+ }
3326
+ );
3327
+ };
3328
+ const HistoryTable = ({ history, templates }) => {
3329
+ const nameByDocId = React.useMemo(() => {
3330
+ const map = /* @__PURE__ */ new Map();
3331
+ templates.forEach((t) => map.set(t.documentId, t.name));
3332
+ return map;
3333
+ }, [templates]);
3334
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", hasRadius: true, shadow: "tableShadow", style: { overflow: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsxs("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: 13 }, children: [
3335
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { style: { background: "var(--crm-table-header-bg)", textAlign: "left" }, children: [
3336
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: { padding: "10px 12px" }, children: "Отправлено" }),
3337
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: { padding: "10px 12px" }, children: "Шаблон" }),
3338
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: { padding: "10px 12px", textAlign: "right" }, children: "В очереди" }),
3339
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: { padding: "10px 12px", textAlign: "right" }, children: "Отпр." }),
3340
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: { padding: "10px 12px", textAlign: "right" }, children: "Ошибки" }),
3341
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: { padding: "10px 12px", textAlign: "right" }, children: "Отмен." }),
3342
+ /* @__PURE__ */ jsxRuntime.jsx("th", { style: { padding: "10px 12px" }, children: "Кем" })
3343
+ ] }) }),
3344
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: history.map((row) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { style: { borderTop: "1px solid var(--crm-border-light)" }, children: [
3345
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: { padding: "10px 12px", whiteSpace: "nowrap" }, children: formatDateTime(row.dispatchedAt) }),
3346
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: { padding: "10px 12px" }, children: nameByDocId.get(row.strapiDocumentId) || row.strapiDocumentId }),
3347
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: { padding: "10px 12px", textAlign: "right" }, children: row.totalQueued }),
3348
+ /* @__PURE__ */ jsxRuntime.jsx(
3349
+ "td",
3350
+ {
3351
+ style: {
3352
+ padding: "10px 12px",
3353
+ textAlign: "right",
3354
+ color: "var(--crm-success)"
3355
+ },
3356
+ children: row.counts.sent
3357
+ }
3358
+ ),
3359
+ /* @__PURE__ */ jsxRuntime.jsx(
3360
+ "td",
3361
+ {
3362
+ style: {
3363
+ padding: "10px 12px",
3364
+ textAlign: "right",
3365
+ color: "var(--crm-danger)"
3366
+ },
3367
+ children: row.counts.failed
3368
+ }
3369
+ ),
3370
+ /* @__PURE__ */ jsxRuntime.jsx("td", { style: { padding: "10px 12px", textAlign: "right" }, children: row.counts.cancelled }),
3371
+ /* @__PURE__ */ jsxRuntime.jsx(
3372
+ "td",
3373
+ {
3374
+ style: {
3375
+ padding: "10px 12px",
3376
+ color: "var(--crm-text-muted)",
3377
+ whiteSpace: "nowrap"
3378
+ },
3379
+ children: row.dispatchedBy || "—"
3380
+ }
3381
+ )
3382
+ ] }, row.manualPushId)) })
3383
+ ] }) });
3384
+ };
3385
+ const DispatchModal = ({ state, onClose }) => {
3386
+ const { template, mode } = state;
3387
+ const isTest = mode === "test";
3388
+ const dispatchMutation = useDispatchManualPush();
3389
+ const dispatchTestMutation = useDispatchTestManualPush();
3390
+ const [dispatchedBy, setDispatchedBy] = React.useState("");
3391
+ const [includeRu, setIncludeRu] = React.useState(false);
3392
+ const [includeEn, setIncludeEn] = React.useState(false);
3393
+ const [userIdsRaw, setUserIdsRaw] = React.useState("");
3394
+ const [excludeOptedOut, setExcludeOptedOut] = React.useState(true);
3395
+ const [dryRunResult, setDryRunResult] = React.useState(null);
3396
+ const [confirmed, setConfirmed] = React.useState(false);
3397
+ const reset = () => {
3398
+ setDispatchedBy("");
3399
+ setIncludeRu(false);
3400
+ setIncludeEn(false);
3401
+ setUserIdsRaw("");
3402
+ setExcludeOptedOut(true);
3403
+ setDryRunResult(null);
3404
+ setConfirmed(false);
3405
+ };
3406
+ const close = () => {
3407
+ reset();
3408
+ onClose();
3409
+ };
3410
+ const buildPayload = (dryRun) => {
3411
+ const userIds = parseUserIdList(userIdsRaw);
3412
+ const languages = [];
3413
+ if (includeRu) languages.push("ru");
3414
+ if (includeEn) languages.push("en");
3415
+ return {
3416
+ strapiDocumentId: template.documentId,
3417
+ segment: {
3418
+ ...userIds.length > 0 ? { userIds } : {},
3419
+ ...languages.length > 0 ? { languages } : {},
3420
+ excludeOptedOut
3421
+ },
3422
+ dryRun,
3423
+ ...dispatchedBy ? { dispatchedBy } : {}
3424
+ };
3425
+ };
3426
+ const onDryRun = async () => {
3427
+ setDryRunResult(null);
3428
+ try {
3429
+ const res = await dispatchMutation.mutateAsync(buildPayload(true));
3430
+ setDryRunResult(res.totalQueued);
3431
+ } catch {
3432
+ }
3433
+ };
3434
+ const onConfirm = async () => {
3435
+ if (isTest) {
3436
+ try {
3437
+ await dispatchTestMutation.mutateAsync({
3438
+ strapiDocumentId: template.documentId,
3439
+ ...dispatchedBy ? { dispatchedBy } : {}
3440
+ });
3441
+ close();
3442
+ } catch {
3443
+ }
3444
+ return;
3445
+ }
3446
+ if (!confirmed) {
3447
+ setConfirmed(true);
3448
+ return;
3449
+ }
3450
+ try {
3451
+ await dispatchMutation.mutateAsync(buildPayload(false));
3452
+ close();
3453
+ } catch {
3454
+ }
3455
+ };
3456
+ const mutation = isTest ? dispatchTestMutation : dispatchMutation;
3457
+ const err = mutation.error;
3458
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: true, onOpenChange: (open) => !open && close(), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { style: { width: 640, maxWidth: "95vw" }, children: [
3459
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Title, { children: [
3460
+ isTest ? "Тест-отправка" : "Отправка",
3461
+ ": ",
3462
+ template.name
3463
+ ] }) }),
3464
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 4, alignItems: "stretch", children: [
3465
+ isTest ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 3, background: "neutral100", hasRadius: true, children: [
3466
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", children: [
3467
+ "Будет отправлено пользователям из ",
3468
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: "testUserIds" }),
3469
+ ":"
3470
+ ] }),
3471
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 8, fontFamily: "monospace", fontSize: 13 }, children: template.testUserIds.join(", ") }),
3472
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 8, fontSize: 12, color: "var(--crm-text-muted)" }, children: "Anti-spam, opt-out и priority конфликты для тест-отправки пропускаются." })
3473
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3474
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
3475
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Языки (опц.)" }),
3476
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, paddingTop: 1, children: [
3477
+ /* @__PURE__ */ jsxRuntime.jsx(
3478
+ designSystem.Checkbox,
3479
+ {
3480
+ checked: includeRu,
3481
+ onCheckedChange: (v) => setIncludeRu(Boolean(v)),
3482
+ children: "Русский"
3483
+ }
3484
+ ),
3485
+ /* @__PURE__ */ jsxRuntime.jsx(
3486
+ designSystem.Checkbox,
3487
+ {
3488
+ checked: includeEn,
3489
+ onCheckedChange: (v) => setIncludeEn(Boolean(v)),
3490
+ children: "English"
3491
+ }
3492
+ )
3493
+ ] }),
3494
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 12, color: "var(--crm-text-muted)", marginTop: 4 }, children: [
3495
+ "Пусто = все языки. Фильтр по ",
3496
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: "languageCode" }),
3497
+ " Telegram-аккаунта."
3498
+ ] })
3499
+ ] }),
3500
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
3501
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "userIds (опц.)" }),
3502
+ /* @__PURE__ */ jsxRuntime.jsx(
3503
+ designSystem.Textarea,
3504
+ {
3505
+ value: userIdsRaw,
3506
+ onChange: (e) => setUserIdsRaw(e.target.value),
3507
+ placeholder: "42, 101, 202 (или с переносами)"
3508
+ }
3509
+ ),
3510
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "var(--crm-text-muted)", marginTop: 4 }, children: "Прямая адресация — если заполнено, все остальные фильтры (кроме языков) игнорируются." })
3511
+ ] }),
3512
+ /* @__PURE__ */ jsxRuntime.jsx(
3513
+ designSystem.Checkbox,
3514
+ {
3515
+ checked: excludeOptedOut,
3516
+ onCheckedChange: (v) => setExcludeOptedOut(Boolean(v)),
3517
+ children: "Исключить отписавшихся (opted_out)"
3518
+ }
3519
+ )
3520
+ ] }),
3521
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
3522
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "dispatchedBy (опц.)" }),
3523
+ /* @__PURE__ */ jsxRuntime.jsx(
3524
+ designSystem.TextInput,
3525
+ {
3526
+ value: dispatchedBy,
3527
+ onChange: (e) => setDispatchedBy(e.target.value),
3528
+ placeholder: "ваш email или ник"
3529
+ }
3530
+ )
3531
+ ] }),
3532
+ !isTest && dryRunResult !== null && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 3, background: "primary100", hasRadius: true, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "omega", children: [
3533
+ "Подойдёт пользователей: ",
3534
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: dryRunResult })
3535
+ ] }) }),
3536
+ confirmed && !isTest && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 3, background: "warning100", hasRadius: true, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { textColor: "warning700", children: [
3537
+ "Подтвердите: будет отправлено ",
3538
+ dryRunResult ?? "?",
3539
+ " сообщений. Повторное нажатие «Отправить» запустит рассылку."
3540
+ ] }) }),
3541
+ err && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 3, background: "danger100", hasRadius: true, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger700", children: err.message }) })
3542
+ ] }) }),
3543
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
3544
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Close, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: close, children: "Отмена" }) }),
3545
+ !isTest && /* @__PURE__ */ jsxRuntime.jsx(
3546
+ designSystem.Button,
3547
+ {
3548
+ variant: "secondary",
3549
+ onClick: onDryRun,
3550
+ loading: dispatchMutation.isPending && dryRunResult === null,
3551
+ disabled: dispatchMutation.isPending,
3552
+ children: "Оценить (dry run)"
3553
+ }
3554
+ ),
3555
+ /* @__PURE__ */ jsxRuntime.jsx(
3556
+ designSystem.Button,
3557
+ {
3558
+ variant: confirmed && !isTest ? "danger-light" : "default",
3559
+ loading: mutation.isPending,
3560
+ onClick: onConfirm,
3561
+ disabled: mutation.isPending,
3562
+ children: isTest ? "Отправить тест" : confirmed ? "Подтвердить отправку" : "Отправить"
3563
+ }
3564
+ )
3565
+ ] })
3566
+ ] }) });
3567
+ };
3568
+ 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";
3105
3569
  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";
3106
3570
  const useIsDarkTheme = () => {
3107
3571
  const theme = styledComponents.useTheme();
@@ -3128,6 +3592,13 @@ const tabs = [
3128
3592
  description: "Заблокированные сообщения",
3129
3593
  icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Cross, { width: 20, height: 20 }),
3130
3594
  color: "#d97706"
3595
+ },
3596
+ {
3597
+ id: "manual-pushes",
3598
+ label: "Ручные рассылки",
3599
+ description: "Шаблоны Manual Push и ручная отправка",
3600
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Message, { width: 20, height: 20 }),
3601
+ color: "#7b79ff"
3131
3602
  }
3132
3603
  ];
3133
3604
  const formatNumber = (num) => {
@@ -3149,11 +3620,12 @@ const DashboardContent = () => {
3149
3620
  );
3150
3621
  const { data: stats, isLoading } = useDashboardStats(defaultFilters);
3151
3622
  const badgeCounts = React.useMemo(() => {
3152
- if (!stats) return { stats: 0, logs: 0, antispam: 0 };
3623
+ if (!stats) return { stats: 0, logs: 0, antispam: 0, "manual-pushes": 0 };
3153
3624
  return {
3154
3625
  stats: stats.totals?.sent ?? 0,
3155
3626
  logs: stats.funnel?.sent ?? 0,
3156
- antispam: stats.funnel?.blocked_total ?? 0
3627
+ antispam: stats.funnel?.blocked_total ?? 0,
3628
+ "manual-pushes": 0
3157
3629
  };
3158
3630
  }, [stats]);
3159
3631
  const renderTabContent = () => {
@@ -3164,6 +3636,8 @@ const DashboardContent = () => {
3164
3636
  return /* @__PURE__ */ jsxRuntime.jsx(LogsTable, {});
3165
3637
  case "antispam":
3166
3638
  return /* @__PURE__ */ jsxRuntime.jsx(AntiSpamLogsTable$1, {});
3639
+ case "manual-pushes":
3640
+ return /* @__PURE__ */ jsxRuntime.jsx(ManualPushesView, {});
3167
3641
  default:
3168
3642
  return null;
3169
3643
  }