@umituz/web-dashboard 2.5.2 → 3.1.0
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/package.json +21 -2
- package/src/domain/config/CalendarConfig.ts +22 -0
- package/src/domain/config/DashboardConfig.ts +116 -0
- package/src/domain/config/index.ts +6 -0
- package/src/domains/analytics/hooks/useAnalytics.ts +101 -5
- package/src/domains/analytics/index.ts +20 -0
- package/src/domains/analytics/services/AnalyticsEngineService.ts +319 -0
- package/src/domains/analytics/services/PerformanceService.ts +321 -0
- package/src/domains/analytics/services/index.ts +6 -0
- package/src/domains/analytics/utils/analytics.ts +1 -1
- package/src/domains/calendar/components/index.ts +7 -0
- package/src/domains/calendar/hooks/index.ts +5 -0
- package/src/domains/calendar/hooks/useCalendar.ts +232 -0
- package/src/domains/calendar/index.ts +9 -0
- package/src/domains/calendar/services/CalendarService.ts +259 -0
- package/src/domains/calendar/services/index.ts +5 -0
- package/src/domains/calendar/types/calendar.types.ts +129 -0
- package/src/domains/calendar/utils/index.ts +131 -0
- package/src/index.ts +140 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/web-dashboard",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Dashboard Layout System -
|
|
3
|
+
"version": "3.1.0",
|
|
4
|
+
"description": "Dashboard Layout System - Comprehensive analytics, calendar, customizable layouts, and config-based architecture",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -33,6 +33,13 @@
|
|
|
33
33
|
"./analytics/hooks": "./src/domains/analytics/hooks/index.ts",
|
|
34
34
|
"./analytics/utils": "./src/domains/analytics/utils/index.ts",
|
|
35
35
|
"./analytics/types": "./src/domains/analytics/types/index.ts",
|
|
36
|
+
"./analytics/services": "./src/domains/analytics/services/index.ts",
|
|
37
|
+
"./calendar": "./src/domains/calendar/index.ts",
|
|
38
|
+
"./calendar/types": "./src/domains/calendar/types/calendar.types",
|
|
39
|
+
"./calendar/services": "./src/domains/calendar/services/index.ts",
|
|
40
|
+
"./calendar/hooks": "./src/domains/calendar/hooks/index.ts",
|
|
41
|
+
"./calendar/utils": "./src/domains/calendar/utils/index.ts",
|
|
42
|
+
"./config": "./src/domain/config/index.ts",
|
|
36
43
|
"./billing": "./src/domains/billing/index.ts",
|
|
37
44
|
"./billing/components": "./src/domains/billing/components/index.ts",
|
|
38
45
|
"./billing/hooks": "./src/domains/billing/hooks/index.ts",
|
|
@@ -85,6 +92,18 @@
|
|
|
85
92
|
"login",
|
|
86
93
|
"register",
|
|
87
94
|
"analytics",
|
|
95
|
+
"analytics-services",
|
|
96
|
+
"cohort-analysis",
|
|
97
|
+
"funnel-analysis",
|
|
98
|
+
"user-segmentation",
|
|
99
|
+
"behavior-prediction",
|
|
100
|
+
"performance-monitoring",
|
|
101
|
+
"realtime-metrics",
|
|
102
|
+
"calendar",
|
|
103
|
+
"content-calendar",
|
|
104
|
+
"scheduler",
|
|
105
|
+
"content-planning",
|
|
106
|
+
"drag-drop",
|
|
88
107
|
"charts",
|
|
89
108
|
"metrics",
|
|
90
109
|
"kpi",
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calendar Configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CalendarConfig } from '../../domains/calendar/types/calendar.types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Default calendar configuration
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_CALENDAR_CONFIG: CalendarConfig = {
|
|
11
|
+
defaultView: 'month',
|
|
12
|
+
showWeekends: true,
|
|
13
|
+
startOfWeek: 0,
|
|
14
|
+
hourRange: {
|
|
15
|
+
start: 0,
|
|
16
|
+
end: 23,
|
|
17
|
+
},
|
|
18
|
+
enableDragDrop: true,
|
|
19
|
+
showPlatformFilters: true,
|
|
20
|
+
platforms: ['instagram', 'facebook', 'twitter', 'linkedin', 'tiktok', 'youtube'],
|
|
21
|
+
slotDuration: 30,
|
|
22
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard Configuration
|
|
3
|
+
* Config-based dashboard system
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ChartType } from '../../domains/analytics/types/analytics';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Analytics service types
|
|
10
|
+
*/
|
|
11
|
+
export type AnalyticsServiceType = 'traffic' | 'cohort' | 'funnel' | 'growth' | 'performance';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Export format
|
|
15
|
+
*/
|
|
16
|
+
export type ExportFormat = 'pdf' | 'excel' | 'csv' | 'json';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Analytics configuration
|
|
20
|
+
*/
|
|
21
|
+
export interface AnalyticsConfig {
|
|
22
|
+
/** Enable analytics */
|
|
23
|
+
enabled?: boolean;
|
|
24
|
+
/** Analytics services to include */
|
|
25
|
+
services?: AnalyticsServiceType[];
|
|
26
|
+
/** Real-time updates */
|
|
27
|
+
realtime?: boolean;
|
|
28
|
+
/** Export configuration */
|
|
29
|
+
export?: {
|
|
30
|
+
enabled?: boolean;
|
|
31
|
+
formats?: ExportFormat[];
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Data refresh configuration
|
|
37
|
+
*/
|
|
38
|
+
export interface DataConfig {
|
|
39
|
+
/** Refresh interval in milliseconds */
|
|
40
|
+
refreshInterval?: number;
|
|
41
|
+
/** Cache duration in milliseconds */
|
|
42
|
+
cacheDuration?: number;
|
|
43
|
+
/** Retry attempts for failed requests */
|
|
44
|
+
retryAttempts?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Charts configuration
|
|
49
|
+
*/
|
|
50
|
+
export interface ChartsConfig {
|
|
51
|
+
/** Default chart type */
|
|
52
|
+
defaultType?: ChartType;
|
|
53
|
+
/** Chart theme */
|
|
54
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
55
|
+
/** Enable animations */
|
|
56
|
+
animations?: boolean;
|
|
57
|
+
/** Color palette */
|
|
58
|
+
colors?: string[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Performance configuration
|
|
63
|
+
*/
|
|
64
|
+
export interface PerformanceConfig {
|
|
65
|
+
/** Enable lazy loading */
|
|
66
|
+
lazyLoad?: boolean;
|
|
67
|
+
/** Enable virtual scrolling */
|
|
68
|
+
virtualScrolling?: boolean;
|
|
69
|
+
/** Pagination size */
|
|
70
|
+
paginationSize?: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Dashboard configuration
|
|
75
|
+
*/
|
|
76
|
+
export interface DashboardConfig {
|
|
77
|
+
/** Analytics configuration */
|
|
78
|
+
analytics?: AnalyticsConfig;
|
|
79
|
+
/** Data configuration */
|
|
80
|
+
data?: DataConfig;
|
|
81
|
+
/** Charts configuration */
|
|
82
|
+
charts?: ChartsConfig;
|
|
83
|
+
/** Performance configuration */
|
|
84
|
+
performance?: PerformanceConfig;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Default dashboard configuration
|
|
89
|
+
*/
|
|
90
|
+
export const DEFAULT_DASHBOARD_CONFIG: DashboardConfig = {
|
|
91
|
+
analytics: {
|
|
92
|
+
enabled: true,
|
|
93
|
+
services: ['traffic', 'cohort', 'funnel', 'growth'],
|
|
94
|
+
realtime: true,
|
|
95
|
+
export: {
|
|
96
|
+
enabled: true,
|
|
97
|
+
formats: ['pdf', 'excel', 'csv'],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
data: {
|
|
101
|
+
refreshInterval: 30000, // 30 seconds
|
|
102
|
+
cacheDuration: 300000, // 5 minutes
|
|
103
|
+
retryAttempts: 3,
|
|
104
|
+
},
|
|
105
|
+
charts: {
|
|
106
|
+
defaultType: 'line',
|
|
107
|
+
theme: 'auto',
|
|
108
|
+
animations: true,
|
|
109
|
+
colors: ['#6366f1', '#a855f7', '#f59e0b', '#10b981'],
|
|
110
|
+
},
|
|
111
|
+
performance: {
|
|
112
|
+
lazyLoad: true,
|
|
113
|
+
virtualScrolling: true,
|
|
114
|
+
paginationSize: 20,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* useAnalytics Hook
|
|
3
3
|
*
|
|
4
4
|
* Core analytics hook for fetching and managing analytics data
|
|
5
|
+
* Enhanced with config support and advanced analytics services
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
|
-
import { useState, useCallback, useEffect } from "react";
|
|
8
|
+
import { useState, useCallback, useEffect, useMemo } from "react";
|
|
8
9
|
import type {
|
|
9
10
|
KPIs,
|
|
10
11
|
TimeSeriesData,
|
|
@@ -13,6 +14,8 @@ import type {
|
|
|
13
14
|
AnalyticsExportOptions,
|
|
14
15
|
} from "../types/analytics";
|
|
15
16
|
import { createKPI } from "../utils/analytics";
|
|
17
|
+
import { analyticsEngineService, performanceService } from "../services";
|
|
18
|
+
import type { DashboardConfig } from "../../../domain/config";
|
|
16
19
|
|
|
17
20
|
interface UseAnalyticsOptions {
|
|
18
21
|
/** Analytics API base URL */
|
|
@@ -21,6 +24,10 @@ interface UseAnalyticsOptions {
|
|
|
21
24
|
initialDateRange?: DateRangeValue;
|
|
22
25
|
/** Auto-refresh interval in ms (0 to disable) */
|
|
23
26
|
refreshInterval?: number;
|
|
27
|
+
/** Dashboard configuration */
|
|
28
|
+
config?: DashboardConfig;
|
|
29
|
+
/** Error callback */
|
|
30
|
+
onError?: (error: Error) => void;
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
interface AnalyticsData {
|
|
@@ -43,7 +50,19 @@ interface AnalyticsData {
|
|
|
43
50
|
* @returns Analytics data and actions
|
|
44
51
|
*/
|
|
45
52
|
export function useAnalytics(options: UseAnalyticsOptions = {}) {
|
|
46
|
-
const {
|
|
53
|
+
const {
|
|
54
|
+
apiUrl = "/api/analytics",
|
|
55
|
+
initialDateRange,
|
|
56
|
+
refreshInterval = 0,
|
|
57
|
+
config,
|
|
58
|
+
onError,
|
|
59
|
+
} = options;
|
|
60
|
+
|
|
61
|
+
// Apply config defaults
|
|
62
|
+
const effectiveRefreshInterval = useMemo(
|
|
63
|
+
() => refreshInterval || config?.data?.refreshInterval || 0,
|
|
64
|
+
[refreshInterval, config]
|
|
65
|
+
);
|
|
47
66
|
|
|
48
67
|
// State
|
|
49
68
|
const [dateRange, setDateRange] = useState<DateRangeValue>(
|
|
@@ -162,11 +181,81 @@ export function useAnalytics(options: UseAnalyticsOptions = {}) {
|
|
|
162
181
|
|
|
163
182
|
// Auto-refresh
|
|
164
183
|
useEffect(() => {
|
|
165
|
-
if (
|
|
166
|
-
const interval = setInterval(fetchAnalytics,
|
|
184
|
+
if (effectiveRefreshInterval > 0) {
|
|
185
|
+
const interval = setInterval(fetchAnalytics, effectiveRefreshInterval);
|
|
167
186
|
return () => clearInterval(interval);
|
|
168
187
|
}
|
|
169
|
-
}, [fetchAnalytics,
|
|
188
|
+
}, [fetchAnalytics, effectiveRefreshInterval]);
|
|
189
|
+
|
|
190
|
+
// Advanced analytics methods
|
|
191
|
+
const calculateRetention = useCallback(
|
|
192
|
+
(userData: any[]) => {
|
|
193
|
+
try {
|
|
194
|
+
return analyticsEngineService.calculateRetention(userData);
|
|
195
|
+
} catch (err) {
|
|
196
|
+
onError?.(err as Error);
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
[onError]
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const calculateFunnel = useCallback(
|
|
204
|
+
(data: any[], steps: string[]) => {
|
|
205
|
+
try {
|
|
206
|
+
return analyticsEngineService.calculateFunnel(data, steps);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
onError?.(err as Error);
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
[onError]
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const segmentUsers = useCallback(
|
|
216
|
+
(userData: any[]) => {
|
|
217
|
+
try {
|
|
218
|
+
return analyticsEngineService.segmentUsers(userData);
|
|
219
|
+
} catch (err) {
|
|
220
|
+
onError?.(err as Error);
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
[onError]
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
const predictUserBehavior = useCallback(
|
|
228
|
+
(user: any, historicalData: any[]) => {
|
|
229
|
+
try {
|
|
230
|
+
return analyticsEngineService.predictUserBehavior(user, historicalData);
|
|
231
|
+
} catch (err) {
|
|
232
|
+
onError?.(err as Error);
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
[onError]
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const getPerformanceMetrics = useCallback(() => {
|
|
240
|
+
try {
|
|
241
|
+
return performanceService.getDashboardMetrics();
|
|
242
|
+
} catch (err) {
|
|
243
|
+
onError?.(err as Error);
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}, [onError]);
|
|
247
|
+
|
|
248
|
+
const getRealtimeMetrics = useCallback(
|
|
249
|
+
(previous?: any) => {
|
|
250
|
+
try {
|
|
251
|
+
return performanceService.simulateRealtimeMetrics(previous);
|
|
252
|
+
} catch (err) {
|
|
253
|
+
onError?.(err as Error);
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
[onError]
|
|
258
|
+
);
|
|
170
259
|
|
|
171
260
|
return {
|
|
172
261
|
...data,
|
|
@@ -174,5 +263,12 @@ export function useAnalytics(options: UseAnalyticsOptions = {}) {
|
|
|
174
263
|
updateDateRange,
|
|
175
264
|
refresh,
|
|
176
265
|
exportData,
|
|
266
|
+
// Advanced analytics
|
|
267
|
+
calculateRetention,
|
|
268
|
+
calculateFunnel,
|
|
269
|
+
segmentUsers,
|
|
270
|
+
predictUserBehavior,
|
|
271
|
+
getPerformanceMetrics,
|
|
272
|
+
getRealtimeMetrics,
|
|
177
273
|
};
|
|
178
274
|
}
|
|
@@ -17,6 +17,26 @@ export {
|
|
|
17
17
|
useAnalytics,
|
|
18
18
|
} from "./hooks";
|
|
19
19
|
|
|
20
|
+
// Services
|
|
21
|
+
export {
|
|
22
|
+
AnalyticsEngineService,
|
|
23
|
+
analyticsEngineService,
|
|
24
|
+
PerformanceService,
|
|
25
|
+
performanceService,
|
|
26
|
+
} from "./services";
|
|
27
|
+
export type {
|
|
28
|
+
ConversionPath,
|
|
29
|
+
HeatmapData,
|
|
30
|
+
UserBehaviorPrediction,
|
|
31
|
+
UserData,
|
|
32
|
+
ConversionData,
|
|
33
|
+
FunnelItem,
|
|
34
|
+
ActivityItem,
|
|
35
|
+
PerformanceMetric,
|
|
36
|
+
DashboardMetrics,
|
|
37
|
+
RealtimeMetrics,
|
|
38
|
+
} from "./services";
|
|
39
|
+
|
|
20
40
|
// Utils
|
|
21
41
|
export {
|
|
22
42
|
formatNumber,
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Engine Service
|
|
3
|
+
*
|
|
4
|
+
* Advanced analytics operations including cohort analysis, funnel analysis,
|
|
5
|
+
* user segmentation, behavior prediction, and conversion path analysis.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
CohortAnalysis,
|
|
10
|
+
FunnelData,
|
|
11
|
+
UserSegment,
|
|
12
|
+
} from '../types/analytics';
|
|
13
|
+
|
|
14
|
+
export interface ConversionPath {
|
|
15
|
+
path: string[];
|
|
16
|
+
conversions: number;
|
|
17
|
+
value: number;
|
|
18
|
+
abandonmentRate: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface HeatmapData {
|
|
22
|
+
day: string;
|
|
23
|
+
hour: number;
|
|
24
|
+
value: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface UserBehaviorPrediction {
|
|
28
|
+
churnProbability: number;
|
|
29
|
+
lifetimeValue: number;
|
|
30
|
+
nextAction: string;
|
|
31
|
+
confidence: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface UserData {
|
|
35
|
+
signup_date?: string | Date;
|
|
36
|
+
last_activity?: string | Date;
|
|
37
|
+
last_login_days?: number;
|
|
38
|
+
session_duration?: number;
|
|
39
|
+
pages_per_session?: number;
|
|
40
|
+
bounce_rate?: number;
|
|
41
|
+
age?: number;
|
|
42
|
+
location?: string;
|
|
43
|
+
churned?: boolean;
|
|
44
|
+
lifetime_value?: number;
|
|
45
|
+
last_action?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ConversionData {
|
|
49
|
+
path?: string[];
|
|
50
|
+
value?: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface FunnelItem {
|
|
54
|
+
[key: string]: boolean | number | undefined;
|
|
55
|
+
time_spent?: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ActivityItem {
|
|
59
|
+
timestamp: string | Date;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Analytics Engine Service
|
|
64
|
+
*
|
|
65
|
+
* Singleton service for advanced analytics calculations
|
|
66
|
+
*/
|
|
67
|
+
export class AnalyticsEngineService {
|
|
68
|
+
private static instance: AnalyticsEngineService;
|
|
69
|
+
|
|
70
|
+
private constructor() {}
|
|
71
|
+
|
|
72
|
+
public static getInstance(): AnalyticsEngineService {
|
|
73
|
+
if (!AnalyticsEngineService.instance) {
|
|
74
|
+
AnalyticsEngineService.instance = new AnalyticsEngineService();
|
|
75
|
+
}
|
|
76
|
+
return AnalyticsEngineService.instance;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Calculate cohort retention analysis
|
|
81
|
+
*
|
|
82
|
+
* @param data - User data with signup and activity dates
|
|
83
|
+
* @returns Cohort analysis with retention rates
|
|
84
|
+
*/
|
|
85
|
+
public calculateRetention(data: UserData[]): CohortAnalysis[] {
|
|
86
|
+
const cohorts: Map<string, UserData[]> = new Map();
|
|
87
|
+
|
|
88
|
+
data.forEach((user) => {
|
|
89
|
+
if (!user.signup_date) return;
|
|
90
|
+
const signupMonth = new Date(user.signup_date).toISOString().slice(0, 7);
|
|
91
|
+
if (!cohorts.has(signupMonth)) {
|
|
92
|
+
cohorts.set(signupMonth, []);
|
|
93
|
+
}
|
|
94
|
+
const cohort = cohorts.get(signupMonth);
|
|
95
|
+
if (cohort) {
|
|
96
|
+
cohort.push(user);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const cohortAnalysis: CohortAnalysis[] = [];
|
|
101
|
+
|
|
102
|
+
cohorts.forEach((users, cohort) => {
|
|
103
|
+
const retention: number[] = [];
|
|
104
|
+
for (let month = 0; month < 12; month++) {
|
|
105
|
+
const activeUsersCount = users.filter((user) => {
|
|
106
|
+
if (!user.last_activity) return false;
|
|
107
|
+
const signupDate = new Date(user.signup_date!);
|
|
108
|
+
const targetDate = new Date(signupDate.getFullYear(), signupDate.getMonth() + month, 1);
|
|
109
|
+
return new Date(user.last_activity) >= targetDate;
|
|
110
|
+
}).length;
|
|
111
|
+
|
|
112
|
+
retention.push((activeUsersCount / users.length) * 100);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
cohortAnalysis.push({
|
|
116
|
+
cohort,
|
|
117
|
+
size: users.length,
|
|
118
|
+
retention,
|
|
119
|
+
averageRetention: retention.reduce((a, b) => a + b, 0) / retention.length,
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
return cohortAnalysis;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Analyze conversion paths
|
|
128
|
+
*
|
|
129
|
+
* @param data - Conversion data with paths and values
|
|
130
|
+
* @returns Conversion path analysis
|
|
131
|
+
*/
|
|
132
|
+
public analyzeConversionPaths(data: ConversionData[]): ConversionPath[] {
|
|
133
|
+
const paths: Map<string, { count: number; value: number }> = new Map();
|
|
134
|
+
let totalConversions = 0;
|
|
135
|
+
|
|
136
|
+
data.forEach((conversion) => {
|
|
137
|
+
if (!conversion.path) return;
|
|
138
|
+
const pathKey = conversion.path.join(' → ');
|
|
139
|
+
if (!paths.has(pathKey)) {
|
|
140
|
+
paths.set(pathKey, { count: 0, value: 0 });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const path = paths.get(pathKey);
|
|
144
|
+
if (path) {
|
|
145
|
+
path.count++;
|
|
146
|
+
path.value += conversion.value || 0;
|
|
147
|
+
totalConversions++;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return Array.from(paths.entries())
|
|
152
|
+
.map(([pathString, path]) => ({
|
|
153
|
+
path: pathString.split(' → '),
|
|
154
|
+
conversions: path.count,
|
|
155
|
+
value: path.value,
|
|
156
|
+
abandonmentRate:
|
|
157
|
+
totalConversions > 0 ? ((totalConversions - path.count) / totalConversions) * 100 : 0,
|
|
158
|
+
}))
|
|
159
|
+
.sort((a, b) => b.conversions - a.conversions);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Calculate funnel analysis
|
|
164
|
+
*
|
|
165
|
+
* @param data - Funnel data with step indicators
|
|
166
|
+
* @param steps - Funnel step names
|
|
167
|
+
* @returns Funnel analysis data
|
|
168
|
+
*/
|
|
169
|
+
public calculateFunnel(data: FunnelItem[], steps: string[]): FunnelData {
|
|
170
|
+
let previousCount = data.length;
|
|
171
|
+
const funnelSteps = steps.map((step, index) => {
|
|
172
|
+
const stepCount = data.filter((item) => item[step]).length;
|
|
173
|
+
const conversionRate =
|
|
174
|
+
index === 0 ? 100 : previousCount > 0 ? (stepCount / previousCount) * 100 : 0;
|
|
175
|
+
previousCount = stepCount;
|
|
176
|
+
|
|
177
|
+
const stepData = data.filter((item) => item[step]);
|
|
178
|
+
const avgTime =
|
|
179
|
+
stepData.length > 0
|
|
180
|
+
? stepData.reduce((sum, item) => sum + (item.time_spent || 0), 0) / stepData.length
|
|
181
|
+
: 0;
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
name: step,
|
|
185
|
+
count: stepCount,
|
|
186
|
+
conversionRate,
|
|
187
|
+
dropOffRate: 100 - conversionRate,
|
|
188
|
+
averageTime: avgTime,
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
title: 'Conversion Funnel',
|
|
194
|
+
steps: funnelSteps,
|
|
195
|
+
totalUsers: data.length,
|
|
196
|
+
finalConversion: funnelSteps[funnelSteps.length - 1]?.conversionRate || 0,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Generate activity heatmap
|
|
202
|
+
*
|
|
203
|
+
* @param data - Activity data with timestamps
|
|
204
|
+
* @returns Heatmap data by day and hour
|
|
205
|
+
*/
|
|
206
|
+
public generateActivityHeatmap(data: ActivityItem[]): HeatmapData[] {
|
|
207
|
+
const heatmap: HeatmapData[] = [];
|
|
208
|
+
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
|
209
|
+
|
|
210
|
+
for (let day = 0; day < 7; day++) {
|
|
211
|
+
for (let hour = 0; hour < 24; hour++) {
|
|
212
|
+
const count = data.filter((item) => {
|
|
213
|
+
const d = new Date(item.timestamp);
|
|
214
|
+
return d.getDay() === day && d.getHours() === hour;
|
|
215
|
+
}).length;
|
|
216
|
+
|
|
217
|
+
heatmap.push({ day: days[day], hour, value: count });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return heatmap;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Segment users by behavior
|
|
225
|
+
*
|
|
226
|
+
* @param data - User data
|
|
227
|
+
* @returns User segments
|
|
228
|
+
*/
|
|
229
|
+
public segmentUsers(data: UserData[]): UserSegment[] {
|
|
230
|
+
const defineSegment = (
|
|
231
|
+
name: string,
|
|
232
|
+
filter: (u: UserData) => boolean,
|
|
233
|
+
chars: string[]
|
|
234
|
+
) => {
|
|
235
|
+
const users = data.filter(filter);
|
|
236
|
+
const avg = (field: string) =>
|
|
237
|
+
users.length === 0
|
|
238
|
+
? 0
|
|
239
|
+
: users.reduce((s, i) => s + ((i as any)[field] || 0), 0) / users.length;
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
name,
|
|
243
|
+
count: users.length,
|
|
244
|
+
percentage: (users.length / data.length) * 100,
|
|
245
|
+
characteristics: chars,
|
|
246
|
+
behavior: {
|
|
247
|
+
avgSessionDuration: avg('session_duration'),
|
|
248
|
+
pagesPerSession: avg('pages_per_session'),
|
|
249
|
+
bounceRate: avg('bounce_rate'),
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
return [
|
|
255
|
+
defineSegment('Active', (u) => u.last_login_days! <= 7, [
|
|
256
|
+
'Recent login',
|
|
257
|
+
'High engagement',
|
|
258
|
+
]),
|
|
259
|
+
defineSegment('Moderate', (u) => u.last_login_days! > 7 && u.last_login_days! <= 30, [
|
|
260
|
+
'Occasional login',
|
|
261
|
+
]),
|
|
262
|
+
defineSegment('Inactive', (u) => u.last_login_days! > 30, [
|
|
263
|
+
'Churn risk',
|
|
264
|
+
'Low activity',
|
|
265
|
+
]),
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Predict user behavior
|
|
271
|
+
*
|
|
272
|
+
* @param user - Target user
|
|
273
|
+
* @param historicalData - Historical user data
|
|
274
|
+
* @returns Behavior prediction
|
|
275
|
+
*/
|
|
276
|
+
public predictUserBehavior(
|
|
277
|
+
user: UserData,
|
|
278
|
+
historicalData: UserData[]
|
|
279
|
+
): UserBehaviorPrediction {
|
|
280
|
+
const similarUsers = historicalData.filter(
|
|
281
|
+
(u) => Math.abs((u.age || 0) - (user.age || 0)) < 5 && u.location === user.location
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
if (similarUsers.length === 0) {
|
|
285
|
+
return {
|
|
286
|
+
churnProbability: 0,
|
|
287
|
+
lifetimeValue: 0,
|
|
288
|
+
nextAction: 'none',
|
|
289
|
+
confidence: 0,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const churnCount = similarUsers.filter((u) => u.churned).length;
|
|
294
|
+
const totalLTV = similarUsers.reduce((s, u) => s + (u.lifetime_value || 0), 0);
|
|
295
|
+
|
|
296
|
+
// Predict next action based on frequency in similar users
|
|
297
|
+
const actions = similarUsers.map((u) => u.last_action).filter(Boolean) as string[];
|
|
298
|
+
const actionCounts = actions.reduce((c: Record<string, number>, a) => {
|
|
299
|
+
c[a] = (c[a] || 0) + 1;
|
|
300
|
+
return c;
|
|
301
|
+
}, {});
|
|
302
|
+
const nextAction = Object.keys(actionCounts).reduce(
|
|
303
|
+
(a, b) => (actionCounts[a] > actionCounts[b] ? a : b),
|
|
304
|
+
'unknown'
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
churnProbability: churnCount / similarUsers.length,
|
|
309
|
+
lifetimeValue: totalLTV / similarUsers.length,
|
|
310
|
+
nextAction,
|
|
311
|
+
confidence: Math.min(0.9, similarUsers.length / 100),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Singleton instance
|
|
318
|
+
*/
|
|
319
|
+
export const analyticsEngineService = AnalyticsEngineService.getInstance();
|