@umituz/react-native-design-system 4.23.66 → 4.23.68
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 +1 -1
- package/src/atoms/icon/AtomicIcon.tsx +41 -112
- package/src/atoms/icon/components/iconRenderer.tsx +118 -0
- package/src/atoms/icon/utils/iconUtils.ts +94 -0
- package/src/exception/presentation/components/ErrorBoundary.tsx +2 -2
- package/src/molecules/alerts/AlertToast.tsx +163 -192
- package/src/molecules/alerts/utils/alertToastHelpers.ts +70 -0
- package/src/services/api/ApiClient.ts +242 -0
- package/src/services/api/index.ts +9 -0
- package/src/services/api/types/ApiTypes.ts +50 -0
- package/src/services/api/utils/requestBuilder.ts +92 -0
- package/src/services/api/utils/responseHandler.ts +130 -0
- package/src/tanstack/domain/repositories/BaseRepository.ts +16 -72
- package/src/tanstack/domain/repositories/helpers/repositoryHelpers.ts +58 -0
- package/src/tanstack/domain/repositories/mixins/repositoryInvalidationMethods.ts +101 -0
- package/src/tanstack/domain/repositories/mixins/repositoryQueryMethods.ts +102 -0
- package/src/tanstack/presentation/hooks/types/prefetchTypes.ts +33 -0
- package/src/tanstack/presentation/hooks/usePrefetch.ts +8 -28
- package/src/tanstack/presentation/hooks/utils/prefetchLogger.ts +27 -0
- package/src/utils/colorMapper.ts +193 -0
- package/src/utils/formatHelper.ts +16 -0
- package/src/utils/formatters/dateFormatter.ts +64 -0
- package/src/utils/formatters/numberFormatter.ts +130 -0
- package/src/utils/formatters/stringFormatter.ts +190 -0
- package/src/utils/index.ts +15 -0
- package/src/utils/styleComposer.ts +94 -0
- package/src/utils/validationHelper.ts +16 -0
- package/src/utils/validators/dataValidators.ts +111 -0
- package/src/utils/validators/numericValidators.ts +106 -0
- package/src/utils/validators/stringValidators.ts +85 -0
|
@@ -6,209 +6,180 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React, { useEffect } from 'react';
|
|
9
|
-
import { StyleSheet, View, Pressable
|
|
9
|
+
import { StyleSheet, View, Pressable } from 'react-native';
|
|
10
10
|
import { AtomicText, AtomicIcon, useIconName } from '../../atoms';
|
|
11
11
|
import { useAppDesignTokens } from '../../theme';
|
|
12
|
-
import { Alert
|
|
12
|
+
import { Alert } from './AlertTypes';
|
|
13
13
|
import { useAlertStore } from './AlertStore';
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
import {
|
|
15
|
+
getAlertBackgroundColor,
|
|
16
|
+
getActionButtonStyle,
|
|
17
|
+
getActionTextColor,
|
|
18
|
+
DEFAULT_TOAST_DURATION,
|
|
19
|
+
} from './utils/alertToastHelpers';
|
|
16
20
|
|
|
17
21
|
interface AlertToastProps {
|
|
18
|
-
|
|
22
|
+
alert: Alert;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
export function AlertToast({ alert }: AlertToastProps) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
<AtomicText
|
|
103
|
-
type="bodyMedium"
|
|
104
|
-
style={[styles.title, { color: textColor }]}
|
|
105
|
-
numberOfLines={2}
|
|
106
|
-
>
|
|
107
|
-
{alert.title}
|
|
108
|
-
</AtomicText>
|
|
109
|
-
|
|
110
|
-
{alert.message && (
|
|
111
|
-
<AtomicText
|
|
112
|
-
type="bodySmall"
|
|
113
|
-
style={[
|
|
114
|
-
styles.message,
|
|
115
|
-
{ color: textColor, marginTop: tokens.spacing.xs },
|
|
116
|
-
]}
|
|
117
|
-
numberOfLines={3}
|
|
118
|
-
>
|
|
119
|
-
{alert.message}
|
|
120
|
-
</AtomicText>
|
|
121
|
-
)}
|
|
122
|
-
</View>
|
|
123
|
-
|
|
124
|
-
{alert.dismissible && (
|
|
125
|
-
<Pressable
|
|
126
|
-
onPress={handleDismiss}
|
|
127
|
-
style={[styles.closeButton, { marginLeft: tokens.spacing.sm }]}
|
|
128
|
-
hitSlop={8}
|
|
129
|
-
>
|
|
130
|
-
<AtomicIcon name={closeIcon} customSize={20} customColor={textColor} />
|
|
131
|
-
</Pressable>
|
|
132
|
-
)}
|
|
133
|
-
</View>
|
|
134
|
-
|
|
135
|
-
{alert.actions && alert.actions.length > 0 && (
|
|
136
|
-
<View style={[styles.actionsContainer, { marginTop: tokens.spacing.sm }]}>
|
|
137
|
-
{alert.actions.map((action) => (
|
|
138
|
-
<Pressable
|
|
139
|
-
key={action.id}
|
|
140
|
-
onPress={async () => {
|
|
141
|
-
await action.onPress();
|
|
142
|
-
if (action.closeOnPress ?? true) {
|
|
143
|
-
dismiss();
|
|
144
|
-
}
|
|
145
|
-
}}
|
|
146
|
-
style={[
|
|
147
|
-
styles.actionButton,
|
|
148
|
-
{
|
|
149
|
-
paddingVertical: tokens.spacing.xs,
|
|
150
|
-
paddingHorizontal: tokens.spacing.sm,
|
|
151
|
-
marginRight: tokens.spacing.xs,
|
|
152
|
-
borderRadius: tokens.borders.radius.sm,
|
|
153
|
-
},
|
|
154
|
-
getActionButtonStyle(action.style),
|
|
155
|
-
]}
|
|
156
|
-
>
|
|
157
|
-
<AtomicText
|
|
158
|
-
type="bodySmall"
|
|
159
|
-
style={[
|
|
160
|
-
styles.actionText,
|
|
161
|
-
{ color: getActionTextColor(action.style) },
|
|
162
|
-
]}
|
|
163
|
-
>
|
|
164
|
-
{action.label}
|
|
165
|
-
</AtomicText>
|
|
166
|
-
</Pressable>
|
|
167
|
-
))}
|
|
168
|
-
</View>
|
|
169
|
-
)}
|
|
26
|
+
const dismissAlert = useAlertStore((state: { dismissAlert: (id: string) => void }) => state.dismissAlert);
|
|
27
|
+
const tokens = useAppDesignTokens();
|
|
28
|
+
const closeIcon = useIconName('close');
|
|
29
|
+
|
|
30
|
+
const dismiss = () => {
|
|
31
|
+
dismissAlert(alert.id);
|
|
32
|
+
alert.onDismiss?.();
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const handleDismiss = () => {
|
|
36
|
+
if (alert.dismissible) {
|
|
37
|
+
dismiss();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Auto-dismiss after duration
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const duration = alert.duration ?? DEFAULT_TOAST_DURATION;
|
|
44
|
+
if (duration <= 0) return;
|
|
45
|
+
|
|
46
|
+
const timer = setTimeout(dismiss, duration);
|
|
47
|
+
return () => clearTimeout(timer);
|
|
48
|
+
}, [alert.id, alert.duration]);
|
|
49
|
+
|
|
50
|
+
const backgroundColor = getAlertBackgroundColor(alert.type, tokens);
|
|
51
|
+
const textColor = tokens.colors.textInverse;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<View
|
|
55
|
+
style={[
|
|
56
|
+
styles.container,
|
|
57
|
+
{
|
|
58
|
+
backgroundColor,
|
|
59
|
+
padding: tokens.spacing.md,
|
|
60
|
+
borderRadius: tokens.borders.radius.md,
|
|
61
|
+
},
|
|
62
|
+
]}
|
|
63
|
+
testID={alert.testID}
|
|
64
|
+
>
|
|
65
|
+
<Pressable onPress={handleDismiss} style={styles.content}>
|
|
66
|
+
<View style={styles.row}>
|
|
67
|
+
{alert.icon && (
|
|
68
|
+
<AtomicIcon
|
|
69
|
+
name={alert.icon}
|
|
70
|
+
customSize={20}
|
|
71
|
+
customColor={textColor}
|
|
72
|
+
style={{ marginRight: tokens.spacing.sm }}
|
|
73
|
+
/>
|
|
74
|
+
)}
|
|
75
|
+
|
|
76
|
+
<View style={styles.textContainer}>
|
|
77
|
+
<AtomicText
|
|
78
|
+
type="bodyMedium"
|
|
79
|
+
style={[styles.title, { color: textColor }]}
|
|
80
|
+
numberOfLines={2}
|
|
81
|
+
>
|
|
82
|
+
{alert.title}
|
|
83
|
+
</AtomicText>
|
|
84
|
+
|
|
85
|
+
{alert.message && (
|
|
86
|
+
<AtomicText
|
|
87
|
+
type="bodySmall"
|
|
88
|
+
style={[
|
|
89
|
+
styles.message,
|
|
90
|
+
{ color: textColor, marginTop: tokens.spacing.xs },
|
|
91
|
+
]}
|
|
92
|
+
numberOfLines={3}
|
|
93
|
+
>
|
|
94
|
+
{alert.message}
|
|
95
|
+
</AtomicText>
|
|
96
|
+
)}
|
|
97
|
+
</View>
|
|
98
|
+
|
|
99
|
+
{alert.dismissible && (
|
|
100
|
+
<Pressable
|
|
101
|
+
onPress={handleDismiss}
|
|
102
|
+
style={[styles.closeButton, { marginLeft: tokens.spacing.sm }]}
|
|
103
|
+
hitSlop={8}
|
|
104
|
+
>
|
|
105
|
+
<AtomicIcon name={closeIcon} customSize={20} customColor={textColor} />
|
|
170
106
|
</Pressable>
|
|
107
|
+
)}
|
|
171
108
|
</View>
|
|
172
|
-
|
|
109
|
+
|
|
110
|
+
{alert.actions && alert.actions.length > 0 && (
|
|
111
|
+
<View style={[styles.actionsContainer, { marginTop: tokens.spacing.sm }]}>
|
|
112
|
+
{alert.actions.map((action) => (
|
|
113
|
+
<Pressable
|
|
114
|
+
key={action.id}
|
|
115
|
+
onPress={async () => {
|
|
116
|
+
await action.onPress();
|
|
117
|
+
if (action.closeOnPress ?? true) {
|
|
118
|
+
dismiss();
|
|
119
|
+
}
|
|
120
|
+
}}
|
|
121
|
+
style={[
|
|
122
|
+
styles.actionButton,
|
|
123
|
+
{
|
|
124
|
+
paddingVertical: tokens.spacing.xs,
|
|
125
|
+
paddingHorizontal: tokens.spacing.sm,
|
|
126
|
+
marginRight: tokens.spacing.xs,
|
|
127
|
+
borderRadius: tokens.borders.radius.sm,
|
|
128
|
+
},
|
|
129
|
+
getActionButtonStyle(action.style, tokens),
|
|
130
|
+
]}
|
|
131
|
+
>
|
|
132
|
+
<AtomicText
|
|
133
|
+
type="bodySmall"
|
|
134
|
+
style={[
|
|
135
|
+
styles.actionText,
|
|
136
|
+
{ color: getActionTextColor(action.style, tokens) },
|
|
137
|
+
]}
|
|
138
|
+
>
|
|
139
|
+
{action.label}
|
|
140
|
+
</AtomicText>
|
|
141
|
+
</Pressable>
|
|
142
|
+
))}
|
|
143
|
+
</View>
|
|
144
|
+
)}
|
|
145
|
+
</Pressable>
|
|
146
|
+
</View>
|
|
147
|
+
);
|
|
173
148
|
}
|
|
174
149
|
|
|
175
150
|
const styles = StyleSheet.create({
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
},
|
|
211
|
-
actionText: {
|
|
212
|
-
fontWeight: '700',
|
|
213
|
-
},
|
|
151
|
+
container: {
|
|
152
|
+
width: '100%',
|
|
153
|
+
},
|
|
154
|
+
content: {
|
|
155
|
+
flex: 1,
|
|
156
|
+
},
|
|
157
|
+
row: {
|
|
158
|
+
flexDirection: 'row',
|
|
159
|
+
alignItems: 'center',
|
|
160
|
+
},
|
|
161
|
+
textContainer: {
|
|
162
|
+
flex: 1,
|
|
163
|
+
},
|
|
164
|
+
title: {
|
|
165
|
+
fontWeight: '700',
|
|
166
|
+
},
|
|
167
|
+
message: {
|
|
168
|
+
opacity: 0.9,
|
|
169
|
+
},
|
|
170
|
+
closeButton: {
|
|
171
|
+
justifyContent: 'center',
|
|
172
|
+
alignItems: 'center',
|
|
173
|
+
},
|
|
174
|
+
actionsContainer: {
|
|
175
|
+
flexDirection: 'row',
|
|
176
|
+
flexWrap: 'wrap',
|
|
177
|
+
},
|
|
178
|
+
actionButton: {
|
|
179
|
+
justifyContent: 'center',
|
|
180
|
+
alignItems: 'center',
|
|
181
|
+
},
|
|
182
|
+
actionText: {
|
|
183
|
+
fontWeight: '700',
|
|
184
|
+
},
|
|
214
185
|
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alert Toast Helper Functions
|
|
3
|
+
* Style and color helpers for alert toast component
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { AlertType } from '../AlertTypes';
|
|
7
|
+
import type { DesignTokens } from '../../../theme';
|
|
8
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Gets background color for alert type
|
|
12
|
+
*
|
|
13
|
+
* @param type - Alert type
|
|
14
|
+
* @param tokens - Design tokens
|
|
15
|
+
* @returns Background color string
|
|
16
|
+
*/
|
|
17
|
+
export function getAlertBackgroundColor(type: AlertType, tokens: DesignTokens): string {
|
|
18
|
+
const colors = {
|
|
19
|
+
[AlertType.SUCCESS]: tokens.colors.success,
|
|
20
|
+
[AlertType.ERROR]: tokens.colors.error,
|
|
21
|
+
[AlertType.WARNING]: tokens.colors.warning,
|
|
22
|
+
[AlertType.INFO]: tokens.colors.info,
|
|
23
|
+
};
|
|
24
|
+
return colors[type] || tokens.colors.backgroundSecondary;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Gets action button style
|
|
29
|
+
*
|
|
30
|
+
* @param style - Button style type
|
|
31
|
+
* @param tokens - Design tokens
|
|
32
|
+
* @returns Style object
|
|
33
|
+
*/
|
|
34
|
+
export function getActionButtonStyle(
|
|
35
|
+
style: 'primary' | 'secondary' | 'destructive' | undefined,
|
|
36
|
+
tokens: DesignTokens
|
|
37
|
+
): StyleProp<ViewStyle> {
|
|
38
|
+
if (style === 'secondary') {
|
|
39
|
+
return {
|
|
40
|
+
backgroundColor: undefined,
|
|
41
|
+
borderWidth: 1,
|
|
42
|
+
borderColor: tokens.colors.textInverse,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const colors = {
|
|
47
|
+
primary: tokens.colors.backgroundPrimary,
|
|
48
|
+
destructive: tokens.colors.error,
|
|
49
|
+
};
|
|
50
|
+
return { backgroundColor: colors[style as keyof typeof colors] || tokens.colors.backgroundSecondary };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Gets action text color
|
|
55
|
+
*
|
|
56
|
+
* @param style - Button style type
|
|
57
|
+
* @param tokens - Design tokens
|
|
58
|
+
* @returns Text color string
|
|
59
|
+
*/
|
|
60
|
+
export function getActionTextColor(
|
|
61
|
+
style: 'primary' | 'secondary' | 'destructive' | undefined,
|
|
62
|
+
tokens: DesignTokens
|
|
63
|
+
): string {
|
|
64
|
+
return style === 'primary' ? tokens.colors.textPrimary : tokens.colors.textInverse;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Default toast duration in milliseconds
|
|
69
|
+
*/
|
|
70
|
+
export const DEFAULT_TOAST_DURATION = 3000;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Client
|
|
3
|
+
* Centralized HTTP client for API communication
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
ApiClientConfig,
|
|
8
|
+
ApiRequestConfig,
|
|
9
|
+
ApiResponse,
|
|
10
|
+
} from './types/ApiTypes';
|
|
11
|
+
import {
|
|
12
|
+
buildURL,
|
|
13
|
+
buildRequestConfig,
|
|
14
|
+
} from './utils/requestBuilder';
|
|
15
|
+
import {
|
|
16
|
+
parseResponse,
|
|
17
|
+
handleHttpError,
|
|
18
|
+
handleNetworkError,
|
|
19
|
+
isSuccessfulResponse,
|
|
20
|
+
fetchWithTimeout,
|
|
21
|
+
} from './utils/responseHandler';
|
|
22
|
+
|
|
23
|
+
export class ApiClient {
|
|
24
|
+
private config: ApiClientConfig;
|
|
25
|
+
|
|
26
|
+
constructor(config: ApiClientConfig) {
|
|
27
|
+
this.config = {
|
|
28
|
+
timeout: 30000,
|
|
29
|
+
...config,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Makes an HTTP request
|
|
35
|
+
*
|
|
36
|
+
* @param requestConfig - Request configuration
|
|
37
|
+
* @returns API response
|
|
38
|
+
*/
|
|
39
|
+
async request<T>(requestConfig: ApiRequestConfig): Promise<ApiResponse<T>> {
|
|
40
|
+
try {
|
|
41
|
+
// Apply request interceptors
|
|
42
|
+
let config = requestConfig;
|
|
43
|
+
for (const interceptor of this.config.requestInterceptors || []) {
|
|
44
|
+
config = await interceptor(config);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Build request
|
|
48
|
+
const fullURL = config.url.startsWith('http')
|
|
49
|
+
? config.url
|
|
50
|
+
: buildURL(this.config.baseURL, config.url, config.params);
|
|
51
|
+
|
|
52
|
+
const fetchOptions = buildRequestConfig({
|
|
53
|
+
...config,
|
|
54
|
+
headers: { ...this.config.headers, ...config.headers },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Make request with timeout
|
|
58
|
+
const response = await fetchWithTimeout(
|
|
59
|
+
fullURL,
|
|
60
|
+
fetchOptions,
|
|
61
|
+
config.timeout || this.config.timeout || 30000
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// Check for HTTP errors
|
|
65
|
+
if (!isSuccessfulResponse(response)) {
|
|
66
|
+
const error = await handleHttpError(response);
|
|
67
|
+
|
|
68
|
+
// Apply error interceptors
|
|
69
|
+
let finalError = error;
|
|
70
|
+
for (const interceptor of this.config.errorInterceptors || []) {
|
|
71
|
+
finalError = await interceptor(finalError);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw finalError;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Parse response
|
|
78
|
+
let parsedResponse = await parseResponse<T>(response);
|
|
79
|
+
|
|
80
|
+
// Apply response interceptors
|
|
81
|
+
for (const interceptor of this.config.responseInterceptors || []) {
|
|
82
|
+
parsedResponse = await interceptor(parsedResponse);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return parsedResponse;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
// Handle network errors
|
|
88
|
+
const apiError = handleNetworkError(error);
|
|
89
|
+
|
|
90
|
+
// Apply error interceptors
|
|
91
|
+
let finalError = apiError;
|
|
92
|
+
for (const interceptor of this.config.errorInterceptors || []) {
|
|
93
|
+
finalError = await interceptor(finalError);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
throw finalError;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Makes a GET request
|
|
102
|
+
*
|
|
103
|
+
* @param url - Request URL
|
|
104
|
+
* @param params - Query parameters
|
|
105
|
+
* @param config - Additional config
|
|
106
|
+
* @returns API response
|
|
107
|
+
*/
|
|
108
|
+
async get<T>(
|
|
109
|
+
url: string,
|
|
110
|
+
params?: Record<string, string | number | boolean | undefined>,
|
|
111
|
+
config?: Partial<ApiRequestConfig>
|
|
112
|
+
): Promise<ApiResponse<T>> {
|
|
113
|
+
return this.request<T>({
|
|
114
|
+
url,
|
|
115
|
+
method: 'GET',
|
|
116
|
+
params,
|
|
117
|
+
...config,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Makes a POST request
|
|
123
|
+
*
|
|
124
|
+
* @param url - Request URL
|
|
125
|
+
* @param body - Request body
|
|
126
|
+
* @param config - Additional config
|
|
127
|
+
* @returns API response
|
|
128
|
+
*/
|
|
129
|
+
async post<T>(
|
|
130
|
+
url: string,
|
|
131
|
+
body?: any,
|
|
132
|
+
config?: Partial<ApiRequestConfig>
|
|
133
|
+
): Promise<ApiResponse<T>> {
|
|
134
|
+
return this.request<T>({
|
|
135
|
+
url,
|
|
136
|
+
method: 'POST',
|
|
137
|
+
body,
|
|
138
|
+
...config,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Makes a PUT request
|
|
144
|
+
*
|
|
145
|
+
* @param url - Request URL
|
|
146
|
+
* @param body - Request body
|
|
147
|
+
* @param config - Additional config
|
|
148
|
+
* @returns API response
|
|
149
|
+
*/
|
|
150
|
+
async put<T>(
|
|
151
|
+
url: string,
|
|
152
|
+
body?: any,
|
|
153
|
+
config?: Partial<ApiRequestConfig>
|
|
154
|
+
): Promise<ApiResponse<T>> {
|
|
155
|
+
return this.request<T>({
|
|
156
|
+
url,
|
|
157
|
+
method: 'PUT',
|
|
158
|
+
body,
|
|
159
|
+
...config,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Makes a PATCH request
|
|
165
|
+
*
|
|
166
|
+
* @param url - Request URL
|
|
167
|
+
* @param body - Request body
|
|
168
|
+
* @param config - Additional config
|
|
169
|
+
* @returns API response
|
|
170
|
+
*/
|
|
171
|
+
async patch<T>(
|
|
172
|
+
url: string,
|
|
173
|
+
body?: any,
|
|
174
|
+
config?: Partial<ApiRequestConfig>
|
|
175
|
+
): Promise<ApiResponse<T>> {
|
|
176
|
+
return this.request<T>({
|
|
177
|
+
url,
|
|
178
|
+
method: 'PATCH',
|
|
179
|
+
body,
|
|
180
|
+
...config,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Makes a DELETE request
|
|
186
|
+
*
|
|
187
|
+
* @param url - Request URL
|
|
188
|
+
* @param config - Additional config
|
|
189
|
+
* @returns API response
|
|
190
|
+
*/
|
|
191
|
+
async delete<T>(
|
|
192
|
+
url: string,
|
|
193
|
+
config?: Partial<ApiRequestConfig>
|
|
194
|
+
): Promise<ApiResponse<T>> {
|
|
195
|
+
return this.request<T>({
|
|
196
|
+
url,
|
|
197
|
+
method: 'DELETE',
|
|
198
|
+
...config,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Updates base configuration
|
|
204
|
+
*
|
|
205
|
+
* @param updates - Configuration updates
|
|
206
|
+
*/
|
|
207
|
+
updateConfig(updates: Partial<ApiClientConfig>): void {
|
|
208
|
+
this.config = { ...this.config, ...updates };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Gets current configuration
|
|
213
|
+
*
|
|
214
|
+
* @returns Current configuration
|
|
215
|
+
*/
|
|
216
|
+
getConfig(): ApiClientConfig {
|
|
217
|
+
return { ...this.config };
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Creates a singleton API client instance
|
|
223
|
+
*
|
|
224
|
+
* @param config - API client configuration
|
|
225
|
+
* @returns API client instance
|
|
226
|
+
*/
|
|
227
|
+
let apiClientInstance: ApiClient | null = null;
|
|
228
|
+
|
|
229
|
+
export function createApiClient(config: ApiClientConfig): ApiClient {
|
|
230
|
+
if (!apiClientInstance) {
|
|
231
|
+
apiClientInstance = new ApiClient(config);
|
|
232
|
+
}
|
|
233
|
+
return apiClientInstance;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function getApiClient(): ApiClient | null {
|
|
237
|
+
return apiClientInstance;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function resetApiClient(): void {
|
|
241
|
+
apiClientInstance = null;
|
|
242
|
+
}
|