@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.
@@ -173,7 +173,7 @@ const index = {
173
173
  Component: async () => {
174
174
  const component = await Promise.resolve().then(() => require(
175
175
  /* webpackChunkName: "crm-dashboard-page" */
176
- "../_chunks/index-DRXMKPXI.js"
176
+ "../_chunks/index-D6jz5MgX.js"
177
177
  ));
178
178
  return component;
179
179
  },
@@ -172,7 +172,7 @@ const index = {
172
172
  Component: async () => {
173
173
  const component = await import(
174
174
  /* webpackChunkName: "crm-dashboard-page" */
175
- "../_chunks/index-LXBoz7PC.mjs"
175
+ "../_chunks/index-B_cJDVsP.mjs"
176
176
  );
177
177
  return component;
178
178
  },
@@ -61,6 +61,53 @@ const controller = ({ strapi }) => ({
61
61
  },
62
62
  async getAntiSpamLogs(ctx) {
63
63
  ctx.body = await strapi.plugin("crm-dashboard").service("service").getAntiSpamLogs(ctx.query);
64
+ },
65
+ async listManualPushTemplates(ctx) {
66
+ ctx.body = await strapi.plugin("crm-dashboard").service("service").listManualPushTemplates();
67
+ },
68
+ async dispatchManualPush(ctx) {
69
+ try {
70
+ ctx.body = await strapi.plugin("crm-dashboard").service("service").dispatchManualPush(ctx.request.body);
71
+ } catch (err) {
72
+ const status = err?.response?.status || 500;
73
+ ctx.status = status;
74
+ ctx.body = {
75
+ error: "Dispatch failed",
76
+ status,
77
+ backend: err?.response?.data ?? err?.message ?? null
78
+ };
79
+ }
80
+ },
81
+ async dispatchTestManualPush(ctx) {
82
+ try {
83
+ ctx.body = await strapi.plugin("crm-dashboard").service("service").dispatchTestManualPush(ctx.request.body);
84
+ } catch (err) {
85
+ const status = err?.response?.status || 500;
86
+ ctx.status = status;
87
+ ctx.body = {
88
+ error: "Dispatch test failed",
89
+ status,
90
+ backend: err?.response?.data ?? err?.message ?? null
91
+ };
92
+ }
93
+ },
94
+ async getManualPushHistory(ctx) {
95
+ try {
96
+ ctx.body = await strapi.plugin("crm-dashboard").service("service").getManualPushHistory(ctx.query);
97
+ } catch (err) {
98
+ const status = err?.response?.status || 500;
99
+ ctx.status = status;
100
+ ctx.body = { error: "Failed to load history", status };
101
+ }
102
+ },
103
+ async getManualPushStats(ctx) {
104
+ try {
105
+ ctx.body = await strapi.plugin("crm-dashboard").service("service").getManualPushStats(ctx.params.manualPushId);
106
+ } catch (err) {
107
+ const status = err?.response?.status || 500;
108
+ ctx.status = status;
109
+ ctx.body = { error: "Failed to load stats", status };
110
+ }
64
111
  }
65
112
  });
66
113
  const controllers = {
@@ -108,6 +155,61 @@ const adminRoutes = [
108
155
  scope: ["plugin::crm-dashboard.access"]
109
156
  }
110
157
  }
158
+ },
159
+ {
160
+ method: "GET",
161
+ path: "/manual-pushes/templates",
162
+ handler: "controller.listManualPushTemplates",
163
+ config: {
164
+ policies: [],
165
+ auth: {
166
+ scope: ["plugin::crm-dashboard.access"]
167
+ }
168
+ }
169
+ },
170
+ {
171
+ method: "POST",
172
+ path: "/manual-pushes/dispatch",
173
+ handler: "controller.dispatchManualPush",
174
+ config: {
175
+ policies: [],
176
+ auth: {
177
+ scope: ["plugin::crm-dashboard.access"]
178
+ }
179
+ }
180
+ },
181
+ {
182
+ method: "POST",
183
+ path: "/manual-pushes/dispatch-test",
184
+ handler: "controller.dispatchTestManualPush",
185
+ config: {
186
+ policies: [],
187
+ auth: {
188
+ scope: ["plugin::crm-dashboard.access"]
189
+ }
190
+ }
191
+ },
192
+ {
193
+ method: "GET",
194
+ path: "/manual-pushes/history",
195
+ handler: "controller.getManualPushHistory",
196
+ config: {
197
+ policies: [],
198
+ auth: {
199
+ scope: ["plugin::crm-dashboard.access"]
200
+ }
201
+ }
202
+ },
203
+ {
204
+ method: "GET",
205
+ path: "/manual-pushes/:manualPushId/stats",
206
+ handler: "controller.getManualPushStats",
207
+ config: {
208
+ policies: [],
209
+ auth: {
210
+ scope: ["plugin::crm-dashboard.access"]
211
+ }
212
+ }
111
213
  }
112
214
  ];
113
215
  const routes = {
@@ -2490,6 +2592,19 @@ const {
2490
2592
  mergeConfig
2491
2593
  } = axios;
2492
2594
  const API_URL = process.env.API_URL || "http://localhost:3100";
2595
+ const STATISTICS_SECRET_KEY = process.env.STATISTICS_SECRET_KEY || "";
2596
+ const authHeaders = () => STATISTICS_SECRET_KEY ? { Authorization: `Bearer ${STATISTICS_SECRET_KEY}` } : {};
2597
+ const formatAxiosError = (err) => {
2598
+ const e = err;
2599
+ const status = e?.response?.status;
2600
+ const data = e?.response?.data;
2601
+ const text = data && typeof data === "object" ? JSON.stringify(data) : String(data ?? "");
2602
+ return [
2603
+ status ? `status=${status}` : "",
2604
+ text ? `body=${text}` : "",
2605
+ e?.message ? `msg=${e.message}` : ""
2606
+ ].filter(Boolean).join(" | ");
2607
+ };
2493
2608
  const service = ({ strapi }) => ({
2494
2609
  async getLogs(query) {
2495
2610
  try {
@@ -2497,7 +2612,10 @@ const service = ({ strapi }) => ({
2497
2612
  return data;
2498
2613
  } catch (err) {
2499
2614
  strapi.log.error("Failed to fetch CRM logs from backend", err);
2500
- return { error: "Failed to fetch logs", details: err instanceof Error ? err.message : String(err) };
2615
+ return {
2616
+ error: "Failed to fetch logs",
2617
+ details: err instanceof Error ? err.message : String(err)
2618
+ };
2501
2619
  }
2502
2620
  },
2503
2621
  async getAntiSpamLogs(query) {
@@ -2506,7 +2624,116 @@ const service = ({ strapi }) => ({
2506
2624
  return data;
2507
2625
  } catch (err) {
2508
2626
  strapi.log.error("Failed to fetch Anti-spam logs from backend", err);
2509
- return { error: "Failed to fetch logs", details: err instanceof Error ? err.message : String(err) };
2627
+ return {
2628
+ error: "Failed to fetch logs",
2629
+ details: err instanceof Error ? err.message : String(err)
2630
+ };
2631
+ }
2632
+ },
2633
+ async listManualPushTemplates() {
2634
+ try {
2635
+ const entries = await strapi.documents("api::manual-push.manual-push").findMany({
2636
+ status: "published",
2637
+ locale: "ru",
2638
+ populate: { image: { fields: ["url", "alternativeText", "width", "height"] } },
2639
+ sort: { updatedAt: "desc" },
2640
+ pagination: { pageSize: 200 }
2641
+ });
2642
+ const enriched = await Promise.all(
2643
+ (entries ?? []).map(async (e) => {
2644
+ let enEntry = null;
2645
+ try {
2646
+ enEntry = await strapi.documents("api::manual-push.manual-push").findOne({
2647
+ documentId: e.documentId,
2648
+ status: "published",
2649
+ locale: "en"
2650
+ });
2651
+ } catch {
2652
+ enEntry = null;
2653
+ }
2654
+ const locales = ["ru"];
2655
+ if (enEntry) locales.push("en");
2656
+ return {
2657
+ documentId: e.documentId,
2658
+ name: e.name,
2659
+ body: e.body,
2660
+ bodyEn: enEntry?.body ?? null,
2661
+ image: e.image ? {
2662
+ url: e.image.url,
2663
+ alternativeText: e.image.alternativeText,
2664
+ width: e.image.width,
2665
+ height: e.image.height
2666
+ } : null,
2667
+ buttonLabel: e.buttonLabel ?? null,
2668
+ buttonUrl: e.buttonUrl ?? null,
2669
+ buttonLabelEn: enEntry?.buttonLabel ?? null,
2670
+ buttonUrlEn: enEntry?.buttonUrl ?? null,
2671
+ testUserIds: Array.isArray(e.testUserIds) ? e.testUserIds : [],
2672
+ locales,
2673
+ updatedAt: e.updatedAt,
2674
+ createdAt: e.createdAt
2675
+ };
2676
+ })
2677
+ );
2678
+ return { data: enriched };
2679
+ } catch (err) {
2680
+ strapi.log.error("Failed to list manual-push templates", err);
2681
+ return {
2682
+ error: "Failed to list templates",
2683
+ details: err instanceof Error ? err.message : String(err)
2684
+ };
2685
+ }
2686
+ },
2687
+ async dispatchManualPush(body) {
2688
+ try {
2689
+ const { data } = await axios.post(`${API_URL}/api/crm/admin/manual-pushes/dispatch`, body, {
2690
+ headers: authHeaders()
2691
+ });
2692
+ return data;
2693
+ } catch (err) {
2694
+ strapi.log.error(`Failed to dispatch manual push: ${formatAxiosError(err)}`);
2695
+ throw err;
2696
+ }
2697
+ },
2698
+ async dispatchTestManualPush(body) {
2699
+ try {
2700
+ const { data } = await axios.post(
2701
+ `${API_URL}/api/crm/admin/manual-pushes/dispatch-test`,
2702
+ body,
2703
+ {
2704
+ headers: authHeaders()
2705
+ }
2706
+ );
2707
+ return data;
2708
+ } catch (err) {
2709
+ strapi.log.error(`Failed to dispatch test manual push: ${formatAxiosError(err)}`);
2710
+ throw err;
2711
+ }
2712
+ },
2713
+ async getManualPushHistory(query) {
2714
+ try {
2715
+ const { data } = await axios.get(`${API_URL}/api/crm/admin/manual-pushes`, {
2716
+ params: query,
2717
+ headers: authHeaders()
2718
+ });
2719
+ return data;
2720
+ } catch (err) {
2721
+ strapi.log.error(`Failed to fetch manual push history: ${formatAxiosError(err)}`);
2722
+ throw err;
2723
+ }
2724
+ },
2725
+ async getManualPushStats(manualPushId) {
2726
+ try {
2727
+ const { data } = await axios.get(
2728
+ `${API_URL}/api/crm/admin/manual-pushes/${manualPushId}/stats`,
2729
+ {
2730
+ headers: authHeaders()
2731
+ }
2732
+ );
2733
+ return data;
2734
+ } catch (err) {
2735
+ strapi.log.error(`Failed to fetch manual push stats: ${formatAxiosError(err)}`);
2736
+ throw err;
2510
2737
  }
2511
2738
  }
2512
2739
  });
@@ -60,6 +60,53 @@ const controller = ({ strapi }) => ({
60
60
  },
61
61
  async getAntiSpamLogs(ctx) {
62
62
  ctx.body = await strapi.plugin("crm-dashboard").service("service").getAntiSpamLogs(ctx.query);
63
+ },
64
+ async listManualPushTemplates(ctx) {
65
+ ctx.body = await strapi.plugin("crm-dashboard").service("service").listManualPushTemplates();
66
+ },
67
+ async dispatchManualPush(ctx) {
68
+ try {
69
+ ctx.body = await strapi.plugin("crm-dashboard").service("service").dispatchManualPush(ctx.request.body);
70
+ } catch (err) {
71
+ const status = err?.response?.status || 500;
72
+ ctx.status = status;
73
+ ctx.body = {
74
+ error: "Dispatch failed",
75
+ status,
76
+ backend: err?.response?.data ?? err?.message ?? null
77
+ };
78
+ }
79
+ },
80
+ async dispatchTestManualPush(ctx) {
81
+ try {
82
+ ctx.body = await strapi.plugin("crm-dashboard").service("service").dispatchTestManualPush(ctx.request.body);
83
+ } catch (err) {
84
+ const status = err?.response?.status || 500;
85
+ ctx.status = status;
86
+ ctx.body = {
87
+ error: "Dispatch test failed",
88
+ status,
89
+ backend: err?.response?.data ?? err?.message ?? null
90
+ };
91
+ }
92
+ },
93
+ async getManualPushHistory(ctx) {
94
+ try {
95
+ ctx.body = await strapi.plugin("crm-dashboard").service("service").getManualPushHistory(ctx.query);
96
+ } catch (err) {
97
+ const status = err?.response?.status || 500;
98
+ ctx.status = status;
99
+ ctx.body = { error: "Failed to load history", status };
100
+ }
101
+ },
102
+ async getManualPushStats(ctx) {
103
+ try {
104
+ ctx.body = await strapi.plugin("crm-dashboard").service("service").getManualPushStats(ctx.params.manualPushId);
105
+ } catch (err) {
106
+ const status = err?.response?.status || 500;
107
+ ctx.status = status;
108
+ ctx.body = { error: "Failed to load stats", status };
109
+ }
63
110
  }
64
111
  });
65
112
  const controllers = {
@@ -107,6 +154,61 @@ const adminRoutes = [
107
154
  scope: ["plugin::crm-dashboard.access"]
108
155
  }
109
156
  }
157
+ },
158
+ {
159
+ method: "GET",
160
+ path: "/manual-pushes/templates",
161
+ handler: "controller.listManualPushTemplates",
162
+ config: {
163
+ policies: [],
164
+ auth: {
165
+ scope: ["plugin::crm-dashboard.access"]
166
+ }
167
+ }
168
+ },
169
+ {
170
+ method: "POST",
171
+ path: "/manual-pushes/dispatch",
172
+ handler: "controller.dispatchManualPush",
173
+ config: {
174
+ policies: [],
175
+ auth: {
176
+ scope: ["plugin::crm-dashboard.access"]
177
+ }
178
+ }
179
+ },
180
+ {
181
+ method: "POST",
182
+ path: "/manual-pushes/dispatch-test",
183
+ handler: "controller.dispatchTestManualPush",
184
+ config: {
185
+ policies: [],
186
+ auth: {
187
+ scope: ["plugin::crm-dashboard.access"]
188
+ }
189
+ }
190
+ },
191
+ {
192
+ method: "GET",
193
+ path: "/manual-pushes/history",
194
+ handler: "controller.getManualPushHistory",
195
+ config: {
196
+ policies: [],
197
+ auth: {
198
+ scope: ["plugin::crm-dashboard.access"]
199
+ }
200
+ }
201
+ },
202
+ {
203
+ method: "GET",
204
+ path: "/manual-pushes/:manualPushId/stats",
205
+ handler: "controller.getManualPushStats",
206
+ config: {
207
+ policies: [],
208
+ auth: {
209
+ scope: ["plugin::crm-dashboard.access"]
210
+ }
211
+ }
110
212
  }
111
213
  ];
112
214
  const routes = {
@@ -2489,6 +2591,19 @@ const {
2489
2591
  mergeConfig
2490
2592
  } = axios;
2491
2593
  const API_URL = process.env.API_URL || "http://localhost:3100";
2594
+ const STATISTICS_SECRET_KEY = process.env.STATISTICS_SECRET_KEY || "";
2595
+ const authHeaders = () => STATISTICS_SECRET_KEY ? { Authorization: `Bearer ${STATISTICS_SECRET_KEY}` } : {};
2596
+ const formatAxiosError = (err) => {
2597
+ const e = err;
2598
+ const status = e?.response?.status;
2599
+ const data = e?.response?.data;
2600
+ const text = data && typeof data === "object" ? JSON.stringify(data) : String(data ?? "");
2601
+ return [
2602
+ status ? `status=${status}` : "",
2603
+ text ? `body=${text}` : "",
2604
+ e?.message ? `msg=${e.message}` : ""
2605
+ ].filter(Boolean).join(" | ");
2606
+ };
2492
2607
  const service = ({ strapi }) => ({
2493
2608
  async getLogs(query) {
2494
2609
  try {
@@ -2496,7 +2611,10 @@ const service = ({ strapi }) => ({
2496
2611
  return data;
2497
2612
  } catch (err) {
2498
2613
  strapi.log.error("Failed to fetch CRM logs from backend", err);
2499
- return { error: "Failed to fetch logs", details: err instanceof Error ? err.message : String(err) };
2614
+ return {
2615
+ error: "Failed to fetch logs",
2616
+ details: err instanceof Error ? err.message : String(err)
2617
+ };
2500
2618
  }
2501
2619
  },
2502
2620
  async getAntiSpamLogs(query) {
@@ -2505,7 +2623,116 @@ const service = ({ strapi }) => ({
2505
2623
  return data;
2506
2624
  } catch (err) {
2507
2625
  strapi.log.error("Failed to fetch Anti-spam logs from backend", err);
2508
- return { error: "Failed to fetch logs", details: err instanceof Error ? err.message : String(err) };
2626
+ return {
2627
+ error: "Failed to fetch logs",
2628
+ details: err instanceof Error ? err.message : String(err)
2629
+ };
2630
+ }
2631
+ },
2632
+ async listManualPushTemplates() {
2633
+ try {
2634
+ const entries = await strapi.documents("api::manual-push.manual-push").findMany({
2635
+ status: "published",
2636
+ locale: "ru",
2637
+ populate: { image: { fields: ["url", "alternativeText", "width", "height"] } },
2638
+ sort: { updatedAt: "desc" },
2639
+ pagination: { pageSize: 200 }
2640
+ });
2641
+ const enriched = await Promise.all(
2642
+ (entries ?? []).map(async (e) => {
2643
+ let enEntry = null;
2644
+ try {
2645
+ enEntry = await strapi.documents("api::manual-push.manual-push").findOne({
2646
+ documentId: e.documentId,
2647
+ status: "published",
2648
+ locale: "en"
2649
+ });
2650
+ } catch {
2651
+ enEntry = null;
2652
+ }
2653
+ const locales = ["ru"];
2654
+ if (enEntry) locales.push("en");
2655
+ return {
2656
+ documentId: e.documentId,
2657
+ name: e.name,
2658
+ body: e.body,
2659
+ bodyEn: enEntry?.body ?? null,
2660
+ image: e.image ? {
2661
+ url: e.image.url,
2662
+ alternativeText: e.image.alternativeText,
2663
+ width: e.image.width,
2664
+ height: e.image.height
2665
+ } : null,
2666
+ buttonLabel: e.buttonLabel ?? null,
2667
+ buttonUrl: e.buttonUrl ?? null,
2668
+ buttonLabelEn: enEntry?.buttonLabel ?? null,
2669
+ buttonUrlEn: enEntry?.buttonUrl ?? null,
2670
+ testUserIds: Array.isArray(e.testUserIds) ? e.testUserIds : [],
2671
+ locales,
2672
+ updatedAt: e.updatedAt,
2673
+ createdAt: e.createdAt
2674
+ };
2675
+ })
2676
+ );
2677
+ return { data: enriched };
2678
+ } catch (err) {
2679
+ strapi.log.error("Failed to list manual-push templates", err);
2680
+ return {
2681
+ error: "Failed to list templates",
2682
+ details: err instanceof Error ? err.message : String(err)
2683
+ };
2684
+ }
2685
+ },
2686
+ async dispatchManualPush(body) {
2687
+ try {
2688
+ const { data } = await axios.post(`${API_URL}/api/crm/admin/manual-pushes/dispatch`, body, {
2689
+ headers: authHeaders()
2690
+ });
2691
+ return data;
2692
+ } catch (err) {
2693
+ strapi.log.error(`Failed to dispatch manual push: ${formatAxiosError(err)}`);
2694
+ throw err;
2695
+ }
2696
+ },
2697
+ async dispatchTestManualPush(body) {
2698
+ try {
2699
+ const { data } = await axios.post(
2700
+ `${API_URL}/api/crm/admin/manual-pushes/dispatch-test`,
2701
+ body,
2702
+ {
2703
+ headers: authHeaders()
2704
+ }
2705
+ );
2706
+ return data;
2707
+ } catch (err) {
2708
+ strapi.log.error(`Failed to dispatch test manual push: ${formatAxiosError(err)}`);
2709
+ throw err;
2710
+ }
2711
+ },
2712
+ async getManualPushHistory(query) {
2713
+ try {
2714
+ const { data } = await axios.get(`${API_URL}/api/crm/admin/manual-pushes`, {
2715
+ params: query,
2716
+ headers: authHeaders()
2717
+ });
2718
+ return data;
2719
+ } catch (err) {
2720
+ strapi.log.error(`Failed to fetch manual push history: ${formatAxiosError(err)}`);
2721
+ throw err;
2722
+ }
2723
+ },
2724
+ async getManualPushStats(manualPushId) {
2725
+ try {
2726
+ const { data } = await axios.get(
2727
+ `${API_URL}/api/crm/admin/manual-pushes/${manualPushId}/stats`,
2728
+ {
2729
+ headers: authHeaders()
2730
+ }
2731
+ );
2732
+ return data;
2733
+ } catch (err) {
2734
+ strapi.log.error(`Failed to fetch manual push stats: ${formatAxiosError(err)}`);
2735
+ throw err;
2509
2736
  }
2510
2737
  }
2511
2738
  });
package/dist/style.css CHANGED
@@ -328,6 +328,7 @@
328
328
  --tab-icon-stats-gradient: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%);
329
329
  --tab-icon-logs-gradient: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
330
330
  --tab-icon-antispam-gradient: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
331
+ --tab-icon-manual-pushes-gradient: linear-gradient(135deg, #e0e7ff 0%, #b4b1ff 100%);
331
332
 
332
333
  min-height: 100vh;
333
334
  background: var(--crm-bg-secondary);
@@ -361,6 +362,7 @@
361
362
  --tab-icon-stats-gradient: linear-gradient(135deg, rgba(123, 121, 255, 0.2) 0%, rgba(99, 102, 241, 0.3) 100%);
362
363
  --tab-icon-logs-gradient: linear-gradient(135deg, rgba(16, 185, 129, 0.2) 0%, rgba(52, 211, 153, 0.3) 100%);
363
364
  --tab-icon-antispam-gradient: linear-gradient(135deg, rgba(245, 158, 11, 0.2) 0%, rgba(251, 191, 36, 0.3) 100%);
365
+ --tab-icon-manual-pushes-gradient: linear-gradient(135deg, rgba(123, 121, 255, 0.2) 0%, rgba(165, 163, 255, 0.3) 100%);
364
366
  }
365
367
 
366
368
  .dashboard-header {
@@ -462,6 +464,11 @@
462
464
  color: var(--crm-warning);
463
465
  }
464
466
 
467
+ .tab-icon.manual-pushes {
468
+ background: var(--tab-icon-manual-pushes-gradient);
469
+ color: var(--crm-accent);
470
+ }
471
+
465
472
  .tab-content-wrapper {
466
473
  display: flex;
467
474
  flex-direction: column;
@@ -515,6 +522,11 @@
515
522
  color: var(--crm-warning);
516
523
  }
517
524
 
525
+ .tab-badge.manual-pushes {
526
+ background: var(--crm-accent-light);
527
+ color: var(--crm-accent);
528
+ }
529
+
518
530
  .tab-card.active .tab-badge.stats {
519
531
  background: var(--crm-accent);
520
532
  color: white;
@@ -530,6 +542,11 @@
530
542
  color: white;
531
543
  }
532
544
 
545
+ .tab-card.active .tab-badge.manual-pushes {
546
+ background: var(--crm-accent);
547
+ color: white;
548
+ }
549
+
533
550
  .tab-content {
534
551
  margin: 0 24px;
535
552
  padding: 24px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inspirer-dev/crm-dashboard",
3
- "version": "1.0.90",
3
+ "version": "1.0.92",
4
4
  "description": "CRM Dashboard and Tools",
5
5
  "strapi": {
6
6
  "name": "crm-dashboard",
@@ -17,6 +17,70 @@ const controller = ({ strapi }: { strapi: Core.Strapi }) => ({
17
17
  async getAntiSpamLogs(ctx: any) {
18
18
  ctx.body = await strapi.plugin('crm-dashboard').service('service').getAntiSpamLogs(ctx.query);
19
19
  },
20
+
21
+ async listManualPushTemplates(ctx: any) {
22
+ ctx.body = await strapi.plugin('crm-dashboard').service('service').listManualPushTemplates();
23
+ },
24
+
25
+ async dispatchManualPush(ctx: any) {
26
+ try {
27
+ ctx.body = await strapi
28
+ .plugin('crm-dashboard')
29
+ .service('service')
30
+ .dispatchManualPush(ctx.request.body);
31
+ } catch (err: any) {
32
+ const status = err?.response?.status || 500;
33
+ ctx.status = status;
34
+ ctx.body = {
35
+ error: 'Dispatch failed',
36
+ status,
37
+ backend: err?.response?.data ?? err?.message ?? null,
38
+ };
39
+ }
40
+ },
41
+
42
+ async dispatchTestManualPush(ctx: any) {
43
+ try {
44
+ ctx.body = await strapi
45
+ .plugin('crm-dashboard')
46
+ .service('service')
47
+ .dispatchTestManualPush(ctx.request.body);
48
+ } catch (err: any) {
49
+ const status = err?.response?.status || 500;
50
+ ctx.status = status;
51
+ ctx.body = {
52
+ error: 'Dispatch test failed',
53
+ status,
54
+ backend: err?.response?.data ?? err?.message ?? null,
55
+ };
56
+ }
57
+ },
58
+
59
+ async getManualPushHistory(ctx: any) {
60
+ try {
61
+ ctx.body = await strapi
62
+ .plugin('crm-dashboard')
63
+ .service('service')
64
+ .getManualPushHistory(ctx.query);
65
+ } catch (err: any) {
66
+ const status = err?.response?.status || 500;
67
+ ctx.status = status;
68
+ ctx.body = { error: 'Failed to load history', status };
69
+ }
70
+ },
71
+
72
+ async getManualPushStats(ctx: any) {
73
+ try {
74
+ ctx.body = await strapi
75
+ .plugin('crm-dashboard')
76
+ .service('service')
77
+ .getManualPushStats(ctx.params.manualPushId);
78
+ } catch (err: any) {
79
+ const status = err?.response?.status || 500;
80
+ ctx.status = status;
81
+ ctx.body = { error: 'Failed to load stats', status };
82
+ }
83
+ },
20
84
  });
21
85
 
22
86
  export default controller;