@inspirer-dev/crm-dashboard 1.0.89 → 1.0.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/src/components/RulesBuilder/constants.ts +16 -0
- package/admin/src/hooks/api/index.ts +17 -0
- package/admin/src/hooks/api/use-manual-pushes.ts +177 -0
- package/admin/src/lib/react-query/query-keys.ts +5 -0
- package/admin/src/pages/HomePage/HomePage.css +17 -0
- package/admin/src/pages/HomePage/components/ManualPushesView.tsx +560 -0
- package/admin/src/pages/HomePage/components/index.ts +1 -0
- package/admin/src/pages/HomePage/index.tsx +14 -3
- package/dist/_chunks/{index-ClbsgJmF.mjs → index-BXWSCH8D.mjs} +1 -1
- package/dist/_chunks/{index-LXBoz7PC.mjs → index-B_cJDVsP.mjs} +481 -7
- package/dist/_chunks/{index-DKJtyGq7.mjs → index-CFfWhleR.mjs} +1 -1
- package/dist/_chunks/{index-DRXMKPXI.js → index-D6jz5MgX.js} +478 -4
- package/dist/_chunks/{index-T7VXjklK.js → index-DGSELI7S.js} +1 -1
- package/dist/_chunks/{index-CapXG1AZ.js → index-hY_mczO-.js} +1 -1
- package/dist/_chunks/{utils-CAs_GSGv.js → utils-CIIhL4KH.js} +16 -0
- package/dist/_chunks/{utils-BHneBJ7U.mjs → utils-DwR0IuyK.mjs} +16 -0
- package/dist/admin/index.js +3 -3
- package/dist/admin/index.mjs +3 -3
- package/dist/server/index.js +222 -2
- package/dist/server/index.mjs +222 -2
- package/dist/style.css +17 -0
- package/package.json +1 -1
- package/server/src/controllers/controller.ts +64 -0
- package/server/src/routes/index.ts +55 -0
- package/server/src/services/service.ts +134 -2
|
@@ -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;
|
|
@@ -41,6 +41,61 @@ const adminRoutes = [
|
|
|
41
41
|
},
|
|
42
42
|
},
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
method: 'GET',
|
|
46
|
+
path: '/manual-pushes/templates',
|
|
47
|
+
handler: 'controller.listManualPushTemplates',
|
|
48
|
+
config: {
|
|
49
|
+
policies: [],
|
|
50
|
+
auth: {
|
|
51
|
+
scope: ['plugin::crm-dashboard.access'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
method: 'POST',
|
|
57
|
+
path: '/manual-pushes/dispatch',
|
|
58
|
+
handler: 'controller.dispatchManualPush',
|
|
59
|
+
config: {
|
|
60
|
+
policies: [],
|
|
61
|
+
auth: {
|
|
62
|
+
scope: ['plugin::crm-dashboard.access'],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
method: 'POST',
|
|
68
|
+
path: '/manual-pushes/dispatch-test',
|
|
69
|
+
handler: 'controller.dispatchTestManualPush',
|
|
70
|
+
config: {
|
|
71
|
+
policies: [],
|
|
72
|
+
auth: {
|
|
73
|
+
scope: ['plugin::crm-dashboard.access'],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
method: 'GET',
|
|
79
|
+
path: '/manual-pushes/history',
|
|
80
|
+
handler: 'controller.getManualPushHistory',
|
|
81
|
+
config: {
|
|
82
|
+
policies: [],
|
|
83
|
+
auth: {
|
|
84
|
+
scope: ['plugin::crm-dashboard.access'],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
method: 'GET',
|
|
90
|
+
path: '/manual-pushes/:manualPushId/stats',
|
|
91
|
+
handler: 'controller.getManualPushStats',
|
|
92
|
+
config: {
|
|
93
|
+
policies: [],
|
|
94
|
+
auth: {
|
|
95
|
+
scope: ['plugin::crm-dashboard.access'],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
44
99
|
];
|
|
45
100
|
|
|
46
101
|
export default {
|
|
@@ -2,6 +2,24 @@ import type { Core } from '@strapi/strapi';
|
|
|
2
2
|
import axios from 'axios';
|
|
3
3
|
|
|
4
4
|
const API_URL = process.env.API_URL || 'http://localhost:3100';
|
|
5
|
+
const STATISTICS_SECRET_KEY = process.env.STATISTICS_SECRET_KEY || '';
|
|
6
|
+
|
|
7
|
+
const authHeaders = () =>
|
|
8
|
+
STATISTICS_SECRET_KEY ? { Authorization: `Bearer ${STATISTICS_SECRET_KEY}` } : {};
|
|
9
|
+
|
|
10
|
+
const formatAxiosError = (err: unknown): string => {
|
|
11
|
+
const e = err as { response?: { status?: number; data?: unknown }; message?: string };
|
|
12
|
+
const status = e?.response?.status;
|
|
13
|
+
const data = e?.response?.data;
|
|
14
|
+
const text = data && typeof data === 'object' ? JSON.stringify(data) : String(data ?? '');
|
|
15
|
+
return [
|
|
16
|
+
status ? `status=${status}` : '',
|
|
17
|
+
text ? `body=${text}` : '',
|
|
18
|
+
e?.message ? `msg=${e.message}` : '',
|
|
19
|
+
]
|
|
20
|
+
.filter(Boolean)
|
|
21
|
+
.join(' | ');
|
|
22
|
+
};
|
|
5
23
|
|
|
6
24
|
export default ({ strapi }: { strapi: Core.Strapi }) => ({
|
|
7
25
|
async getLogs(query: Record<string, unknown>) {
|
|
@@ -10,7 +28,10 @@ export default ({ strapi }: { strapi: Core.Strapi }) => ({
|
|
|
10
28
|
return data;
|
|
11
29
|
} catch (err) {
|
|
12
30
|
strapi.log.error('Failed to fetch CRM logs from backend', err);
|
|
13
|
-
return {
|
|
31
|
+
return {
|
|
32
|
+
error: 'Failed to fetch logs',
|
|
33
|
+
details: err instanceof Error ? err.message : String(err),
|
|
34
|
+
};
|
|
14
35
|
}
|
|
15
36
|
},
|
|
16
37
|
|
|
@@ -20,7 +41,118 @@ export default ({ strapi }: { strapi: Core.Strapi }) => ({
|
|
|
20
41
|
return data;
|
|
21
42
|
} catch (err) {
|
|
22
43
|
strapi.log.error('Failed to fetch Anti-spam logs from backend', err);
|
|
23
|
-
return {
|
|
44
|
+
return {
|
|
45
|
+
error: 'Failed to fetch logs',
|
|
46
|
+
details: err instanceof Error ? err.message : String(err),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
async listManualPushTemplates() {
|
|
52
|
+
try {
|
|
53
|
+
const entries = await strapi.documents('api::manual-push.manual-push').findMany({
|
|
54
|
+
status: 'published',
|
|
55
|
+
locale: 'ru',
|
|
56
|
+
populate: { image: { fields: ['url', 'alternativeText', 'width', 'height'] } },
|
|
57
|
+
sort: { updatedAt: 'desc' },
|
|
58
|
+
pagination: { pageSize: 200 },
|
|
59
|
+
} as any);
|
|
60
|
+
|
|
61
|
+
const enriched = await Promise.all(
|
|
62
|
+
(entries ?? []).map(async (e: any) => {
|
|
63
|
+
let enEntry: any = null;
|
|
64
|
+
try {
|
|
65
|
+
enEntry = await strapi.documents('api::manual-push.manual-push').findOne({
|
|
66
|
+
documentId: e.documentId,
|
|
67
|
+
status: 'published',
|
|
68
|
+
locale: 'en',
|
|
69
|
+
} as any);
|
|
70
|
+
} catch {
|
|
71
|
+
enEntry = null;
|
|
72
|
+
}
|
|
73
|
+
const locales: string[] = ['ru'];
|
|
74
|
+
if (enEntry) locales.push('en');
|
|
75
|
+
return {
|
|
76
|
+
documentId: e.documentId,
|
|
77
|
+
name: e.name,
|
|
78
|
+
body: e.body,
|
|
79
|
+
bodyEn: enEntry?.body ?? null,
|
|
80
|
+
image: e.image
|
|
81
|
+
? {
|
|
82
|
+
url: e.image.url,
|
|
83
|
+
alternativeText: e.image.alternativeText,
|
|
84
|
+
width: e.image.width,
|
|
85
|
+
height: e.image.height,
|
|
86
|
+
}
|
|
87
|
+
: null,
|
|
88
|
+
buttonLabel: e.buttonLabel ?? null,
|
|
89
|
+
buttonUrl: e.buttonUrl ?? null,
|
|
90
|
+
buttonLabelEn: enEntry?.buttonLabel ?? null,
|
|
91
|
+
buttonUrlEn: enEntry?.buttonUrl ?? null,
|
|
92
|
+
testUserIds: Array.isArray(e.testUserIds) ? e.testUserIds : [],
|
|
93
|
+
locales,
|
|
94
|
+
updatedAt: e.updatedAt,
|
|
95
|
+
createdAt: e.createdAt,
|
|
96
|
+
};
|
|
97
|
+
})
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
return { data: enriched };
|
|
101
|
+
} catch (err) {
|
|
102
|
+
strapi.log.error('Failed to list manual-push templates', err);
|
|
103
|
+
return {
|
|
104
|
+
error: 'Failed to list templates',
|
|
105
|
+
details: err instanceof Error ? err.message : String(err),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
async dispatchManualPush(body: Record<string, unknown>) {
|
|
111
|
+
try {
|
|
112
|
+
const { data } = await axios.post(`${API_URL}/crm/admin/manual-pushes/dispatch`, body, {
|
|
113
|
+
headers: authHeaders(),
|
|
114
|
+
});
|
|
115
|
+
return data;
|
|
116
|
+
} catch (err) {
|
|
117
|
+
strapi.log.error(`Failed to dispatch manual push: ${formatAxiosError(err)}`);
|
|
118
|
+
throw err;
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
async dispatchTestManualPush(body: Record<string, unknown>) {
|
|
123
|
+
try {
|
|
124
|
+
const { data } = await axios.post(`${API_URL}/crm/admin/manual-pushes/dispatch-test`, body, {
|
|
125
|
+
headers: authHeaders(),
|
|
126
|
+
});
|
|
127
|
+
return data;
|
|
128
|
+
} catch (err) {
|
|
129
|
+
strapi.log.error(`Failed to dispatch test manual push: ${formatAxiosError(err)}`);
|
|
130
|
+
throw err;
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
async getManualPushHistory(query: Record<string, unknown>) {
|
|
135
|
+
try {
|
|
136
|
+
const { data } = await axios.get(`${API_URL}/crm/admin/manual-pushes`, {
|
|
137
|
+
params: query,
|
|
138
|
+
headers: authHeaders(),
|
|
139
|
+
});
|
|
140
|
+
return data;
|
|
141
|
+
} catch (err) {
|
|
142
|
+
strapi.log.error(`Failed to fetch manual push history: ${formatAxiosError(err)}`);
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
async getManualPushStats(manualPushId: string) {
|
|
148
|
+
try {
|
|
149
|
+
const { data } = await axios.get(`${API_URL}/crm/admin/manual-pushes/${manualPushId}/stats`, {
|
|
150
|
+
headers: authHeaders(),
|
|
151
|
+
});
|
|
152
|
+
return data;
|
|
153
|
+
} catch (err) {
|
|
154
|
+
strapi.log.error(`Failed to fetch manual push stats: ${formatAxiosError(err)}`);
|
|
155
|
+
throw err;
|
|
24
156
|
}
|
|
25
157
|
},
|
|
26
158
|
});
|