@inspirer-dev/crm-dashboard 1.0.52 → 1.0.54

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.
Files changed (29) hide show
  1. package/admin/src/components/StepFlowBuilder/index.tsx +20 -28
  2. package/admin/src/components/StepFlowBuilder/panels/EntryConfig.tsx +2 -6
  3. package/admin/src/hooks/api/use-ab-tests.ts +1 -1
  4. package/admin/src/hooks/api/use-anti-spam-logs.ts +1 -1
  5. package/admin/src/hooks/api/use-campaign-mutations.ts +3 -3
  6. package/admin/src/hooks/api/use-campaigns.ts +3 -3
  7. package/admin/src/hooks/api/use-cohort-data.ts +1 -1
  8. package/admin/src/hooks/api/use-crm-logs.ts +1 -1
  9. package/admin/src/hooks/api/use-dashboard-stats.ts +1 -1
  10. package/admin/src/hooks/api/use-filter-options.ts +1 -1
  11. package/admin/src/hooks/api/use-realtime-stats.ts +1 -1
  12. package/admin/src/hooks/api/use-segment-stats.ts +1 -1
  13. package/admin/src/hooks/api/use-send-times.ts +1 -1
  14. package/admin/src/utils/getBackendUrl.ts +20 -6
  15. package/dist/_chunks/{index--Qa1Bd7y.js → index--YXTcU9K.js} +5 -28
  16. package/dist/_chunks/{index-wE233MhG.js → index-BZKQTqh5.js} +26 -14
  17. package/dist/_chunks/{index-5WkcveGM.mjs → index-CLXsV-bm.mjs} +5 -28
  18. package/dist/_chunks/{index-DLPqbGjQ.mjs → index-DPVdz0kv.mjs} +26 -14
  19. package/dist/admin/index.js +2 -2
  20. package/dist/admin/index.mjs +2 -2
  21. package/dist/server/index.js +18 -44
  22. package/dist/server/index.mjs +18 -44
  23. package/package.json +1 -1
  24. package/server/src/controllers/controller.ts +5 -0
  25. package/server/src/controllers/index.ts +0 -2
  26. package/server/src/routes/index.ts +8 -0
  27. package/server/src/services/index.ts +0 -2
  28. package/server/src/controllers/proxy.ts +0 -22
  29. package/server/src/services/proxy.ts +0 -28
@@ -2,7 +2,11 @@ import React, { forwardRef, useCallback, useEffect, useMemo, useState, useRef }
2
2
  import { ReactFlowProvider } from 'reactflow';
3
3
  import 'reactflow/dist/style.css';
4
4
  import { Box, Field, Flex, Typography, Badge } from '@strapi/design-system';
5
- import { useForm, unstable_useContentManagerContext as useContentManagerContext, useFetchClient } from '@strapi/strapi/admin';
5
+ import {
6
+ useForm,
7
+ unstable_useContentManagerContext as useContentManagerContext,
8
+ useFetchClient,
9
+ } from '@strapi/strapi/admin';
6
10
 
7
11
  import type { StepFlowBuilderProps, FlowStep, CampaignContext, EntrySegment } from './types';
8
12
  import type { ValidationResult } from './validation';
@@ -49,18 +53,16 @@ const StepFlowBuilderInner = forwardRef<HTMLDivElement, StepFlowBuilderProps>(
49
53
  },
50
54
  });
51
55
 
52
- const data = response?.data as { entrySegment?: { id: number; name: string } } | undefined;
53
- console.log('[StepFlowBuilder] Fetch response data:', JSON.stringify(data, null, 2));
54
- console.log('[StepFlowBuilder] entrySegment from API:', data?.entrySegment);
55
- if (data?.entrySegment?.id) {
56
+ const responseData = response?.data as
57
+ | { data?: { entrySegment?: { id: number; name: string } } }
58
+ | undefined;
59
+ const campaignData = responseData?.data;
60
+ if (campaignData?.entrySegment?.id) {
56
61
  const seg = {
57
- id: data.entrySegment.id,
58
- name: data.entrySegment.name || `Segment #${data.entrySegment.id}`,
62
+ id: campaignData.entrySegment.id,
63
+ name: campaignData.entrySegment.name || `Segment #${campaignData.entrySegment.id}`,
59
64
  };
60
- console.log('[StepFlowBuilder] Setting initialSegment:', seg);
61
65
  setInitialSegment(seg);
62
- } else {
63
- console.log('[StepFlowBuilder] No entrySegment.id found in API response');
64
66
  }
65
67
  } catch (err) {
66
68
  console.error('[StepFlowBuilder] Fetch error:', err);
@@ -76,51 +78,36 @@ const StepFlowBuilderInner = forwardRef<HTMLDivElement, StepFlowBuilderProps>(
76
78
  const values = form?.values as Record<string, unknown> | undefined;
77
79
  const entrySegmentValue = values?.entrySegment as ConnectDisconnect | SegmentItem | undefined;
78
80
 
79
- console.log('[StepFlowBuilder] currentSegment memo - form.values.entrySegment:', entrySegmentValue);
80
- console.log('[StepFlowBuilder] currentSegment memo - initialSegment:', initialSegment);
81
- console.log('[StepFlowBuilder] currentSegment memo - typeof entrySegmentValue:', typeof entrySegmentValue);
82
- if (entrySegmentValue) {
83
- console.log('[StepFlowBuilder] currentSegment memo - entrySegmentValue keys:', Object.keys(entrySegmentValue));
84
- }
85
-
86
81
  if (!entrySegmentValue) {
87
- console.log('[StepFlowBuilder] currentSegment memo - no entrySegmentValue, returning initialSegment');
88
82
  return initialSegment;
89
83
  }
90
84
 
91
85
  // Handle connect/disconnect structure (user changed the segment in UI)
92
86
  if ('connect' in entrySegmentValue || 'disconnect' in entrySegmentValue) {
93
- console.log('[StepFlowBuilder] currentSegment memo - detected connect/disconnect structure');
94
87
  const { connect = [], disconnect = [] } = entrySegmentValue as ConnectDisconnect;
95
88
 
96
89
  if (connect.length > 0) {
97
90
  const seg = connect[0];
98
- console.log('[StepFlowBuilder] currentSegment memo - returning from connect:', seg);
99
91
  return { id: seg.id!, name: seg.name || `Segment #${seg.id}` };
100
92
  }
101
93
 
102
94
  if (disconnect.length > 0 && initialSegment) {
103
95
  const disconnectedId = disconnect[0]?.id;
104
96
  if (disconnectedId === initialSegment.id) {
105
- console.log('[StepFlowBuilder] currentSegment memo - segment disconnected, returning null');
106
97
  return null;
107
98
  }
108
99
  }
109
100
 
110
- console.log('[StepFlowBuilder] currentSegment memo - connect/disconnect but falling back to initialSegment');
111
101
  return initialSegment;
112
102
  }
113
103
 
114
104
  // Handle direct segment object (loaded from server)
115
105
  const segmentObj = entrySegmentValue as SegmentItem;
116
- console.log('[StepFlowBuilder] currentSegment memo - treating as direct segment object:', segmentObj);
117
106
  if (segmentObj.id) {
118
107
  const result = { id: segmentObj.id, name: segmentObj.name || `Segment #${segmentObj.id}` };
119
- console.log('[StepFlowBuilder] currentSegment memo - returning direct segment:', result);
120
108
  return result;
121
109
  }
122
110
 
123
- console.log('[StepFlowBuilder] currentSegment memo - no id in segmentObj, returning initialSegment');
124
111
  return initialSegment;
125
112
  }, [form?.values, initialSegment]);
126
113
 
@@ -132,7 +119,6 @@ const StepFlowBuilderInner = forwardRef<HTMLDivElement, StepFlowBuilderProps>(
132
119
  triggerConfig: values.triggerConfig as CampaignContext['triggerConfig'],
133
120
  entrySegment: currentSegment,
134
121
  };
135
- console.log('[StepFlowBuilder] campaignContext:', ctx);
136
122
  return ctx;
137
123
  }, [form?.values, currentSegment]);
138
124
 
@@ -203,12 +189,18 @@ const StepFlowBuilderInner = forwardRef<HTMLDivElement, StepFlowBuilderProps>(
203
189
  <Field.Label>{displayLabel}</Field.Label>
204
190
  {validation && !validation.isValid && (
205
191
  <Badge backgroundColor="danger100" textColor="danger600">
206
- {validation.errors.length} {validation.errors.length === 1 ? 'ошибка' : validation.errors.length < 5 ? 'ошибки' : 'ошибок'}
192
+ {validation.errors.length}{' '}
193
+ {validation.errors.length === 1
194
+ ? 'ошибка'
195
+ : validation.errors.length < 5
196
+ ? 'ошибки'
197
+ : 'ошибок'}
207
198
  </Badge>
208
199
  )}
209
200
  {validation && validation.isValid && validation.warnings.length > 0 && (
210
201
  <Badge backgroundColor="warning100" textColor="warning600">
211
- {validation.warnings.length} {validation.warnings.length === 1 ? 'предупр.' : 'предупр.'}
202
+ {validation.warnings.length}{' '}
203
+ {validation.warnings.length === 1 ? 'предупр.' : 'предупр.'}
212
204
  </Badge>
213
205
  )}
214
206
  </Flex>
@@ -88,7 +88,6 @@ const EntryConfigInner: React.FC<EntryConfigProps & { theme: FlowThemeColors }>
88
88
  entrySegment,
89
89
  theme,
90
90
  }) => {
91
- console.log('[EntryConfig] Received entrySegment:', entrySegment);
92
91
  const config = parseTriggerConfig(triggerConfig);
93
92
 
94
93
  const renderTriggerInfo = () => {
@@ -235,11 +234,8 @@ const EntryConfigInner: React.FC<EntryConfigProps & { theme: FlowThemeColors }>
235
234
 
236
235
  const renderSegmentInfo = () => {
237
236
  const hasSegment = entrySegment && (entrySegment.id || entrySegment.name);
238
- const segmentDisplayName = entrySegment?.name || (entrySegment?.id ? `Segment #${entrySegment.id}` : null);
239
-
240
- console.log('[EntryConfig] renderSegmentInfo - entrySegment:', entrySegment);
241
- console.log('[EntryConfig] renderSegmentInfo - hasSegment:', hasSegment);
242
- console.log('[EntryConfig] renderSegmentInfo - segmentDisplayName:', segmentDisplayName);
237
+ const segmentDisplayName =
238
+ entrySegment?.name || (entrySegment?.id ? `Segment #${entrySegment.id}` : null);
243
239
 
244
240
  if (!hasSegment || !segmentDisplayName) {
245
241
  return (
@@ -16,7 +16,7 @@ const fetchABTestData = async (filters: StatsFilters): Promise<ABTestsResponse>
16
16
  params.set('campaignIds', filters.campaigns.join(','));
17
17
  }
18
18
 
19
- const backendUrl = getBackendUrl();
19
+ const backendUrl = await getBackendUrl();
20
20
  const res = await fetch(`${backendUrl}/api/crm/dashboard/ab-tests?${params}`);
21
21
 
22
22
  if (!res.ok) {
@@ -24,7 +24,7 @@ const fetchAntiSpamLogs = async (
24
24
  if (filters.dateFrom) params.set('from', filters.dateFrom.toISOString());
25
25
  if (filters.dateTo) params.set('to', filters.dateTo.toISOString());
26
26
 
27
- const backendUrl = getBackendUrl();
27
+ const backendUrl = await getBackendUrl();
28
28
  const res = await fetch(new URL(`/api/crm/anti-spam-logs?${params}`, backendUrl).toString());
29
29
 
30
30
  if (!res.ok) {
@@ -7,7 +7,7 @@ type CreateCampaignInput = Omit<Campaign, 'id' | 'documentId'>;
7
7
  type UpdateCampaignInput = Partial<Campaign> & { documentId: string };
8
8
 
9
9
  const createCampaign = async (input: CreateCampaignInput): Promise<Campaign> => {
10
- const backendUrl = getBackendUrl();
10
+ const backendUrl = await getBackendUrl();
11
11
  const res = await fetch(new URL('/api/crm/campaigns', backendUrl).toString(), {
12
12
  method: 'POST',
13
13
  headers: { 'Content-Type': 'application/json' },
@@ -23,7 +23,7 @@ const createCampaign = async (input: CreateCampaignInput): Promise<Campaign> =>
23
23
 
24
24
  const updateCampaign = async (input: UpdateCampaignInput): Promise<Campaign> => {
25
25
  const { documentId, ...data } = input;
26
- const backendUrl = getBackendUrl();
26
+ const backendUrl = await getBackendUrl();
27
27
  const res = await fetch(new URL(`/api/crm/campaigns/${documentId}`, backendUrl).toString(), {
28
28
  method: 'PUT',
29
29
  headers: { 'Content-Type': 'application/json' },
@@ -38,7 +38,7 @@ const updateCampaign = async (input: UpdateCampaignInput): Promise<Campaign> =>
38
38
  };
39
39
 
40
40
  const deleteCampaign = async (documentId: string): Promise<void> => {
41
- const backendUrl = getBackendUrl();
41
+ const backendUrl = await getBackendUrl();
42
42
  const res = await fetch(new URL(`/api/crm/campaigns/${documentId}`, backendUrl).toString(), {
43
43
  method: 'DELETE',
44
44
  });
@@ -5,7 +5,7 @@ import type { Campaign } from './types';
5
5
  import type { CrmSegment, CrmTemplate } from '../../types/crm';
6
6
 
7
7
  const fetchCampaigns = async (): Promise<Campaign[]> => {
8
- const backendUrl = getBackendUrl();
8
+ const backendUrl = await getBackendUrl();
9
9
  const res = await fetch(new URL('/api/crm/campaigns', backendUrl).toString());
10
10
 
11
11
  if (!res.ok) {
@@ -17,7 +17,7 @@ const fetchCampaigns = async (): Promise<Campaign[]> => {
17
17
  };
18
18
 
19
19
  const fetchSegments = async (): Promise<CrmSegment[]> => {
20
- const backendUrl = getBackendUrl();
20
+ const backendUrl = await getBackendUrl();
21
21
  const res = await fetch(new URL('/api/crm/segments', backendUrl).toString());
22
22
 
23
23
  if (!res.ok) {
@@ -29,7 +29,7 @@ const fetchSegments = async (): Promise<CrmSegment[]> => {
29
29
  };
30
30
 
31
31
  const fetchTemplates = async (): Promise<CrmTemplate[]> => {
32
- const backendUrl = getBackendUrl();
32
+ const backendUrl = await getBackendUrl();
33
33
  const res = await fetch(new URL('/api/crm/templates', backendUrl).toString());
34
34
 
35
35
  if (!res.ok) {
@@ -20,7 +20,7 @@ const fetchCohortData = async (filters: CohortFilters): Promise<CohortData> => {
20
20
  params.set('campaignIds', filters.campaigns.join(','));
21
21
  }
22
22
 
23
- const backendUrl = getBackendUrl();
23
+ const backendUrl = await getBackendUrl();
24
24
  const res = await fetch(`${backendUrl}/api/crm/dashboard/cohorts?${params}`);
25
25
 
26
26
  if (!res.ok) {
@@ -18,7 +18,7 @@ const fetchCrmLogs = async (filters: LogsFilters, page: number): Promise<CrmLogs
18
18
  if (filters.sentFrom) params.set('sentFrom', filters.sentFrom.toISOString());
19
19
  if (filters.sentTo) params.set('sentTo', filters.sentTo.toISOString());
20
20
 
21
- const backendUrl = getBackendUrl();
21
+ const backendUrl = await getBackendUrl();
22
22
  const res = await fetch(new URL(`/api/crm/logs?${params}`, backendUrl).toString());
23
23
 
24
24
  if (!res.ok) {
@@ -13,7 +13,7 @@ const fetchDashboardStats = async (filters: StatsFilters): Promise<DashboardStat
13
13
  if (filters.compareToPrevious) params.set('compareToPrevious', 'true');
14
14
  if (filters.granularity) params.set('granularity', filters.granularity);
15
15
 
16
- const backendUrl = getBackendUrl();
16
+ const backendUrl = await getBackendUrl();
17
17
  const res = await fetch(new URL(`/api/crm/dashboard/stats?${params}`, backendUrl).toString());
18
18
 
19
19
  if (!res.ok) {
@@ -4,7 +4,7 @@ import getBackendUrl from '../../utils/getBackendUrl';
4
4
  import type { FilterOptions } from './types';
5
5
 
6
6
  const fetchFilterOptions = async (): Promise<FilterOptions> => {
7
- const backendUrl = getBackendUrl();
7
+ const backendUrl = await getBackendUrl();
8
8
  const res = await fetch(new URL('/api/crm/filter-options', backendUrl).toString());
9
9
 
10
10
  if (!res.ok) {
@@ -10,7 +10,7 @@ const fetchRealtimeStats = async (campaignIds?: string[]): Promise<RealtimeStats
10
10
  params.set('campaignIds', campaignIds.join(','));
11
11
  }
12
12
 
13
- const backendUrl = getBackendUrl();
13
+ const backendUrl = await getBackendUrl();
14
14
  const url = params.toString()
15
15
  ? `${backendUrl}/api/crm/dashboard/realtime?${params}`
16
16
  : `${backendUrl}/api/crm/dashboard/realtime`;
@@ -12,7 +12,7 @@ const fetchSegmentStats = async (filters: StatsFilters): Promise<SegmentStats> =
12
12
  params.set('campaignIds', filters.campaigns.join(','));
13
13
  }
14
14
 
15
- const backendUrl = getBackendUrl();
15
+ const backendUrl = await getBackendUrl();
16
16
  const res = await fetch(`${backendUrl}/api/crm/dashboard/segments?${params}`);
17
17
 
18
18
  if (!res.ok) {
@@ -12,7 +12,7 @@ const fetchSendTimeData = async (filters: StatsFilters): Promise<SendTimeData> =
12
12
  params.set('campaignIds', filters.campaigns.join(','));
13
13
  }
14
14
 
15
- const backendUrl = getBackendUrl();
15
+ const backendUrl = await getBackendUrl();
16
16
  const res = await fetch(`${backendUrl}/api/crm/dashboard/send-times?${params}`);
17
17
 
18
18
  if (!res.ok) {
@@ -1,11 +1,25 @@
1
- const getBackendUrl = (): string => {
2
- const raw = process.env.STRAPI_ADMIN_API_URL || 'http://localhost:3100';
1
+ let cachedUrl: string | undefined;
3
2
 
4
- if (!/^https?:\/\//i.test(raw)) {
5
- return 'http://localhost:3100';
3
+ const getBackendUrl = async (): Promise<string> => {
4
+ if (cachedUrl !== undefined) {
5
+ return cachedUrl;
6
6
  }
7
7
 
8
- return raw.endsWith('/') ? raw.slice(0, -1) : raw;
8
+ try {
9
+ const res = await fetch('/crm-dashboard/config');
10
+ if (res.ok) {
11
+ const data = await res.json();
12
+ cachedUrl = data.apiUrl || '';
13
+ }
14
+ } catch (e) {
15
+ console.error('[CRM]: Failed to fetch config', e);
16
+ }
17
+
18
+ if (cachedUrl === undefined) {
19
+ cachedUrl = '';
20
+ }
21
+
22
+ return cachedUrl;
9
23
  };
10
24
 
11
- export default getBackendUrl;
25
+ export default getBackendUrl;
@@ -1926,7 +1926,6 @@ const EntryConfigInner = ({
1926
1926
  entrySegment,
1927
1927
  theme
1928
1928
  }) => {
1929
- console.log("[EntryConfig] Received entrySegment:", entrySegment);
1930
1929
  const config = parseTriggerConfig(triggerConfig);
1931
1930
  const renderTriggerInfo = () => {
1932
1931
  if (!config) {
@@ -2058,9 +2057,6 @@ const EntryConfigInner = ({
2058
2057
  const renderSegmentInfo = () => {
2059
2058
  const hasSegment = entrySegment && (entrySegment.id || entrySegment.name);
2060
2059
  const segmentDisplayName = entrySegment?.name || (entrySegment?.id ? `Segment #${entrySegment.id}` : null);
2061
- console.log("[EntryConfig] renderSegmentInfo - entrySegment:", entrySegment);
2062
- console.log("[EntryConfig] renderSegmentInfo - hasSegment:", hasSegment);
2063
- console.log("[EntryConfig] renderSegmentInfo - segmentDisplayName:", segmentDisplayName);
2064
2060
  if (!hasSegment || !segmentDisplayName) {
2065
2061
  return /* @__PURE__ */ jsxRuntime.jsx(
2066
2062
  "div",
@@ -3682,18 +3678,14 @@ const StepFlowBuilderInner = React.forwardRef(
3682
3678
  "populate[entrySegment][fields][1]": "name"
3683
3679
  }
3684
3680
  });
3685
- const data = response?.data;
3686
- console.log("[StepFlowBuilder] Fetch response data:", JSON.stringify(data, null, 2));
3687
- console.log("[StepFlowBuilder] entrySegment from API:", data?.entrySegment);
3688
- if (data?.entrySegment?.id) {
3681
+ const responseData = response?.data;
3682
+ const campaignData = responseData?.data;
3683
+ if (campaignData?.entrySegment?.id) {
3689
3684
  const seg = {
3690
- id: data.entrySegment.id,
3691
- name: data.entrySegment.name || `Segment #${data.entrySegment.id}`
3685
+ id: campaignData.entrySegment.id,
3686
+ name: campaignData.entrySegment.name || `Segment #${campaignData.entrySegment.id}`
3692
3687
  };
3693
- console.log("[StepFlowBuilder] Setting initialSegment:", seg);
3694
3688
  setInitialSegment(seg);
3695
- } else {
3696
- console.log("[StepFlowBuilder] No entrySegment.id found in API response");
3697
3689
  }
3698
3690
  } catch (err) {
3699
3691
  console.error("[StepFlowBuilder] Fetch error:", err);
@@ -3706,42 +3698,28 @@ const StepFlowBuilderInner = React.forwardRef(
3706
3698
  const currentSegment = React.useMemo(() => {
3707
3699
  const values = form?.values;
3708
3700
  const entrySegmentValue = values?.entrySegment;
3709
- console.log("[StepFlowBuilder] currentSegment memo - form.values.entrySegment:", entrySegmentValue);
3710
- console.log("[StepFlowBuilder] currentSegment memo - initialSegment:", initialSegment);
3711
- console.log("[StepFlowBuilder] currentSegment memo - typeof entrySegmentValue:", typeof entrySegmentValue);
3712
- if (entrySegmentValue) {
3713
- console.log("[StepFlowBuilder] currentSegment memo - entrySegmentValue keys:", Object.keys(entrySegmentValue));
3714
- }
3715
3701
  if (!entrySegmentValue) {
3716
- console.log("[StepFlowBuilder] currentSegment memo - no entrySegmentValue, returning initialSegment");
3717
3702
  return initialSegment;
3718
3703
  }
3719
3704
  if ("connect" in entrySegmentValue || "disconnect" in entrySegmentValue) {
3720
- console.log("[StepFlowBuilder] currentSegment memo - detected connect/disconnect structure");
3721
3705
  const { connect = [], disconnect = [] } = entrySegmentValue;
3722
3706
  if (connect.length > 0) {
3723
3707
  const seg = connect[0];
3724
- console.log("[StepFlowBuilder] currentSegment memo - returning from connect:", seg);
3725
3708
  return { id: seg.id, name: seg.name || `Segment #${seg.id}` };
3726
3709
  }
3727
3710
  if (disconnect.length > 0 && initialSegment) {
3728
3711
  const disconnectedId = disconnect[0]?.id;
3729
3712
  if (disconnectedId === initialSegment.id) {
3730
- console.log("[StepFlowBuilder] currentSegment memo - segment disconnected, returning null");
3731
3713
  return null;
3732
3714
  }
3733
3715
  }
3734
- console.log("[StepFlowBuilder] currentSegment memo - connect/disconnect but falling back to initialSegment");
3735
3716
  return initialSegment;
3736
3717
  }
3737
3718
  const segmentObj = entrySegmentValue;
3738
- console.log("[StepFlowBuilder] currentSegment memo - treating as direct segment object:", segmentObj);
3739
3719
  if (segmentObj.id) {
3740
3720
  const result = { id: segmentObj.id, name: segmentObj.name || `Segment #${segmentObj.id}` };
3741
- console.log("[StepFlowBuilder] currentSegment memo - returning direct segment:", result);
3742
3721
  return result;
3743
3722
  }
3744
- console.log("[StepFlowBuilder] currentSegment memo - no id in segmentObj, returning initialSegment");
3745
3723
  return initialSegment;
3746
3724
  }, [form?.values, initialSegment]);
3747
3725
  const campaignContext = React.useMemo(() => {
@@ -3751,7 +3729,6 @@ const StepFlowBuilderInner = React.forwardRef(
3751
3729
  triggerConfig: values.triggerConfig,
3752
3730
  entrySegment: currentSegment
3753
3731
  };
3754
- console.log("[StepFlowBuilder] campaignContext:", ctx);
3755
3732
  return ctx;
3756
3733
  }, [form?.values, currentSegment]);
3757
3734
  React.useEffect(() => {
@@ -41,15 +41,27 @@ const QueryProvider = ({ children }) => {
41
41
  /* @__PURE__ */ jsxRuntime.jsx(reactQueryDevtools.ReactQueryDevtools, { initialIsOpen: false })
42
42
  ] });
43
43
  };
44
- const getBackendUrl = () => {
45
- const raw = process.env.STRAPI_ADMIN_API_URL || "http://localhost:3100";
46
- if (!/^https?:\/\//i.test(raw)) {
47
- return "http://localhost:3100";
44
+ let cachedUrl;
45
+ const getBackendUrl = async () => {
46
+ if (cachedUrl !== void 0) {
47
+ return cachedUrl;
48
48
  }
49
- return raw.endsWith("/") ? raw.slice(0, -1) : raw;
49
+ try {
50
+ const res = await fetch("/crm-dashboard/config");
51
+ if (res.ok) {
52
+ const data = await res.json();
53
+ cachedUrl = data.apiUrl || "";
54
+ }
55
+ } catch (e) {
56
+ console.error("[CRM]: Failed to fetch config", e);
57
+ }
58
+ if (cachedUrl === void 0) {
59
+ cachedUrl = "";
60
+ }
61
+ return cachedUrl;
50
62
  };
51
63
  const fetchFilterOptions = async () => {
52
- const backendUrl = getBackendUrl();
64
+ const backendUrl = await getBackendUrl();
53
65
  const res = await fetch(new URL("/api/crm/filter-options", backendUrl).toString());
54
66
  if (!res.ok) {
55
67
  throw new Error("Failed to fetch filter options");
@@ -76,7 +88,7 @@ const fetchDashboardStats = async (filters) => {
76
88
  if (filters.segments.length > 0) params.set("segmentIds", filters.segments.join(","));
77
89
  if (filters.compareToPrevious) params.set("compareToPrevious", "true");
78
90
  if (filters.granularity) params.set("granularity", filters.granularity);
79
- const backendUrl = getBackendUrl();
91
+ const backendUrl = await getBackendUrl();
80
92
  const res = await fetch(new URL(`/api/crm/dashboard/stats?${params}`, backendUrl).toString());
81
93
  if (!res.ok) {
82
94
  throw new Error("Failed to fetch dashboard stats");
@@ -103,7 +115,7 @@ const fetchCrmLogs = async (filters, page) => {
103
115
  if (filters.scheduledTo) params.set("scheduledTo", filters.scheduledTo.toISOString());
104
116
  if (filters.sentFrom) params.set("sentFrom", filters.sentFrom.toISOString());
105
117
  if (filters.sentTo) params.set("sentTo", filters.sentTo.toISOString());
106
- const backendUrl = getBackendUrl();
118
+ const backendUrl = await getBackendUrl();
107
119
  const res = await fetch(new URL(`/api/crm/logs?${params}`, backendUrl).toString());
108
120
  if (!res.ok) {
109
121
  throw new Error("Failed to fetch CRM logs");
@@ -131,7 +143,7 @@ const fetchAntiSpamLogs = async (filters, page) => {
131
143
  if (filters.campaigns.length > 0) params.set("campaignIds", filters.campaigns.join(","));
132
144
  if (filters.dateFrom) params.set("from", filters.dateFrom.toISOString());
133
145
  if (filters.dateTo) params.set("to", filters.dateTo.toISOString());
134
- const backendUrl = getBackendUrl();
146
+ const backendUrl = await getBackendUrl();
135
147
  const res = await fetch(new URL(`/api/crm/anti-spam-logs?${params}`, backendUrl).toString());
136
148
  if (!res.ok) {
137
149
  throw new Error("Failed to fetch anti-spam logs");
@@ -156,7 +168,7 @@ const fetchRealtimeStats = async (campaignIds) => {
156
168
  if (campaignIds && campaignIds.length > 0) {
157
169
  params.set("campaignIds", campaignIds.join(","));
158
170
  }
159
- const backendUrl = getBackendUrl();
171
+ const backendUrl = await getBackendUrl();
160
172
  const url = params.toString() ? `${backendUrl}/api/crm/dashboard/realtime?${params}` : `${backendUrl}/api/crm/dashboard/realtime`;
161
173
  const res = await fetch(url);
162
174
  if (!res.ok) {
@@ -181,7 +193,7 @@ const fetchCohortData = async (filters) => {
181
193
  if (filters.campaigns && filters.campaigns.length > 0) {
182
194
  params.set("campaignIds", filters.campaigns.join(","));
183
195
  }
184
- const backendUrl = getBackendUrl();
196
+ const backendUrl = await getBackendUrl();
185
197
  const res = await fetch(`${backendUrl}/api/crm/dashboard/cohorts?${params}`);
186
198
  if (!res.ok) {
187
199
  throw new Error("Failed to fetch cohort data");
@@ -207,7 +219,7 @@ const fetchSegmentStats = async (filters) => {
207
219
  if (filters.campaigns && filters.campaigns.length > 0) {
208
220
  params.set("campaignIds", filters.campaigns.join(","));
209
221
  }
210
- const backendUrl = getBackendUrl();
222
+ const backendUrl = await getBackendUrl();
211
223
  const res = await fetch(`${backendUrl}/api/crm/dashboard/segments?${params}`);
212
224
  if (!res.ok) {
213
225
  throw new Error("Failed to fetch segment stats");
@@ -228,7 +240,7 @@ const fetchSendTimeData = async (filters) => {
228
240
  if (filters.campaigns && filters.campaigns.length > 0) {
229
241
  params.set("campaignIds", filters.campaigns.join(","));
230
242
  }
231
- const backendUrl = getBackendUrl();
243
+ const backendUrl = await getBackendUrl();
232
244
  const res = await fetch(`${backendUrl}/api/crm/dashboard/send-times?${params}`);
233
245
  if (!res.ok) {
234
246
  throw new Error("Failed to fetch send time data");
@@ -249,7 +261,7 @@ const fetchABTestData = async (filters) => {
249
261
  if (filters.campaigns && filters.campaigns.length > 0) {
250
262
  params.set("campaignIds", filters.campaigns.join(","));
251
263
  }
252
- const backendUrl = getBackendUrl();
264
+ const backendUrl = await getBackendUrl();
253
265
  const res = await fetch(`${backendUrl}/api/crm/dashboard/ab-tests?${params}`);
254
266
  if (!res.ok) {
255
267
  throw new Error("Failed to fetch A/B test data");
@@ -1921,7 +1921,6 @@ const EntryConfigInner = ({
1921
1921
  entrySegment,
1922
1922
  theme
1923
1923
  }) => {
1924
- console.log("[EntryConfig] Received entrySegment:", entrySegment);
1925
1924
  const config = parseTriggerConfig(triggerConfig);
1926
1925
  const renderTriggerInfo = () => {
1927
1926
  if (!config) {
@@ -2053,9 +2052,6 @@ const EntryConfigInner = ({
2053
2052
  const renderSegmentInfo = () => {
2054
2053
  const hasSegment = entrySegment && (entrySegment.id || entrySegment.name);
2055
2054
  const segmentDisplayName = entrySegment?.name || (entrySegment?.id ? `Segment #${entrySegment.id}` : null);
2056
- console.log("[EntryConfig] renderSegmentInfo - entrySegment:", entrySegment);
2057
- console.log("[EntryConfig] renderSegmentInfo - hasSegment:", hasSegment);
2058
- console.log("[EntryConfig] renderSegmentInfo - segmentDisplayName:", segmentDisplayName);
2059
2055
  if (!hasSegment || !segmentDisplayName) {
2060
2056
  return /* @__PURE__ */ jsx(
2061
2057
  "div",
@@ -3677,18 +3673,14 @@ const StepFlowBuilderInner = forwardRef(
3677
3673
  "populate[entrySegment][fields][1]": "name"
3678
3674
  }
3679
3675
  });
3680
- const data = response?.data;
3681
- console.log("[StepFlowBuilder] Fetch response data:", JSON.stringify(data, null, 2));
3682
- console.log("[StepFlowBuilder] entrySegment from API:", data?.entrySegment);
3683
- if (data?.entrySegment?.id) {
3676
+ const responseData = response?.data;
3677
+ const campaignData = responseData?.data;
3678
+ if (campaignData?.entrySegment?.id) {
3684
3679
  const seg = {
3685
- id: data.entrySegment.id,
3686
- name: data.entrySegment.name || `Segment #${data.entrySegment.id}`
3680
+ id: campaignData.entrySegment.id,
3681
+ name: campaignData.entrySegment.name || `Segment #${campaignData.entrySegment.id}`
3687
3682
  };
3688
- console.log("[StepFlowBuilder] Setting initialSegment:", seg);
3689
3683
  setInitialSegment(seg);
3690
- } else {
3691
- console.log("[StepFlowBuilder] No entrySegment.id found in API response");
3692
3684
  }
3693
3685
  } catch (err) {
3694
3686
  console.error("[StepFlowBuilder] Fetch error:", err);
@@ -3701,42 +3693,28 @@ const StepFlowBuilderInner = forwardRef(
3701
3693
  const currentSegment = useMemo(() => {
3702
3694
  const values = form?.values;
3703
3695
  const entrySegmentValue = values?.entrySegment;
3704
- console.log("[StepFlowBuilder] currentSegment memo - form.values.entrySegment:", entrySegmentValue);
3705
- console.log("[StepFlowBuilder] currentSegment memo - initialSegment:", initialSegment);
3706
- console.log("[StepFlowBuilder] currentSegment memo - typeof entrySegmentValue:", typeof entrySegmentValue);
3707
- if (entrySegmentValue) {
3708
- console.log("[StepFlowBuilder] currentSegment memo - entrySegmentValue keys:", Object.keys(entrySegmentValue));
3709
- }
3710
3696
  if (!entrySegmentValue) {
3711
- console.log("[StepFlowBuilder] currentSegment memo - no entrySegmentValue, returning initialSegment");
3712
3697
  return initialSegment;
3713
3698
  }
3714
3699
  if ("connect" in entrySegmentValue || "disconnect" in entrySegmentValue) {
3715
- console.log("[StepFlowBuilder] currentSegment memo - detected connect/disconnect structure");
3716
3700
  const { connect = [], disconnect = [] } = entrySegmentValue;
3717
3701
  if (connect.length > 0) {
3718
3702
  const seg = connect[0];
3719
- console.log("[StepFlowBuilder] currentSegment memo - returning from connect:", seg);
3720
3703
  return { id: seg.id, name: seg.name || `Segment #${seg.id}` };
3721
3704
  }
3722
3705
  if (disconnect.length > 0 && initialSegment) {
3723
3706
  const disconnectedId = disconnect[0]?.id;
3724
3707
  if (disconnectedId === initialSegment.id) {
3725
- console.log("[StepFlowBuilder] currentSegment memo - segment disconnected, returning null");
3726
3708
  return null;
3727
3709
  }
3728
3710
  }
3729
- console.log("[StepFlowBuilder] currentSegment memo - connect/disconnect but falling back to initialSegment");
3730
3711
  return initialSegment;
3731
3712
  }
3732
3713
  const segmentObj = entrySegmentValue;
3733
- console.log("[StepFlowBuilder] currentSegment memo - treating as direct segment object:", segmentObj);
3734
3714
  if (segmentObj.id) {
3735
3715
  const result = { id: segmentObj.id, name: segmentObj.name || `Segment #${segmentObj.id}` };
3736
- console.log("[StepFlowBuilder] currentSegment memo - returning direct segment:", result);
3737
3716
  return result;
3738
3717
  }
3739
- console.log("[StepFlowBuilder] currentSegment memo - no id in segmentObj, returning initialSegment");
3740
3718
  return initialSegment;
3741
3719
  }, [form?.values, initialSegment]);
3742
3720
  const campaignContext = useMemo(() => {
@@ -3746,7 +3724,6 @@ const StepFlowBuilderInner = forwardRef(
3746
3724
  triggerConfig: values.triggerConfig,
3747
3725
  entrySegment: currentSegment
3748
3726
  };
3749
- console.log("[StepFlowBuilder] campaignContext:", ctx);
3750
3727
  return ctx;
3751
3728
  }, [form?.values, currentSegment]);
3752
3729
  useEffect(() => {
@@ -37,15 +37,27 @@ const QueryProvider = ({ children }) => {
37
37
  /* @__PURE__ */ jsx(ReactQueryDevtools, { initialIsOpen: false })
38
38
  ] });
39
39
  };
40
- const getBackendUrl = () => {
41
- const raw = process.env.STRAPI_ADMIN_API_URL || "http://localhost:3100";
42
- if (!/^https?:\/\//i.test(raw)) {
43
- return "http://localhost:3100";
40
+ let cachedUrl;
41
+ const getBackendUrl = async () => {
42
+ if (cachedUrl !== void 0) {
43
+ return cachedUrl;
44
44
  }
45
- return raw.endsWith("/") ? raw.slice(0, -1) : raw;
45
+ try {
46
+ const res = await fetch("/crm-dashboard/config");
47
+ if (res.ok) {
48
+ const data = await res.json();
49
+ cachedUrl = data.apiUrl || "";
50
+ }
51
+ } catch (e) {
52
+ console.error("[CRM]: Failed to fetch config", e);
53
+ }
54
+ if (cachedUrl === void 0) {
55
+ cachedUrl = "";
56
+ }
57
+ return cachedUrl;
46
58
  };
47
59
  const fetchFilterOptions = async () => {
48
- const backendUrl = getBackendUrl();
60
+ const backendUrl = await getBackendUrl();
49
61
  const res = await fetch(new URL("/api/crm/filter-options", backendUrl).toString());
50
62
  if (!res.ok) {
51
63
  throw new Error("Failed to fetch filter options");
@@ -72,7 +84,7 @@ const fetchDashboardStats = async (filters) => {
72
84
  if (filters.segments.length > 0) params.set("segmentIds", filters.segments.join(","));
73
85
  if (filters.compareToPrevious) params.set("compareToPrevious", "true");
74
86
  if (filters.granularity) params.set("granularity", filters.granularity);
75
- const backendUrl = getBackendUrl();
87
+ const backendUrl = await getBackendUrl();
76
88
  const res = await fetch(new URL(`/api/crm/dashboard/stats?${params}`, backendUrl).toString());
77
89
  if (!res.ok) {
78
90
  throw new Error("Failed to fetch dashboard stats");
@@ -99,7 +111,7 @@ const fetchCrmLogs = async (filters, page) => {
99
111
  if (filters.scheduledTo) params.set("scheduledTo", filters.scheduledTo.toISOString());
100
112
  if (filters.sentFrom) params.set("sentFrom", filters.sentFrom.toISOString());
101
113
  if (filters.sentTo) params.set("sentTo", filters.sentTo.toISOString());
102
- const backendUrl = getBackendUrl();
114
+ const backendUrl = await getBackendUrl();
103
115
  const res = await fetch(new URL(`/api/crm/logs?${params}`, backendUrl).toString());
104
116
  if (!res.ok) {
105
117
  throw new Error("Failed to fetch CRM logs");
@@ -127,7 +139,7 @@ const fetchAntiSpamLogs = async (filters, page) => {
127
139
  if (filters.campaigns.length > 0) params.set("campaignIds", filters.campaigns.join(","));
128
140
  if (filters.dateFrom) params.set("from", filters.dateFrom.toISOString());
129
141
  if (filters.dateTo) params.set("to", filters.dateTo.toISOString());
130
- const backendUrl = getBackendUrl();
142
+ const backendUrl = await getBackendUrl();
131
143
  const res = await fetch(new URL(`/api/crm/anti-spam-logs?${params}`, backendUrl).toString());
132
144
  if (!res.ok) {
133
145
  throw new Error("Failed to fetch anti-spam logs");
@@ -152,7 +164,7 @@ const fetchRealtimeStats = async (campaignIds) => {
152
164
  if (campaignIds && campaignIds.length > 0) {
153
165
  params.set("campaignIds", campaignIds.join(","));
154
166
  }
155
- const backendUrl = getBackendUrl();
167
+ const backendUrl = await getBackendUrl();
156
168
  const url = params.toString() ? `${backendUrl}/api/crm/dashboard/realtime?${params}` : `${backendUrl}/api/crm/dashboard/realtime`;
157
169
  const res = await fetch(url);
158
170
  if (!res.ok) {
@@ -177,7 +189,7 @@ const fetchCohortData = async (filters) => {
177
189
  if (filters.campaigns && filters.campaigns.length > 0) {
178
190
  params.set("campaignIds", filters.campaigns.join(","));
179
191
  }
180
- const backendUrl = getBackendUrl();
192
+ const backendUrl = await getBackendUrl();
181
193
  const res = await fetch(`${backendUrl}/api/crm/dashboard/cohorts?${params}`);
182
194
  if (!res.ok) {
183
195
  throw new Error("Failed to fetch cohort data");
@@ -203,7 +215,7 @@ const fetchSegmentStats = async (filters) => {
203
215
  if (filters.campaigns && filters.campaigns.length > 0) {
204
216
  params.set("campaignIds", filters.campaigns.join(","));
205
217
  }
206
- const backendUrl = getBackendUrl();
218
+ const backendUrl = await getBackendUrl();
207
219
  const res = await fetch(`${backendUrl}/api/crm/dashboard/segments?${params}`);
208
220
  if (!res.ok) {
209
221
  throw new Error("Failed to fetch segment stats");
@@ -224,7 +236,7 @@ const fetchSendTimeData = async (filters) => {
224
236
  if (filters.campaigns && filters.campaigns.length > 0) {
225
237
  params.set("campaignIds", filters.campaigns.join(","));
226
238
  }
227
- const backendUrl = getBackendUrl();
239
+ const backendUrl = await getBackendUrl();
228
240
  const res = await fetch(`${backendUrl}/api/crm/dashboard/send-times?${params}`);
229
241
  if (!res.ok) {
230
242
  throw new Error("Failed to fetch send time data");
@@ -245,7 +257,7 @@ const fetchABTestData = async (filters) => {
245
257
  if (filters.campaigns && filters.campaigns.length > 0) {
246
258
  params.set("campaignIds", filters.campaigns.join(","));
247
259
  }
248
- const backendUrl = getBackendUrl();
260
+ const backendUrl = await getBackendUrl();
249
261
  const res = await fetch(`${backendUrl}/api/crm/dashboard/ab-tests?${params}`);
250
262
  if (!res.ok) {
251
263
  throw new Error("Failed to fetch A/B test data");
@@ -131,7 +131,7 @@ const index = {
131
131
  components: {
132
132
  Input: async () => Promise.resolve().then(() => require(
133
133
  /* webpackChunkName: "crm-step-flow-builder" */
134
- "../_chunks/index--Qa1Bd7y.js"
134
+ "../_chunks/index--YXTcU9K.js"
135
135
  ))
136
136
  },
137
137
  options: {
@@ -149,7 +149,7 @@ const index = {
149
149
  Component: async () => {
150
150
  const component = await Promise.resolve().then(() => require(
151
151
  /* webpackChunkName: "crm-dashboard-page" */
152
- "../_chunks/index-wE233MhG.js"
152
+ "../_chunks/index-BZKQTqh5.js"
153
153
  ));
154
154
  return component;
155
155
  },
@@ -130,7 +130,7 @@ const index = {
130
130
  components: {
131
131
  Input: async () => import(
132
132
  /* webpackChunkName: "crm-step-flow-builder" */
133
- "../_chunks/index-5WkcveGM.mjs"
133
+ "../_chunks/index-CLXsV-bm.mjs"
134
134
  )
135
135
  },
136
136
  options: {
@@ -148,7 +148,7 @@ const index = {
148
148
  Component: async () => {
149
149
  const component = await import(
150
150
  /* webpackChunkName: "crm-dashboard-page" */
151
- "../_chunks/index-DLPqbGjQ.mjs"
151
+ "../_chunks/index-DPVdz0kv.mjs"
152
152
  );
153
153
  return component;
154
154
  },
@@ -36,10 +36,14 @@ const config = {
36
36
  }
37
37
  };
38
38
  const contentTypes = {};
39
- const controller$1 = ({ strapi }) => ({
39
+ const controller = ({ strapi }) => ({
40
40
  async index(ctx) {
41
41
  ctx.body = await strapi.plugin("crm-dashboard").service("service").getWelcomeMessage();
42
42
  },
43
+ async getConfig(ctx) {
44
+ const apiUrl = strapi.config.get("server.apiUrl");
45
+ ctx.body = { apiUrl: apiUrl || "" };
46
+ },
43
47
  async getLogs(ctx) {
44
48
  ctx.body = await strapi.plugin("crm-dashboard").service("service").getLogs(ctx.query);
45
49
  },
@@ -47,21 +51,8 @@ const controller$1 = ({ strapi }) => ({
47
51
  ctx.body = await strapi.plugin("crm-dashboard").service("service").getAntiSpamLogs(ctx.query);
48
52
  }
49
53
  });
50
- const controller = ({ strapi }) => ({
51
- async proxy(ctx) {
52
- const { path } = ctx.params;
53
- const method = ctx.method.toUpperCase();
54
- const fullPath = path ? `/${path}` : "";
55
- const result = await strapi.plugin("crm-dashboard").service("proxy").proxy(fullPath, method, ctx.query, ctx.request.body);
56
- if (result?.error && result?.status) {
57
- ctx.status = result.status;
58
- }
59
- ctx.body = result;
60
- }
61
- });
62
54
  const controllers = {
63
- controller: controller$1,
64
- proxy: controller
55
+ controller
65
56
  };
66
57
  const routes = [
67
58
  {
@@ -72,6 +63,14 @@ const routes = [
72
63
  policies: []
73
64
  }
74
65
  },
66
+ {
67
+ method: "GET",
68
+ path: "/config",
69
+ handler: "controller.getConfig",
70
+ config: {
71
+ policies: []
72
+ }
73
+ },
75
74
  {
76
75
  method: "GET",
77
76
  path: "/logs",
@@ -2462,11 +2461,11 @@ const {
2462
2461
  getAdapter,
2463
2462
  mergeConfig
2464
2463
  } = axios;
2465
- const API_URL$1 = process.env.API_URL || "http://localhost:3100";
2464
+ const API_URL = process.env.API_URL || "http://localhost:3100";
2466
2465
  const service = ({ strapi }) => ({
2467
2466
  async getLogs(query) {
2468
2467
  try {
2469
- const { data } = await axios.get(`${API_URL$1}/api/crm/logs`, { params: query });
2468
+ const { data } = await axios.get(`${API_URL}/api/crm/logs`, { params: query });
2470
2469
  return data;
2471
2470
  } catch (err) {
2472
2471
  strapi.log.error("Failed to fetch CRM logs from backend", err);
@@ -2475,7 +2474,7 @@ const service = ({ strapi }) => ({
2475
2474
  },
2476
2475
  async getAntiSpamLogs(query) {
2477
2476
  try {
2478
- const { data } = await axios.get(`${API_URL$1}/api/crm/anti-spam-logs`, { params: query });
2477
+ const { data } = await axios.get(`${API_URL}/api/crm/anti-spam-logs`, { params: query });
2479
2478
  return data;
2480
2479
  } catch (err) {
2481
2480
  strapi.log.error("Failed to fetch Anti-spam logs from backend", err);
@@ -2483,33 +2482,8 @@ const service = ({ strapi }) => ({
2483
2482
  }
2484
2483
  }
2485
2484
  });
2486
- const API_URL = process.env.API_URL ?? "";
2487
- const proxy = ({ strapi }) => ({
2488
- async proxy(path, method, query, body) {
2489
- try {
2490
- const url = `${API_URL}/api/crm${path}`;
2491
- const { data } = await axios({
2492
- method,
2493
- url,
2494
- params: query,
2495
- data: body,
2496
- headers: body ? { "Content-Type": "application/json" } : void 0
2497
- });
2498
- return data;
2499
- } catch (err) {
2500
- strapi.log.error(`CRM proxy failed: ${method} /api/crm${path}`, err);
2501
- const error = err;
2502
- return {
2503
- error: "CRM API request failed",
2504
- status: error.response?.status,
2505
- details: error.response?.data || error.message
2506
- };
2507
- }
2508
- }
2509
- });
2510
2485
  const services = {
2511
- service,
2512
- proxy
2486
+ service
2513
2487
  };
2514
2488
  const index = {
2515
2489
  register,
@@ -35,10 +35,14 @@ const config = {
35
35
  }
36
36
  };
37
37
  const contentTypes = {};
38
- const controller$1 = ({ strapi }) => ({
38
+ const controller = ({ strapi }) => ({
39
39
  async index(ctx) {
40
40
  ctx.body = await strapi.plugin("crm-dashboard").service("service").getWelcomeMessage();
41
41
  },
42
+ async getConfig(ctx) {
43
+ const apiUrl = strapi.config.get("server.apiUrl");
44
+ ctx.body = { apiUrl: apiUrl || "" };
45
+ },
42
46
  async getLogs(ctx) {
43
47
  ctx.body = await strapi.plugin("crm-dashboard").service("service").getLogs(ctx.query);
44
48
  },
@@ -46,21 +50,8 @@ const controller$1 = ({ strapi }) => ({
46
50
  ctx.body = await strapi.plugin("crm-dashboard").service("service").getAntiSpamLogs(ctx.query);
47
51
  }
48
52
  });
49
- const controller = ({ strapi }) => ({
50
- async proxy(ctx) {
51
- const { path } = ctx.params;
52
- const method = ctx.method.toUpperCase();
53
- const fullPath = path ? `/${path}` : "";
54
- const result = await strapi.plugin("crm-dashboard").service("proxy").proxy(fullPath, method, ctx.query, ctx.request.body);
55
- if (result?.error && result?.status) {
56
- ctx.status = result.status;
57
- }
58
- ctx.body = result;
59
- }
60
- });
61
53
  const controllers = {
62
- controller: controller$1,
63
- proxy: controller
54
+ controller
64
55
  };
65
56
  const routes = [
66
57
  {
@@ -71,6 +62,14 @@ const routes = [
71
62
  policies: []
72
63
  }
73
64
  },
65
+ {
66
+ method: "GET",
67
+ path: "/config",
68
+ handler: "controller.getConfig",
69
+ config: {
70
+ policies: []
71
+ }
72
+ },
74
73
  {
75
74
  method: "GET",
76
75
  path: "/logs",
@@ -2461,11 +2460,11 @@ const {
2461
2460
  getAdapter,
2462
2461
  mergeConfig
2463
2462
  } = axios;
2464
- const API_URL$1 = process.env.API_URL || "http://localhost:3100";
2463
+ const API_URL = process.env.API_URL || "http://localhost:3100";
2465
2464
  const service = ({ strapi }) => ({
2466
2465
  async getLogs(query) {
2467
2466
  try {
2468
- const { data } = await axios.get(`${API_URL$1}/api/crm/logs`, { params: query });
2467
+ const { data } = await axios.get(`${API_URL}/api/crm/logs`, { params: query });
2469
2468
  return data;
2470
2469
  } catch (err) {
2471
2470
  strapi.log.error("Failed to fetch CRM logs from backend", err);
@@ -2474,7 +2473,7 @@ const service = ({ strapi }) => ({
2474
2473
  },
2475
2474
  async getAntiSpamLogs(query) {
2476
2475
  try {
2477
- const { data } = await axios.get(`${API_URL$1}/api/crm/anti-spam-logs`, { params: query });
2476
+ const { data } = await axios.get(`${API_URL}/api/crm/anti-spam-logs`, { params: query });
2478
2477
  return data;
2479
2478
  } catch (err) {
2480
2479
  strapi.log.error("Failed to fetch Anti-spam logs from backend", err);
@@ -2482,33 +2481,8 @@ const service = ({ strapi }) => ({
2482
2481
  }
2483
2482
  }
2484
2483
  });
2485
- const API_URL = process.env.API_URL ?? "";
2486
- const proxy = ({ strapi }) => ({
2487
- async proxy(path, method, query, body) {
2488
- try {
2489
- const url = `${API_URL}/api/crm${path}`;
2490
- const { data } = await axios({
2491
- method,
2492
- url,
2493
- params: query,
2494
- data: body,
2495
- headers: body ? { "Content-Type": "application/json" } : void 0
2496
- });
2497
- return data;
2498
- } catch (err) {
2499
- strapi.log.error(`CRM proxy failed: ${method} /api/crm${path}`, err);
2500
- const error = err;
2501
- return {
2502
- error: "CRM API request failed",
2503
- status: error.response?.status,
2504
- details: error.response?.data || error.message
2505
- };
2506
- }
2507
- }
2508
- });
2509
2484
  const services = {
2510
- service,
2511
- proxy
2485
+ service
2512
2486
  };
2513
2487
  const index = {
2514
2488
  register,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inspirer-dev/crm-dashboard",
3
- "version": "1.0.52",
3
+ "version": "1.0.54",
4
4
  "description": "CRM Dashboard and Tools",
5
5
  "strapi": {
6
6
  "name": "crm-dashboard",
@@ -8,6 +8,11 @@ const controller = ({ strapi }: { strapi: Core.Strapi }) => ({
8
8
  .getWelcomeMessage();
9
9
  },
10
10
 
11
+ async getConfig(ctx: any) {
12
+ const apiUrl = strapi.config.get('server.apiUrl') as string;
13
+ ctx.body = { apiUrl: apiUrl || '' };
14
+ },
15
+
11
16
  async getLogs(ctx: any) {
12
17
  ctx.body = await strapi
13
18
  .plugin('crm-dashboard')
@@ -1,7 +1,5 @@
1
1
  import controller from './controller';
2
- import proxy from './proxy';
3
2
 
4
3
  export default {
5
4
  controller,
6
- proxy,
7
5
  };
@@ -7,6 +7,14 @@ export default [
7
7
  policies: [],
8
8
  },
9
9
  },
10
+ {
11
+ method: 'GET',
12
+ path: '/config',
13
+ handler: 'controller.getConfig',
14
+ config: {
15
+ policies: [],
16
+ },
17
+ },
10
18
  {
11
19
  method: 'GET',
12
20
  path: '/logs',
@@ -1,7 +1,5 @@
1
1
  import service from './service';
2
- import proxy from './proxy';
3
2
 
4
3
  export default {
5
4
  service,
6
- proxy,
7
5
  };
@@ -1,22 +0,0 @@
1
- import type { Core } from '@strapi/strapi';
2
-
3
- const controller = ({ strapi }: { strapi: Core.Strapi }) => ({
4
- async proxy(ctx: any) {
5
- const { path } = ctx.params;
6
- const method = ctx.method.toUpperCase();
7
- const fullPath = path ? `/${path}` : '';
8
-
9
- const result = await strapi
10
- .plugin('crm-dashboard')
11
- .service('proxy')
12
- .proxy(fullPath, method, ctx.query, ctx.request.body);
13
-
14
- if (result?.error && result?.status) {
15
- ctx.status = result.status;
16
- }
17
-
18
- ctx.body = result;
19
- },
20
- });
21
-
22
- export default controller;
@@ -1,28 +0,0 @@
1
- import type { Core } from '@strapi/strapi';
2
- import axios, { Method } from 'axios';
3
-
4
- const API_URL = process.env.API_URL ?? '';
5
-
6
- export default ({ strapi }: { strapi: Core.Strapi }) => ({
7
- async proxy(path: string, method: Method, query: Record<string, unknown>, body?: unknown) {
8
- try {
9
- const url = `${API_URL}/api/crm${path}`;
10
- const { data } = await axios({
11
- method,
12
- url,
13
- params: query,
14
- data: body,
15
- headers: body ? { 'Content-Type': 'application/json' } : undefined,
16
- });
17
- return data;
18
- } catch (err) {
19
- strapi.log.error(`CRM proxy failed: ${method} /api/crm${path}`, err);
20
- const error = err as { response?: { status?: number; data?: unknown }; message?: string };
21
- return {
22
- error: 'CRM API request failed',
23
- status: error.response?.status,
24
- details: error.response?.data || error.message,
25
- };
26
- }
27
- },
28
- });