@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.
Files changed (30) hide show
  1. package/package.json +1 -1
  2. package/src/atoms/icon/AtomicIcon.tsx +41 -112
  3. package/src/atoms/icon/components/iconRenderer.tsx +118 -0
  4. package/src/atoms/icon/utils/iconUtils.ts +94 -0
  5. package/src/exception/presentation/components/ErrorBoundary.tsx +2 -2
  6. package/src/molecules/alerts/AlertToast.tsx +163 -192
  7. package/src/molecules/alerts/utils/alertToastHelpers.ts +70 -0
  8. package/src/services/api/ApiClient.ts +242 -0
  9. package/src/services/api/index.ts +9 -0
  10. package/src/services/api/types/ApiTypes.ts +50 -0
  11. package/src/services/api/utils/requestBuilder.ts +92 -0
  12. package/src/services/api/utils/responseHandler.ts +130 -0
  13. package/src/tanstack/domain/repositories/BaseRepository.ts +16 -72
  14. package/src/tanstack/domain/repositories/helpers/repositoryHelpers.ts +58 -0
  15. package/src/tanstack/domain/repositories/mixins/repositoryInvalidationMethods.ts +101 -0
  16. package/src/tanstack/domain/repositories/mixins/repositoryQueryMethods.ts +102 -0
  17. package/src/tanstack/presentation/hooks/types/prefetchTypes.ts +33 -0
  18. package/src/tanstack/presentation/hooks/usePrefetch.ts +8 -28
  19. package/src/tanstack/presentation/hooks/utils/prefetchLogger.ts +27 -0
  20. package/src/utils/colorMapper.ts +193 -0
  21. package/src/utils/formatHelper.ts +16 -0
  22. package/src/utils/formatters/dateFormatter.ts +64 -0
  23. package/src/utils/formatters/numberFormatter.ts +130 -0
  24. package/src/utils/formatters/stringFormatter.ts +190 -0
  25. package/src/utils/index.ts +15 -0
  26. package/src/utils/styleComposer.ts +94 -0
  27. package/src/utils/validationHelper.ts +16 -0
  28. package/src/utils/validators/dataValidators.ts +111 -0
  29. package/src/utils/validators/numericValidators.ts +106 -0
  30. 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, StyleProp, ViewStyle } from 'react-native';
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, AlertType } from './AlertTypes';
12
+ import { Alert } from './AlertTypes';
13
13
  import { useAlertStore } from './AlertStore';
14
-
15
- const DEFAULT_DURATION = 3000;
14
+ import {
15
+ getAlertBackgroundColor,
16
+ getActionButtonStyle,
17
+ getActionTextColor,
18
+ DEFAULT_TOAST_DURATION,
19
+ } from './utils/alertToastHelpers';
16
20
 
17
21
  interface AlertToastProps {
18
- alert: Alert;
22
+ alert: Alert;
19
23
  }
20
24
 
21
25
  export function AlertToast({ alert }: AlertToastProps) {
22
- const dismissAlert = useAlertStore((state: { dismissAlert: (id: string) => void }) => state.dismissAlert);
23
- const tokens = useAppDesignTokens();
24
- const closeIcon = useIconName('close');
25
-
26
- const dismiss = () => {
27
- dismissAlert(alert.id);
28
- alert.onDismiss?.();
29
- };
30
-
31
- const handleDismiss = () => {
32
- if (alert.dismissible) {
33
- dismiss();
34
- }
35
- };
36
-
37
- // Auto-dismiss after duration
38
- useEffect(() => {
39
- const duration = alert.duration ?? DEFAULT_DURATION;
40
- if (duration <= 0) return;
41
-
42
- const timer = setTimeout(dismiss, duration);
43
- return () => clearTimeout(timer);
44
- }, [alert.id, alert.duration]);
45
-
46
- const getBackgroundColor = (type: AlertType): string => {
47
- const colors = {
48
- [AlertType.SUCCESS]: tokens.colors.success,
49
- [AlertType.ERROR]: tokens.colors.error,
50
- [AlertType.WARNING]: tokens.colors.warning,
51
- [AlertType.INFO]: tokens.colors.info,
52
- };
53
- return colors[type] || tokens.colors.backgroundSecondary;
54
- };
55
-
56
- const getActionButtonStyle = (style?: 'primary' | 'secondary' | 'destructive'): StyleProp<ViewStyle> => {
57
- if (style === 'secondary') {
58
- return {
59
- backgroundColor: undefined,
60
- borderWidth: 1,
61
- borderColor: tokens.colors.textInverse,
62
- };
63
- }
64
- const colors = {
65
- primary: tokens.colors.backgroundPrimary,
66
- destructive: tokens.colors.error,
67
- };
68
- return { backgroundColor: colors[style as keyof typeof colors] || tokens.colors.backgroundSecondary };
69
- };
70
-
71
- const getActionTextColor = (style?: 'primary' | 'secondary' | 'destructive'): string => {
72
- return style === 'primary' ? tokens.colors.textPrimary : tokens.colors.textInverse;
73
- };
74
-
75
- const backgroundColor = getBackgroundColor(alert.type);
76
- const textColor = tokens.colors.textInverse;
77
-
78
- return (
79
- <View
80
- style={[
81
- styles.container,
82
- {
83
- backgroundColor,
84
- padding: tokens.spacing.md,
85
- borderRadius: tokens.borders.radius.md,
86
- },
87
- ]}
88
- testID={alert.testID}
89
- >
90
- <Pressable onPress={handleDismiss} style={styles.content}>
91
- <View style={styles.row}>
92
- {alert.icon && (
93
- <AtomicIcon
94
- name={alert.icon}
95
- customSize={20}
96
- customColor={textColor}
97
- style={{ marginRight: tokens.spacing.sm }}
98
- />
99
- )}
100
-
101
- <View style={styles.textContainer}>
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
- container: {
177
- width: '100%',
178
- },
179
- content: {
180
- flex: 1,
181
- },
182
- row: {
183
- flexDirection: 'row',
184
- alignItems: 'center',
185
- },
186
- iconContainer: {
187
- justifyContent: 'center',
188
- alignItems: 'center',
189
- },
190
- textContainer: {
191
- flex: 1,
192
- },
193
- title: {
194
- fontWeight: '700',
195
- },
196
- message: {
197
- opacity: 0.9,
198
- },
199
- closeButton: {
200
- justifyContent: 'center',
201
- alignItems: 'center',
202
- },
203
- actionsContainer: {
204
- flexDirection: 'row',
205
- flexWrap: 'wrap',
206
- },
207
- actionButton: {
208
- justifyContent: 'center',
209
- alignItems: 'center',
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
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * API Service Index
3
+ * Centralized export of API service components
4
+ */
5
+
6
+ export * from './ApiClient';
7
+ export * from './types/ApiTypes';
8
+ export * from './utils/requestBuilder';
9
+ export * from './utils/responseHandler';