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.
@@ -1,150 +0,0 @@
1
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
- import { apiClient, } from "../services/apiClient";
3
- const queryRegistry = new Map();
4
- const defaultError = {
5
- message: "Unknown API error",
6
- code: "UNKNOWN",
7
- };
8
- export const registerQuery = (key, refetcher) => {
9
- if (!key)
10
- return;
11
- const existing = queryRegistry.get(key) ?? new Set();
12
- existing.add(refetcher);
13
- queryRegistry.set(key, existing);
14
- };
15
- export const unregisterQuery = (key, refetcher) => {
16
- const existing = queryRegistry.get(key);
17
- if (!existing)
18
- return;
19
- existing.delete(refetcher);
20
- if (existing.size === 0) {
21
- queryRegistry.delete(key);
22
- }
23
- };
24
- export const invalidateQueries = async (keys) => {
25
- const tasks = [];
26
- keys.forEach((key) => {
27
- const refetchers = queryRegistry.get(key);
28
- if (!refetchers)
29
- return;
30
- refetchers.forEach((refetcher) => {
31
- tasks.push(Promise.resolve()
32
- .then(() => refetcher())
33
- .catch(() => {
34
- // Swallow refetch errors during invalidation.
35
- }));
36
- });
37
- });
38
- await Promise.allSettled(tasks);
39
- };
40
- // UI components can map `error` or `loading` to themed indicators such as `Badge`
41
- // or in-card alerts without modifying this hook. See the docs example for guidance.
42
- const callMethod = async (method, options) => {
43
- switch (method) {
44
- case "get":
45
- return apiClient.get(options);
46
- case "delete":
47
- return apiClient.delete(options);
48
- case "post":
49
- return apiClient.post(options);
50
- case "put":
51
- return apiClient.put(options);
52
- case "patch":
53
- return apiClient.patch(options);
54
- default:
55
- return apiClient.get(options);
56
- }
57
- };
58
- const buildRequestOptions = (base, override) => ({
59
- url: override?.url ?? base.url,
60
- params: override?.params ?? base.params,
61
- data: override?.data ?? base.data,
62
- headers: {
63
- ...base.headers,
64
- ...(override?.headers ?? {}),
65
- },
66
- config: {
67
- ...base.config,
68
- ...(override?.config ?? {}),
69
- },
70
- });
71
- export const useApi = (options) => {
72
- const { method = "get", enabled = true, transform, onSuccess, onError, queryKey, } = options;
73
- const [data, setData] = useState();
74
- const [loading, setLoading] = useState(enabled);
75
- const [error, setError] = useState();
76
- const latestOptionsRef = useRef(options);
77
- latestOptionsRef.current = options;
78
- const transformRef = useRef(transform);
79
- transformRef.current = transform;
80
- const onSuccessRef = useRef(onSuccess);
81
- onSuccessRef.current = onSuccess;
82
- const onErrorRef = useRef(onError);
83
- onErrorRef.current = onError;
84
- const refetch = useCallback(async (override) => {
85
- const activeOptions = latestOptionsRef.current;
86
- const requestOptions = buildRequestOptions(activeOptions, override);
87
- setLoading(true);
88
- setError(undefined);
89
- try {
90
- const response = await callMethod(method, requestOptions);
91
- const transformed = (transformRef.current
92
- ? transformRef.current(response)
93
- : response);
94
- setData(transformed);
95
- onSuccessRef.current?.(transformed);
96
- return transformed;
97
- }
98
- catch (err) {
99
- const appError = err ?? defaultError;
100
- setError(appError);
101
- onErrorRef.current?.(appError);
102
- throw appError;
103
- }
104
- finally {
105
- setLoading(false);
106
- }
107
- }, [method]);
108
- const stableRefetch = useMemo(() => refetch, [refetch]);
109
- useEffect(() => {
110
- if (queryKey) {
111
- registerQuery(queryKey, stableRefetch);
112
- return () => {
113
- unregisterQuery(queryKey, stableRefetch);
114
- };
115
- }
116
- return undefined;
117
- }, [queryKey, stableRefetch]);
118
- useEffect(() => {
119
- if (!enabled) {
120
- setLoading(false);
121
- return;
122
- }
123
- let cancelled = false;
124
- const run = async () => {
125
- try {
126
- await refetch();
127
- }
128
- catch {
129
- if (!cancelled) {
130
- // Error state already handled inside refetch.
131
- }
132
- }
133
- };
134
- void run();
135
- return () => {
136
- cancelled = true;
137
- };
138
- }, [enabled, refetch]);
139
- const reset = useCallback(() => {
140
- setData(undefined);
141
- setError(undefined);
142
- }, []);
143
- return {
144
- data,
145
- loading,
146
- error,
147
- refetch,
148
- reset,
149
- };
150
- };
@@ -1,22 +0,0 @@
1
- import type { AxiosRequestConfig } from "axios";
2
- import { type AppApiError, type HttpMethod, type RequestOptions } from "../services/apiClient";
3
- type MutationMethod = Exclude<HttpMethod, "get">;
4
- export interface UseApiMutationOptions<TResponse, TBody = unknown, TParams = Record<string, unknown>> extends Omit<RequestOptions<TBody, TParams>, "config"> {
5
- method?: MutationMethod;
6
- config?: AxiosRequestConfig;
7
- invalidateKeys?: string[];
8
- onSuccess?: (data: TResponse) => void | Promise<void>;
9
- onError?: (error: AppApiError) => void | Promise<void>;
10
- onSettled?: (data: TResponse | undefined, error: AppApiError | undefined) => void | Promise<void>;
11
- }
12
- export interface UseApiMutationResult<TResponse, TBody = unknown, TParams = Record<string, unknown>> {
13
- data: TResponse | undefined;
14
- loading: boolean;
15
- error: AppApiError | undefined;
16
- mutate: (override?: Partial<RequestOptions<TBody, TParams>>) => void;
17
- mutateAsync: (override?: Partial<RequestOptions<TBody, TParams>>) => Promise<TResponse>;
18
- reset: () => void;
19
- }
20
- export declare const useApiMutation: <TResponse, TBody = unknown, TParams = Record<string, unknown>>(options: UseApiMutationOptions<TResponse, TBody, TParams>) => UseApiMutationResult<TResponse, TBody, TParams>;
21
- export {};
22
- //# sourceMappingURL=useApiMutation.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useApiMutation.d.ts","sourceRoot":"","sources":["../../src/hooks/useApiMutation.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;AAG/B,KAAK,cAAc,GAAG,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;AAEjD,MAAM,WAAW,qBAAqB,CACpC,SAAS,EACT,KAAK,GAAG,OAAO,EACf,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CACjC,SAAQ,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC;IACtD,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE,CACV,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,KAAK,EAAE,WAAW,GAAG,SAAS,KAC3B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,oBAAoB,CACnC,SAAS,EACT,KAAK,GAAG,OAAO,EACf,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEjC,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,WAAW,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,CACN,QAAQ,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,KAC/C,IAAI,CAAC;IACV,WAAW,EAAE,CACX,QAAQ,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,KAC/C,OAAO,CAAC,SAAS,CAAC,CAAC;IACxB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AA0CD,eAAO,MAAM,cAAc,GACzB,SAAS,EACT,KAAK,GAAG,OAAO,EACf,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEjC,SAAS,qBAAqB,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,KACxD,oBAAoB,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAyEhD,CAAC"}
@@ -1,85 +0,0 @@
1
- import { useCallback, useRef, useState } from "react";
2
- import { apiClient, } from "../services/apiClient";
3
- import { invalidateQueries } from "./useApi";
4
- const callMutation = async (method, options) => {
5
- switch (method) {
6
- case "post":
7
- return apiClient.post(options);
8
- case "put":
9
- return apiClient.put(options);
10
- case "patch":
11
- return apiClient.patch(options);
12
- case "delete":
13
- return apiClient.delete({
14
- url: options.url,
15
- params: options.params,
16
- headers: options.headers,
17
- config: options.config,
18
- });
19
- default:
20
- return apiClient.post(options);
21
- }
22
- };
23
- const buildMutationOptions = (base, override) => ({
24
- url: override?.url ?? base.url,
25
- params: override?.params ?? base.params,
26
- data: override?.data ?? base.data,
27
- headers: {
28
- ...base.headers,
29
- ...(override?.headers ?? {}),
30
- },
31
- config: {
32
- ...base.config,
33
- ...(override?.config ?? {}),
34
- },
35
- });
36
- export const useApiMutation = (options) => {
37
- const { method = "post", invalidateKeys, onSuccess, onError, onSettled, } = options;
38
- const [data, setData] = useState();
39
- const [loading, setLoading] = useState(false);
40
- const [error, setError] = useState();
41
- const latestOptionsRef = useRef(options);
42
- latestOptionsRef.current = options;
43
- const mutateAsync = useCallback(async (override) => {
44
- const activeOptions = latestOptionsRef.current;
45
- const requestOptions = buildMutationOptions(activeOptions, override);
46
- setLoading(true);
47
- setError(undefined);
48
- try {
49
- const response = await callMutation(method, requestOptions);
50
- setData(response);
51
- await onSuccess?.(response);
52
- if (invalidateKeys?.length) {
53
- await invalidateQueries(invalidateKeys);
54
- }
55
- await onSettled?.(response, undefined);
56
- return response;
57
- }
58
- catch (err) {
59
- const appError = err;
60
- setError(appError);
61
- await onError?.(appError);
62
- await onSettled?.(undefined, appError);
63
- throw appError;
64
- }
65
- finally {
66
- setLoading(false);
67
- }
68
- }, [invalidateKeys, method, onError, onSettled, onSuccess]);
69
- const mutate = useCallback((override) => {
70
- void mutateAsync(override);
71
- }, [mutateAsync]);
72
- const reset = useCallback(() => {
73
- setData(undefined);
74
- setError(undefined);
75
- setLoading(false);
76
- }, []);
77
- return {
78
- data,
79
- loading,
80
- error,
81
- mutate,
82
- mutateAsync,
83
- reset,
84
- };
85
- };