@chainloyalty/react 1.0.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.
@@ -0,0 +1,273 @@
1
+ /**
2
+ * @chainloyalty/react - useTrackEvent Hook
3
+ *
4
+ * Purpose:
5
+ * This hook provides a simple way to track user events in ChainLoyalty.
6
+ * It automatically captures the connected wallet address and handles:
7
+ * - Multiple event types (subscription, referral, feature_usage, milestone, purchase)
8
+ * - Loading, success, and error states
9
+ * - Toast notifications for user feedback
10
+ * - Returning structured responses
11
+ *
12
+ * Functionality:
13
+ * - useTrackEvent: React hook for tracking events within components
14
+ * - trackEvent: Standalone async function for tracking events programmatically
15
+ * - Automatic wallet address attachment
16
+ * - Built-in error handling and user notifications
17
+ */
18
+
19
+ import { useState, useCallback } from 'react';
20
+
21
+ // ============================================================================
22
+ // Types & Interfaces
23
+ // ============================================================================
24
+
25
+ export type EventType = 'subscription' | 'referral' | 'feature_usage' | 'milestone' | 'purchase';
26
+
27
+ export interface EventData {
28
+ type: EventType;
29
+ metadata?: Record<string, any>;
30
+ value?: number;
31
+ description?: string;
32
+ }
33
+
34
+ export interface TrackEventResponse {
35
+ success: boolean;
36
+ eventId?: string;
37
+ message: string;
38
+ data?: Record<string, any>;
39
+ error?: string;
40
+ }
41
+
42
+ export interface UseTrackEventState {
43
+ loading: boolean;
44
+ error: string | null;
45
+ success: boolean;
46
+ }
47
+
48
+ // ============================================================================
49
+ // Mock Toast Notification System (Production: replace with your toast library)
50
+ // ============================================================================
51
+
52
+ const showToast = (message: string, type: 'success' | 'error' = 'success') => {
53
+ // In production, use react-hot-toast, sonner, or similar
54
+ console.log(`[${type.toUpperCase()}]`, message);
55
+
56
+ // Create a simple visual toast for demo
57
+ const toast = document.createElement('div');
58
+ toast.style.cssText = `
59
+ position: fixed;
60
+ bottom: 20px;
61
+ right: 20px;
62
+ padding: 12px 20px;
63
+ border-radius: 6px;
64
+ background-color: ${type === 'success' ? '#3FB950' : '#f85149'};
65
+ color: white;
66
+ font-size: 14px;
67
+ font-weight: 500;
68
+ z-index: 9999;
69
+ animation: slideInUp 0.3s ease-out;
70
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
71
+ `;
72
+ toast.textContent = message;
73
+ document.body.appendChild(toast);
74
+
75
+ setTimeout(() => {
76
+ toast.style.animation = 'slideOutDown 0.3s ease-in';
77
+ setTimeout(() => toast.remove(), 300);
78
+ }, 3000);
79
+ };
80
+
81
+ // ============================================================================
82
+ // Mock API Call Function
83
+ // ============================================================================
84
+
85
+ const callEventIngestionAPI = async (
86
+ eventData: EventData & { walletAddress: string }
87
+ ): Promise<TrackEventResponse> => {
88
+ // In production, replace this with actual API call:
89
+ // return fetch('/api/events/track', {
90
+ // method: 'POST',
91
+ // headers: { 'Content-Type': 'application/json' },
92
+ // body: JSON.stringify(eventData),
93
+ // }).then(res => res.json());
94
+
95
+ // Mock API call with realistic delay
96
+ return new Promise((resolve) => {
97
+ setTimeout(() => {
98
+ const eventId = `evt_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
99
+ resolve({
100
+ success: true,
101
+ eventId,
102
+ message: `Event "${eventData.type}" tracked successfully`,
103
+ data: {
104
+ type: eventData.type,
105
+ walletAddress: eventData.walletAddress,
106
+ timestamp: new Date().toISOString(),
107
+ eventId,
108
+ },
109
+ });
110
+ }, 500);
111
+ });
112
+ };
113
+
114
+ // ============================================================================
115
+ // Standalone trackEvent Function
116
+ // ============================================================================
117
+
118
+ /**
119
+ * Standalone function to track an event programmatically.
120
+ * Can be called outside of React components.
121
+ *
122
+ * @param eventData - Event details (type, metadata, value, description)
123
+ * @param walletAddress - Connected wallet address (required)
124
+ * @returns Promise with tracking result
125
+ */
126
+ export const trackEvent = async (
127
+ eventData: EventData,
128
+ walletAddress: string
129
+ ): Promise<TrackEventResponse> => {
130
+ try {
131
+ if (!walletAddress) {
132
+ const error = 'Wallet address is required to track events';
133
+ showToast(error, 'error');
134
+ return {
135
+ success: false,
136
+ message: error,
137
+ error,
138
+ };
139
+ }
140
+
141
+ // Validate event type
142
+ const validEventTypes: EventType[] = [
143
+ 'subscription',
144
+ 'referral',
145
+ 'feature_usage',
146
+ 'milestone',
147
+ 'purchase',
148
+ ];
149
+ if (!validEventTypes.includes(eventData.type)) {
150
+ const error = `Invalid event type: ${eventData.type}`;
151
+ showToast(error, 'error');
152
+ return {
153
+ success: false,
154
+ message: error,
155
+ error,
156
+ };
157
+ }
158
+
159
+ // Call API
160
+ const response = await callEventIngestionAPI({
161
+ ...eventData,
162
+ walletAddress,
163
+ });
164
+
165
+ // Show success notification
166
+ if (response.success) {
167
+ showToast(response.message, 'success');
168
+ } else {
169
+ showToast(response.message || 'Failed to track event', 'error');
170
+ }
171
+
172
+ return response;
173
+ } catch (err) {
174
+ const errorMessage = err instanceof Error ? err.message : 'Failed to track event';
175
+ showToast(errorMessage, 'error');
176
+ return {
177
+ success: false,
178
+ message: errorMessage,
179
+ error: errorMessage,
180
+ };
181
+ }
182
+ };
183
+
184
+ // ============================================================================
185
+ // useTrackEvent Hook
186
+ // ============================================================================
187
+
188
+ /**
189
+ * React hook for tracking events within components.
190
+ * Provides loading, error, and success states.
191
+ * Automatically manages wallet state.
192
+ *
193
+ * @param walletAddress - Connected wallet address (required)
194
+ * @returns Object with track function and state
195
+ */
196
+ export const useTrackEvent = (walletAddress: string | null) => {
197
+ const [state, setState] = useState<UseTrackEventState>({
198
+ loading: false,
199
+ error: null,
200
+ success: false,
201
+ });
202
+
203
+ const track = useCallback(
204
+ async (eventData: EventData): Promise<TrackEventResponse> => {
205
+ // Reset state
206
+ setState({ loading: true, error: null, success: false });
207
+
208
+ try {
209
+ if (!walletAddress) {
210
+ throw new Error('Wallet not connected. Please connect your wallet first.');
211
+ }
212
+
213
+ // Call tracking function
214
+ const response = await trackEvent(eventData, walletAddress);
215
+
216
+ // Update state based on response
217
+ if (response.success) {
218
+ setState({ loading: false, error: null, success: true });
219
+ } else {
220
+ setState({
221
+ loading: false,
222
+ error: response.error || response.message,
223
+ success: false,
224
+ });
225
+ }
226
+
227
+ return response;
228
+ } catch (err) {
229
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
230
+ setState({
231
+ loading: false,
232
+ error: errorMessage,
233
+ success: false,
234
+ });
235
+ showToast(errorMessage, 'error');
236
+ return {
237
+ success: false,
238
+ message: errorMessage,
239
+ error: errorMessage,
240
+ };
241
+ }
242
+ },
243
+ [walletAddress]
244
+ );
245
+
246
+ return {
247
+ track,
248
+ loading: state.loading,
249
+ error: state.error,
250
+ success: state.success,
251
+ };
252
+ };
253
+
254
+ // ============================================================================
255
+ // Example Usage Hook (for documentation)
256
+ // ============================================================================
257
+
258
+ /**
259
+ * Example of how to use useTrackEvent in a component:
260
+ *
261
+ * const { track, loading, error, success } = useTrackEvent(walletAddress);
262
+ *
263
+ * const handlePurchase = async () => {
264
+ * const response = await track({
265
+ * type: 'purchase',
266
+ * value: 99.99,
267
+ * description: 'Premium subscription',
268
+ * metadata: { productId: 'prod_123' },
269
+ * });
270
+ * };
271
+ */
272
+
273
+ export default useTrackEvent;
package/tsconfig.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+ "esModuleInterop": true,
9
+ "allowSyntheticDefaultImports": true,
10
+ "strict": true,
11
+ "noUnusedLocals": true,
12
+ "noUnusedParameters": true,
13
+ "noFallthroughCasesInSwitch": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "resolveJsonModule": true,
16
+ "isolatedModules": true,
17
+ "noEmit": false,
18
+ "jsx": "react-jsx",
19
+ "moduleResolution": "bundler",
20
+ "declaration": true,
21
+ "declarationMap": true,
22
+ "sourceMap": true,
23
+ "outDir": "./dist",
24
+ "types": ["react/jsx-runtime"]
25
+ },
26
+ "include": ["src"]
27
+ }