flysoft-react-ui 0.2.0 → 0.2.2
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/docs/DocsMenu.d.ts.map +1 -1
- package/dist/docs/DocsMenu.js +1 -1
- package/dist/docs/DocsRouter.d.ts.map +1 -1
- package/dist/docs/DocsRouter.js +1 -2
- package/dist/hooks/index.d.ts +0 -5
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +0 -3
- package/dist/index.css +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/services/apiClient.d.ts +75 -29
- package/dist/services/apiClient.d.ts.map +1 -1
- package/dist/services/apiClient.js +161 -135
- package/package.json +1 -1
- package/dist/docs/ApiClientDocs.d.ts +0 -4
- package/dist/docs/ApiClientDocs.d.ts.map +0 -1
- package/dist/docs/ApiClientDocs.js +0 -66
- package/dist/hooks/useApi.d.ts +0 -27
- package/dist/hooks/useApi.d.ts.map +0 -1
- package/dist/hooks/useApi.js +0 -150
- package/dist/hooks/useApiMutation.d.ts +0 -22
- package/dist/hooks/useApiMutation.d.ts.map +0 -1
- package/dist/hooks/useApiMutation.js +0 -85
|
@@ -1,46 +1,92 @@
|
|
|
1
|
-
import type
|
|
2
|
-
export type HttpMethod = "get" | "post" | "put" | "delete" | "patch";
|
|
3
|
-
export interface AppApiError {
|
|
4
|
-
message: string;
|
|
5
|
-
code?: string;
|
|
6
|
-
status?: number;
|
|
7
|
-
details?: unknown;
|
|
8
|
-
originalError?: unknown;
|
|
9
|
-
}
|
|
1
|
+
import { type AxiosResponse } from "axios";
|
|
10
2
|
export interface ApiClientConfig {
|
|
11
3
|
baseURL?: string;
|
|
12
4
|
timeout?: number;
|
|
13
5
|
headers?: Record<string, string>;
|
|
14
6
|
}
|
|
15
|
-
export interface RequestOptions<TBody = unknown, TParams = Record<string, unknown>> {
|
|
16
|
-
url: string;
|
|
17
|
-
params?: TParams;
|
|
18
|
-
data?: TBody;
|
|
19
|
-
headers?: Record<string, string>;
|
|
20
|
-
config?: AxiosRequestConfig;
|
|
21
|
-
}
|
|
22
7
|
type TokenProvider = () => string | undefined;
|
|
23
|
-
|
|
24
|
-
|
|
8
|
+
interface FileResponse {
|
|
9
|
+
data: Blob;
|
|
10
|
+
headers: AxiosResponse["headers"];
|
|
11
|
+
}
|
|
12
|
+
interface UploadFileOptions {
|
|
13
|
+
paramName?: string;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
declare class ApiClientService {
|
|
17
|
+
private instance;
|
|
25
18
|
private tokenProvider?;
|
|
26
19
|
constructor(config?: ApiClientConfig);
|
|
27
20
|
private setupInterceptors;
|
|
21
|
+
/**
|
|
22
|
+
* Establece el proveedor de token que se usará en todas las peticiones
|
|
23
|
+
* @param provider Función que retorna el token de autorización
|
|
24
|
+
*/
|
|
28
25
|
setTokenProvider(provider: TokenProvider | undefined): void;
|
|
26
|
+
/**
|
|
27
|
+
* Limpia el proveedor de token
|
|
28
|
+
*/
|
|
29
29
|
clearTokenProvider(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Actualiza la configuración por defecto del cliente
|
|
32
|
+
*/
|
|
30
33
|
updateDefaults(config: ApiClientConfig): void;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
private axiosRequest;
|
|
35
|
+
/**
|
|
36
|
+
* Realiza una petición GET
|
|
37
|
+
* @param url URL del endpoint
|
|
38
|
+
* @param params Parámetros opcionales que se enviarán como query string
|
|
39
|
+
* @param headers Headers opcionales de la petición
|
|
40
|
+
*/
|
|
41
|
+
get<T = unknown>(url: string, params?: Record<string, unknown>, headers?: Record<string, string>): Promise<T>;
|
|
42
|
+
/**
|
|
43
|
+
* Realiza una petición POST
|
|
44
|
+
*/
|
|
45
|
+
post<T = unknown>(url: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
46
|
+
/**
|
|
47
|
+
* Realiza una petición PUT
|
|
48
|
+
*/
|
|
49
|
+
put<T = unknown>(url: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
50
|
+
/**
|
|
51
|
+
* Realiza una petición DELETE
|
|
52
|
+
*/
|
|
53
|
+
del<T = unknown>(url: string, headers?: Record<string, string>): Promise<T>;
|
|
54
|
+
/**
|
|
55
|
+
* Obtiene un archivo como Blob
|
|
56
|
+
*/
|
|
57
|
+
getFile(url: string, headers?: Record<string, string>): Promise<FileResponse>;
|
|
58
|
+
/**
|
|
59
|
+
* Obtiene un archivo y retorna su URL como objeto
|
|
60
|
+
*/
|
|
61
|
+
getFileAsUrl(url: string, headers?: Record<string, string>): Promise<string>;
|
|
62
|
+
/**
|
|
63
|
+
* Abre un archivo en una nueva ventana
|
|
64
|
+
*/
|
|
65
|
+
openFile(url: string, headers?: Record<string, string>): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Descarga un archivo
|
|
68
|
+
*/
|
|
69
|
+
downloadFile(url: string, headers?: Record<string, string>): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Sube uno o más archivos usando FormData
|
|
72
|
+
*/
|
|
73
|
+
uploadFile<T = unknown>(url: string, files: FileList | File[], headers?: UploadFileOptions): Promise<T>;
|
|
40
74
|
}
|
|
41
|
-
|
|
42
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Cliente de API compartido con todas las funciones de HTTP
|
|
77
|
+
*/
|
|
78
|
+
export declare const apiClient: ApiClientService;
|
|
79
|
+
/**
|
|
80
|
+
* Crea una nueva instancia del cliente de API
|
|
81
|
+
*/
|
|
82
|
+
export declare const createApiClient: (config?: ApiClientConfig) => ApiClientService;
|
|
83
|
+
/**
|
|
84
|
+
* Establece el proveedor de token global para el cliente compartido
|
|
85
|
+
*/
|
|
43
86
|
export declare const setApiClientTokenProvider: (provider: TokenProvider | undefined) => void;
|
|
87
|
+
/**
|
|
88
|
+
* Limpia el proveedor de token global
|
|
89
|
+
*/
|
|
44
90
|
export declare const clearApiClientTokenProvider: () => void;
|
|
45
91
|
export {};
|
|
46
92
|
//# sourceMappingURL=apiClient.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiClient.d.ts","sourceRoot":"","sources":["../../src/services/apiClient.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"apiClient.d.ts","sourceRoot":"","sources":["../../src/services/apiClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
|
-
|
|
3
|
-
baseURL: "/api",
|
|
4
|
-
timeout: 15000,
|
|
5
|
-
};
|
|
6
|
-
const isObject = (value) => typeof value === "object" && value !== null;
|
|
7
|
-
const extractErrorData = (error) => {
|
|
8
|
-
if (isObject(error.response?.data)) {
|
|
9
|
-
return error.response?.data;
|
|
10
|
-
}
|
|
11
|
-
if (typeof error.response?.data === "string") {
|
|
12
|
-
return { message: error.response?.data };
|
|
13
|
-
}
|
|
14
|
-
return {};
|
|
15
|
-
};
|
|
16
|
-
export class ApiClient {
|
|
1
|
+
import axios, {} from "axios";
|
|
2
|
+
class ApiClientService {
|
|
17
3
|
instance;
|
|
18
4
|
tokenProvider;
|
|
19
5
|
constructor(config) {
|
|
20
6
|
this.instance = axios.create({
|
|
21
|
-
baseURL: config?.baseURL ??
|
|
22
|
-
timeout: config?.timeout ??
|
|
7
|
+
baseURL: config?.baseURL ?? "",
|
|
8
|
+
timeout: config?.timeout ?? 15000,
|
|
23
9
|
headers: {
|
|
24
10
|
"Content-Type": "application/json",
|
|
25
|
-
Accept: "application/json",
|
|
26
11
|
...config?.headers,
|
|
27
12
|
},
|
|
28
13
|
});
|
|
29
14
|
this.setupInterceptors();
|
|
30
15
|
}
|
|
31
16
|
setupInterceptors() {
|
|
32
|
-
|
|
17
|
+
// Request interceptor para inyectar el token automáticamente
|
|
18
|
+
this.instance.interceptors.request.use((config) => {
|
|
33
19
|
const token = this.tokenProvider?.();
|
|
34
|
-
if (token) {
|
|
35
|
-
|
|
36
|
-
|
|
20
|
+
if (token && config.headers) {
|
|
21
|
+
// Manejo compatible con diferentes versiones de axios
|
|
22
|
+
if ("set" in config.headers &&
|
|
23
|
+
typeof config.headers.set === "function") {
|
|
24
|
+
config.headers.set("Authorization", `Bearer ${token}`);
|
|
37
25
|
}
|
|
38
26
|
else {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
...existingHeaders,
|
|
42
|
-
Authorization: `Bearer ${token}`,
|
|
43
|
-
};
|
|
27
|
+
const headers = config.headers;
|
|
28
|
+
headers.Authorization = `Bearer ${token}`;
|
|
44
29
|
}
|
|
45
30
|
}
|
|
46
|
-
return
|
|
31
|
+
return config;
|
|
32
|
+
}, (error) => {
|
|
33
|
+
return Promise.reject(error);
|
|
34
|
+
});
|
|
35
|
+
// Response interceptor para manejo de errores (opcional, puede extenderse)
|
|
36
|
+
this.instance.interceptors.response.use((response) => response, (error) => {
|
|
37
|
+
return Promise.reject(error);
|
|
47
38
|
});
|
|
48
|
-
this.instance.interceptors.response.use((response) => response, (error) => Promise.reject(this.toAppApiError(error)));
|
|
49
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Establece el proveedor de token que se usará en todas las peticiones
|
|
42
|
+
* @param provider Función que retorna el token de autorización
|
|
43
|
+
*/
|
|
50
44
|
setTokenProvider(provider) {
|
|
51
45
|
this.tokenProvider = provider;
|
|
52
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Limpia el proveedor de token
|
|
49
|
+
*/
|
|
53
50
|
clearTokenProvider() {
|
|
54
51
|
this.tokenProvider = undefined;
|
|
55
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Actualiza la configuración por defecto del cliente
|
|
55
|
+
*/
|
|
56
56
|
updateDefaults(config) {
|
|
57
57
|
if (config.baseURL) {
|
|
58
58
|
this.instance.defaults.baseURL = config.baseURL;
|
|
@@ -61,127 +61,153 @@ export class ApiClient {
|
|
|
61
61
|
this.instance.defaults.timeout = config.timeout;
|
|
62
62
|
}
|
|
63
63
|
if (config.headers) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
...config.headers,
|
|
67
|
-
};
|
|
64
|
+
// Actualizar headers comunes de forma segura
|
|
65
|
+
Object.assign(this.instance.defaults.headers.common || {}, config.headers);
|
|
68
66
|
}
|
|
69
67
|
}
|
|
70
|
-
async
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
throw this.ensureAppApiError(error);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
async delete(options) {
|
|
84
|
-
try {
|
|
85
|
-
const response = await this.instance.delete(options.url, {
|
|
86
|
-
params: options.params,
|
|
87
|
-
headers: options.headers,
|
|
88
|
-
...options.config,
|
|
89
|
-
});
|
|
90
|
-
return this.transformResponse(response);
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
throw this.ensureAppApiError(error);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
async post(options) {
|
|
97
|
-
try {
|
|
98
|
-
const response = await this.instance.post(options.url, options.data, {
|
|
99
|
-
params: options.params,
|
|
100
|
-
headers: options.headers,
|
|
101
|
-
...options.config,
|
|
102
|
-
});
|
|
103
|
-
return this.transformResponse(response);
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
throw this.ensureAppApiError(error);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
async put(options) {
|
|
110
|
-
try {
|
|
111
|
-
const response = await this.instance.put(options.url, options.data, {
|
|
112
|
-
params: options.params,
|
|
113
|
-
headers: options.headers,
|
|
114
|
-
...options.config,
|
|
115
|
-
});
|
|
116
|
-
return this.transformResponse(response);
|
|
117
|
-
}
|
|
118
|
-
catch (error) {
|
|
119
|
-
throw this.ensureAppApiError(error);
|
|
120
|
-
}
|
|
68
|
+
async axiosRequest({ method, url, headers, body, params, }) {
|
|
69
|
+
return await this.instance({
|
|
70
|
+
method,
|
|
71
|
+
headers,
|
|
72
|
+
url,
|
|
73
|
+
data: body,
|
|
74
|
+
params,
|
|
75
|
+
});
|
|
121
76
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Realiza una petición GET
|
|
79
|
+
* @param url URL del endpoint
|
|
80
|
+
* @param params Parámetros opcionales que se enviarán como query string
|
|
81
|
+
* @param headers Headers opcionales de la petición
|
|
82
|
+
*/
|
|
83
|
+
async get(url, params, headers) {
|
|
84
|
+
const response = await this.axiosRequest({
|
|
85
|
+
method: "GET",
|
|
86
|
+
url,
|
|
87
|
+
params,
|
|
88
|
+
headers,
|
|
89
|
+
});
|
|
90
|
+
return response.data;
|
|
134
91
|
}
|
|
135
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Realiza una petición POST
|
|
94
|
+
*/
|
|
95
|
+
async post(url, body, headers) {
|
|
96
|
+
const response = await this.axiosRequest({
|
|
97
|
+
method: "POST",
|
|
98
|
+
url,
|
|
99
|
+
headers,
|
|
100
|
+
body,
|
|
101
|
+
});
|
|
136
102
|
return response.data;
|
|
137
103
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Realiza una petición PUT
|
|
106
|
+
*/
|
|
107
|
+
async put(url, body, headers) {
|
|
108
|
+
const response = await this.axiosRequest({
|
|
109
|
+
method: "PUT",
|
|
110
|
+
url,
|
|
111
|
+
headers,
|
|
112
|
+
body,
|
|
113
|
+
});
|
|
114
|
+
return response.data;
|
|
143
115
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Realiza una petición DELETE
|
|
118
|
+
*/
|
|
119
|
+
async del(url, headers) {
|
|
120
|
+
const response = await this.axiosRequest({
|
|
121
|
+
method: "DELETE",
|
|
122
|
+
url,
|
|
123
|
+
headers,
|
|
124
|
+
});
|
|
125
|
+
return response.data;
|
|
148
126
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
error.code ??
|
|
158
|
-
"API_ERROR",
|
|
159
|
-
status: error.response?.status ?? error.status,
|
|
160
|
-
details: extracted,
|
|
161
|
-
originalError: error,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
if (error instanceof Error) {
|
|
165
|
-
return {
|
|
166
|
-
message: error.message,
|
|
167
|
-
code: "UNKNOWN_ERROR",
|
|
168
|
-
details: undefined,
|
|
169
|
-
originalError: error,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
127
|
+
/**
|
|
128
|
+
* Obtiene un archivo como Blob
|
|
129
|
+
*/
|
|
130
|
+
async getFile(url, headers = {}) {
|
|
131
|
+
const response = await this.instance.get(url, {
|
|
132
|
+
responseType: "blob",
|
|
133
|
+
headers,
|
|
134
|
+
});
|
|
172
135
|
return {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
details: error,
|
|
136
|
+
data: response.data,
|
|
137
|
+
headers: response.headers,
|
|
176
138
|
};
|
|
177
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Obtiene un archivo y retorna su URL como objeto
|
|
142
|
+
*/
|
|
143
|
+
async getFileAsUrl(url, headers = {}) {
|
|
144
|
+
const { data } = await this.getFile(url, headers);
|
|
145
|
+
const blob = new Blob([data], { type: data.type });
|
|
146
|
+
return URL.createObjectURL(blob);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Abre un archivo en una nueva ventana
|
|
150
|
+
*/
|
|
151
|
+
async openFile(url, headers) {
|
|
152
|
+
const { data } = await this.getFile(url, headers);
|
|
153
|
+
const urlData = URL.createObjectURL(data);
|
|
154
|
+
window.open(urlData);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Descarga un archivo
|
|
158
|
+
*/
|
|
159
|
+
async downloadFile(url, headers) {
|
|
160
|
+
const { data, headers: dataHeaders } = await this.getFile(url, headers);
|
|
161
|
+
const contentDisposition = dataHeaders["content-disposition"] || dataHeaders["Content-Disposition"];
|
|
162
|
+
const fileName = contentDisposition
|
|
163
|
+
?.split("filename=")[1]
|
|
164
|
+
?.split(";")[0]
|
|
165
|
+
.replaceAll('"', "");
|
|
166
|
+
const blob = new Blob([data], { type: data.type });
|
|
167
|
+
const link = document.createElement("a");
|
|
168
|
+
link.href = window.URL.createObjectURL(blob);
|
|
169
|
+
link.setAttribute("download", fileName || "");
|
|
170
|
+
document.body.appendChild(link);
|
|
171
|
+
link.click();
|
|
172
|
+
link.parentNode?.removeChild(link);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Sube uno o más archivos usando FormData
|
|
176
|
+
*/
|
|
177
|
+
async uploadFile(url, files, headers) {
|
|
178
|
+
const formData = new FormData();
|
|
179
|
+
const { paramName = "file", ...newHeaders } = headers || {};
|
|
180
|
+
const fileArray = Array.from(files);
|
|
181
|
+
for (const file of fileArray) {
|
|
182
|
+
formData.append(paramName, file, file.name);
|
|
183
|
+
}
|
|
184
|
+
const response = await this.instance.post(url, formData, {
|
|
185
|
+
headers: newHeaders,
|
|
186
|
+
});
|
|
187
|
+
return response.data;
|
|
188
|
+
}
|
|
178
189
|
}
|
|
179
|
-
|
|
190
|
+
// Instancia compartida del cliente
|
|
191
|
+
const sharedClient = new ApiClientService();
|
|
192
|
+
/**
|
|
193
|
+
* Cliente de API compartido con todas las funciones de HTTP
|
|
194
|
+
*/
|
|
180
195
|
export const apiClient = sharedClient;
|
|
181
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Crea una nueva instancia del cliente de API
|
|
198
|
+
*/
|
|
199
|
+
export const createApiClient = (config) => {
|
|
200
|
+
return new ApiClientService(config);
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* Establece el proveedor de token global para el cliente compartido
|
|
204
|
+
*/
|
|
182
205
|
export const setApiClientTokenProvider = (provider) => {
|
|
183
206
|
sharedClient.setTokenProvider(provider);
|
|
184
207
|
};
|
|
208
|
+
/**
|
|
209
|
+
* Limpia el proveedor de token global
|
|
210
|
+
*/
|
|
185
211
|
export const clearApiClientTokenProvider = () => {
|
|
186
212
|
sharedClient.clearTokenProvider();
|
|
187
213
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flysoft-react-ui",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A modern React UI component library with Tailwind CSS, TypeScript, and FontAwesome 5. Includes forms, layouts, themes, and templates for rapid development.",
|
|
7
7
|
"keywords": [
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ApiClientDocs.d.ts","sourceRoot":"","sources":["../../src/docs/ApiClientDocs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAarD,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAiO1B,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React, { useCallback, useState } from "react";
|
|
3
|
-
import { Badge, Button, Card, Input, useApi, useApiMutation, } from "../index";
|
|
4
|
-
const API_BASE = "https://jsonplaceholder.typicode.com";
|
|
5
|
-
const ApiClientDocs = () => {
|
|
6
|
-
const [postId, setPostId] = useState("1");
|
|
7
|
-
const [formState, setFormState] = useState({
|
|
8
|
-
title: "",
|
|
9
|
-
body: "",
|
|
10
|
-
});
|
|
11
|
-
const { data: postData, loading: loadingPost, error: postError, refetch: refetchPost, } = useApi({
|
|
12
|
-
url: `${API_BASE}/posts/${postId || 1}`,
|
|
13
|
-
method: "get",
|
|
14
|
-
enabled: false,
|
|
15
|
-
queryKey: "docs.posts.detail",
|
|
16
|
-
onError: (error) => {
|
|
17
|
-
console.warn("Example query error", error);
|
|
18
|
-
},
|
|
19
|
-
});
|
|
20
|
-
const { data: createdPost, loading: creatingPost, error: createError, mutateAsync: createPost, reset: resetMutation, } = useApiMutation({
|
|
21
|
-
url: `${API_BASE}/posts`,
|
|
22
|
-
method: "post",
|
|
23
|
-
invalidateKeys: ["docs.posts.detail"],
|
|
24
|
-
onSuccess: () => {
|
|
25
|
-
// Invalidate re-executes the query registered under the same key.
|
|
26
|
-
setFormState({ title: "", body: "" });
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
const handleFetchPost = useCallback(() => {
|
|
30
|
-
void refetchPost();
|
|
31
|
-
}, [refetchPost]);
|
|
32
|
-
const handleSubmit = useCallback(async (event) => {
|
|
33
|
-
event.preventDefault();
|
|
34
|
-
try {
|
|
35
|
-
await createPost({
|
|
36
|
-
data: {
|
|
37
|
-
title: formState.title,
|
|
38
|
-
body: formState.body,
|
|
39
|
-
userId: 1,
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
catch (error) {
|
|
44
|
-
console.info("Example mutation error", error);
|
|
45
|
-
}
|
|
46
|
-
}, [createPost, formState.body, formState.title]);
|
|
47
|
-
const renderErrorBadge = (error) => {
|
|
48
|
-
if (!error)
|
|
49
|
-
return null;
|
|
50
|
-
return (_jsx(Badge, { variant: "danger", icon: "fa-exclamation-triangle", children: error.message }));
|
|
51
|
-
};
|
|
52
|
-
return (_jsxs("div", { className: "max-w-5xl mx-auto space-y-8", children: [_jsx(Card, { title: "ApiClient - Resumen", children: _jsxs("div", { className: "space-y-4", children: [_jsxs("p", { style: { color: "var(--flysoft-text-secondary)" }, children: ["La capa de networking usa ", _jsx("code", { children: "axios" }), " con interceptores listos para inyectar tokens y normalizar errores en", _jsx("code", { children: "AppApiError" }), ". Importa los estilos con", _jsx("code", { children: "import \"flysoft-react-ui/styles\";" }), " y consume", _jsx("code", { children: "apiClient" }), ",", _jsx("code", { children: "useApi" }), " y ", _jsx("code", { children: "useApiMutation" }), " directamente desde la librer\u00EDa."] }), _jsxs("ul", { className: "list-disc list-inside space-y-2 text-sm", children: [_jsxs("li", { children: ["M\u00E9todos disponibles: ", _jsx("code", { children: "get" }), ", ", _jsx("code", { children: "post" }), ",", _jsx("code", { children: "put" }), ", ", _jsx("code", { children: "delete" }), " y ", _jsx("code", { children: "patch" }), " con opciones tipadas."] }), _jsxs("li", { children: ["Los errores se entregan como ", _jsx("code", { children: "AppApiError" }), " para que los componentes usen badges, alerts u otros patrones ya disponibles sin tocar los hooks."] }), _jsxs("li", { children: ["Usa ", _jsx("code", { children: "setApiClientTokenProvider" }), " para inyectar tokens (placeholder listo para integrarse con tu ThemeProvider o contextos existentes)."] })] })] }) }), _jsx(Card, { title: "Consulta simple con useApi", children: _jsxs("div", { className: "space-y-6", children: [_jsxs("form", { className: "grid grid-cols-1 sm:grid-cols-[1fr_auto] gap-4 items-end", onSubmit: (event) => {
|
|
53
|
-
event.preventDefault();
|
|
54
|
-
handleFetchPost();
|
|
55
|
-
}, children: [_jsx(Input, { label: "ID del post", icon: "fa-hashtag", value: postId, onChange: (event) => setPostId(event.target.value) }), _jsx(Button, { type: "submit", variant: "primary", icon: "fa-search", loading: loadingPost, children: "Consultar" })] }), _jsxs("div", { className: "space-y-3", children: [loadingPost && (_jsx(Badge, { variant: "info", icon: "fa-spinner", className: "animate-pulse", children: "Cargando post..." })), renderErrorBadge(postError), postData && !loadingPost && (_jsx(Card, { title: `Post ${postData.id}`, children: _jsxs("div", { className: "space-y-2", children: [_jsx("h4", { className: "text-lg font-semibold", style: { color: "var(--flysoft-text-primary)" }, children: postData.title }), _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: postData.body })] }) }))] })] }) }), _jsx(Card, { title: "Mutaci\u00F3n con useApiMutation", children: _jsxs("form", { className: "space-y-6", onSubmit: handleSubmit, children: [_jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [_jsx(Input, { label: "T\u00EDtulo", icon: "fa-heading", value: formState.title, onChange: (event) => setFormState((prev) => ({
|
|
56
|
-
...prev,
|
|
57
|
-
title: event.target.value,
|
|
58
|
-
})) }), _jsx(Input, { label: "Contenido", icon: "fa-align-left", value: formState.body, onChange: (event) => setFormState((prev) => ({
|
|
59
|
-
...prev,
|
|
60
|
-
body: event.target.value,
|
|
61
|
-
})) })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [_jsx(Button, { type: "submit", variant: "primary", icon: "fa-paper-plane", loading: creatingPost, children: "Enviar datos" }), _jsx(Button, { type: "button", variant: "outline", icon: "fa-undo", onClick: () => {
|
|
62
|
-
setFormState({ title: "", body: "" });
|
|
63
|
-
resetMutation();
|
|
64
|
-
}, children: "Reiniciar" })] }), _jsxs("div", { className: "space-y-3", children: [creatingPost && (_jsx(Badge, { variant: "info", icon: "fa-spinner", className: "animate-pulse", children: "Enviando..." })), renderErrorBadge(createError), createdPost && !creatingPost && (_jsxs(Badge, { variant: "success", icon: "fa-check-circle", children: ["Post #", createdPost.id, " creado (ejemplo)."] })), _jsxs("p", { className: "text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: ["Usa ", _jsx("code", { children: "invalidateKeys" }), " para refrescar queries que hayan sido registradas con ", _jsx("code", { children: "queryKey" }), " en ", _jsx("code", { children: "useApi" }), ". Esto permite mantener badges y alerts sincronizados con el estado de la UI sin alterar los hooks."] })] })] }) })] }));
|
|
65
|
-
};
|
|
66
|
-
export default ApiClientDocs;
|
package/dist/hooks/useApi.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { AxiosRequestConfig } from "axios";
|
|
2
|
-
import { type AppApiError, type HttpMethod, type RequestOptions } from "../services/apiClient";
|
|
3
|
-
type AnyRequestOptions = RequestOptions<unknown, Record<string, unknown>>;
|
|
4
|
-
type QueryRefetcher = (override?: Partial<AnyRequestOptions>) => Promise<unknown>;
|
|
5
|
-
export declare const registerQuery: (key: string, refetcher: QueryRefetcher) => void;
|
|
6
|
-
export declare const unregisterQuery: (key: string, refetcher: QueryRefetcher) => void;
|
|
7
|
-
export declare const invalidateQueries: (keys: string[]) => Promise<void>;
|
|
8
|
-
type CleanHttpMethod = HttpMethod;
|
|
9
|
-
export interface UseApiOptions<TResponse, TParams = Record<string, unknown>, TBody = unknown, TTransformed = TResponse> extends Omit<RequestOptions<TBody, TParams>, "config"> {
|
|
10
|
-
method?: CleanHttpMethod;
|
|
11
|
-
transform?: (response: TResponse) => TTransformed;
|
|
12
|
-
enabled?: boolean;
|
|
13
|
-
queryKey?: string;
|
|
14
|
-
config?: AxiosRequestConfig;
|
|
15
|
-
onSuccess?: (data: TTransformed) => void;
|
|
16
|
-
onError?: (error: AppApiError) => void;
|
|
17
|
-
}
|
|
18
|
-
export interface UseApiResult<TData, TBody = unknown, TParams = Record<string, unknown>> {
|
|
19
|
-
data: TData | undefined;
|
|
20
|
-
loading: boolean;
|
|
21
|
-
error: AppApiError | undefined;
|
|
22
|
-
refetch: (override?: Partial<RequestOptions<TBody, TParams>>) => Promise<TData>;
|
|
23
|
-
reset: () => void;
|
|
24
|
-
}
|
|
25
|
-
export declare const useApi: <TResponse, TParams = Record<string, unknown>, TBody = unknown, TTransformed = TResponse>(options: UseApiOptions<TResponse, TParams, TBody, TTransformed>) => UseApiResult<TTransformed, TBody, TParams>;
|
|
26
|
-
export {};
|
|
27
|
-
//# sourceMappingURL=useApi.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useApi.d.ts","sourceRoot":"","sources":["../../src/hooks/useApi.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAChD,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAE/B,KAAK,iBAAiB,GAAG,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAE1E,KAAK,cAAc,GAAG,CACpB,QAAQ,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,KAClC,OAAO,CAAC,OAAO,CAAC,CAAC;AAStB,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,EAAE,WAAW,cAAc,SAKnE,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,EAAE,WAAW,cAAc,SAOrE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAU,MAAM,MAAM,EAAE,kBAgBrD,CAAC;AAEF,KAAK,eAAe,GAAG,UAAU,CAAC;AAElC,MAAM,WAAW,aAAa,CAC5B,SAAS,EACT,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,KAAK,GAAG,OAAO,EACf,YAAY,GAAG,SAAS,CACxB,SAAQ,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC;IACtD,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,YAAY,CAAC;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACzC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;CACxC;AAED,MAAM,WAAW,YAAY,CAC3B,KAAK,EACL,KAAK,GAAG,OAAO,EACf,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEjC,IAAI,EAAE,KAAK,GAAG,SAAS,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,WAAW,GAAG,SAAS,CAAC;IAC/B,OAAO,EAAE,CACP,QAAQ,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,KAC/C,OAAO,CAAC,KAAK,CAAC,CAAC;IACpB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAqDD,eAAO,MAAM,MAAM,GACjB,SAAS,EACT,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,KAAK,GAAG,OAAO,EACf,YAAY,GAAG,SAAS,EAExB,SAAS,aAAa,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,CAAC,KAC9D,YAAY,CAAC,YAAY,EAAE,KAAK,EAAE,OAAO,CAyG3C,CAAC"}
|