@diviswap/sdk 1.7.6
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/LICENSE +21 -0
- package/README.md +510 -0
- package/bin/create-diviswap-app.js +25 -0
- package/bin/diviswap-sdk.js +4 -0
- package/dist/cli/index.js +1888 -0
- package/dist/cli/templates/nextjs-app/actions.ts.hbs +259 -0
- package/dist/cli/templates/nextjs-app/api-hooks.ts.hbs +439 -0
- package/dist/cli/templates/nextjs-app/api-route.ts.hbs +502 -0
- package/dist/cli/templates/nextjs-app/auth-context.tsx.hbs +59 -0
- package/dist/cli/templates/nextjs-app/client.ts.hbs +116 -0
- package/dist/cli/templates/nextjs-app/dashboard-hooks.ts.hbs +180 -0
- package/dist/cli/templates/nextjs-app/example-page.tsx.hbs +276 -0
- package/dist/cli/templates/nextjs-app/hooks.ts.hbs +252 -0
- package/dist/cli/templates/nextjs-app/kyc-hooks.ts.hbs +87 -0
- package/dist/cli/templates/nextjs-app/kyc-wizard.css.hbs +433 -0
- package/dist/cli/templates/nextjs-app/kyc-wizard.tsx.hbs +711 -0
- package/dist/cli/templates/nextjs-app/layout-wrapper.tsx.hbs +13 -0
- package/dist/cli/templates/nextjs-app/layout.tsx.hbs +13 -0
- package/dist/cli/templates/nextjs-app/middleware.ts.hbs +49 -0
- package/dist/cli/templates/nextjs-app/provider-wrapper.tsx.hbs +8 -0
- package/dist/cli/templates/nextjs-app/provider.tsx.hbs +408 -0
- package/dist/cli/templates/nextjs-app/setup-provider.tsx.hbs +25 -0
- package/dist/cli/templates/nextjs-app/types.ts.hbs +159 -0
- package/dist/cli/templates/react/api-client-wrapper.ts.hbs +89 -0
- package/dist/cli/templates/react/example.tsx.hbs +69 -0
- package/dist/cli/templates/react/tanstack-hooks.ts.hbs +185 -0
- package/dist/cli/templates/webhooks/nextjs.hbs +98 -0
- package/dist/index.d.mts +91 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.js +2339 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2313 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react/index.d.mts +192 -0
- package/dist/react/index.d.ts +192 -0
- package/dist/react/index.js +1083 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +1064 -0
- package/dist/react/index.mjs.map +1 -0
- package/dist/wallet-BEGvzNtB.d.mts +1614 -0
- package/dist/wallet-BEGvzNtB.d.ts +1614 -0
- package/package.json +102 -0
- package/src/cli/templates/index.ts +65 -0
- package/src/cli/templates/nextjs-app/actions.ts.hbs +259 -0
- package/src/cli/templates/nextjs-app/api-hooks.ts.hbs +439 -0
- package/src/cli/templates/nextjs-app/api-route.ts.hbs +502 -0
- package/src/cli/templates/nextjs-app/auth-context.tsx.hbs +59 -0
- package/src/cli/templates/nextjs-app/client.ts.hbs +116 -0
- package/src/cli/templates/nextjs-app/dashboard-hooks.ts.hbs +180 -0
- package/src/cli/templates/nextjs-app/example-page.tsx.hbs +276 -0
- package/src/cli/templates/nextjs-app/hooks.ts.hbs +252 -0
- package/src/cli/templates/nextjs-app/kyc-hooks.ts.hbs +87 -0
- package/src/cli/templates/nextjs-app/kyc-wizard.css.hbs +433 -0
- package/src/cli/templates/nextjs-app/kyc-wizard.tsx.hbs +711 -0
- package/src/cli/templates/nextjs-app/layout-wrapper.tsx.hbs +13 -0
- package/src/cli/templates/nextjs-app/layout.tsx.hbs +13 -0
- package/src/cli/templates/nextjs-app/middleware.ts.hbs +49 -0
- package/src/cli/templates/nextjs-app/provider-wrapper.tsx.hbs +8 -0
- package/src/cli/templates/nextjs-app/provider.tsx.hbs +408 -0
- package/src/cli/templates/nextjs-app/setup-provider.tsx.hbs +25 -0
- package/src/cli/templates/nextjs-app/types.ts.hbs +159 -0
- package/src/cli/templates/react/api-client-wrapper.ts.hbs +89 -0
- package/src/cli/templates/react/example.tsx.hbs +69 -0
- package/src/cli/templates/react/tanstack-hooks.ts.hbs +185 -0
- package/src/cli/templates/shared/client.ts +78 -0
- package/src/cli/templates/webhooks/nextjs.hbs +98 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
// Authentication context hook (implement based on your auth system)
|
|
4
|
+
export function useAuth() {
|
|
5
|
+
// Replace this with your actual authentication logic
|
|
6
|
+
// This could be from a context provider, session storage, etc.
|
|
7
|
+
const [customerId, setCustomerId] = useState<string | null>(null);
|
|
8
|
+
const [customerEmail, setCustomerEmail] = useState<string | null>(null);
|
|
9
|
+
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
10
|
+
const [loading, setLoading] = useState(true);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
// Initialize auth state from your auth system
|
|
14
|
+
// Example: get from session, context, or other auth provider
|
|
15
|
+
const initAuth = async () => {
|
|
16
|
+
try {
|
|
17
|
+
// Your auth initialization logic here
|
|
18
|
+
// Example:
|
|
19
|
+
// const session = await getSession();
|
|
20
|
+
// if (session) {
|
|
21
|
+
// setCustomerId(session.customerId);
|
|
22
|
+
// setCustomerEmail(session.customerEmail);
|
|
23
|
+
// setIsAuthenticated(true);
|
|
24
|
+
// }
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('Auth initialization failed:', error);
|
|
27
|
+
} finally {
|
|
28
|
+
setLoading(false);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
initAuth();
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
const setCustomer = useCallback(async (customerId: string, customerEmail: string) => {
|
|
36
|
+
try {
|
|
37
|
+
const response = await fetch('/api/diviswap', {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: { 'Content-Type': 'application/json' },
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
action: 'setCustomer',
|
|
42
|
+
customerId,
|
|
43
|
+
customerEmail
|
|
44
|
+
})
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (response.ok) {
|
|
48
|
+
setCustomerId(customerId);
|
|
49
|
+
setCustomerEmail(customerEmail);
|
|
50
|
+
setIsAuthenticated(true);
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Failed to set customer context:', error);
|
|
54
|
+
}
|
|
55
|
+
}, []);
|
|
56
|
+
|
|
57
|
+
const clearCustomer = useCallback(async () => {
|
|
58
|
+
try {
|
|
59
|
+
await fetch('/api/diviswap', {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: { 'Content-Type': 'application/json' },
|
|
62
|
+
body: JSON.stringify({ action: 'clearCustomer' })
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
setCustomerId(null);
|
|
66
|
+
setCustomerEmail(null);
|
|
67
|
+
setIsAuthenticated(false);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error('Failed to clear customer context:', error);
|
|
70
|
+
}
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
customerId,
|
|
75
|
+
customerEmail,
|
|
76
|
+
isAuthenticated,
|
|
77
|
+
loading,
|
|
78
|
+
setCustomer,
|
|
79
|
+
clearCustomer
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Hook for managing transactions
|
|
84
|
+
export function useTransactions() {
|
|
85
|
+
const { customerId, customerEmail, isAuthenticated } = useAuth();
|
|
86
|
+
const [transactions, setTransactions] = useState<any[]>([]);
|
|
87
|
+
const [loading, setLoading] = useState(false);
|
|
88
|
+
const [error, setError] = useState<Error | null>(null);
|
|
89
|
+
|
|
90
|
+
const fetchTransactions = useCallback(async (filters?: {
|
|
91
|
+
limit?: number;
|
|
92
|
+
offset?: number;
|
|
93
|
+
status?: string;
|
|
94
|
+
type?: 'onramp' | 'offramp';
|
|
95
|
+
}) => {
|
|
96
|
+
if (!isAuthenticated || !customerId) return [];
|
|
97
|
+
|
|
98
|
+
setLoading(true);
|
|
99
|
+
setError(null);
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const queryParams = new URLSearchParams({
|
|
103
|
+
resource: 'transactions',
|
|
104
|
+
customerId,
|
|
105
|
+
customerEmail,
|
|
106
|
+
...filters
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const response = await fetch(`/api/diviswap?${queryParams}`);
|
|
110
|
+
if (!response.ok) throw new Error('Failed to fetch transactions');
|
|
111
|
+
|
|
112
|
+
const data = await response.json();
|
|
113
|
+
setTransactions(data);
|
|
114
|
+
return data;
|
|
115
|
+
} catch (err) {
|
|
116
|
+
const error = err as Error;
|
|
117
|
+
setError(error);
|
|
118
|
+
console.error('Failed to fetch transactions:', error);
|
|
119
|
+
throw error;
|
|
120
|
+
} finally {
|
|
121
|
+
setLoading(false);
|
|
122
|
+
}
|
|
123
|
+
}, [customerId, customerEmail, isAuthenticated]);
|
|
124
|
+
|
|
125
|
+
const createTransaction = useCallback(async (data: any) => {
|
|
126
|
+
if (!isAuthenticated || !customerId) throw new Error('Not authenticated');
|
|
127
|
+
|
|
128
|
+
setLoading(true);
|
|
129
|
+
setError(null);
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const response = await fetch('/api/diviswap', {
|
|
133
|
+
method: 'POST',
|
|
134
|
+
headers: { 'Content-Type': 'application/json' },
|
|
135
|
+
body: JSON.stringify({
|
|
136
|
+
action: 'createTransaction',
|
|
137
|
+
customerId,
|
|
138
|
+
customerEmail,
|
|
139
|
+
...data
|
|
140
|
+
})
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (!response.ok) throw new Error('Failed to create transaction');
|
|
144
|
+
|
|
145
|
+
const transaction = await response.json();
|
|
146
|
+
// Refresh transactions list
|
|
147
|
+
await fetchTransactions();
|
|
148
|
+
return transaction;
|
|
149
|
+
} catch (err) {
|
|
150
|
+
const error = err as Error;
|
|
151
|
+
setError(error);
|
|
152
|
+
console.error('Failed to create transaction:', error);
|
|
153
|
+
throw error;
|
|
154
|
+
} finally {
|
|
155
|
+
setLoading(false);
|
|
156
|
+
}
|
|
157
|
+
}, [customerId, customerEmail, isAuthenticated, fetchTransactions]);
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
transactions,
|
|
161
|
+
loading,
|
|
162
|
+
error,
|
|
163
|
+
fetchTransactions,
|
|
164
|
+
createTransaction,
|
|
165
|
+
refresh: fetchTransactions,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Hook for managing payees
|
|
170
|
+
export function usePayees() {
|
|
171
|
+
const { customerId, customerEmail, isAuthenticated } = useAuth();
|
|
172
|
+
const [payees, setPayees] = useState<any[]>([]);
|
|
173
|
+
const [loading, setLoading] = useState(false);
|
|
174
|
+
const [error, setError] = useState<Error | null>(null);
|
|
175
|
+
|
|
176
|
+
const fetchPayees = useCallback(async () => {
|
|
177
|
+
if (!isAuthenticated || !customerId) return [];
|
|
178
|
+
|
|
179
|
+
setLoading(true);
|
|
180
|
+
setError(null);
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const response = await fetch(`/api/diviswap?resource=payees&customerId=${customerId}&customerEmail=${customerEmail}`);
|
|
184
|
+
if (!response.ok) throw new Error('Failed to fetch payees');
|
|
185
|
+
|
|
186
|
+
const data = await response.json();
|
|
187
|
+
setPayees(data);
|
|
188
|
+
return data;
|
|
189
|
+
} catch (err) {
|
|
190
|
+
const error = err as Error;
|
|
191
|
+
setError(error);
|
|
192
|
+
console.error('Failed to fetch payees:', error);
|
|
193
|
+
throw error;
|
|
194
|
+
} finally {
|
|
195
|
+
setLoading(false);
|
|
196
|
+
}
|
|
197
|
+
}, [customerId, customerEmail, isAuthenticated]);
|
|
198
|
+
|
|
199
|
+
const createPayee = useCallback(async (data: any) => {
|
|
200
|
+
if (!isAuthenticated || !customerId) throw new Error('Not authenticated');
|
|
201
|
+
|
|
202
|
+
setLoading(true);
|
|
203
|
+
setError(null);
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const response = await fetch('/api/diviswap', {
|
|
207
|
+
method: 'POST',
|
|
208
|
+
headers: { 'Content-Type': 'application/json' },
|
|
209
|
+
body: JSON.stringify({
|
|
210
|
+
action: 'createPayee',
|
|
211
|
+
customerId,
|
|
212
|
+
customerEmail,
|
|
213
|
+
...data
|
|
214
|
+
})
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (!response.ok) throw new Error('Failed to create payee');
|
|
218
|
+
|
|
219
|
+
const payee = await response.json();
|
|
220
|
+
// Refresh payees list
|
|
221
|
+
await fetchPayees();
|
|
222
|
+
return payee;
|
|
223
|
+
} catch (err) {
|
|
224
|
+
const error = err as Error;
|
|
225
|
+
setError(error);
|
|
226
|
+
console.error('Failed to create payee:', error);
|
|
227
|
+
throw error;
|
|
228
|
+
} finally {
|
|
229
|
+
setLoading(false);
|
|
230
|
+
}
|
|
231
|
+
}, [customerId, customerEmail, isAuthenticated, fetchPayees]);
|
|
232
|
+
|
|
233
|
+
const deletePayee = useCallback(async (id: string) => {
|
|
234
|
+
if (!isAuthenticated || !customerId) throw new Error('Not authenticated');
|
|
235
|
+
|
|
236
|
+
setLoading(true);
|
|
237
|
+
setError(null);
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const response = await fetch(`/api/diviswap?resource=payee&id=${id}&customerId=${customerId}&customerEmail=${customerEmail}`, {
|
|
241
|
+
method: 'DELETE'
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (!response.ok) throw new Error('Failed to delete payee');
|
|
245
|
+
|
|
246
|
+
// Refresh payees list
|
|
247
|
+
await fetchPayees();
|
|
248
|
+
} catch (err) {
|
|
249
|
+
const error = err as Error;
|
|
250
|
+
setError(error);
|
|
251
|
+
console.error('Failed to delete payee:', error);
|
|
252
|
+
throw error;
|
|
253
|
+
} finally {
|
|
254
|
+
setLoading(false);
|
|
255
|
+
}
|
|
256
|
+
}, [customerId, customerEmail, isAuthenticated, fetchPayees]);
|
|
257
|
+
|
|
258
|
+
// Auto-load payees when authenticated
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
if (isAuthenticated && customerId) {
|
|
261
|
+
fetchPayees();
|
|
262
|
+
}
|
|
263
|
+
}, [isAuthenticated, customerId, fetchPayees]);
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
payees,
|
|
267
|
+
loading,
|
|
268
|
+
error,
|
|
269
|
+
createPayee,
|
|
270
|
+
deletePayee,
|
|
271
|
+
refresh: fetchPayees,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Hook for KYC status and submission
|
|
276
|
+
export function useKYC() {
|
|
277
|
+
const { customerId, customerEmail, isAuthenticated } = useAuth();
|
|
278
|
+
const [kycStatus, setKycStatus] = useState<any>(null);
|
|
279
|
+
const [loading, setLoading] = useState(false);
|
|
280
|
+
const [error, setError] = useState<Error | null>(null);
|
|
281
|
+
|
|
282
|
+
const fetchKYCStatus = useCallback(async () => {
|
|
283
|
+
if (!isAuthenticated || !customerId) return null;
|
|
284
|
+
|
|
285
|
+
setLoading(true);
|
|
286
|
+
setError(null);
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
const response = await fetch(`/api/diviswap?resource=kycStatus&customerId=${customerId}&customerEmail=${customerEmail}`);
|
|
290
|
+
if (!response.ok) throw new Error('Failed to fetch KYC status');
|
|
291
|
+
|
|
292
|
+
const data = await response.json();
|
|
293
|
+
setKycStatus(data);
|
|
294
|
+
return data;
|
|
295
|
+
} catch (err) {
|
|
296
|
+
const error = err as Error;
|
|
297
|
+
setError(error);
|
|
298
|
+
console.error('Failed to fetch KYC status:', error);
|
|
299
|
+
throw error;
|
|
300
|
+
} finally {
|
|
301
|
+
setLoading(false);
|
|
302
|
+
}
|
|
303
|
+
}, [customerId, customerEmail, isAuthenticated]);
|
|
304
|
+
|
|
305
|
+
const submitKYC = useCallback(async (kycData: any) => {
|
|
306
|
+
if (!isAuthenticated || !customerId) throw new Error('Not authenticated');
|
|
307
|
+
|
|
308
|
+
setLoading(true);
|
|
309
|
+
setError(null);
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
const response = await fetch('/api/diviswap', {
|
|
313
|
+
method: 'POST',
|
|
314
|
+
headers: { 'Content-Type': 'application/json' },
|
|
315
|
+
body: JSON.stringify({
|
|
316
|
+
action: 'submitKYC',
|
|
317
|
+
customerId,
|
|
318
|
+
customerEmail,
|
|
319
|
+
...kycData
|
|
320
|
+
})
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
if (!response.ok) throw new Error('Failed to submit KYC');
|
|
324
|
+
|
|
325
|
+
const result = await response.json();
|
|
326
|
+
// Refresh KYC status
|
|
327
|
+
await fetchKYCStatus();
|
|
328
|
+
return result;
|
|
329
|
+
} catch (err) {
|
|
330
|
+
const error = err as Error;
|
|
331
|
+
setError(error);
|
|
332
|
+
console.error('Failed to submit KYC:', error);
|
|
333
|
+
throw error;
|
|
334
|
+
} finally {
|
|
335
|
+
setLoading(false);
|
|
336
|
+
}
|
|
337
|
+
}, [customerId, customerEmail, isAuthenticated, fetchKYCStatus]);
|
|
338
|
+
|
|
339
|
+
// Auto-load KYC status when authenticated
|
|
340
|
+
useEffect(() => {
|
|
341
|
+
if (isAuthenticated && customerId) {
|
|
342
|
+
fetchKYCStatus();
|
|
343
|
+
}
|
|
344
|
+
}, [isAuthenticated, customerId, fetchKYCStatus]);
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
kycStatus,
|
|
348
|
+
loading,
|
|
349
|
+
error,
|
|
350
|
+
submitKYC,
|
|
351
|
+
refresh: fetchKYCStatus,
|
|
352
|
+
isKycApproved: kycStatus?.kycStatus === 'APPROVED',
|
|
353
|
+
needsKyc: kycStatus?.requiresAction && kycStatus?.nextStep === 'COMPLETE_KYC'
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
{{#if (includes features "fees")}}
|
|
358
|
+
// Hook for fee calculations
|
|
359
|
+
export function useFeeCalculator() {
|
|
360
|
+
const { customerId, customerEmail, isAuthenticated } = useAuth();
|
|
361
|
+
const [fees, setFees] = useState<any>(null);
|
|
362
|
+
const [loading, setLoading] = useState(false);
|
|
363
|
+
const [error, setError] = useState<Error | null>(null);
|
|
364
|
+
|
|
365
|
+
const calculateFees = useCallback(async (amount: number) => {
|
|
366
|
+
if (!isAuthenticated || !customerId || amount <= 0) {
|
|
367
|
+
setFees(null);
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
setLoading(true);
|
|
372
|
+
setError(null);
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
const response = await fetch('/api/diviswap', {
|
|
376
|
+
method: 'POST',
|
|
377
|
+
headers: { 'Content-Type': 'application/json' },
|
|
378
|
+
body: JSON.stringify({
|
|
379
|
+
action: 'calculateFees',
|
|
380
|
+
amount,
|
|
381
|
+
customerId,
|
|
382
|
+
customerEmail
|
|
383
|
+
})
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
if (!response.ok) throw new Error('Failed to calculate fees');
|
|
387
|
+
|
|
388
|
+
const calculatedFees = await response.json();
|
|
389
|
+
setFees(calculatedFees);
|
|
390
|
+
return calculatedFees;
|
|
391
|
+
} catch (err) {
|
|
392
|
+
const error = err as Error;
|
|
393
|
+
setError(error);
|
|
394
|
+
setFees(null);
|
|
395
|
+
console.error('Failed to calculate fees:', error);
|
|
396
|
+
throw error;
|
|
397
|
+
} finally {
|
|
398
|
+
setLoading(false);
|
|
399
|
+
}
|
|
400
|
+
}, [customerId, customerEmail, isAuthenticated]);
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
fees,
|
|
404
|
+
loading,
|
|
405
|
+
error,
|
|
406
|
+
calculateFees,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
{{/if}}
|
|
410
|
+
|
|
411
|
+
// Hook for handling async operations with loading states
|
|
412
|
+
export function useAsyncOperation<T extends (...args: any[]) => Promise<any>>() {
|
|
413
|
+
const [loading, setLoading] = useState(false);
|
|
414
|
+
const [error, setError] = useState<Error | null>(null);
|
|
415
|
+
|
|
416
|
+
const execute = useCallback(async <TResult>(
|
|
417
|
+
operation: T,
|
|
418
|
+
...args: Parameters<T>
|
|
419
|
+
): Promise<TResult | undefined> => {
|
|
420
|
+
setLoading(true);
|
|
421
|
+
setError(null);
|
|
422
|
+
|
|
423
|
+
try {
|
|
424
|
+
const result = await operation(...args);
|
|
425
|
+
return result as TResult;
|
|
426
|
+
} catch (err) {
|
|
427
|
+
setError(err as Error);
|
|
428
|
+
throw err;
|
|
429
|
+
} finally {
|
|
430
|
+
setLoading(false);
|
|
431
|
+
}
|
|
432
|
+
}, []);
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
loading,
|
|
436
|
+
error,
|
|
437
|
+
execute,
|
|
438
|
+
};
|
|
439
|
+
}
|