api-core-lib 3.2.2 → 3.3.3
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/dist/index.d.mts +137 -51
- package/dist/index.d.ts +137 -51
- package/dist/index.js +87 -31
- package/dist/index.mjs +86 -31
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { ResponseType, AxiosRequestConfig, AxiosProgressEvent, AxiosInstance, AxiosResponse } from 'axios';
|
1
|
+
import { ResponseType, AxiosRequestConfig, AxiosProgressEvent, AxiosInstance, Method, AxiosResponse } from 'axios';
|
2
2
|
import * as react from 'react';
|
3
3
|
|
4
4
|
/**
|
@@ -18,15 +18,6 @@ interface PaginationMeta {
|
|
18
18
|
currentPage: number;
|
19
19
|
totalPages: number;
|
20
20
|
}
|
21
|
-
/**
|
22
|
-
* يمثل بنية الاستجابة القياسية من الـ API.
|
23
|
-
* @template T نوع البيانات الأساسية في الاستجابة.
|
24
|
-
*/
|
25
|
-
interface ApiResponse<T = any> {
|
26
|
-
data: T;
|
27
|
-
message?: string;
|
28
|
-
meta?: PaginationMeta;
|
29
|
-
}
|
30
21
|
/**
|
31
22
|
* يمثل خطأ التحقق من صحة حقل معين.
|
32
23
|
*/
|
@@ -44,18 +35,6 @@ interface ApiError {
|
|
44
35
|
errors?: ValidationError[];
|
45
36
|
requestId?: string;
|
46
37
|
}
|
47
|
-
/**
|
48
|
-
* يمثل كائن الاستجابة الموحد الذي يرجعه كل طلب.
|
49
|
-
* @template T نوع البيانات المتوقعة في حالة النجاح.
|
50
|
-
*/
|
51
|
-
interface StandardResponse<T> {
|
52
|
-
response?: ApiResponse<T> | T;
|
53
|
-
error: ApiError | null;
|
54
|
-
loading: boolean;
|
55
|
-
success: boolean;
|
56
|
-
message?: string;
|
57
|
-
validationErrors?: ValidationError[];
|
58
|
-
}
|
59
38
|
/**
|
60
39
|
* يمثل مجموعة التوكنات التي يتم إدارتها.
|
61
40
|
*/
|
@@ -96,19 +75,6 @@ interface PaginateQueryOptions {
|
|
96
75
|
}[];
|
97
76
|
filter?: Record<string, any>;
|
98
77
|
}
|
99
|
-
/**
|
100
|
-
* واجهة لتهيئة الهوك `useApi`.
|
101
|
-
* @template T نوع البيانات التي يتعامل معها الهوك.
|
102
|
-
*/
|
103
|
-
interface UseApiConfig<T> {
|
104
|
-
endpoint: string;
|
105
|
-
initialData?: T | T[];
|
106
|
-
initialQuery?: PaginateQueryOptions;
|
107
|
-
enabled?: boolean;
|
108
|
-
refetchAfterChange?: boolean;
|
109
|
-
onSuccess?: (message: string, data?: T) => void;
|
110
|
-
onError?: (message: string, error?: ApiError) => void;
|
111
|
-
}
|
112
78
|
/**
|
113
79
|
* واجهة لتخصيص عملية تجديد التوكن بشكل كامل.
|
114
80
|
*/
|
@@ -162,6 +128,82 @@ interface ApiClientConfig {
|
|
162
128
|
refreshTokenConfig?: RefreshTokenConfig;
|
163
129
|
onRefreshError?: (error: any) => void;
|
164
130
|
}
|
131
|
+
/**
|
132
|
+
* يمثل بنية الاستجابة القياسية من الـ API.
|
133
|
+
* @template T نوع البيانات الأساسية في الاستجابة.
|
134
|
+
*/
|
135
|
+
interface ApiResponse<T = any> {
|
136
|
+
data: T;
|
137
|
+
message?: string;
|
138
|
+
meta?: PaginationMeta;
|
139
|
+
}
|
140
|
+
/**
|
141
|
+
* يمثل بنية الاستجابة القياسية المغلفة التي قد تأتي من بعض نقاط الـ API.
|
142
|
+
* @template T نوع البيانات الأساسية في الاستجابة.
|
143
|
+
*/
|
144
|
+
interface ApiResponse<T = any> {
|
145
|
+
data: T;
|
146
|
+
message?: string;
|
147
|
+
meta?: PaginationMeta;
|
148
|
+
success?: boolean;
|
149
|
+
}
|
150
|
+
/**
|
151
|
+
* يمثل كائن الاستجابة الموحد والنهائي الذي يرجعه كل طلب.
|
152
|
+
* هذا هو النوع الذي ستتعامل معه دائمًا.
|
153
|
+
* @template T نوع البيانات النهائية التي تريد الوصول إليها.
|
154
|
+
*/
|
155
|
+
interface StandardResponse<T> {
|
156
|
+
data: T | null;
|
157
|
+
rawResponse: any;
|
158
|
+
error: ApiError | null;
|
159
|
+
loading: boolean;
|
160
|
+
success: boolean;
|
161
|
+
message?: string;
|
162
|
+
validationErrors?: ValidationError[];
|
163
|
+
}
|
164
|
+
/**
|
165
|
+
* NEW: واجهة استعلام مرنة تسمح بالبارامترات القياسية (للفلترة والترقيم)
|
166
|
+
* بالإضافة إلى أي بارامترات مخصصة أخرى عبر الـ index signature.
|
167
|
+
* @example
|
168
|
+
* { page: 1, limit: 10, search: 'term', status: 'published', authorId: 123 }
|
169
|
+
*/
|
170
|
+
interface QueryOptions {
|
171
|
+
page?: number;
|
172
|
+
limit?: number;
|
173
|
+
search?: string;
|
174
|
+
sortBy?: {
|
175
|
+
key: string;
|
176
|
+
direction: 'asc' | 'desc';
|
177
|
+
}[];
|
178
|
+
filter?: Record<string, any>;
|
179
|
+
[key: string]: any;
|
180
|
+
}
|
181
|
+
/**
|
182
|
+
* NEW: واجهة لتمرير خيارات إضافية لدوال الأكشن (create, update, remove).
|
183
|
+
*/
|
184
|
+
interface ActionOptions {
|
185
|
+
/**
|
186
|
+
* يسمح لك بتجاوز الـ endpoint الافتراضي المحدد في الهوك.
|
187
|
+
* مفيد لإرسال طلبات لنقاط نهاية متخصصة.
|
188
|
+
* @example
|
189
|
+
* // لتحديث عنصر وتغيير حالته عبر مسار مختلف
|
190
|
+
* update('123', { status: 'active' }, { endpoint: '/items/123/activate' })
|
191
|
+
*/
|
192
|
+
endpoint?: string;
|
193
|
+
}
|
194
|
+
/**
|
195
|
+
* واجهة لتهيئة الهوك `useApi`.
|
196
|
+
* @template T نوع البيانات التي يتعامل معها الهوك.
|
197
|
+
*/
|
198
|
+
interface UseApiConfig<T> {
|
199
|
+
endpoint: string;
|
200
|
+
initialData?: T | T[];
|
201
|
+
initialQuery?: QueryOptions;
|
202
|
+
enabled?: boolean;
|
203
|
+
refetchAfterChange?: boolean;
|
204
|
+
onSuccess?: (message: string, data?: T) => void;
|
205
|
+
onError?: (message: string, error?: ApiError) => void;
|
206
|
+
}
|
165
207
|
|
166
208
|
/**
|
167
209
|
* @file src/core/client.ts
|
@@ -173,12 +215,13 @@ interface ApiClientConfig {
|
|
173
215
|
|
174
216
|
declare function createApiClient(config: ApiClientConfig): AxiosInstance;
|
175
217
|
|
218
|
+
type CrudRequestConfig = RequestConfig & ActionOptions;
|
176
219
|
declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: string): {
|
177
220
|
get: (id?: string, config?: RequestConfig) => Promise<StandardResponse<T | T[]>>;
|
178
221
|
getWithQuery: (query: string, config?: RequestConfig) => Promise<StandardResponse<T[]>>;
|
179
|
-
post: (data: Partial<T>, config?:
|
180
|
-
patch: (id: string, data: Partial<T>, config?:
|
181
|
-
remove: (id: string, config?:
|
222
|
+
post: (data: Partial<T>, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
223
|
+
patch: (id: string, data: Partial<T>, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
224
|
+
remove: (id: string, config?: CrudRequestConfig) => Promise<StandardResponse<any>>;
|
182
225
|
};
|
183
226
|
|
184
227
|
/**
|
@@ -189,7 +232,46 @@ declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: st
|
|
189
232
|
* بالإضافة إلى دوال التحقق من الأنواع (type guards).
|
190
233
|
*/
|
191
234
|
|
192
|
-
|
235
|
+
/**
|
236
|
+
* يبني سلسلة استعلام (query string) من كائن خيارات مرن.
|
237
|
+
* يتعامل مع البارامترات القياسية والمخصصة.
|
238
|
+
* @param query - كائن من نوع QueryOptions.
|
239
|
+
* @returns سلسلة استعلام جاهزة للإضافة للرابط.
|
240
|
+
*/
|
241
|
+
declare function buildPaginateQuery(query: QueryOptions): string;
|
242
|
+
|
243
|
+
/**
|
244
|
+
* Defines a single custom API action.
|
245
|
+
* @template TRequest - The type of the data sent in the request body/params.
|
246
|
+
* @template TResponse - The type of the data expected in the successful API response.
|
247
|
+
*/
|
248
|
+
type ApiAction<TRequest, TResponse> = (payload: TRequest, config?: RequestConfig) => Promise<StandardResponse<TResponse>>;
|
249
|
+
/**
|
250
|
+
* A factory function to create a collection of typed, custom API actions.
|
251
|
+
*
|
252
|
+
* @template TActions - An object type where keys are action names and values are objects
|
253
|
+
* defining the endpoint, method, and types for that action.
|
254
|
+
* @param axiosInstance - The configured Axios instance from `createApiClient`.
|
255
|
+
* @param actionsConfig - An object defining the configuration for each custom action.
|
256
|
+
* @returns A fully-typed object of executable API action functions.
|
257
|
+
*
|
258
|
+
* @example
|
259
|
+
* const authActions = createApiActions(apiClient, {
|
260
|
+
* login: { method: 'POST', endpoint: '/auth/login', requestType: {} as LoginCredentials, responseType: {} as AuthResponse },
|
261
|
+
* getProfile: { method: 'GET', endpoint: '/user/profile', requestType: {} as void, responseType: {} as UserProfile }
|
262
|
+
* });
|
263
|
+
*
|
264
|
+
* // Usage:
|
265
|
+
* const result = await authActions.login({ email: '..', password: '..' });
|
266
|
+
*/
|
267
|
+
declare function createApiActions<TActions extends Record<string, {
|
268
|
+
method: Method;
|
269
|
+
endpoint: string;
|
270
|
+
requestType: any;
|
271
|
+
responseType: any;
|
272
|
+
}>>(axiosInstance: AxiosInstance, actionsConfig: TActions): {
|
273
|
+
[K in keyof TActions]: ApiAction<TActions[K]['requestType'], TActions[K]['responseType']>;
|
274
|
+
};
|
193
275
|
|
194
276
|
declare class CacheManager {
|
195
277
|
private cache;
|
@@ -202,12 +284,15 @@ declare class CacheManager {
|
|
202
284
|
declare const cacheManager: CacheManager;
|
203
285
|
|
204
286
|
/**
|
205
|
-
*
|
206
|
-
*
|
207
|
-
* ...
|
287
|
+
* A smart response processor that normalizes API responses.
|
288
|
+
* It intelligently unwraps nested data from standard API envelopes
|
289
|
+
* (like { success: true, data: {...} }) and provides direct access
|
290
|
+
* to the core data, while still handling errors consistently.
|
291
|
+
*
|
292
|
+
* @param responseOrError The raw Axios response or a pre-processed ApiError.
|
293
|
+
* @returns A standardized `StandardResponse` object with direct access to `.data`.
|
208
294
|
*/
|
209
|
-
|
210
|
-
declare const processResponse: <T>(responseOrError: AxiosResponse<ApiResponse<T>> | ApiError) => StandardResponse<T>;
|
295
|
+
declare const processResponse: <T>(responseOrError: AxiosResponse<any> | ApiError) => StandardResponse<T>;
|
211
296
|
|
212
297
|
declare function useApi<T extends {
|
213
298
|
id?: string | number;
|
@@ -215,14 +300,14 @@ declare function useApi<T extends {
|
|
215
300
|
state: StandardResponse<T | T[]>;
|
216
301
|
setState: react.Dispatch<react.SetStateAction<StandardResponse<T | T[]>>>;
|
217
302
|
actions: {
|
218
|
-
fetch: () => Promise<void>;
|
219
|
-
create: (newItem: Partial<T
|
220
|
-
update: (id: string, updatedItem: Partial<T
|
221
|
-
remove: (id: string) => Promise<StandardResponse<any>>;
|
303
|
+
fetch: (options?: QueryOptions) => Promise<void>;
|
304
|
+
create: (newItem: Partial<T>, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
305
|
+
update: (id: string, updatedItem: Partial<T>, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
306
|
+
remove: (id: string, options?: ActionOptions) => Promise<StandardResponse<any>>;
|
222
307
|
};
|
223
308
|
query: {
|
224
|
-
options:
|
225
|
-
setOptions: react.Dispatch<react.SetStateAction<
|
309
|
+
options: QueryOptions;
|
310
|
+
setOptions: react.Dispatch<react.SetStateAction<QueryOptions>>;
|
226
311
|
setPage: (page: number) => void;
|
227
312
|
setLimit: (limit: number) => void;
|
228
313
|
setSearchTerm: (search: string) => void;
|
@@ -231,8 +316,9 @@ declare function useApi<T extends {
|
|
231
316
|
direction: "asc" | "desc";
|
232
317
|
}[]) => void;
|
233
318
|
setFilters: (filter: Record<string, any>) => void;
|
319
|
+
setQueryParam: (key: string, value: any) => void;
|
234
320
|
reset: () => void;
|
235
321
|
};
|
236
322
|
};
|
237
323
|
|
238
|
-
export { type ApiClientConfig, type ApiError, type ApiResponse, type PaginateQueryOptions, type PaginationMeta, type RefreshTokenConfig, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, cacheManager, createApiClient, createApiServices, processResponse, useApi };
|
324
|
+
export { type ActionOptions, type ApiClientConfig, type ApiError, type ApiResponse, type PaginateQueryOptions, type PaginationMeta, type QueryOptions, type RefreshTokenConfig, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, cacheManager, createApiActions, createApiClient, createApiServices, processResponse, useApi };
|
package/dist/index.d.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { ResponseType, AxiosRequestConfig, AxiosProgressEvent, AxiosInstance, AxiosResponse } from 'axios';
|
1
|
+
import { ResponseType, AxiosRequestConfig, AxiosProgressEvent, AxiosInstance, Method, AxiosResponse } from 'axios';
|
2
2
|
import * as react from 'react';
|
3
3
|
|
4
4
|
/**
|
@@ -18,15 +18,6 @@ interface PaginationMeta {
|
|
18
18
|
currentPage: number;
|
19
19
|
totalPages: number;
|
20
20
|
}
|
21
|
-
/**
|
22
|
-
* يمثل بنية الاستجابة القياسية من الـ API.
|
23
|
-
* @template T نوع البيانات الأساسية في الاستجابة.
|
24
|
-
*/
|
25
|
-
interface ApiResponse<T = any> {
|
26
|
-
data: T;
|
27
|
-
message?: string;
|
28
|
-
meta?: PaginationMeta;
|
29
|
-
}
|
30
21
|
/**
|
31
22
|
* يمثل خطأ التحقق من صحة حقل معين.
|
32
23
|
*/
|
@@ -44,18 +35,6 @@ interface ApiError {
|
|
44
35
|
errors?: ValidationError[];
|
45
36
|
requestId?: string;
|
46
37
|
}
|
47
|
-
/**
|
48
|
-
* يمثل كائن الاستجابة الموحد الذي يرجعه كل طلب.
|
49
|
-
* @template T نوع البيانات المتوقعة في حالة النجاح.
|
50
|
-
*/
|
51
|
-
interface StandardResponse<T> {
|
52
|
-
response?: ApiResponse<T> | T;
|
53
|
-
error: ApiError | null;
|
54
|
-
loading: boolean;
|
55
|
-
success: boolean;
|
56
|
-
message?: string;
|
57
|
-
validationErrors?: ValidationError[];
|
58
|
-
}
|
59
38
|
/**
|
60
39
|
* يمثل مجموعة التوكنات التي يتم إدارتها.
|
61
40
|
*/
|
@@ -96,19 +75,6 @@ interface PaginateQueryOptions {
|
|
96
75
|
}[];
|
97
76
|
filter?: Record<string, any>;
|
98
77
|
}
|
99
|
-
/**
|
100
|
-
* واجهة لتهيئة الهوك `useApi`.
|
101
|
-
* @template T نوع البيانات التي يتعامل معها الهوك.
|
102
|
-
*/
|
103
|
-
interface UseApiConfig<T> {
|
104
|
-
endpoint: string;
|
105
|
-
initialData?: T | T[];
|
106
|
-
initialQuery?: PaginateQueryOptions;
|
107
|
-
enabled?: boolean;
|
108
|
-
refetchAfterChange?: boolean;
|
109
|
-
onSuccess?: (message: string, data?: T) => void;
|
110
|
-
onError?: (message: string, error?: ApiError) => void;
|
111
|
-
}
|
112
78
|
/**
|
113
79
|
* واجهة لتخصيص عملية تجديد التوكن بشكل كامل.
|
114
80
|
*/
|
@@ -162,6 +128,82 @@ interface ApiClientConfig {
|
|
162
128
|
refreshTokenConfig?: RefreshTokenConfig;
|
163
129
|
onRefreshError?: (error: any) => void;
|
164
130
|
}
|
131
|
+
/**
|
132
|
+
* يمثل بنية الاستجابة القياسية من الـ API.
|
133
|
+
* @template T نوع البيانات الأساسية في الاستجابة.
|
134
|
+
*/
|
135
|
+
interface ApiResponse<T = any> {
|
136
|
+
data: T;
|
137
|
+
message?: string;
|
138
|
+
meta?: PaginationMeta;
|
139
|
+
}
|
140
|
+
/**
|
141
|
+
* يمثل بنية الاستجابة القياسية المغلفة التي قد تأتي من بعض نقاط الـ API.
|
142
|
+
* @template T نوع البيانات الأساسية في الاستجابة.
|
143
|
+
*/
|
144
|
+
interface ApiResponse<T = any> {
|
145
|
+
data: T;
|
146
|
+
message?: string;
|
147
|
+
meta?: PaginationMeta;
|
148
|
+
success?: boolean;
|
149
|
+
}
|
150
|
+
/**
|
151
|
+
* يمثل كائن الاستجابة الموحد والنهائي الذي يرجعه كل طلب.
|
152
|
+
* هذا هو النوع الذي ستتعامل معه دائمًا.
|
153
|
+
* @template T نوع البيانات النهائية التي تريد الوصول إليها.
|
154
|
+
*/
|
155
|
+
interface StandardResponse<T> {
|
156
|
+
data: T | null;
|
157
|
+
rawResponse: any;
|
158
|
+
error: ApiError | null;
|
159
|
+
loading: boolean;
|
160
|
+
success: boolean;
|
161
|
+
message?: string;
|
162
|
+
validationErrors?: ValidationError[];
|
163
|
+
}
|
164
|
+
/**
|
165
|
+
* NEW: واجهة استعلام مرنة تسمح بالبارامترات القياسية (للفلترة والترقيم)
|
166
|
+
* بالإضافة إلى أي بارامترات مخصصة أخرى عبر الـ index signature.
|
167
|
+
* @example
|
168
|
+
* { page: 1, limit: 10, search: 'term', status: 'published', authorId: 123 }
|
169
|
+
*/
|
170
|
+
interface QueryOptions {
|
171
|
+
page?: number;
|
172
|
+
limit?: number;
|
173
|
+
search?: string;
|
174
|
+
sortBy?: {
|
175
|
+
key: string;
|
176
|
+
direction: 'asc' | 'desc';
|
177
|
+
}[];
|
178
|
+
filter?: Record<string, any>;
|
179
|
+
[key: string]: any;
|
180
|
+
}
|
181
|
+
/**
|
182
|
+
* NEW: واجهة لتمرير خيارات إضافية لدوال الأكشن (create, update, remove).
|
183
|
+
*/
|
184
|
+
interface ActionOptions {
|
185
|
+
/**
|
186
|
+
* يسمح لك بتجاوز الـ endpoint الافتراضي المحدد في الهوك.
|
187
|
+
* مفيد لإرسال طلبات لنقاط نهاية متخصصة.
|
188
|
+
* @example
|
189
|
+
* // لتحديث عنصر وتغيير حالته عبر مسار مختلف
|
190
|
+
* update('123', { status: 'active' }, { endpoint: '/items/123/activate' })
|
191
|
+
*/
|
192
|
+
endpoint?: string;
|
193
|
+
}
|
194
|
+
/**
|
195
|
+
* واجهة لتهيئة الهوك `useApi`.
|
196
|
+
* @template T نوع البيانات التي يتعامل معها الهوك.
|
197
|
+
*/
|
198
|
+
interface UseApiConfig<T> {
|
199
|
+
endpoint: string;
|
200
|
+
initialData?: T | T[];
|
201
|
+
initialQuery?: QueryOptions;
|
202
|
+
enabled?: boolean;
|
203
|
+
refetchAfterChange?: boolean;
|
204
|
+
onSuccess?: (message: string, data?: T) => void;
|
205
|
+
onError?: (message: string, error?: ApiError) => void;
|
206
|
+
}
|
165
207
|
|
166
208
|
/**
|
167
209
|
* @file src/core/client.ts
|
@@ -173,12 +215,13 @@ interface ApiClientConfig {
|
|
173
215
|
|
174
216
|
declare function createApiClient(config: ApiClientConfig): AxiosInstance;
|
175
217
|
|
218
|
+
type CrudRequestConfig = RequestConfig & ActionOptions;
|
176
219
|
declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: string): {
|
177
220
|
get: (id?: string, config?: RequestConfig) => Promise<StandardResponse<T | T[]>>;
|
178
221
|
getWithQuery: (query: string, config?: RequestConfig) => Promise<StandardResponse<T[]>>;
|
179
|
-
post: (data: Partial<T>, config?:
|
180
|
-
patch: (id: string, data: Partial<T>, config?:
|
181
|
-
remove: (id: string, config?:
|
222
|
+
post: (data: Partial<T>, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
223
|
+
patch: (id: string, data: Partial<T>, config?: CrudRequestConfig) => Promise<StandardResponse<T>>;
|
224
|
+
remove: (id: string, config?: CrudRequestConfig) => Promise<StandardResponse<any>>;
|
182
225
|
};
|
183
226
|
|
184
227
|
/**
|
@@ -189,7 +232,46 @@ declare function createApiServices<T>(axiosInstance: AxiosInstance, endpoint: st
|
|
189
232
|
* بالإضافة إلى دوال التحقق من الأنواع (type guards).
|
190
233
|
*/
|
191
234
|
|
192
|
-
|
235
|
+
/**
|
236
|
+
* يبني سلسلة استعلام (query string) من كائن خيارات مرن.
|
237
|
+
* يتعامل مع البارامترات القياسية والمخصصة.
|
238
|
+
* @param query - كائن من نوع QueryOptions.
|
239
|
+
* @returns سلسلة استعلام جاهزة للإضافة للرابط.
|
240
|
+
*/
|
241
|
+
declare function buildPaginateQuery(query: QueryOptions): string;
|
242
|
+
|
243
|
+
/**
|
244
|
+
* Defines a single custom API action.
|
245
|
+
* @template TRequest - The type of the data sent in the request body/params.
|
246
|
+
* @template TResponse - The type of the data expected in the successful API response.
|
247
|
+
*/
|
248
|
+
type ApiAction<TRequest, TResponse> = (payload: TRequest, config?: RequestConfig) => Promise<StandardResponse<TResponse>>;
|
249
|
+
/**
|
250
|
+
* A factory function to create a collection of typed, custom API actions.
|
251
|
+
*
|
252
|
+
* @template TActions - An object type where keys are action names and values are objects
|
253
|
+
* defining the endpoint, method, and types for that action.
|
254
|
+
* @param axiosInstance - The configured Axios instance from `createApiClient`.
|
255
|
+
* @param actionsConfig - An object defining the configuration for each custom action.
|
256
|
+
* @returns A fully-typed object of executable API action functions.
|
257
|
+
*
|
258
|
+
* @example
|
259
|
+
* const authActions = createApiActions(apiClient, {
|
260
|
+
* login: { method: 'POST', endpoint: '/auth/login', requestType: {} as LoginCredentials, responseType: {} as AuthResponse },
|
261
|
+
* getProfile: { method: 'GET', endpoint: '/user/profile', requestType: {} as void, responseType: {} as UserProfile }
|
262
|
+
* });
|
263
|
+
*
|
264
|
+
* // Usage:
|
265
|
+
* const result = await authActions.login({ email: '..', password: '..' });
|
266
|
+
*/
|
267
|
+
declare function createApiActions<TActions extends Record<string, {
|
268
|
+
method: Method;
|
269
|
+
endpoint: string;
|
270
|
+
requestType: any;
|
271
|
+
responseType: any;
|
272
|
+
}>>(axiosInstance: AxiosInstance, actionsConfig: TActions): {
|
273
|
+
[K in keyof TActions]: ApiAction<TActions[K]['requestType'], TActions[K]['responseType']>;
|
274
|
+
};
|
193
275
|
|
194
276
|
declare class CacheManager {
|
195
277
|
private cache;
|
@@ -202,12 +284,15 @@ declare class CacheManager {
|
|
202
284
|
declare const cacheManager: CacheManager;
|
203
285
|
|
204
286
|
/**
|
205
|
-
*
|
206
|
-
*
|
207
|
-
* ...
|
287
|
+
* A smart response processor that normalizes API responses.
|
288
|
+
* It intelligently unwraps nested data from standard API envelopes
|
289
|
+
* (like { success: true, data: {...} }) and provides direct access
|
290
|
+
* to the core data, while still handling errors consistently.
|
291
|
+
*
|
292
|
+
* @param responseOrError The raw Axios response or a pre-processed ApiError.
|
293
|
+
* @returns A standardized `StandardResponse` object with direct access to `.data`.
|
208
294
|
*/
|
209
|
-
|
210
|
-
declare const processResponse: <T>(responseOrError: AxiosResponse<ApiResponse<T>> | ApiError) => StandardResponse<T>;
|
295
|
+
declare const processResponse: <T>(responseOrError: AxiosResponse<any> | ApiError) => StandardResponse<T>;
|
211
296
|
|
212
297
|
declare function useApi<T extends {
|
213
298
|
id?: string | number;
|
@@ -215,14 +300,14 @@ declare function useApi<T extends {
|
|
215
300
|
state: StandardResponse<T | T[]>;
|
216
301
|
setState: react.Dispatch<react.SetStateAction<StandardResponse<T | T[]>>>;
|
217
302
|
actions: {
|
218
|
-
fetch: () => Promise<void>;
|
219
|
-
create: (newItem: Partial<T
|
220
|
-
update: (id: string, updatedItem: Partial<T
|
221
|
-
remove: (id: string) => Promise<StandardResponse<any>>;
|
303
|
+
fetch: (options?: QueryOptions) => Promise<void>;
|
304
|
+
create: (newItem: Partial<T>, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
305
|
+
update: (id: string, updatedItem: Partial<T>, options?: ActionOptions) => Promise<StandardResponse<T>>;
|
306
|
+
remove: (id: string, options?: ActionOptions) => Promise<StandardResponse<any>>;
|
222
307
|
};
|
223
308
|
query: {
|
224
|
-
options:
|
225
|
-
setOptions: react.Dispatch<react.SetStateAction<
|
309
|
+
options: QueryOptions;
|
310
|
+
setOptions: react.Dispatch<react.SetStateAction<QueryOptions>>;
|
226
311
|
setPage: (page: number) => void;
|
227
312
|
setLimit: (limit: number) => void;
|
228
313
|
setSearchTerm: (search: string) => void;
|
@@ -231,8 +316,9 @@ declare function useApi<T extends {
|
|
231
316
|
direction: "asc" | "desc";
|
232
317
|
}[]) => void;
|
233
318
|
setFilters: (filter: Record<string, any>) => void;
|
319
|
+
setQueryParam: (key: string, value: any) => void;
|
234
320
|
reset: () => void;
|
235
321
|
};
|
236
322
|
};
|
237
323
|
|
238
|
-
export { type ApiClientConfig, type ApiError, type ApiResponse, type PaginateQueryOptions, type PaginationMeta, type RefreshTokenConfig, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, cacheManager, createApiClient, createApiServices, processResponse, useApi };
|
324
|
+
export { type ActionOptions, type ApiClientConfig, type ApiError, type ApiResponse, type PaginateQueryOptions, type PaginationMeta, type QueryOptions, type RefreshTokenConfig, type RequestConfig, type StandardResponse, type TokenManager, type Tokens, type UseApiConfig, type ValidationError, buildPaginateQuery, cacheManager, createApiActions, createApiClient, createApiServices, processResponse, useApi };
|
package/dist/index.js
CHANGED
@@ -32,6 +32,7 @@ var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
33
33
|
buildPaginateQuery: () => buildPaginateQuery,
|
34
34
|
cacheManager: () => cacheManager,
|
35
|
+
createApiActions: () => createApiActions,
|
35
36
|
createApiClient: () => createApiClient,
|
36
37
|
createApiServices: () => createApiServices,
|
37
38
|
processResponse: () => processResponse,
|
@@ -163,6 +164,7 @@ var import_axios2 = __toESM(require("axios"));
|
|
163
164
|
function buildPaginateQuery(query) {
|
164
165
|
if (!query) return "";
|
165
166
|
const params = new URLSearchParams();
|
167
|
+
const standardKeys = /* @__PURE__ */ new Set(["page", "limit", "search", "sortBy", "filter"]);
|
166
168
|
if (query.page) params.append("page", query.page.toString());
|
167
169
|
if (query.limit) params.append("limit", query.limit.toString());
|
168
170
|
if (query.search) params.append("search", query.search);
|
@@ -174,6 +176,14 @@ function buildPaginateQuery(query) {
|
|
174
176
|
params.append(`filter.${field}`, String(value));
|
175
177
|
});
|
176
178
|
}
|
179
|
+
for (const key in query) {
|
180
|
+
if (Object.prototype.hasOwnProperty.call(query, key) && !standardKeys.has(key)) {
|
181
|
+
const value = query[key];
|
182
|
+
if (value !== void 0 && value !== null) {
|
183
|
+
params.append(key, String(value));
|
184
|
+
}
|
185
|
+
}
|
186
|
+
}
|
177
187
|
const queryString = params.toString();
|
178
188
|
return queryString ? `?${queryString}` : "";
|
179
189
|
}
|
@@ -188,37 +198,38 @@ function isAxiosResponse(obj) {
|
|
188
198
|
var processResponse = (responseOrError) => {
|
189
199
|
if (isApiError(responseOrError)) {
|
190
200
|
return {
|
191
|
-
|
201
|
+
data: null,
|
202
|
+
rawResponse: void 0,
|
192
203
|
error: responseOrError,
|
193
204
|
validationErrors: responseOrError.errors || [],
|
194
|
-
// الخطأ الآن صحيح
|
195
205
|
success: false,
|
196
206
|
loading: false,
|
197
207
|
message: responseOrError.message
|
198
208
|
};
|
199
209
|
}
|
200
210
|
if (isAxiosResponse(responseOrError)) {
|
201
|
-
const
|
211
|
+
const rawData = responseOrError.data;
|
212
|
+
const isWrappedResponse = rawData && typeof rawData.success === "boolean" && rawData.data !== void 0;
|
213
|
+
const finalData = isWrappedResponse ? rawData.data : rawData;
|
214
|
+
const message = isWrappedResponse ? rawData.message : "Request successful.";
|
202
215
|
return {
|
203
|
-
|
216
|
+
data: finalData,
|
217
|
+
// <-- وصول مباشر للبيانات النهائية!
|
218
|
+
rawResponse: rawData,
|
219
|
+
// احتفظ بالاستجابة الكاملة إذا احتجت إليها
|
204
220
|
loading: false,
|
205
221
|
success: true,
|
206
222
|
error: null,
|
207
|
-
message
|
223
|
+
message,
|
208
224
|
validationErrors: []
|
209
|
-
// <-- أضف قيمة افتراضية هنا للاتساق
|
210
225
|
};
|
211
226
|
}
|
212
227
|
return {
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
status: 500
|
217
|
-
},
|
228
|
+
data: null,
|
229
|
+
rawResponse: void 0,
|
230
|
+
error: { message: "An unknown error occurred during response processing.", status: 500 },
|
218
231
|
success: false,
|
219
|
-
loading: false
|
220
|
-
validationErrors: []
|
221
|
-
// <-- أضف قيمة افتراضية هنا أيضًا
|
232
|
+
loading: false
|
222
233
|
};
|
223
234
|
};
|
224
235
|
|
@@ -242,24 +253,27 @@ function createApiServices(axiosInstance, endpoint) {
|
|
242
253
|
}
|
243
254
|
};
|
244
255
|
const post = async (data, config) => {
|
256
|
+
const finalUrl = config?.endpoint || endpoint;
|
245
257
|
try {
|
246
|
-
const response = await axiosInstance.post(
|
258
|
+
const response = await axiosInstance.post(finalUrl, data, config);
|
247
259
|
return processResponse(response);
|
248
260
|
} catch (error) {
|
249
261
|
return processResponse(error);
|
250
262
|
}
|
251
263
|
};
|
252
264
|
const patch = async (id, data, config) => {
|
265
|
+
const finalUrl = config?.endpoint || `${endpoint}/${id}`;
|
253
266
|
try {
|
254
|
-
const response = await axiosInstance.patch(
|
267
|
+
const response = await axiosInstance.patch(finalUrl, data, config);
|
255
268
|
return processResponse(response);
|
256
269
|
} catch (error) {
|
257
270
|
return processResponse(error);
|
258
271
|
}
|
259
272
|
};
|
260
273
|
const remove = async (id, config) => {
|
274
|
+
const finalUrl = config?.endpoint || `${endpoint}/${id}`;
|
261
275
|
try {
|
262
|
-
const response = await axiosInstance.delete(
|
276
|
+
const response = await axiosInstance.delete(finalUrl, config);
|
263
277
|
return processResponse(response);
|
264
278
|
} catch (error) {
|
265
279
|
return processResponse(error);
|
@@ -268,6 +282,34 @@ function createApiServices(axiosInstance, endpoint) {
|
|
268
282
|
return { get, getWithQuery, post, patch, remove };
|
269
283
|
}
|
270
284
|
|
285
|
+
// src/services/actions.ts
|
286
|
+
function createAction(axiosInstance, method, endpoint) {
|
287
|
+
return async (payload, config) => {
|
288
|
+
try {
|
289
|
+
const response = await axiosInstance.request({
|
290
|
+
// <--- غيرنا النوع إلى any
|
291
|
+
url: endpoint,
|
292
|
+
method,
|
293
|
+
...method.toUpperCase() === "GET" ? { params: payload } : { data: payload },
|
294
|
+
...config
|
295
|
+
});
|
296
|
+
return processResponse(response);
|
297
|
+
} catch (error) {
|
298
|
+
return processResponse(error);
|
299
|
+
}
|
300
|
+
};
|
301
|
+
}
|
302
|
+
function createApiActions(axiosInstance, actionsConfig) {
|
303
|
+
const actions = {};
|
304
|
+
for (const actionName in actionsConfig) {
|
305
|
+
if (Object.prototype.hasOwnProperty.call(actionsConfig, actionName)) {
|
306
|
+
const { method, endpoint } = actionsConfig[actionName];
|
307
|
+
actions[actionName] = createAction(axiosInstance, method, endpoint);
|
308
|
+
}
|
309
|
+
}
|
310
|
+
return actions;
|
311
|
+
}
|
312
|
+
|
271
313
|
// src/core/cache.ts
|
272
314
|
var CacheManager = class {
|
273
315
|
cache = /* @__PURE__ */ new Map();
|
@@ -305,23 +347,26 @@ function useApi(axiosInstance, config) {
|
|
305
347
|
const {
|
306
348
|
endpoint,
|
307
349
|
initialData,
|
308
|
-
initialQuery = {
|
350
|
+
initialQuery = { limit: 10 },
|
309
351
|
enabled = true,
|
310
352
|
refetchAfterChange = true,
|
353
|
+
// تم تعديل القيمة الافتراضية لتكون أكثر شيوعًا
|
311
354
|
onSuccess,
|
312
355
|
onError
|
313
356
|
} = config;
|
314
357
|
const [state, setState] = (0, import_react.useState)({
|
315
|
-
|
358
|
+
data: initialData || null,
|
359
|
+
rawResponse: null,
|
316
360
|
loading: enabled,
|
317
361
|
error: null,
|
318
362
|
success: false
|
319
363
|
});
|
320
364
|
const [queryOptions, setQueryOptions] = (0, import_react.useState)(initialQuery);
|
321
365
|
const apiServices = (0, import_react.useRef)(createApiServices(axiosInstance, endpoint)).current;
|
322
|
-
const fetchData = (0, import_react.useCallback)(async () => {
|
323
|
-
|
324
|
-
|
366
|
+
const fetchData = (0, import_react.useCallback)(async (options) => {
|
367
|
+
const currentQuery = options || queryOptions;
|
368
|
+
setState((prev) => ({ ...prev, data: null, loading: true, error: null }));
|
369
|
+
const queryString = buildPaginateQuery(currentQuery);
|
325
370
|
const result = await apiServices.getWithQuery(queryString, { cancelTokenKey: endpoint });
|
326
371
|
setState(result);
|
327
372
|
if (!result.success && onError) {
|
@@ -333,35 +378,35 @@ function useApi(axiosInstance, config) {
|
|
333
378
|
fetchData();
|
334
379
|
}
|
335
380
|
}, [enabled, queryOptions]);
|
336
|
-
const createItem = async (newItem) => {
|
381
|
+
const createItem = async (newItem, options) => {
|
337
382
|
setState((prev) => ({ ...prev, loading: true }));
|
338
|
-
const result = await apiServices.post(newItem);
|
383
|
+
const result = await apiServices.post(newItem, options);
|
339
384
|
if (result.success) {
|
340
385
|
if (refetchAfterChange) await fetchData();
|
341
386
|
else setState((prev) => ({ ...prev, loading: false }));
|
342
|
-
if (onSuccess) onSuccess(result.message || "Item created successfully!", result.
|
387
|
+
if (onSuccess) onSuccess(result.message || "Item created successfully!", result.data);
|
343
388
|
} else {
|
344
389
|
setState((prev) => ({ ...prev, loading: false, error: result.error }));
|
345
390
|
if (onError) onError(result.message || "Create failed", result.error || void 0);
|
346
391
|
}
|
347
392
|
return result;
|
348
393
|
};
|
349
|
-
const updateItem = async (id, updatedItem) => {
|
394
|
+
const updateItem = async (id, updatedItem, options) => {
|
350
395
|
setState((prev) => ({ ...prev, loading: true }));
|
351
|
-
const result = await apiServices.patch(id, updatedItem);
|
396
|
+
const result = await apiServices.patch(id, updatedItem, options);
|
352
397
|
if (result.success) {
|
353
398
|
if (refetchAfterChange) await fetchData();
|
354
399
|
else setState((prev) => ({ ...prev, loading: false }));
|
355
|
-
if (onSuccess) onSuccess(result.message || "Item updated successfully!", result.
|
400
|
+
if (onSuccess) onSuccess(result.message || "Item updated successfully!", result.data);
|
356
401
|
} else {
|
357
402
|
setState((prev) => ({ ...prev, loading: false, error: result.error }));
|
358
403
|
if (onError) onError(result.message || "Update failed", result.error || void 0);
|
359
404
|
}
|
360
405
|
return result;
|
361
406
|
};
|
362
|
-
const deleteItem = async (id) => {
|
407
|
+
const deleteItem = async (id, options) => {
|
363
408
|
setState((prev) => ({ ...prev, loading: true }));
|
364
|
-
const result = await apiServices.remove(id);
|
409
|
+
const result = await apiServices.remove(id, options);
|
365
410
|
if (result.success) {
|
366
411
|
if (refetchAfterChange) await fetchData();
|
367
412
|
else setState((prev) => ({ ...prev, loading: false }));
|
@@ -377,11 +422,19 @@ function useApi(axiosInstance, config) {
|
|
377
422
|
const setSearchTerm = (search) => setQueryOptions((prev) => ({ ...prev, search, page: 1 }));
|
378
423
|
const setSorting = (sortBy) => setQueryOptions((prev) => ({ ...prev, sortBy }));
|
379
424
|
const setFilters = (filter) => setQueryOptions((prev) => ({ ...prev, filter, page: 1 }));
|
425
|
+
const setQueryParam = (key, value) => {
|
426
|
+
setQueryOptions((prev) => {
|
427
|
+
const newQuery = { ...prev, [key]: value };
|
428
|
+
if (key !== "page") {
|
429
|
+
newQuery.page = 1;
|
430
|
+
}
|
431
|
+
return newQuery;
|
432
|
+
});
|
433
|
+
};
|
380
434
|
const resetQuery = () => setQueryOptions(initialQuery);
|
381
435
|
return {
|
382
436
|
state,
|
383
437
|
setState,
|
384
|
-
// Exposing setState for direct manipulation or connection to Redux
|
385
438
|
actions: {
|
386
439
|
fetch: fetchData,
|
387
440
|
create: createItem,
|
@@ -396,6 +449,8 @@ function useApi(axiosInstance, config) {
|
|
396
449
|
setSearchTerm,
|
397
450
|
setSorting,
|
398
451
|
setFilters,
|
452
|
+
setQueryParam,
|
453
|
+
// <-- NEW
|
399
454
|
reset: resetQuery
|
400
455
|
}
|
401
456
|
};
|
@@ -404,6 +459,7 @@ function useApi(axiosInstance, config) {
|
|
404
459
|
0 && (module.exports = {
|
405
460
|
buildPaginateQuery,
|
406
461
|
cacheManager,
|
462
|
+
createApiActions,
|
407
463
|
createApiClient,
|
408
464
|
createApiServices,
|
409
465
|
processResponse,
|
package/dist/index.mjs
CHANGED
@@ -122,6 +122,7 @@ import axios2 from "axios";
|
|
122
122
|
function buildPaginateQuery(query) {
|
123
123
|
if (!query) return "";
|
124
124
|
const params = new URLSearchParams();
|
125
|
+
const standardKeys = /* @__PURE__ */ new Set(["page", "limit", "search", "sortBy", "filter"]);
|
125
126
|
if (query.page) params.append("page", query.page.toString());
|
126
127
|
if (query.limit) params.append("limit", query.limit.toString());
|
127
128
|
if (query.search) params.append("search", query.search);
|
@@ -133,6 +134,14 @@ function buildPaginateQuery(query) {
|
|
133
134
|
params.append(`filter.${field}`, String(value));
|
134
135
|
});
|
135
136
|
}
|
137
|
+
for (const key in query) {
|
138
|
+
if (Object.prototype.hasOwnProperty.call(query, key) && !standardKeys.has(key)) {
|
139
|
+
const value = query[key];
|
140
|
+
if (value !== void 0 && value !== null) {
|
141
|
+
params.append(key, String(value));
|
142
|
+
}
|
143
|
+
}
|
144
|
+
}
|
136
145
|
const queryString = params.toString();
|
137
146
|
return queryString ? `?${queryString}` : "";
|
138
147
|
}
|
@@ -147,37 +156,38 @@ function isAxiosResponse(obj) {
|
|
147
156
|
var processResponse = (responseOrError) => {
|
148
157
|
if (isApiError(responseOrError)) {
|
149
158
|
return {
|
150
|
-
|
159
|
+
data: null,
|
160
|
+
rawResponse: void 0,
|
151
161
|
error: responseOrError,
|
152
162
|
validationErrors: responseOrError.errors || [],
|
153
|
-
// الخطأ الآن صحيح
|
154
163
|
success: false,
|
155
164
|
loading: false,
|
156
165
|
message: responseOrError.message
|
157
166
|
};
|
158
167
|
}
|
159
168
|
if (isAxiosResponse(responseOrError)) {
|
160
|
-
const
|
169
|
+
const rawData = responseOrError.data;
|
170
|
+
const isWrappedResponse = rawData && typeof rawData.success === "boolean" && rawData.data !== void 0;
|
171
|
+
const finalData = isWrappedResponse ? rawData.data : rawData;
|
172
|
+
const message = isWrappedResponse ? rawData.message : "Request successful.";
|
161
173
|
return {
|
162
|
-
|
174
|
+
data: finalData,
|
175
|
+
// <-- وصول مباشر للبيانات النهائية!
|
176
|
+
rawResponse: rawData,
|
177
|
+
// احتفظ بالاستجابة الكاملة إذا احتجت إليها
|
163
178
|
loading: false,
|
164
179
|
success: true,
|
165
180
|
error: null,
|
166
|
-
message
|
181
|
+
message,
|
167
182
|
validationErrors: []
|
168
|
-
// <-- أضف قيمة افتراضية هنا للاتساق
|
169
183
|
};
|
170
184
|
}
|
171
185
|
return {
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
status: 500
|
176
|
-
},
|
186
|
+
data: null,
|
187
|
+
rawResponse: void 0,
|
188
|
+
error: { message: "An unknown error occurred during response processing.", status: 500 },
|
177
189
|
success: false,
|
178
|
-
loading: false
|
179
|
-
validationErrors: []
|
180
|
-
// <-- أضف قيمة افتراضية هنا أيضًا
|
190
|
+
loading: false
|
181
191
|
};
|
182
192
|
};
|
183
193
|
|
@@ -201,24 +211,27 @@ function createApiServices(axiosInstance, endpoint) {
|
|
201
211
|
}
|
202
212
|
};
|
203
213
|
const post = async (data, config) => {
|
214
|
+
const finalUrl = config?.endpoint || endpoint;
|
204
215
|
try {
|
205
|
-
const response = await axiosInstance.post(
|
216
|
+
const response = await axiosInstance.post(finalUrl, data, config);
|
206
217
|
return processResponse(response);
|
207
218
|
} catch (error) {
|
208
219
|
return processResponse(error);
|
209
220
|
}
|
210
221
|
};
|
211
222
|
const patch = async (id, data, config) => {
|
223
|
+
const finalUrl = config?.endpoint || `${endpoint}/${id}`;
|
212
224
|
try {
|
213
|
-
const response = await axiosInstance.patch(
|
225
|
+
const response = await axiosInstance.patch(finalUrl, data, config);
|
214
226
|
return processResponse(response);
|
215
227
|
} catch (error) {
|
216
228
|
return processResponse(error);
|
217
229
|
}
|
218
230
|
};
|
219
231
|
const remove = async (id, config) => {
|
232
|
+
const finalUrl = config?.endpoint || `${endpoint}/${id}`;
|
220
233
|
try {
|
221
|
-
const response = await axiosInstance.delete(
|
234
|
+
const response = await axiosInstance.delete(finalUrl, config);
|
222
235
|
return processResponse(response);
|
223
236
|
} catch (error) {
|
224
237
|
return processResponse(error);
|
@@ -227,6 +240,34 @@ function createApiServices(axiosInstance, endpoint) {
|
|
227
240
|
return { get, getWithQuery, post, patch, remove };
|
228
241
|
}
|
229
242
|
|
243
|
+
// src/services/actions.ts
|
244
|
+
function createAction(axiosInstance, method, endpoint) {
|
245
|
+
return async (payload, config) => {
|
246
|
+
try {
|
247
|
+
const response = await axiosInstance.request({
|
248
|
+
// <--- غيرنا النوع إلى any
|
249
|
+
url: endpoint,
|
250
|
+
method,
|
251
|
+
...method.toUpperCase() === "GET" ? { params: payload } : { data: payload },
|
252
|
+
...config
|
253
|
+
});
|
254
|
+
return processResponse(response);
|
255
|
+
} catch (error) {
|
256
|
+
return processResponse(error);
|
257
|
+
}
|
258
|
+
};
|
259
|
+
}
|
260
|
+
function createApiActions(axiosInstance, actionsConfig) {
|
261
|
+
const actions = {};
|
262
|
+
for (const actionName in actionsConfig) {
|
263
|
+
if (Object.prototype.hasOwnProperty.call(actionsConfig, actionName)) {
|
264
|
+
const { method, endpoint } = actionsConfig[actionName];
|
265
|
+
actions[actionName] = createAction(axiosInstance, method, endpoint);
|
266
|
+
}
|
267
|
+
}
|
268
|
+
return actions;
|
269
|
+
}
|
270
|
+
|
230
271
|
// src/core/cache.ts
|
231
272
|
var CacheManager = class {
|
232
273
|
cache = /* @__PURE__ */ new Map();
|
@@ -264,23 +305,26 @@ function useApi(axiosInstance, config) {
|
|
264
305
|
const {
|
265
306
|
endpoint,
|
266
307
|
initialData,
|
267
|
-
initialQuery = {
|
308
|
+
initialQuery = { limit: 10 },
|
268
309
|
enabled = true,
|
269
310
|
refetchAfterChange = true,
|
311
|
+
// تم تعديل القيمة الافتراضية لتكون أكثر شيوعًا
|
270
312
|
onSuccess,
|
271
313
|
onError
|
272
314
|
} = config;
|
273
315
|
const [state, setState] = useState({
|
274
|
-
|
316
|
+
data: initialData || null,
|
317
|
+
rawResponse: null,
|
275
318
|
loading: enabled,
|
276
319
|
error: null,
|
277
320
|
success: false
|
278
321
|
});
|
279
322
|
const [queryOptions, setQueryOptions] = useState(initialQuery);
|
280
323
|
const apiServices = useRef(createApiServices(axiosInstance, endpoint)).current;
|
281
|
-
const fetchData = useCallback(async () => {
|
282
|
-
|
283
|
-
|
324
|
+
const fetchData = useCallback(async (options) => {
|
325
|
+
const currentQuery = options || queryOptions;
|
326
|
+
setState((prev) => ({ ...prev, data: null, loading: true, error: null }));
|
327
|
+
const queryString = buildPaginateQuery(currentQuery);
|
284
328
|
const result = await apiServices.getWithQuery(queryString, { cancelTokenKey: endpoint });
|
285
329
|
setState(result);
|
286
330
|
if (!result.success && onError) {
|
@@ -292,35 +336,35 @@ function useApi(axiosInstance, config) {
|
|
292
336
|
fetchData();
|
293
337
|
}
|
294
338
|
}, [enabled, queryOptions]);
|
295
|
-
const createItem = async (newItem) => {
|
339
|
+
const createItem = async (newItem, options) => {
|
296
340
|
setState((prev) => ({ ...prev, loading: true }));
|
297
|
-
const result = await apiServices.post(newItem);
|
341
|
+
const result = await apiServices.post(newItem, options);
|
298
342
|
if (result.success) {
|
299
343
|
if (refetchAfterChange) await fetchData();
|
300
344
|
else setState((prev) => ({ ...prev, loading: false }));
|
301
|
-
if (onSuccess) onSuccess(result.message || "Item created successfully!", result.
|
345
|
+
if (onSuccess) onSuccess(result.message || "Item created successfully!", result.data);
|
302
346
|
} else {
|
303
347
|
setState((prev) => ({ ...prev, loading: false, error: result.error }));
|
304
348
|
if (onError) onError(result.message || "Create failed", result.error || void 0);
|
305
349
|
}
|
306
350
|
return result;
|
307
351
|
};
|
308
|
-
const updateItem = async (id, updatedItem) => {
|
352
|
+
const updateItem = async (id, updatedItem, options) => {
|
309
353
|
setState((prev) => ({ ...prev, loading: true }));
|
310
|
-
const result = await apiServices.patch(id, updatedItem);
|
354
|
+
const result = await apiServices.patch(id, updatedItem, options);
|
311
355
|
if (result.success) {
|
312
356
|
if (refetchAfterChange) await fetchData();
|
313
357
|
else setState((prev) => ({ ...prev, loading: false }));
|
314
|
-
if (onSuccess) onSuccess(result.message || "Item updated successfully!", result.
|
358
|
+
if (onSuccess) onSuccess(result.message || "Item updated successfully!", result.data);
|
315
359
|
} else {
|
316
360
|
setState((prev) => ({ ...prev, loading: false, error: result.error }));
|
317
361
|
if (onError) onError(result.message || "Update failed", result.error || void 0);
|
318
362
|
}
|
319
363
|
return result;
|
320
364
|
};
|
321
|
-
const deleteItem = async (id) => {
|
365
|
+
const deleteItem = async (id, options) => {
|
322
366
|
setState((prev) => ({ ...prev, loading: true }));
|
323
|
-
const result = await apiServices.remove(id);
|
367
|
+
const result = await apiServices.remove(id, options);
|
324
368
|
if (result.success) {
|
325
369
|
if (refetchAfterChange) await fetchData();
|
326
370
|
else setState((prev) => ({ ...prev, loading: false }));
|
@@ -336,11 +380,19 @@ function useApi(axiosInstance, config) {
|
|
336
380
|
const setSearchTerm = (search) => setQueryOptions((prev) => ({ ...prev, search, page: 1 }));
|
337
381
|
const setSorting = (sortBy) => setQueryOptions((prev) => ({ ...prev, sortBy }));
|
338
382
|
const setFilters = (filter) => setQueryOptions((prev) => ({ ...prev, filter, page: 1 }));
|
383
|
+
const setQueryParam = (key, value) => {
|
384
|
+
setQueryOptions((prev) => {
|
385
|
+
const newQuery = { ...prev, [key]: value };
|
386
|
+
if (key !== "page") {
|
387
|
+
newQuery.page = 1;
|
388
|
+
}
|
389
|
+
return newQuery;
|
390
|
+
});
|
391
|
+
};
|
339
392
|
const resetQuery = () => setQueryOptions(initialQuery);
|
340
393
|
return {
|
341
394
|
state,
|
342
395
|
setState,
|
343
|
-
// Exposing setState for direct manipulation or connection to Redux
|
344
396
|
actions: {
|
345
397
|
fetch: fetchData,
|
346
398
|
create: createItem,
|
@@ -355,6 +407,8 @@ function useApi(axiosInstance, config) {
|
|
355
407
|
setSearchTerm,
|
356
408
|
setSorting,
|
357
409
|
setFilters,
|
410
|
+
setQueryParam,
|
411
|
+
// <-- NEW
|
358
412
|
reset: resetQuery
|
359
413
|
}
|
360
414
|
};
|
@@ -362,6 +416,7 @@ function useApi(axiosInstance, config) {
|
|
362
416
|
export {
|
363
417
|
buildPaginateQuery,
|
364
418
|
cacheManager,
|
419
|
+
createApiActions,
|
365
420
|
createApiClient,
|
366
421
|
createApiServices,
|
367
422
|
processResponse,
|