[TASK-2026-02-03] fix: Convert remaining hooks from @tanstack/react-query to useState/useEffect
Converts 5 modules to use the project's standard hook pattern: - dashboard: useDashboard hooks - geolocation: useGeolocation, useCountries, useRegions, useCities, etc. - mcp: useMcpServers, useMcpTools, useMcpResources, etc. - payment-terminals: usePaymentTerminals, usePaymentTerminalTransactions, etc. - scanning: useScanningSessions, useScanningDevices, etc. All hooks now use useState/useEffect instead of @tanstack/react-query. Build passes with 0 TypeScript errors. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b9068be3d9
commit
da2e391b7c
@ -1,4 +1,4 @@
|
||||
import { useQuery, useMutation, useQueryClient, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { dashboardApi } from '../api/dashboard.api';
|
||||
import type {
|
||||
Dashboard,
|
||||
@ -11,224 +11,382 @@ import type {
|
||||
DashboardMetrics,
|
||||
} from '../../shared/types/api.types';
|
||||
|
||||
// Query Keys
|
||||
export const dashboardKeys = {
|
||||
all: ['dashboard'] as const,
|
||||
lists: () => [...dashboardKeys.all, 'list'] as const,
|
||||
list: (filters: DashboardFilters) => [...dashboardKeys.lists(), filters] as const,
|
||||
details: () => [...dashboardKeys.all, 'detail'] as const,
|
||||
detail: (id: string) => [...dashboardKeys.details(), id] as const,
|
||||
widgets: (id: string) => [...dashboardKeys.detail(id), 'widgets'] as const,
|
||||
metrics: (id: string) => [...dashboardKeys.detail(id), 'metrics'] as const,
|
||||
// ============================================================================
|
||||
// Query Hooks (useState/useEffect pattern)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Hook para obtener lista de dashboards con filtros
|
||||
*/
|
||||
export function useDashboards(filters?: DashboardFilters) {
|
||||
const [data, setData] = useState<DashboardsResponse | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await dashboardApi.getAll(filters);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al obtener dashboards');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [filters?.search, filters?.page, filters?.limit, filters?.sortBy, filters?.sortOrder]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const create = useCallback(async (createData: CreateDashboardDto): Promise<Dashboard | null> => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const newDashboard = await dashboardApi.create(createData);
|
||||
await fetch();
|
||||
return newDashboard;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al crear dashboard');
|
||||
return null;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [fetch]);
|
||||
|
||||
const deleteDashboard = useCallback(async (id: string): Promise<boolean> => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
await dashboardApi.delete(id);
|
||||
await fetch();
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al eliminar dashboard');
|
||||
return false;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [fetch]);
|
||||
|
||||
const duplicate = useCallback(async (id: string, name: string): Promise<Dashboard | null> => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const duplicated = await dashboardApi.duplicate(id, name);
|
||||
await fetch();
|
||||
return duplicated;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al duplicar dashboard');
|
||||
return null;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [fetch]);
|
||||
|
||||
const importDashboard = useCallback(async (file: File): Promise<Dashboard | null> => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const imported = await dashboardApi.import(file);
|
||||
await fetch();
|
||||
return imported;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al importar dashboard');
|
||||
return null;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [fetch]);
|
||||
|
||||
return {
|
||||
data,
|
||||
isLoading,
|
||||
error,
|
||||
refresh: fetch,
|
||||
create,
|
||||
delete: deleteDashboard,
|
||||
duplicate,
|
||||
import: importDashboard,
|
||||
};
|
||||
}
|
||||
|
||||
// Queries
|
||||
export const useDashboards = (
|
||||
filters?: DashboardFilters,
|
||||
options?: Omit<UseQueryOptions<DashboardsResponse>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: dashboardKeys.list(filters || {}),
|
||||
queryFn: () => dashboardApi.getAll(filters),
|
||||
...options,
|
||||
});
|
||||
/**
|
||||
* Hook para obtener un dashboard por ID
|
||||
*/
|
||||
export function useDashboard(id: string) {
|
||||
const [data, setData] = useState<Dashboard | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await dashboardApi.getById(id);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al obtener dashboard');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const update = useCallback(async (updateData: UpdateDashboardDto): Promise<Dashboard | null> => {
|
||||
if (!id) return null;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const updated = await dashboardApi.update(id, updateData);
|
||||
setData(updated);
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al actualizar dashboard');
|
||||
return null;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
const setAsDefault = useCallback(async (): Promise<boolean> => {
|
||||
if (!id) return false;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
await dashboardApi.setAsDefault(id);
|
||||
await fetch();
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al establecer como predeterminado');
|
||||
return false;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id, fetch]);
|
||||
|
||||
const share = useCallback(async (userIds: string[]): Promise<boolean> => {
|
||||
if (!id) return false;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
await dashboardApi.share(id, userIds);
|
||||
await fetch();
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al compartir dashboard');
|
||||
return false;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id, fetch]);
|
||||
|
||||
const unshare = useCallback(async (userIds: string[]): Promise<boolean> => {
|
||||
if (!id) return false;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
await dashboardApi.unshare(id, userIds);
|
||||
await fetch();
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al dejar de compartir dashboard');
|
||||
return false;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id, fetch]);
|
||||
|
||||
const refreshDashboard = useCallback(async (): Promise<Dashboard | null> => {
|
||||
if (!id) return null;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const refreshed = await dashboardApi.refresh(id);
|
||||
setData(refreshed);
|
||||
return refreshed;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al refrescar dashboard');
|
||||
return null;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
const exportDashboard = useCallback(async (format: 'json' | 'pdf' | 'png'): Promise<Blob | null> => {
|
||||
if (!id) return null;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const blob = await dashboardApi.export(id, format);
|
||||
return blob;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al exportar dashboard');
|
||||
return null;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
return {
|
||||
data,
|
||||
isLoading,
|
||||
error,
|
||||
refresh: fetch,
|
||||
update,
|
||||
setAsDefault,
|
||||
share,
|
||||
unshare,
|
||||
refreshDashboard,
|
||||
export: exportDashboard,
|
||||
};
|
||||
}
|
||||
|
||||
export const useDashboard = (
|
||||
id: string,
|
||||
options?: Omit<UseQueryOptions<Dashboard>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: dashboardKeys.detail(id),
|
||||
queryFn: () => dashboardApi.getById(id),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
/**
|
||||
* Hook para obtener el dashboard predeterminado
|
||||
*/
|
||||
export function useDefaultDashboard() {
|
||||
const [data, setData] = useState<Dashboard | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await dashboardApi.getDefault();
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al obtener dashboard predeterminado');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook para obtener widgets de un dashboard
|
||||
*/
|
||||
export function useDashboardWidgets(dashboardId: string) {
|
||||
const [data, setData] = useState<DashboardWidget[] | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!dashboardId) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await dashboardApi.getWidgets(dashboardId);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al obtener widgets');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [dashboardId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const addWidget = useCallback(async (widget: CreateDashboardDto): Promise<DashboardWidget | null> => {
|
||||
if (!dashboardId) return null;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const newWidget = await dashboardApi.addWidget(dashboardId, widget);
|
||||
await fetch();
|
||||
return newWidget;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al agregar widget');
|
||||
return null;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [dashboardId, fetch]);
|
||||
|
||||
const updateWidget = useCallback(async (widgetId: string, config: WidgetConfig): Promise<DashboardWidget | null> => {
|
||||
if (!dashboardId) return null;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const updated = await dashboardApi.updateWidget(dashboardId, widgetId, config);
|
||||
await fetch();
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al actualizar widget');
|
||||
return null;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [dashboardId, fetch]);
|
||||
|
||||
const removeWidget = useCallback(async (widgetId: string): Promise<boolean> => {
|
||||
if (!dashboardId) return false;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
await dashboardApi.removeWidget(dashboardId, widgetId);
|
||||
await fetch();
|
||||
return true;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al eliminar widget');
|
||||
return false;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [dashboardId, fetch]);
|
||||
|
||||
return {
|
||||
data,
|
||||
isLoading,
|
||||
error,
|
||||
refresh: fetch,
|
||||
addWidget,
|
||||
updateWidget,
|
||||
removeWidget,
|
||||
};
|
||||
}
|
||||
|
||||
export const useDefaultDashboard = (
|
||||
options?: Omit<UseQueryOptions<Dashboard>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: [...dashboardKeys.all, 'default'],
|
||||
queryFn: () => dashboardApi.getDefault(),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Hook para obtener metricas de un dashboard
|
||||
*/
|
||||
export function useDashboardMetrics(dashboardId: string, filters?: Record<string, unknown>) {
|
||||
const [data, setData] = useState<DashboardMetrics | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
export const useDashboardWidgets = (
|
||||
dashboardId: string,
|
||||
options?: Omit<UseQueryOptions<DashboardWidget[]>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: dashboardKeys.widgets(dashboardId),
|
||||
queryFn: () => dashboardApi.getWidgets(dashboardId),
|
||||
enabled: !!dashboardId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
const filtersKey = filters ? JSON.stringify(filters) : '';
|
||||
|
||||
export const useDashboardMetrics = (
|
||||
dashboardId: string,
|
||||
filters?: any,
|
||||
options?: Omit<UseQueryOptions<DashboardMetrics>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: [...dashboardKeys.metrics(dashboardId), filters],
|
||||
queryFn: () => dashboardApi.getMetrics(dashboardId, filters),
|
||||
enabled: !!dashboardId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
const fetch = useCallback(async () => {
|
||||
if (!dashboardId) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await dashboardApi.getMetrics(dashboardId, filters);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al obtener metricas');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [dashboardId, filtersKey]);
|
||||
|
||||
// Mutations
|
||||
export const useCreateDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: CreateDashboardDto) => dashboardApi.create(data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: UpdateDashboardDto }) =>
|
||||
dashboardApi.update(id, data),
|
||||
onSuccess: (_, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => dashboardApi.delete(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDuplicateDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, name }: { id: string; name: string }) => dashboardApi.duplicate(id, name),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useSetAsDefaultDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => dashboardApi.setAsDefault(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: [...dashboardKeys.all, 'default'] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useShareDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, userIds }: { id: string; userIds: string[] }) =>
|
||||
dashboardApi.share(id, userIds),
|
||||
onSuccess: (_, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUnshareDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, userIds }: { id: string; userIds: string[] }) =>
|
||||
dashboardApi.unshare(id, userIds),
|
||||
onSuccess: (_, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useAddWidget = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ dashboardId, widget }: { dashboardId: string; widget: CreateDashboardDto }) =>
|
||||
dashboardApi.addWidget(dashboardId, widget),
|
||||
onSuccess: (_, { dashboardId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.widgets(dashboardId) });
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(dashboardId) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateWidget = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ dashboardId, widgetId, config }: { dashboardId: string; widgetId: string; config: WidgetConfig }) =>
|
||||
dashboardApi.updateWidget(dashboardId, widgetId, config),
|
||||
onSuccess: (_, { dashboardId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.widgets(dashboardId) });
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(dashboardId) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useRemoveWidget = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ dashboardId, widgetId }: { dashboardId: string; widgetId: string }) =>
|
||||
dashboardApi.removeWidget(dashboardId, widgetId),
|
||||
onSuccess: (_, { dashboardId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.widgets(dashboardId) });
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(dashboardId) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useRefreshDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => dashboardApi.refresh(id),
|
||||
onSuccess: (_, id) => {
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.metrics(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useExportDashboard = () => {
|
||||
return useMutation({
|
||||
mutationFn: ({ id, format }: { id: string; format: 'json' | 'pdf' | 'png' }) =>
|
||||
dashboardApi.export(id, format),
|
||||
});
|
||||
};
|
||||
|
||||
export const useImportDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (file: File) => dashboardApi.import(file),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useQuery, useMutation, useQueryClient, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { geolocationApi } from '../api/geolocation.api';
|
||||
import type {
|
||||
Geolocation,
|
||||
@ -7,171 +7,317 @@ import type {
|
||||
CreateGeolocationDto,
|
||||
UpdateGeolocationDto,
|
||||
GeolocationQuery,
|
||||
} from '../types/api.types';
|
||||
} from '../../shared/types/api.types';
|
||||
|
||||
// Query Keys
|
||||
export const geolocationKeys = {
|
||||
all: ['geolocation'] as const,
|
||||
lists: () => [...geolocationKeys.all, 'list'] as const,
|
||||
list: (filters: GeolocationFilters) => [...geolocationKeys.lists(), filters] as const,
|
||||
details: () => [...geolocationKeys.all, 'detail'] as const,
|
||||
detail: (id: string) => [...geolocationKeys.details(), id] as const,
|
||||
countries: () => [...geolocationKeys.all, 'countries'] as const,
|
||||
regions: (countryId: string) => [...geolocationKeys.all, 'regions', countryId] as const,
|
||||
cities: (regionId: string) => [...geolocationKeys.all, 'cities', regionId] as const,
|
||||
};
|
||||
// Hook para obtener lista de geolocalizaciones con filtros
|
||||
export function useGeolocations(filters?: GeolocationFilters) {
|
||||
const [data, setData] = useState<GeolocationsResponse | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Queries
|
||||
export const useGeolocations = (
|
||||
filters?: GeolocationFilters,
|
||||
options?: Omit<UseQueryOptions<GeolocationsResponse>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: geolocationKeys.list(filters || {}),
|
||||
queryFn: () => geolocationApi.getAll(filters),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
const fetch = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await geolocationApi.getAll(filters);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al obtener geolocalizaciones');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [
|
||||
filters?.search,
|
||||
filters?.page,
|
||||
filters?.limit,
|
||||
filters?.sortBy,
|
||||
filters?.sortOrder,
|
||||
filters?.countryId,
|
||||
filters?.regionId,
|
||||
filters?.cityId,
|
||||
filters?.postalCode,
|
||||
]);
|
||||
|
||||
export const useGeolocation = (
|
||||
id: string,
|
||||
options?: Omit<UseQueryOptions<Geolocation>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: geolocationKeys.detail(id),
|
||||
queryFn: () => geolocationApi.getById(id),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
export const useCountries = (
|
||||
options?: Omit<UseQueryOptions<Geolocation[]>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: geolocationKeys.countries(),
|
||||
queryFn: () => geolocationApi.getCountries(),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
const create = useCallback(async (createData: CreateGeolocationDto): Promise<Geolocation> => {
|
||||
const created = await geolocationApi.create(createData);
|
||||
await fetch();
|
||||
return created;
|
||||
}, [fetch]);
|
||||
|
||||
export const useRegions = (
|
||||
countryId: string,
|
||||
options?: Omit<UseQueryOptions<Geolocation[]>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: geolocationKeys.regions(countryId),
|
||||
queryFn: () => geolocationApi.getRegions(countryId),
|
||||
enabled: !!countryId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
const update = useCallback(async (id: string, updateData: UpdateGeolocationDto): Promise<Geolocation> => {
|
||||
const updated = await geolocationApi.update(id, updateData);
|
||||
await fetch();
|
||||
return updated;
|
||||
}, [fetch]);
|
||||
|
||||
export const useCities = (
|
||||
regionId: string,
|
||||
options?: Omit<UseQueryOptions<Geolocation[]>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: geolocationKeys.cities(regionId),
|
||||
queryFn: () => geolocationApi.getCities(regionId),
|
||||
enabled: !!regionId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
const remove = useCallback(async (id: string): Promise<void> => {
|
||||
await geolocationApi.delete(id);
|
||||
await fetch();
|
||||
}, [fetch]);
|
||||
|
||||
export const useGeocode = (
|
||||
query: GeolocationQuery,
|
||||
options?: Omit<UseQueryOptions<Geolocation[]>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: [...geolocationKeys.all, 'geocode', query],
|
||||
queryFn: () => geolocationApi.geocode(query),
|
||||
enabled: !!(query.address || query.postalCode || query.city || query.country),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
return { data, isLoading, error, refresh: fetch, create, update, remove };
|
||||
}
|
||||
|
||||
export const useReverseGeocode = (
|
||||
latitude: number,
|
||||
longitude: number,
|
||||
options?: Omit<UseQueryOptions<Geolocation>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: [...geolocationKeys.all, 'reverse', latitude, longitude],
|
||||
queryFn: () => geolocationApi.reverseGeocode(latitude, longitude),
|
||||
enabled: !!(latitude && longitude),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
// Hook para obtener una geolocalización por ID
|
||||
export function useGeolocation(id: string) {
|
||||
const [data, setData] = useState<Geolocation | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
export const useTimezone = (
|
||||
latitude: number,
|
||||
longitude: number,
|
||||
options?: Omit<UseQueryOptions<string>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: [...geolocationKeys.all, 'timezone', latitude, longitude],
|
||||
queryFn: () => geolocationApi.getTimezone(latitude, longitude),
|
||||
enabled: !!(latitude && longitude),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await geolocationApi.getById(id);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al obtener geolocalización');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
export const useDistance = (
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const update = useCallback(async (updateData: UpdateGeolocationDto): Promise<Geolocation> => {
|
||||
const updated = await geolocationApi.update(id, updateData);
|
||||
setData(updated);
|
||||
return updated;
|
||||
}, [id]);
|
||||
|
||||
const remove = useCallback(async (): Promise<void> => {
|
||||
await geolocationApi.delete(id);
|
||||
setData(null);
|
||||
}, [id]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, update, remove };
|
||||
}
|
||||
|
||||
// Hook para obtener países
|
||||
export function useCountries() {
|
||||
const [data, setData] = useState<Geolocation[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await geolocationApi.getCountries();
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al obtener países');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
// Hook para obtener regiones de un país
|
||||
export function useRegions(countryId: string) {
|
||||
const [data, setData] = useState<Geolocation[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!countryId) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await geolocationApi.getRegions(countryId);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al obtener regiones');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [countryId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
// Hook para obtener ciudades de una región
|
||||
export function useCities(regionId: string) {
|
||||
const [data, setData] = useState<Geolocation[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!regionId) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await geolocationApi.getCities(regionId);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al obtener ciudades');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [regionId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
// Hook para geocodificación (dirección a coordenadas)
|
||||
export function useGeocode(query: GeolocationQuery) {
|
||||
const [data, setData] = useState<Geolocation[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const isEnabled = !!(query.address || query.postalCode || query.city || query.country);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!isEnabled) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await geolocationApi.geocode(query);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error en geocodificación');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [query.address, query.postalCode, query.city, query.country, query.limit, isEnabled]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
// Hook para geocodificación inversa (coordenadas a dirección)
|
||||
export function useReverseGeocode(latitude: number, longitude: number) {
|
||||
const [data, setData] = useState<Geolocation | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const isEnabled = !!(latitude && longitude);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!isEnabled) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await geolocationApi.reverseGeocode(latitude, longitude);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error en geocodificación inversa');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [latitude, longitude, isEnabled]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
// Hook para obtener zona horaria
|
||||
export function useTimezone(latitude: number, longitude: number) {
|
||||
const [data, setData] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const isEnabled = !!(latitude && longitude);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!isEnabled) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await geolocationApi.getTimezone(latitude, longitude);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al obtener zona horaria');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [latitude, longitude, isEnabled]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
// Hook para calcular distancia entre dos puntos
|
||||
export function useDistance(
|
||||
fromLat: number,
|
||||
fromLng: number,
|
||||
toLat: number,
|
||||
toLng: number,
|
||||
unit: 'km' | 'miles' = 'km',
|
||||
options?: Omit<UseQueryOptions<number>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: [...geolocationKeys.all, 'distance', fromLat, fromLng, toLat, toLng, unit],
|
||||
queryFn: () => geolocationApi.calculateDistance(fromLat, fromLng, toLat, toLng, unit),
|
||||
enabled: !!(fromLat && fromLng && toLat && toLng),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
unit: 'km' | 'miles' = 'km'
|
||||
) {
|
||||
const [data, setData] = useState<number | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Mutations
|
||||
export const useCreateGeolocation = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const isEnabled = !!(fromLat && fromLng && toLat && toLng);
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: CreateGeolocationDto) => geolocationApi.create(data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: geolocationKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
const fetch = useCallback(async () => {
|
||||
if (!isEnabled) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await geolocationApi.calculateDistance(fromLat, fromLng, toLat, toLng, unit);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error al calcular distancia');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [fromLat, fromLng, toLat, toLng, unit, isEnabled]);
|
||||
|
||||
export const useUpdateGeolocation = () => {
|
||||
const queryClient = useQueryClient();
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: UpdateGeolocationDto }) =>
|
||||
geolocationApi.update(id, data),
|
||||
onSuccess: (_, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: geolocationKeys.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: geolocationKeys.detail(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
export const useDeleteGeolocation = () => {
|
||||
const queryClient = useQueryClient();
|
||||
// Función para crear geolocalización (sin hook)
|
||||
export async function createGeolocation(data: CreateGeolocationDto): Promise<Geolocation> {
|
||||
return geolocationApi.create(data);
|
||||
}
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => geolocationApi.delete(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: geolocationKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
// Función para actualizar geolocalización (sin hook)
|
||||
export async function updateGeolocation(id: string, data: UpdateGeolocationDto): Promise<Geolocation> {
|
||||
return geolocationApi.update(id, data);
|
||||
}
|
||||
|
||||
export const useValidatePostalCode = () => {
|
||||
return useMutation({
|
||||
mutationFn: ({ postalCode, countryId }: { postalCode: string; countryId?: string }) =>
|
||||
geolocationApi.validatePostalCode(postalCode, countryId),
|
||||
});
|
||||
};
|
||||
// Función para eliminar geolocalización (sin hook)
|
||||
export async function deleteGeolocation(id: string): Promise<void> {
|
||||
return geolocationApi.delete(id);
|
||||
}
|
||||
|
||||
// Función para validar código postal (sin hook)
|
||||
export async function validatePostalCode(postalCode: string, countryId?: string): Promise<boolean> {
|
||||
return geolocationApi.validatePostalCode(postalCode, countryId);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useQuery, useMutation, useQueryClient, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { mcpApi } from '../api/mcp.api';
|
||||
import type {
|
||||
McpServer,
|
||||
@ -10,354 +10,574 @@ import type {
|
||||
CreateMcpServerDto,
|
||||
UpdateMcpServerDto,
|
||||
McpServerStatus,
|
||||
McpExecutionResult,
|
||||
McpToolCall,
|
||||
McpResourceAccess,
|
||||
} from '../../shared/types/api.types';
|
||||
|
||||
// Query Keys
|
||||
export const mcpKeys = {
|
||||
all: ['mcp'] as const,
|
||||
lists: () => [...mcpKeys.all, 'list'] as const,
|
||||
list: (filters: McpFilters) => [...mcpKeys.lists(), filters] as const,
|
||||
details: () => [...mcpKeys.all, 'detail'] as const,
|
||||
detail: (id: string) => [...mcpKeys.details(), id] as const,
|
||||
status: (id: string) => [...mcpKeys.detail(id), 'status'] as const,
|
||||
tools: (serverId: string) => [...mcpKeys.detail(serverId), 'tools'] as const,
|
||||
tool: (serverId: string, toolName: string) =>
|
||||
[...mcpKeys.tools(serverId), toolName] as const,
|
||||
resources: (serverId: string) => [...mcpKeys.detail(serverId), 'resources'] as const,
|
||||
resource: (serverId: string, resourceName: string) =>
|
||||
[...mcpKeys.resources(serverId), resourceName] as const,
|
||||
prompts: (serverId: string) => [...mcpKeys.detail(serverId), 'prompts'] as const,
|
||||
prompt: (serverId: string, promptName: string) =>
|
||||
[...mcpKeys.prompts(serverId), promptName] as const,
|
||||
toolHistory: (serverId: string) => [...mcpKeys.detail(serverId), 'toolHistory'] as const,
|
||||
resourceHistory: (serverId: string) => [...mcpKeys.detail(serverId), 'resourceHistory'] as const,
|
||||
logs: (id: string) => [...mcpKeys.detail(id), 'logs'] as const,
|
||||
metrics: (id: string) => [...mcpKeys.detail(id), 'metrics'] as const,
|
||||
};
|
||||
// ============================================================================
|
||||
// Query Hooks - Using useState/useEffect pattern
|
||||
// ============================================================================
|
||||
|
||||
// Queries
|
||||
export const useMcpServers = (
|
||||
filters?: McpFilters,
|
||||
options?: Omit<UseQueryOptions<McpServersResponse>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: mcpKeys.list(filters || {}),
|
||||
queryFn: () => mcpApi.getAll(filters),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
export function useMcpServers(filters?: McpFilters) {
|
||||
const [data, setData] = useState<McpServersResponse | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
export const useMcpServer = (
|
||||
id: string,
|
||||
options?: Omit<UseQueryOptions<McpServer>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: mcpKeys.detail(id),
|
||||
queryFn: () => mcpApi.getById(id),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
const fetch = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await mcpApi.getAll(filters);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching MCP servers');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [
|
||||
filters?.search,
|
||||
filters?.status,
|
||||
filters?.protocol,
|
||||
filters?.isConnected,
|
||||
filters?.page,
|
||||
filters?.limit,
|
||||
filters?.sortBy,
|
||||
filters?.sortOrder,
|
||||
]);
|
||||
|
||||
export const useMcpServerStatus = (
|
||||
id: string,
|
||||
options?: Omit<UseQueryOptions<McpServerStatus>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: mcpKeys.status(id),
|
||||
queryFn: () => mcpApi.getStatus(id),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
export const useMcpServerTools = (
|
||||
serverId: string,
|
||||
options?: Omit<UseQueryOptions<McpTool[]>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: mcpKeys.tools(serverId),
|
||||
queryFn: () => mcpApi.getTools(serverId),
|
||||
enabled: !!serverId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
const createServer = useCallback(async (serverData: CreateMcpServerDto): Promise<McpServer> => {
|
||||
const newServer = await mcpApi.create(serverData);
|
||||
await fetch();
|
||||
return newServer;
|
||||
}, [fetch]);
|
||||
|
||||
export const useMcpServerTool = (
|
||||
serverId: string,
|
||||
toolName: string,
|
||||
options?: Omit<UseQueryOptions<McpTool>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: mcpKeys.tool(serverId, toolName),
|
||||
queryFn: () => mcpApi.getTool(serverId, toolName),
|
||||
enabled: !!(serverId && toolName),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
const updateServer = useCallback(async (id: string, serverData: UpdateMcpServerDto): Promise<McpServer> => {
|
||||
const updatedServer = await mcpApi.update(id, serverData);
|
||||
await fetch();
|
||||
return updatedServer;
|
||||
}, [fetch]);
|
||||
|
||||
export const useMcpServerResources = (
|
||||
serverId: string,
|
||||
options?: Omit<UseQueryOptions<McpResource[]>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: mcpKeys.resources(serverId),
|
||||
queryFn: () => mcpApi.getResources(serverId),
|
||||
enabled: !!serverId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
const deleteServer = useCallback(async (id: string): Promise<void> => {
|
||||
await mcpApi.delete(id);
|
||||
await fetch();
|
||||
}, [fetch]);
|
||||
|
||||
export const useMcpServerResource = (
|
||||
serverId: string,
|
||||
resourceName: string,
|
||||
options?: Omit<UseQueryOptions<McpResource>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: mcpKeys.resource(serverId, resourceName),
|
||||
queryFn: () => mcpApi.getResource(serverId, resourceName),
|
||||
enabled: !!(serverId && resourceName),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
const importConfig = useCallback(async (file: File): Promise<McpServer> => {
|
||||
const importedServer = await mcpApi.importConfig(file);
|
||||
await fetch();
|
||||
return importedServer;
|
||||
}, [fetch]);
|
||||
|
||||
export const useMcpServerPrompts = (
|
||||
serverId: string,
|
||||
options?: Omit<UseQueryOptions<McpPrompt[]>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: mcpKeys.prompts(serverId),
|
||||
queryFn: () => mcpApi.getPrompts(serverId),
|
||||
enabled: !!serverId,
|
||||
...options,
|
||||
});
|
||||
return {
|
||||
data,
|
||||
isLoading,
|
||||
error,
|
||||
refresh: fetch,
|
||||
createServer,
|
||||
updateServer,
|
||||
deleteServer,
|
||||
importConfig,
|
||||
};
|
||||
}
|
||||
|
||||
export const useMcpServerPrompt = (
|
||||
serverId: string,
|
||||
promptName: string,
|
||||
options?: Omit<UseQueryOptions<McpPrompt>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: mcpKeys.prompt(serverId, promptName),
|
||||
queryFn: () => mcpApi.getPrompt(serverId, promptName),
|
||||
enabled: !!(serverId && promptName),
|
||||
...options,
|
||||
});
|
||||
export function useMcpServer(id: string) {
|
||||
const [data, setData] = useState<McpServer | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await mcpApi.getById(id);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching MCP server');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const update = useCallback(async (serverData: UpdateMcpServerDto): Promise<McpServer> => {
|
||||
const updatedServer = await mcpApi.update(id, serverData);
|
||||
setData(updatedServer);
|
||||
return updatedServer;
|
||||
}, [id]);
|
||||
|
||||
const connect = useCallback(async (): Promise<McpServer> => {
|
||||
const connectedServer = await mcpApi.connect(id);
|
||||
setData(connectedServer);
|
||||
return connectedServer;
|
||||
}, [id]);
|
||||
|
||||
const disconnect = useCallback(async (): Promise<McpServer> => {
|
||||
const disconnectedServer = await mcpApi.disconnect(id);
|
||||
setData(disconnectedServer);
|
||||
return disconnectedServer;
|
||||
}, [id]);
|
||||
|
||||
const restart = useCallback(async (): Promise<McpServer> => {
|
||||
const restartedServer = await mcpApi.restart(id);
|
||||
setData(restartedServer);
|
||||
return restartedServer;
|
||||
}, [id]);
|
||||
|
||||
const exportConfig = useCallback(async (): Promise<Blob> => {
|
||||
return await mcpApi.exportConfig(id);
|
||||
}, [id]);
|
||||
|
||||
return {
|
||||
data,
|
||||
isLoading,
|
||||
error,
|
||||
refresh: fetch,
|
||||
update,
|
||||
connect,
|
||||
disconnect,
|
||||
restart,
|
||||
exportConfig,
|
||||
};
|
||||
}
|
||||
|
||||
export const useMcpToolHistory = (
|
||||
serverId: string,
|
||||
filters?: {
|
||||
export function useMcpServerStatus(id: string) {
|
||||
const [data, setData] = useState<McpServerStatus | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await mcpApi.getStatus(id);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching server status');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
export function useMcpServerTools(serverId: string) {
|
||||
const [data, setData] = useState<McpTool[] | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!serverId) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await mcpApi.getTools(serverId);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching server tools');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [serverId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
export function useMcpServerTool(serverId: string, toolName: string) {
|
||||
const [data, setData] = useState<McpTool | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!serverId || !toolName) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await mcpApi.getTool(serverId, toolName);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching tool details');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [serverId, toolName]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const execute = useCallback(async (args: Record<string, any>) => {
|
||||
return await mcpApi.executeTool(serverId, toolName, args);
|
||||
}, [serverId, toolName]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, execute };
|
||||
}
|
||||
|
||||
export function useMcpServerResources(serverId: string) {
|
||||
const [data, setData] = useState<McpResource[] | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!serverId) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await mcpApi.getResources(serverId);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching server resources');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [serverId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
export function useMcpServerResource(serverId: string, resourceName: string) {
|
||||
const [data, setData] = useState<McpResource | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!serverId || !resourceName) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await mcpApi.getResource(serverId, resourceName);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching resource details');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [serverId, resourceName]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const read = useCallback(async (uri?: string) => {
|
||||
return await mcpApi.readResource(serverId, resourceName, uri);
|
||||
}, [serverId, resourceName]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, read };
|
||||
}
|
||||
|
||||
export function useMcpServerPrompts(serverId: string) {
|
||||
const [data, setData] = useState<McpPrompt[] | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!serverId) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await mcpApi.getPrompts(serverId);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching server prompts');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [serverId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
export function useMcpServerPrompt(serverId: string, promptName: string) {
|
||||
const [data, setData] = useState<McpPrompt | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!serverId || !promptName) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await mcpApi.getPrompt(serverId, promptName);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching prompt details');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [serverId, promptName]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const execute = useCallback(async (args?: Record<string, any>) => {
|
||||
return await mcpApi.executePrompt(serverId, promptName, args);
|
||||
}, [serverId, promptName]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, execute };
|
||||
}
|
||||
|
||||
interface ToolHistoryFilters {
|
||||
toolName?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
status?: 'success' | 'error';
|
||||
page?: number;
|
||||
limit?: number;
|
||||
},
|
||||
options?: Omit<UseQueryOptions<{ executions: McpToolCall[]; total: number }>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: [...mcpKeys.toolHistory(serverId), filters],
|
||||
queryFn: () => mcpApi.getToolHistory(serverId, filters),
|
||||
enabled: !!serverId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export const useMcpResourceHistory = (
|
||||
serverId: string,
|
||||
filters?: {
|
||||
export function useMcpToolHistory(serverId: string, filters?: ToolHistoryFilters) {
|
||||
const [data, setData] = useState<{ executions: McpToolCall[]; total: number } | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!serverId) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await mcpApi.getToolHistory(serverId, filters);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching tool history');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [
|
||||
serverId,
|
||||
filters?.toolName,
|
||||
filters?.startDate,
|
||||
filters?.endDate,
|
||||
filters?.status,
|
||||
filters?.page,
|
||||
filters?.limit,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
interface ResourceHistoryFilters {
|
||||
resourceName?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
},
|
||||
options?: Omit<UseQueryOptions<{ accesses: McpResourceAccess[]; total: number }>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: [...mcpKeys.resourceHistory(serverId), filters],
|
||||
queryFn: () => mcpApi.getResourceHistory(serverId, filters),
|
||||
enabled: !!serverId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export const useMcpServerLogs = (
|
||||
id: string,
|
||||
filters?: {
|
||||
export function useMcpResourceHistory(serverId: string, filters?: ResourceHistoryFilters) {
|
||||
const [data, setData] = useState<{ accesses: McpResourceAccess[]; total: number } | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!serverId) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await mcpApi.getResourceHistory(serverId, filters);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching resource history');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [
|
||||
serverId,
|
||||
filters?.resourceName,
|
||||
filters?.startDate,
|
||||
filters?.endDate,
|
||||
filters?.page,
|
||||
filters?.limit,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
interface LogFilters {
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
level?: 'error' | 'warn' | 'info' | 'debug';
|
||||
page?: number;
|
||||
limit?: number;
|
||||
},
|
||||
options?: Omit<UseQueryOptions<{ logs: any[]; total: number }>, 'queryKey' | 'queryFn'>
|
||||
}
|
||||
|
||||
export function useMcpServerLogs(id: string, filters?: LogFilters) {
|
||||
const [data, setData] = useState<{ logs: any[]; total: number } | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await mcpApi.getLogs(id, filters);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching server logs');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [
|
||||
id,
|
||||
filters?.startDate,
|
||||
filters?.endDate,
|
||||
filters?.level,
|
||||
filters?.page,
|
||||
filters?.limit,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
export function useMcpServerMetrics(id: string) {
|
||||
const [data, setData] = useState<{
|
||||
uptime: number;
|
||||
totalRequests: number;
|
||||
successfulRequests: number;
|
||||
failedRequests: number;
|
||||
averageResponseTime: number;
|
||||
memoryUsage: number;
|
||||
cpuUsage: number;
|
||||
} | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await mcpApi.getMetrics(id);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching server metrics');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Action Hooks - Simple async functions for mutations
|
||||
// ============================================================================
|
||||
|
||||
export function useMcpActions() {
|
||||
const testConnection = useCallback(async (serverConfig: CreateMcpServerDto) => {
|
||||
return await mcpApi.testConnection(serverConfig);
|
||||
}, []);
|
||||
|
||||
const createServer = useCallback(async (data: CreateMcpServerDto) => {
|
||||
return await mcpApi.create(data);
|
||||
}, []);
|
||||
|
||||
const updateServer = useCallback(async (id: string, data: UpdateMcpServerDto) => {
|
||||
return await mcpApi.update(id, data);
|
||||
}, []);
|
||||
|
||||
const deleteServer = useCallback(async (id: string) => {
|
||||
return await mcpApi.delete(id);
|
||||
}, []);
|
||||
|
||||
const connectServer = useCallback(async (id: string) => {
|
||||
return await mcpApi.connect(id);
|
||||
}, []);
|
||||
|
||||
const disconnectServer = useCallback(async (id: string) => {
|
||||
return await mcpApi.disconnect(id);
|
||||
}, []);
|
||||
|
||||
const restartServer = useCallback(async (id: string) => {
|
||||
return await mcpApi.restart(id);
|
||||
}, []);
|
||||
|
||||
const executeTool = useCallback(async (
|
||||
serverId: string,
|
||||
toolName: string,
|
||||
args: Record<string, any>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: [...mcpKeys.logs(id), filters],
|
||||
queryFn: () => mcpApi.getLogs(id, filters),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Mutations
|
||||
export const useCreateMcpServer = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: CreateMcpServerDto) => mcpApi.create(data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateMcpServer = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: UpdateMcpServerDto }) =>
|
||||
mcpApi.update(id, data),
|
||||
onSuccess: (_, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.detail(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteMcpServer = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => mcpApi.delete(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useConnectMcpServer = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => mcpApi.connect(id),
|
||||
onSuccess: (_, id) => {
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.status(id) });
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.tools(id) });
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.resources(id) });
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.prompts(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDisconnectMcpServer = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => mcpApi.disconnect(id),
|
||||
onSuccess: (_, id) => {
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.status(id) });
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.tools(id) });
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.resources(id) });
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.prompts(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useExecuteMcpTool = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
serverId,
|
||||
toolName,
|
||||
args
|
||||
}: {
|
||||
serverId: string;
|
||||
toolName: string;
|
||||
args: Record<string, any>;
|
||||
}) => mcpApi.executeTool(serverId, toolName, args),
|
||||
onSuccess: (_, { serverId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.toolHistory(serverId) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useReadMcpResource = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
serverId,
|
||||
resourceName,
|
||||
uri
|
||||
}: {
|
||||
serverId: string;
|
||||
resourceName: string;
|
||||
uri?: string;
|
||||
}) => mcpApi.readResource(serverId, resourceName, uri),
|
||||
onSuccess: (_, { serverId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.resourceHistory(serverId) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useExecuteMcpPrompt = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
serverId,
|
||||
promptName,
|
||||
args
|
||||
}: {
|
||||
serverId: string;
|
||||
promptName: string;
|
||||
args?: Record<string, any>;
|
||||
}) => mcpApi.executePrompt(serverId, promptName, args),
|
||||
});
|
||||
};
|
||||
|
||||
export const useTestMcpConnection = () => {
|
||||
return useMutation({
|
||||
mutationFn: (serverConfig: CreateMcpServerDto) => mcpApi.testConnection(serverConfig),
|
||||
});
|
||||
};
|
||||
|
||||
export const useRestartMcpServer = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => mcpApi.restart(id),
|
||||
onSuccess: (_, id) => {
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.status(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useExportMcpConfig = () => {
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => mcpApi.exportConfig(id),
|
||||
});
|
||||
};
|
||||
|
||||
export const useImportMcpConfig = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (file: File) => mcpApi.importConfig(file),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: mcpKeys.lists() });
|
||||
},
|
||||
});
|
||||
return await mcpApi.executeTool(serverId, toolName, args);
|
||||
}, []);
|
||||
|
||||
const readResource = useCallback(async (
|
||||
serverId: string,
|
||||
resourceName: string,
|
||||
uri?: string
|
||||
) => {
|
||||
return await mcpApi.readResource(serverId, resourceName, uri);
|
||||
}, []);
|
||||
|
||||
const executePrompt = useCallback(async (
|
||||
serverId: string,
|
||||
promptName: string,
|
||||
args?: Record<string, any>
|
||||
) => {
|
||||
return await mcpApi.executePrompt(serverId, promptName, args);
|
||||
}, []);
|
||||
|
||||
const exportConfig = useCallback(async (id: string) => {
|
||||
return await mcpApi.exportConfig(id);
|
||||
}, []);
|
||||
|
||||
const importConfig = useCallback(async (file: File) => {
|
||||
return await mcpApi.importConfig(file);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
testConnection,
|
||||
createServer,
|
||||
updateServer,
|
||||
deleteServer,
|
||||
connectServer,
|
||||
disconnectServer,
|
||||
restartServer,
|
||||
executeTool,
|
||||
readResource,
|
||||
executePrompt,
|
||||
exportConfig,
|
||||
importConfig,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useQuery, useMutation, useQueryClient, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { paymentTerminalsApi } from '../api/payment-terminals.api';
|
||||
import type {
|
||||
PaymentTerminal,
|
||||
@ -12,320 +12,420 @@ import type {
|
||||
PaymentMethod,
|
||||
} from '../../shared/types/api.types';
|
||||
|
||||
// Query Keys
|
||||
export const paymentTerminalsKeys = {
|
||||
all: ['payment-terminals'] as const,
|
||||
lists: () => [...paymentTerminalsKeys.all, 'list'] as const,
|
||||
list: (filters: PaymentTerminalFilters) => [...paymentTerminalsKeys.lists(), filters] as const,
|
||||
details: () => [...paymentTerminalsKeys.all, 'detail'] as const,
|
||||
detail: (id: string) => [...paymentTerminalsKeys.details(), id] as const,
|
||||
transactions: (terminalId: string) => [...paymentTerminalsKeys.detail(terminalId), 'transactions'] as const,
|
||||
transaction: (terminalId: string, transactionId: string) =>
|
||||
[...paymentTerminalsKeys.transactions(terminalId), transactionId] as const,
|
||||
status: (id: string) => [...paymentTerminalsKeys.detail(id), 'status'] as const,
|
||||
config: (id: string) => [...paymentTerminalsKeys.detail(id), 'config'] as const,
|
||||
logs: (id: string) => [...paymentTerminalsKeys.detail(id), 'logs'] as const,
|
||||
metrics: (id: string) => [...paymentTerminalsKeys.detail(id), 'metrics'] as const,
|
||||
};
|
||||
// Types for hook returns
|
||||
interface UseQueryResult<T> {
|
||||
data: T | null;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
refresh: () => Promise<void>;
|
||||
}
|
||||
|
||||
// Queries
|
||||
export const usePaymentTerminals = (
|
||||
filters?: PaymentTerminalFilters,
|
||||
options?: Omit<UseQueryOptions<PaymentTerminalsResponse>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: paymentTerminalsKeys.list(filters || {}),
|
||||
queryFn: () => paymentTerminalsApi.getAll(filters),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
interface TransactionsResponse {
|
||||
transactions: PaymentTerminalTransaction[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
export const usePaymentTerminal = (
|
||||
id: string,
|
||||
options?: Omit<UseQueryOptions<PaymentTerminal>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: paymentTerminalsKeys.detail(id),
|
||||
queryFn: () => paymentTerminalsApi.getById(id),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
interface LogsResponse {
|
||||
logs: any[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
export const usePaymentTerminalTransactions = (
|
||||
terminalId: string,
|
||||
filters?: {
|
||||
interface HealthCheckResponse {
|
||||
healthy: boolean;
|
||||
issues: string[];
|
||||
}
|
||||
|
||||
interface TransactionFilters {
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
status?: TransactionStatus;
|
||||
paymentMethod?: PaymentMethod;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
},
|
||||
options?: Omit<UseQueryOptions<{ transactions: PaymentTerminalTransaction[]; total: number }>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: [...paymentTerminalsKeys.transactions(terminalId), filters],
|
||||
queryFn: () => paymentTerminalsApi.getTransactions(terminalId, filters),
|
||||
enabled: !!terminalId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export const usePaymentTerminalTransaction = (
|
||||
terminalId: string,
|
||||
transactionId: string,
|
||||
options?: Omit<UseQueryOptions<PaymentTerminalTransaction>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: paymentTerminalsKeys.transaction(terminalId, transactionId),
|
||||
queryFn: () => paymentTerminalsApi.getTransaction(terminalId, transactionId),
|
||||
enabled: !!(terminalId && transactionId),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const usePaymentTerminalStatus = (
|
||||
id: string,
|
||||
options?: Omit<UseQueryOptions<TerminalStatus>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: paymentTerminalsKeys.status(id),
|
||||
queryFn: () => paymentTerminalsApi.getStatus(id),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const usePaymentTerminalConfig = (
|
||||
id: string,
|
||||
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: paymentTerminalsKeys.config(id),
|
||||
queryFn: () => paymentTerminalsApi.getConfig(id),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const usePaymentTerminalHealthCheck = (
|
||||
id: string,
|
||||
options?: Omit<UseQueryOptions<{ healthy: boolean; issues: string[] }>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: [...paymentTerminalsKeys.detail(id), 'health'],
|
||||
queryFn: () => paymentTerminalsApi.healthCheck(id),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const usePaymentTerminalLogs = (
|
||||
id: string,
|
||||
filters?: {
|
||||
interface LogsFilters {
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
level?: 'error' | 'warn' | 'info' | 'debug';
|
||||
page?: number;
|
||||
limit?: number;
|
||||
},
|
||||
options?: Omit<UseQueryOptions<{ logs: any[]; total: number }>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: [...paymentTerminalsKeys.logs(id), filters],
|
||||
queryFn: () => paymentTerminalsApi.getLogs(id, filters),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export const usePaymentTerminalMetrics = (
|
||||
id: string,
|
||||
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: paymentTerminalsKeys.metrics(id),
|
||||
queryFn: () => paymentTerminalsApi.getMetrics(id),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Mutations
|
||||
export const useCreatePaymentTerminal = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: CreatePaymentTerminalDto) => paymentTerminalsApi.create(data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdatePaymentTerminal = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: UpdatePaymentTerminalDto }) =>
|
||||
paymentTerminalsApi.update(id, data),
|
||||
onSuccess: (_, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.detail(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeletePaymentTerminal = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => paymentTerminalsApi.delete(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useActivatePaymentTerminal = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => paymentTerminalsApi.activate(id),
|
||||
onSuccess: (_, id) => {
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeactivatePaymentTerminal = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => paymentTerminalsApi.deactivate(id),
|
||||
onSuccess: (_, id) => {
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useProcessPayment = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
terminalId,
|
||||
paymentData
|
||||
}: {
|
||||
terminalId: string;
|
||||
paymentData: {
|
||||
interface PaymentData {
|
||||
amount: number;
|
||||
currency: string;
|
||||
paymentMethod: PaymentMethod;
|
||||
reference?: string;
|
||||
metadata?: Record<string, any>;
|
||||
};
|
||||
}) => paymentTerminalsApi.processPayment(terminalId, paymentData),
|
||||
onSuccess: (_, { terminalId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.transactions(terminalId) });
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export const useRefundTransaction = () => {
|
||||
const queryClient = useQueryClient();
|
||||
// Hook: usePaymentTerminals
|
||||
export function usePaymentTerminals(
|
||||
filters?: PaymentTerminalFilters
|
||||
): UseQueryResult<PaymentTerminalsResponse> & {
|
||||
create: (data: CreatePaymentTerminalDto) => Promise<PaymentTerminal>;
|
||||
remove: (id: string) => Promise<void>;
|
||||
} {
|
||||
const [data, setData] = useState<PaymentTerminalsResponse | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
const fetch = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await paymentTerminalsApi.getAll(filters);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching payment terminals');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [
|
||||
filters?.search,
|
||||
filters?.status,
|
||||
filters?.storeId,
|
||||
filters?.page,
|
||||
filters?.limit,
|
||||
filters?.sortBy,
|
||||
filters?.sortOrder,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const create = useCallback(async (createData: CreatePaymentTerminalDto): Promise<PaymentTerminal> => {
|
||||
const newTerminal = await paymentTerminalsApi.create(createData);
|
||||
await fetch();
|
||||
return newTerminal;
|
||||
}, [fetch]);
|
||||
|
||||
const remove = useCallback(async (id: string): Promise<void> => {
|
||||
await paymentTerminalsApi.delete(id);
|
||||
await fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, create, remove };
|
||||
}
|
||||
|
||||
// Hook: usePaymentTerminal
|
||||
export function usePaymentTerminal(
|
||||
id: string
|
||||
): UseQueryResult<PaymentTerminal> & {
|
||||
update: (data: UpdatePaymentTerminalDto) => Promise<PaymentTerminal>;
|
||||
activate: () => Promise<void>;
|
||||
deactivate: () => Promise<void>;
|
||||
sync: () => Promise<void>;
|
||||
reboot: () => Promise<void>;
|
||||
} {
|
||||
const [data, setData] = useState<PaymentTerminal | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await paymentTerminalsApi.getById(id);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching payment terminal');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const update = useCallback(async (updateData: UpdatePaymentTerminalDto): Promise<PaymentTerminal> => {
|
||||
const updated = await paymentTerminalsApi.update(id, updateData);
|
||||
setData(updated);
|
||||
return updated;
|
||||
}, [id]);
|
||||
|
||||
const activate = useCallback(async (): Promise<void> => {
|
||||
await paymentTerminalsApi.activate(id);
|
||||
await fetch();
|
||||
}, [id, fetch]);
|
||||
|
||||
const deactivate = useCallback(async (): Promise<void> => {
|
||||
await paymentTerminalsApi.deactivate(id);
|
||||
await fetch();
|
||||
}, [id, fetch]);
|
||||
|
||||
const sync = useCallback(async (): Promise<void> => {
|
||||
await paymentTerminalsApi.sync(id);
|
||||
await fetch();
|
||||
}, [id, fetch]);
|
||||
|
||||
const reboot = useCallback(async (): Promise<void> => {
|
||||
await paymentTerminalsApi.reboot(id);
|
||||
await fetch();
|
||||
}, [id, fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, update, activate, deactivate, sync, reboot };
|
||||
}
|
||||
|
||||
// Hook: usePaymentTerminalTransactions
|
||||
export function usePaymentTerminalTransactions(
|
||||
terminalId: string,
|
||||
filters?: TransactionFilters
|
||||
): UseQueryResult<TransactionsResponse> & {
|
||||
processPayment: (paymentData: PaymentData) => Promise<PaymentTerminalTransaction>;
|
||||
refund: (transactionId: string, amount?: number, reason?: string) => Promise<void>;
|
||||
voidTransaction: (transactionId: string, reason?: string) => Promise<void>;
|
||||
exportTransactions: (format: 'json' | 'csv' | 'pdf', exportFilters?: { startDate?: string; endDate?: string; status?: TransactionStatus }) => Promise<Blob>;
|
||||
} {
|
||||
const [data, setData] = useState<TransactionsResponse | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!terminalId) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await paymentTerminalsApi.getTransactions(terminalId, filters);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching transactions');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [
|
||||
terminalId,
|
||||
transactionId,
|
||||
amount,
|
||||
reason
|
||||
}: {
|
||||
terminalId: string;
|
||||
transactionId: string;
|
||||
amount?: number;
|
||||
reason?: string;
|
||||
}) => paymentTerminalsApi.refundTransaction(terminalId, transactionId, amount, reason),
|
||||
onSuccess: (_, { terminalId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.transactions(terminalId) });
|
||||
},
|
||||
filters?.startDate,
|
||||
filters?.endDate,
|
||||
filters?.status,
|
||||
filters?.paymentMethod,
|
||||
filters?.page,
|
||||
filters?.limit,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const processPayment = useCallback(async (paymentData: PaymentData): Promise<PaymentTerminalTransaction> => {
|
||||
const transaction = await paymentTerminalsApi.processPayment(terminalId, paymentData);
|
||||
await fetch();
|
||||
return transaction;
|
||||
}, [terminalId, fetch]);
|
||||
|
||||
const refund = useCallback(async (transactionId: string, amount?: number, reason?: string): Promise<void> => {
|
||||
await paymentTerminalsApi.refundTransaction(terminalId, transactionId, amount, reason);
|
||||
await fetch();
|
||||
}, [terminalId, fetch]);
|
||||
|
||||
const voidTransaction = useCallback(async (transactionId: string, reason?: string): Promise<void> => {
|
||||
await paymentTerminalsApi.voidTransaction(terminalId, transactionId, reason);
|
||||
await fetch();
|
||||
}, [terminalId, fetch]);
|
||||
|
||||
const exportTransactions = useCallback(async (
|
||||
format: 'json' | 'csv' | 'pdf',
|
||||
exportFilters?: { startDate?: string; endDate?: string; status?: TransactionStatus }
|
||||
): Promise<Blob> => {
|
||||
return paymentTerminalsApi.exportTransactions(terminalId, format, exportFilters);
|
||||
}, [terminalId]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, processPayment, refund, voidTransaction, exportTransactions };
|
||||
}
|
||||
|
||||
// Hook: usePaymentTerminalTransaction
|
||||
export function usePaymentTerminalTransaction(
|
||||
terminalId: string,
|
||||
transactionId: string
|
||||
): UseQueryResult<PaymentTerminalTransaction> {
|
||||
const [data, setData] = useState<PaymentTerminalTransaction | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!terminalId || !transactionId) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await paymentTerminalsApi.getTransaction(terminalId, transactionId);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching transaction');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [terminalId, transactionId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
// Hook: usePaymentTerminalStatus
|
||||
export function usePaymentTerminalStatus(id: string): UseQueryResult<TerminalStatus> {
|
||||
const [data, setData] = useState<TerminalStatus | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await paymentTerminalsApi.getStatus(id);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching terminal status');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
// Hook: usePaymentTerminalConfig
|
||||
export function usePaymentTerminalConfig(
|
||||
id: string
|
||||
): UseQueryResult<any> & {
|
||||
updateConfig: (config: any) => Promise<void>;
|
||||
} {
|
||||
const [data, setData] = useState<any | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await paymentTerminalsApi.getConfig(id);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching terminal config');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const updateConfig = useCallback(async (config: any): Promise<void> => {
|
||||
await paymentTerminalsApi.updateConfig(id, config);
|
||||
await fetch();
|
||||
}, [id, fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, updateConfig };
|
||||
}
|
||||
|
||||
// Hook: usePaymentTerminalHealthCheck
|
||||
export function usePaymentTerminalHealthCheck(id: string): UseQueryResult<HealthCheckResponse> {
|
||||
const [data, setData] = useState<HealthCheckResponse | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await paymentTerminalsApi.healthCheck(id);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error checking terminal health');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
// Hook: usePaymentTerminalLogs
|
||||
export function usePaymentTerminalLogs(
|
||||
id: string,
|
||||
filters?: LogsFilters
|
||||
): UseQueryResult<LogsResponse> {
|
||||
const [data, setData] = useState<LogsResponse | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await paymentTerminalsApi.getLogs(id, filters);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching terminal logs');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [
|
||||
id,
|
||||
filters?.startDate,
|
||||
filters?.endDate,
|
||||
filters?.level,
|
||||
filters?.page,
|
||||
filters?.limit,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
// Hook: usePaymentTerminalMetrics
|
||||
// Uses status and health check to build metrics data
|
||||
export function usePaymentTerminalMetrics(id: string): UseQueryResult<any> {
|
||||
const [data, setData] = useState<any | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
// Fetch status and health check to build metrics
|
||||
const [status, health] = await Promise.all([
|
||||
paymentTerminalsApi.getStatus(id),
|
||||
paymentTerminalsApi.healthCheck(id),
|
||||
]);
|
||||
setData({
|
||||
status,
|
||||
health,
|
||||
lastUpdated: new Date().toISOString(),
|
||||
});
|
||||
};
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching terminal metrics');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
export const useVoidTransaction = () => {
|
||||
const queryClient = useQueryClient();
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
terminalId,
|
||||
transactionId,
|
||||
reason
|
||||
}: {
|
||||
terminalId: string;
|
||||
transactionId: string;
|
||||
reason?: string;
|
||||
}) => paymentTerminalsApi.voidTransaction(terminalId, transactionId, reason),
|
||||
onSuccess: (_, { terminalId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.transactions(terminalId) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useSyncPaymentTerminal = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => paymentTerminalsApi.sync(id),
|
||||
onSuccess: (_, id) => {
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.status(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdatePaymentTerminalConfig = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, config }: { id: string; config: any }) =>
|
||||
paymentTerminalsApi.updateConfig(id, config),
|
||||
onSuccess: (_, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.config(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useRebootPaymentTerminal = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => paymentTerminalsApi.reboot(id),
|
||||
onSuccess: (_, id) => {
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.status(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useExportTransactions = () => {
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
terminalId,
|
||||
format,
|
||||
filters
|
||||
}: {
|
||||
terminalId: string;
|
||||
format: 'json' | 'csv' | 'pdf';
|
||||
filters?: {
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
status?: TransactionStatus;
|
||||
};
|
||||
}) => paymentTerminalsApi.exportTransactions(terminalId, format, filters),
|
||||
});
|
||||
};
|
||||
return { data, isLoading, error, refresh: fetch };
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useQuery, useMutation, useQueryClient, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { scanningApi } from '../api/scanning.api';
|
||||
import type {
|
||||
ScanningSession,
|
||||
@ -10,302 +10,381 @@ import type {
|
||||
UpdateScanningSessionDto,
|
||||
ScanningConfig,
|
||||
ScanningTemplate,
|
||||
DeviceStatus,
|
||||
ScanningStatus,
|
||||
} from '../../shared/types/api.types';
|
||||
|
||||
// Query Keys
|
||||
export const scanningKeys = {
|
||||
all: ['scanning'] as const,
|
||||
lists: () => [...scanningKeys.all, 'list'] as const,
|
||||
list: (filters: ScanningFilters) => [...scanningKeys.lists(), filters] as const,
|
||||
details: () => [...scanningKeys.all, 'detail'] as const,
|
||||
detail: (id: string) => [...scanningKeys.details(), id] as const,
|
||||
devices: () => [...scanningKeys.all, 'devices'] as const,
|
||||
device: (id: string) => [...scanningKeys.devices(), id] as const,
|
||||
templates: () => [...scanningKeys.all, 'templates'] as const,
|
||||
template: (id: string) => [...scanningKeys.templates(), id] as const,
|
||||
results: (sessionId: string) => [...scanningKeys.detail(sessionId), 'results'] as const,
|
||||
config: () => [...scanningKeys.all, 'config'] as const,
|
||||
};
|
||||
// ============================================================================
|
||||
// Sessions Hooks
|
||||
// ============================================================================
|
||||
|
||||
// Queries
|
||||
export const useScanningSessions = (
|
||||
filters?: ScanningFilters,
|
||||
options?: Omit<UseQueryOptions<ScanningSessionsResponse>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: scanningKeys.list(filters || {}),
|
||||
queryFn: () => scanningApi.getAll(filters),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
export function useScanningSessions(filters?: ScanningFilters) {
|
||||
const [data, setData] = useState<ScanningSessionsResponse | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
export const useScanningSession = (
|
||||
id: string,
|
||||
options?: Omit<UseQueryOptions<ScanningSession>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: scanningKeys.detail(id),
|
||||
queryFn: () => scanningApi.getById(id),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useScanningDevices = (
|
||||
options?: Omit<UseQueryOptions<ScanningDevice[]>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: scanningKeys.devices(),
|
||||
queryFn: () => scanningApi.getDevices(),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useScanningDevice = (
|
||||
id: string,
|
||||
options?: Omit<UseQueryOptions<ScanningDevice>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: scanningKeys.device(id),
|
||||
queryFn: () => scanningApi.getDevice(id),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useScanningTemplates = (
|
||||
options?: Omit<UseQueryOptions<ScanningTemplate[]>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: scanningKeys.templates(),
|
||||
queryFn: () => scanningApi.getTemplates(),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useScanningTemplate = (
|
||||
id: string,
|
||||
options?: Omit<UseQueryOptions<ScanningTemplate>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: scanningKeys.template(id),
|
||||
queryFn: () => scanningApi.getTemplate(id),
|
||||
enabled: !!id,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useScanningResults = (
|
||||
sessionId: string,
|
||||
options?: Omit<UseQueryOptions<ScanningResult[]>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: scanningKeys.results(sessionId),
|
||||
queryFn: () => scanningApi.getResults(sessionId),
|
||||
enabled: !!sessionId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useScanningConfig = (
|
||||
options?: Omit<UseQueryOptions<ScanningConfig>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: scanningKeys.config(),
|
||||
queryFn: () => scanningApi.getConfig(),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Mutations
|
||||
export const useCreateScanningSession = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: CreateScanningSessionDto) => scanningApi.create(data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateScanningSession = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: UpdateScanningSessionDto }) =>
|
||||
scanningApi.update(id, data),
|
||||
onSuccess: (_, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.detail(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteScanningSession = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => scanningApi.delete(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useStartScanningSession = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => scanningApi.start(id),
|
||||
onSuccess: (_, id) => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.lists() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const usePauseScanningSession = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => scanningApi.pause(id),
|
||||
onSuccess: (_, id) => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.detail(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useResumeScanningSession = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => scanningApi.resume(id),
|
||||
onSuccess: (_, id) => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.detail(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useStopScanningSession = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => scanningApi.stop(id),
|
||||
onSuccess: (_, id) => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.detail(id) });
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.results(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useRegisterScanningDevice = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (device: Partial<ScanningDevice>) => scanningApi.registerDevice(device),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.devices() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateScanningDevice = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, device }: { id: string; device: Partial<ScanningDevice> }) =>
|
||||
scanningApi.updateDevice(id, device),
|
||||
onSuccess: (_, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.devices() });
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.device(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useRemoveScanningDevice = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => scanningApi.removeDevice(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.devices() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateScanningTemplate = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (template: Partial<ScanningTemplate>) => scanningApi.createTemplate(template),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.templates() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateScanningTemplate = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, template }: { id: string; template: Partial<ScanningTemplate> }) =>
|
||||
scanningApi.updateTemplate(id, template),
|
||||
onSuccess: (_, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.templates() });
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.template(id) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteScanningTemplate = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => scanningApi.deleteTemplate(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.templates() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateScanningConfig = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (config: Partial<ScanningConfig>) => scanningApi.updateConfig(config),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.config() });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useExportScanningResults = () => {
|
||||
return useMutation({
|
||||
mutationFn: ({ sessionId, format }: { sessionId: string; format: 'json' | 'csv' | 'pdf' }) =>
|
||||
scanningApi.exportResults(sessionId, format),
|
||||
});
|
||||
};
|
||||
|
||||
export const useImportScanningData = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ file, sessionId }: { file: File; sessionId?: string }) =>
|
||||
scanningApi.importData(file, sessionId),
|
||||
onSuccess: (_, { sessionId }) => {
|
||||
if (sessionId) {
|
||||
queryClient.invalidateQueries({ queryKey: scanningKeys.results(sessionId) });
|
||||
const fetch = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await scanningApi.getAll(filters);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching sessions');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
}, [
|
||||
filters?.search,
|
||||
filters?.page,
|
||||
filters?.limit,
|
||||
filters?.status,
|
||||
filters?.deviceId,
|
||||
filters?.startDate,
|
||||
filters?.endDate,
|
||||
]);
|
||||
|
||||
export const useValidateScanningData = () => {
|
||||
return useMutation({
|
||||
mutationFn: ({ data, templateId }: { data: any; templateId: string }) =>
|
||||
scanningApi.validateData(data, templateId),
|
||||
});
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const create = useCallback(async (dto: CreateScanningSessionDto): Promise<ScanningSession> => {
|
||||
const session = await scanningApi.create(dto);
|
||||
await fetch();
|
||||
return session;
|
||||
}, [fetch]);
|
||||
|
||||
const update = useCallback(async (id: string, dto: UpdateScanningSessionDto): Promise<ScanningSession> => {
|
||||
const session = await scanningApi.update(id, dto);
|
||||
await fetch();
|
||||
return session;
|
||||
}, [fetch]);
|
||||
|
||||
const remove = useCallback(async (id: string): Promise<void> => {
|
||||
await scanningApi.delete(id);
|
||||
await fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, create, update, remove };
|
||||
}
|
||||
|
||||
export function useScanningSession(id: string) {
|
||||
const [data, setData] = useState<ScanningSession | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await scanningApi.getById(id);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching session');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const update = useCallback(async (dto: UpdateScanningSessionDto): Promise<ScanningSession> => {
|
||||
const session = await scanningApi.update(id, dto);
|
||||
setData(session);
|
||||
return session;
|
||||
}, [id]);
|
||||
|
||||
const start = useCallback(async (): Promise<ScanningSession> => {
|
||||
const session = await scanningApi.start(id);
|
||||
setData(session);
|
||||
return session;
|
||||
}, [id]);
|
||||
|
||||
const pause = useCallback(async (): Promise<ScanningSession> => {
|
||||
const session = await scanningApi.pause(id);
|
||||
setData(session);
|
||||
return session;
|
||||
}, [id]);
|
||||
|
||||
const resume = useCallback(async (): Promise<ScanningSession> => {
|
||||
const session = await scanningApi.resume(id);
|
||||
setData(session);
|
||||
return session;
|
||||
}, [id]);
|
||||
|
||||
const stop = useCallback(async (): Promise<ScanningSession> => {
|
||||
const session = await scanningApi.stop(id);
|
||||
setData(session);
|
||||
return session;
|
||||
}, [id]);
|
||||
|
||||
return {
|
||||
data,
|
||||
isLoading,
|
||||
error,
|
||||
refresh: fetch,
|
||||
update,
|
||||
start,
|
||||
pause,
|
||||
resume,
|
||||
stop,
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Devices Hooks
|
||||
// ============================================================================
|
||||
|
||||
export function useScanningDevices() {
|
||||
const [data, setData] = useState<ScanningDevice[] | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await scanningApi.getDevices();
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching devices');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const register = useCallback(async (device: Partial<ScanningDevice>): Promise<ScanningDevice> => {
|
||||
const registered = await scanningApi.registerDevice(device);
|
||||
await fetch();
|
||||
return registered;
|
||||
}, [fetch]);
|
||||
|
||||
const update = useCallback(async (id: string, device: Partial<ScanningDevice>): Promise<ScanningDevice> => {
|
||||
const updated = await scanningApi.updateDevice(id, device);
|
||||
await fetch();
|
||||
return updated;
|
||||
}, [fetch]);
|
||||
|
||||
const remove = useCallback(async (id: string): Promise<void> => {
|
||||
await scanningApi.removeDevice(id);
|
||||
await fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, register, update, remove };
|
||||
}
|
||||
|
||||
export function useScanningDevice(id: string) {
|
||||
const [data, setData] = useState<ScanningDevice | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await scanningApi.getDevice(id);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching device');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const update = useCallback(async (device: Partial<ScanningDevice>): Promise<ScanningDevice> => {
|
||||
const updated = await scanningApi.updateDevice(id, device);
|
||||
setData(updated);
|
||||
return updated;
|
||||
}, [id]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, update };
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Templates Hooks
|
||||
// ============================================================================
|
||||
|
||||
export function useScanningTemplates() {
|
||||
const [data, setData] = useState<ScanningTemplate[] | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await scanningApi.getTemplates();
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching templates');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const create = useCallback(async (template: Partial<ScanningTemplate>): Promise<ScanningTemplate> => {
|
||||
const created = await scanningApi.createTemplate(template);
|
||||
await fetch();
|
||||
return created;
|
||||
}, [fetch]);
|
||||
|
||||
const update = useCallback(async (id: string, template: Partial<ScanningTemplate>): Promise<ScanningTemplate> => {
|
||||
const updated = await scanningApi.updateTemplate(id, template);
|
||||
await fetch();
|
||||
return updated;
|
||||
}, [fetch]);
|
||||
|
||||
const remove = useCallback(async (id: string): Promise<void> => {
|
||||
await scanningApi.deleteTemplate(id);
|
||||
await fetch();
|
||||
}, [fetch]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, create, update, remove };
|
||||
}
|
||||
|
||||
export function useScanningTemplate(id: string) {
|
||||
const [data, setData] = useState<ScanningTemplate | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!id) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await scanningApi.getTemplate(id);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching template');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const update = useCallback(async (template: Partial<ScanningTemplate>): Promise<ScanningTemplate> => {
|
||||
const updated = await scanningApi.updateTemplate(id, template);
|
||||
setData(updated);
|
||||
return updated;
|
||||
}, [id]);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, update };
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Results Hooks
|
||||
// ============================================================================
|
||||
|
||||
export function useScanningResults(sessionId: string) {
|
||||
const [data, setData] = useState<ScanningResult[] | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
if (!sessionId) return;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await scanningApi.getResults(sessionId);
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching results');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [sessionId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const exportResults = useCallback(async (format: 'json' | 'csv' | 'pdf'): Promise<Blob> => {
|
||||
return scanningApi.exportResults(sessionId, format);
|
||||
}, [sessionId]);
|
||||
|
||||
const importData = useCallback(async (file: File): Promise<void> => {
|
||||
await scanningApi.importData(file, sessionId);
|
||||
await fetch();
|
||||
}, [sessionId, fetch]);
|
||||
|
||||
const validateData = useCallback(async (data: unknown, templateId: string): Promise<unknown> => {
|
||||
return scanningApi.validateData(data, templateId);
|
||||
}, []);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, exportResults, importData, validateData };
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Config Hook
|
||||
// ============================================================================
|
||||
|
||||
export function useScanningConfig() {
|
||||
const [data, setData] = useState<ScanningConfig | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetch = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await scanningApi.getConfig();
|
||||
setData(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Error fetching config');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, [fetch]);
|
||||
|
||||
const update = useCallback(async (config: Partial<ScanningConfig>): Promise<ScanningConfig> => {
|
||||
const updated = await scanningApi.updateConfig(config);
|
||||
setData(updated);
|
||||
return updated;
|
||||
}, []);
|
||||
|
||||
return { data, isLoading, error, refresh: fetch, update };
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Utility Functions (Non-hook async functions)
|
||||
// ============================================================================
|
||||
|
||||
export async function createScanningSession(dto: CreateScanningSessionDto): Promise<ScanningSession> {
|
||||
return scanningApi.create(dto);
|
||||
}
|
||||
|
||||
export async function exportScanningResults(
|
||||
sessionId: string,
|
||||
format: 'json' | 'csv' | 'pdf'
|
||||
): Promise<Blob> {
|
||||
return scanningApi.exportResults(sessionId, format);
|
||||
}
|
||||
|
||||
export async function importScanningData(file: File, sessionId?: string): Promise<void> {
|
||||
await scanningApi.importData(file, sessionId);
|
||||
}
|
||||
|
||||
export async function validateScanningData(data: unknown, templateId: string): Promise<unknown> {
|
||||
return scanningApi.validateData(data, templateId);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user