[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 { dashboardApi } from '../api/dashboard.api';
|
||||||
import type {
|
import type {
|
||||||
Dashboard,
|
Dashboard,
|
||||||
@ -11,224 +11,382 @@ import type {
|
|||||||
DashboardMetrics,
|
DashboardMetrics,
|
||||||
} from '../../shared/types/api.types';
|
} from '../../shared/types/api.types';
|
||||||
|
|
||||||
// Query Keys
|
// ============================================================================
|
||||||
export const dashboardKeys = {
|
// Query Hooks (useState/useEffect pattern)
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Queries
|
/**
|
||||||
export const useDashboards = (
|
* Hook para obtener lista de dashboards con filtros
|
||||||
filters?: DashboardFilters,
|
*/
|
||||||
options?: Omit<UseQueryOptions<DashboardsResponse>, 'queryKey' | 'queryFn'>
|
export function useDashboards(filters?: DashboardFilters) {
|
||||||
) => {
|
const [data, setData] = useState<DashboardsResponse | null>(null);
|
||||||
return useQuery({
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
queryKey: dashboardKeys.list(filters || {}),
|
const [error, setError] = useState<string | null>(null);
|
||||||
queryFn: () => dashboardApi.getAll(filters),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useDashboard = (
|
const fetch = useCallback(async () => {
|
||||||
id: string,
|
setIsLoading(true);
|
||||||
options?: Omit<UseQueryOptions<Dashboard>, 'queryKey' | 'queryFn'>
|
setError(null);
|
||||||
) => {
|
try {
|
||||||
return useQuery({
|
const response = await dashboardApi.getAll(filters);
|
||||||
queryKey: dashboardKeys.detail(id),
|
setData(response);
|
||||||
queryFn: () => dashboardApi.getById(id),
|
} catch (err) {
|
||||||
enabled: !!id,
|
setError(err instanceof Error ? err.message : 'Error al obtener dashboards');
|
||||||
...options,
|
} finally {
|
||||||
});
|
setIsLoading(false);
|
||||||
};
|
}
|
||||||
|
}, [filters?.search, filters?.page, filters?.limit, filters?.sortBy, filters?.sortOrder]);
|
||||||
|
|
||||||
export const useDefaultDashboard = (
|
useEffect(() => {
|
||||||
options?: Omit<UseQueryOptions<Dashboard>, 'queryKey' | 'queryFn'>
|
fetch();
|
||||||
) => {
|
}, [fetch]);
|
||||||
return useQuery({
|
|
||||||
queryKey: [...dashboardKeys.all, 'default'],
|
|
||||||
queryFn: () => dashboardApi.getDefault(),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useDashboardWidgets = (
|
const create = useCallback(async (createData: CreateDashboardDto): Promise<Dashboard | null> => {
|
||||||
dashboardId: string,
|
setIsLoading(true);
|
||||||
options?: Omit<UseQueryOptions<DashboardWidget[]>, 'queryKey' | 'queryFn'>
|
setError(null);
|
||||||
) => {
|
try {
|
||||||
return useQuery({
|
const newDashboard = await dashboardApi.create(createData);
|
||||||
queryKey: dashboardKeys.widgets(dashboardId),
|
await fetch();
|
||||||
queryFn: () => dashboardApi.getWidgets(dashboardId),
|
return newDashboard;
|
||||||
enabled: !!dashboardId,
|
} catch (err) {
|
||||||
...options,
|
setError(err instanceof Error ? err.message : 'Error al crear dashboard');
|
||||||
});
|
return null;
|
||||||
};
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [fetch]);
|
||||||
|
|
||||||
export const useDashboardMetrics = (
|
const deleteDashboard = useCallback(async (id: string): Promise<boolean> => {
|
||||||
dashboardId: string,
|
setIsLoading(true);
|
||||||
filters?: any,
|
setError(null);
|
||||||
options?: Omit<UseQueryOptions<DashboardMetrics>, 'queryKey' | 'queryFn'>
|
try {
|
||||||
) => {
|
await dashboardApi.delete(id);
|
||||||
return useQuery({
|
await fetch();
|
||||||
queryKey: [...dashboardKeys.metrics(dashboardId), filters],
|
return true;
|
||||||
queryFn: () => dashboardApi.getMetrics(dashboardId, filters),
|
} catch (err) {
|
||||||
enabled: !!dashboardId,
|
setError(err instanceof Error ? err.message : 'Error al eliminar dashboard');
|
||||||
...options,
|
return false;
|
||||||
});
|
} finally {
|
||||||
};
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [fetch]);
|
||||||
|
|
||||||
// Mutations
|
const duplicate = useCallback(async (id: string, name: string): Promise<Dashboard | null> => {
|
||||||
export const useCreateDashboard = () => {
|
setIsLoading(true);
|
||||||
const queryClient = useQueryClient();
|
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]);
|
||||||
|
|
||||||
return useMutation({
|
const importDashboard = useCallback(async (file: File): Promise<Dashboard | null> => {
|
||||||
mutationFn: (data: CreateDashboardDto) => dashboardApi.create(data),
|
setIsLoading(true);
|
||||||
onSuccess: () => {
|
setError(null);
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() });
|
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]);
|
||||||
|
|
||||||
export const useUpdateDashboard = () => {
|
return {
|
||||||
const queryClient = useQueryClient();
|
data,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
refresh: fetch,
|
||||||
|
create,
|
||||||
|
delete: deleteDashboard,
|
||||||
|
duplicate,
|
||||||
|
import: importDashboard,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return useMutation({
|
/**
|
||||||
mutationFn: ({ id, data }: { id: string; data: UpdateDashboardDto }) =>
|
* Hook para obtener un dashboard por ID
|
||||||
dashboardApi.update(id, data),
|
*/
|
||||||
onSuccess: (_, { id }) => {
|
export function useDashboard(id: string) {
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() });
|
const [data, setData] = useState<Dashboard | null>(null);
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(id) });
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
},
|
const [error, setError] = useState<string | null>(null);
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useDeleteDashboard = () => {
|
const fetch = useCallback(async () => {
|
||||||
const queryClient = useQueryClient();
|
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]);
|
||||||
|
|
||||||
return useMutation({
|
useEffect(() => {
|
||||||
mutationFn: (id: string) => dashboardApi.delete(id),
|
fetch();
|
||||||
onSuccess: () => {
|
}, [fetch]);
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useDuplicateDashboard = () => {
|
const update = useCallback(async (updateData: UpdateDashboardDto): Promise<Dashboard | null> => {
|
||||||
const queryClient = useQueryClient();
|
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]);
|
||||||
|
|
||||||
return useMutation({
|
const setAsDefault = useCallback(async (): Promise<boolean> => {
|
||||||
mutationFn: ({ id, name }: { id: string; name: string }) => dashboardApi.duplicate(id, name),
|
if (!id) return false;
|
||||||
onSuccess: () => {
|
setIsLoading(true);
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() });
|
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]);
|
||||||
|
|
||||||
export const useSetAsDefaultDashboard = () => {
|
const share = useCallback(async (userIds: string[]): Promise<boolean> => {
|
||||||
const queryClient = useQueryClient();
|
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]);
|
||||||
|
|
||||||
return useMutation({
|
const unshare = useCallback(async (userIds: string[]): Promise<boolean> => {
|
||||||
mutationFn: (id: string) => dashboardApi.setAsDefault(id),
|
if (!id) return false;
|
||||||
onSuccess: () => {
|
setIsLoading(true);
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() });
|
setError(null);
|
||||||
queryClient.invalidateQueries({ queryKey: [...dashboardKeys.all, 'default'] });
|
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]);
|
||||||
|
|
||||||
export const useShareDashboard = () => {
|
const refreshDashboard = useCallback(async (): Promise<Dashboard | null> => {
|
||||||
const queryClient = useQueryClient();
|
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]);
|
||||||
|
|
||||||
return useMutation({
|
const exportDashboard = useCallback(async (format: 'json' | 'pdf' | 'png'): Promise<Blob | null> => {
|
||||||
mutationFn: ({ id, userIds }: { id: string; userIds: string[] }) =>
|
if (!id) return null;
|
||||||
dashboardApi.share(id, userIds),
|
setIsLoading(true);
|
||||||
onSuccess: (_, { id }) => {
|
setError(null);
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(id) });
|
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]);
|
||||||
|
|
||||||
export const useUnshareDashboard = () => {
|
return {
|
||||||
const queryClient = useQueryClient();
|
data,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
refresh: fetch,
|
||||||
|
update,
|
||||||
|
setAsDefault,
|
||||||
|
share,
|
||||||
|
unshare,
|
||||||
|
refreshDashboard,
|
||||||
|
export: exportDashboard,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return useMutation({
|
/**
|
||||||
mutationFn: ({ id, userIds }: { id: string; userIds: string[] }) =>
|
* Hook para obtener el dashboard predeterminado
|
||||||
dashboardApi.unshare(id, userIds),
|
*/
|
||||||
onSuccess: (_, { id }) => {
|
export function useDefaultDashboard() {
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(id) });
|
const [data, setData] = useState<Dashboard | null>(null);
|
||||||
},
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
});
|
const [error, setError] = useState<string | null>(null);
|
||||||
};
|
|
||||||
|
|
||||||
export const useAddWidget = () => {
|
const fetch = useCallback(async () => {
|
||||||
const queryClient = useQueryClient();
|
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);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return useMutation({
|
useEffect(() => {
|
||||||
mutationFn: ({ dashboardId, widget }: { dashboardId: string; widget: CreateDashboardDto }) =>
|
fetch();
|
||||||
dashboardApi.addWidget(dashboardId, widget),
|
}, [fetch]);
|
||||||
onSuccess: (_, { dashboardId }) => {
|
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.widgets(dashboardId) });
|
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(dashboardId) });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useUpdateWidget = () => {
|
return { data, isLoading, error, refresh: fetch };
|
||||||
const queryClient = useQueryClient();
|
}
|
||||||
|
|
||||||
return useMutation({
|
/**
|
||||||
mutationFn: ({ dashboardId, widgetId, config }: { dashboardId: string; widgetId: string; config: WidgetConfig }) =>
|
* Hook para obtener widgets de un dashboard
|
||||||
dashboardApi.updateWidget(dashboardId, widgetId, config),
|
*/
|
||||||
onSuccess: (_, { dashboardId }) => {
|
export function useDashboardWidgets(dashboardId: string) {
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.widgets(dashboardId) });
|
const [data, setData] = useState<DashboardWidget[] | null>(null);
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(dashboardId) });
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
},
|
const [error, setError] = useState<string | null>(null);
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useRemoveWidget = () => {
|
const fetch = useCallback(async () => {
|
||||||
const queryClient = useQueryClient();
|
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]);
|
||||||
|
|
||||||
return useMutation({
|
useEffect(() => {
|
||||||
mutationFn: ({ dashboardId, widgetId }: { dashboardId: string; widgetId: string }) =>
|
fetch();
|
||||||
dashboardApi.removeWidget(dashboardId, widgetId),
|
}, [fetch]);
|
||||||
onSuccess: (_, { dashboardId }) => {
|
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.widgets(dashboardId) });
|
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(dashboardId) });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useRefreshDashboard = () => {
|
const addWidget = useCallback(async (widget: CreateDashboardDto): Promise<DashboardWidget | null> => {
|
||||||
const queryClient = useQueryClient();
|
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]);
|
||||||
|
|
||||||
return useMutation({
|
const updateWidget = useCallback(async (widgetId: string, config: WidgetConfig): Promise<DashboardWidget | null> => {
|
||||||
mutationFn: (id: string) => dashboardApi.refresh(id),
|
if (!dashboardId) return null;
|
||||||
onSuccess: (_, id) => {
|
setIsLoading(true);
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(id) });
|
setError(null);
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.metrics(id) });
|
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]);
|
||||||
|
|
||||||
export const useExportDashboard = () => {
|
const removeWidget = useCallback(async (widgetId: string): Promise<boolean> => {
|
||||||
return useMutation({
|
if (!dashboardId) return false;
|
||||||
mutationFn: ({ id, format }: { id: string; format: 'json' | 'pdf' | 'png' }) =>
|
setIsLoading(true);
|
||||||
dashboardApi.export(id, format),
|
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]);
|
||||||
|
|
||||||
export const useImportDashboard = () => {
|
return {
|
||||||
const queryClient = useQueryClient();
|
data,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
refresh: fetch,
|
||||||
|
addWidget,
|
||||||
|
updateWidget,
|
||||||
|
removeWidget,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return useMutation({
|
/**
|
||||||
mutationFn: (file: File) => dashboardApi.import(file),
|
* Hook para obtener metricas de un dashboard
|
||||||
onSuccess: () => {
|
*/
|
||||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() });
|
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);
|
||||||
|
|
||||||
|
const filtersKey = filters ? JSON.stringify(filters) : '';
|
||||||
|
|
||||||
|
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]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch();
|
||||||
|
}, [fetch]);
|
||||||
|
|
||||||
|
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 { geolocationApi } from '../api/geolocation.api';
|
||||||
import type {
|
import type {
|
||||||
Geolocation,
|
Geolocation,
|
||||||
@ -7,171 +7,317 @@ import type {
|
|||||||
CreateGeolocationDto,
|
CreateGeolocationDto,
|
||||||
UpdateGeolocationDto,
|
UpdateGeolocationDto,
|
||||||
GeolocationQuery,
|
GeolocationQuery,
|
||||||
} from '../types/api.types';
|
} from '../../shared/types/api.types';
|
||||||
|
|
||||||
// Query Keys
|
// Hook para obtener lista de geolocalizaciones con filtros
|
||||||
export const geolocationKeys = {
|
export function useGeolocations(filters?: GeolocationFilters) {
|
||||||
all: ['geolocation'] as const,
|
const [data, setData] = useState<GeolocationsResponse | null>(null);
|
||||||
lists: () => [...geolocationKeys.all, 'list'] as const,
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
list: (filters: GeolocationFilters) => [...geolocationKeys.lists(), filters] as const,
|
const [error, setError] = useState<string | null>(null);
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Queries
|
const fetch = useCallback(async () => {
|
||||||
export const useGeolocations = (
|
setIsLoading(true);
|
||||||
filters?: GeolocationFilters,
|
setError(null);
|
||||||
options?: Omit<UseQueryOptions<GeolocationsResponse>, 'queryKey' | 'queryFn'>
|
try {
|
||||||
) => {
|
const response = await geolocationApi.getAll(filters);
|
||||||
return useQuery({
|
setData(response);
|
||||||
queryKey: geolocationKeys.list(filters || {}),
|
} catch (err) {
|
||||||
queryFn: () => geolocationApi.getAll(filters),
|
setError(err instanceof Error ? err.message : 'Error al obtener geolocalizaciones');
|
||||||
...options,
|
} finally {
|
||||||
});
|
setIsLoading(false);
|
||||||
};
|
}
|
||||||
|
}, [
|
||||||
|
filters?.search,
|
||||||
|
filters?.page,
|
||||||
|
filters?.limit,
|
||||||
|
filters?.sortBy,
|
||||||
|
filters?.sortOrder,
|
||||||
|
filters?.countryId,
|
||||||
|
filters?.regionId,
|
||||||
|
filters?.cityId,
|
||||||
|
filters?.postalCode,
|
||||||
|
]);
|
||||||
|
|
||||||
export const useGeolocation = (
|
useEffect(() => {
|
||||||
id: string,
|
fetch();
|
||||||
options?: Omit<UseQueryOptions<Geolocation>, 'queryKey' | 'queryFn'>
|
}, [fetch]);
|
||||||
) => {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: geolocationKeys.detail(id),
|
|
||||||
queryFn: () => geolocationApi.getById(id),
|
|
||||||
enabled: !!id,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useCountries = (
|
const create = useCallback(async (createData: CreateGeolocationDto): Promise<Geolocation> => {
|
||||||
options?: Omit<UseQueryOptions<Geolocation[]>, 'queryKey' | 'queryFn'>
|
const created = await geolocationApi.create(createData);
|
||||||
) => {
|
await fetch();
|
||||||
return useQuery({
|
return created;
|
||||||
queryKey: geolocationKeys.countries(),
|
}, [fetch]);
|
||||||
queryFn: () => geolocationApi.getCountries(),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useRegions = (
|
const update = useCallback(async (id: string, updateData: UpdateGeolocationDto): Promise<Geolocation> => {
|
||||||
countryId: string,
|
const updated = await geolocationApi.update(id, updateData);
|
||||||
options?: Omit<UseQueryOptions<Geolocation[]>, 'queryKey' | 'queryFn'>
|
await fetch();
|
||||||
) => {
|
return updated;
|
||||||
return useQuery({
|
}, [fetch]);
|
||||||
queryKey: geolocationKeys.regions(countryId),
|
|
||||||
queryFn: () => geolocationApi.getRegions(countryId),
|
|
||||||
enabled: !!countryId,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useCities = (
|
const remove = useCallback(async (id: string): Promise<void> => {
|
||||||
regionId: string,
|
await geolocationApi.delete(id);
|
||||||
options?: Omit<UseQueryOptions<Geolocation[]>, 'queryKey' | 'queryFn'>
|
await fetch();
|
||||||
) => {
|
}, [fetch]);
|
||||||
return useQuery({
|
|
||||||
queryKey: geolocationKeys.cities(regionId),
|
|
||||||
queryFn: () => geolocationApi.getCities(regionId),
|
|
||||||
enabled: !!regionId,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useGeocode = (
|
return { data, isLoading, error, refresh: fetch, create, update, remove };
|
||||||
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,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useReverseGeocode = (
|
// Hook para obtener una geolocalización por ID
|
||||||
latitude: number,
|
export function useGeolocation(id: string) {
|
||||||
longitude: number,
|
const [data, setData] = useState<Geolocation | null>(null);
|
||||||
options?: Omit<UseQueryOptions<Geolocation>, 'queryKey' | 'queryFn'>
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
) => {
|
const [error, setError] = useState<string | null>(null);
|
||||||
return useQuery({
|
|
||||||
queryKey: [...geolocationKeys.all, 'reverse', latitude, longitude],
|
|
||||||
queryFn: () => geolocationApi.reverseGeocode(latitude, longitude),
|
|
||||||
enabled: !!(latitude && longitude),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useTimezone = (
|
const fetch = useCallback(async () => {
|
||||||
latitude: number,
|
if (!id) return;
|
||||||
longitude: number,
|
setIsLoading(true);
|
||||||
options?: Omit<UseQueryOptions<string>, 'queryKey' | 'queryFn'>
|
setError(null);
|
||||||
) => {
|
try {
|
||||||
return useQuery({
|
const response = await geolocationApi.getById(id);
|
||||||
queryKey: [...geolocationKeys.all, 'timezone', latitude, longitude],
|
setData(response);
|
||||||
queryFn: () => geolocationApi.getTimezone(latitude, longitude),
|
} catch (err) {
|
||||||
enabled: !!(latitude && longitude),
|
setError(err instanceof Error ? err.message : 'Error al obtener geolocalización');
|
||||||
...options,
|
} 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,
|
fromLat: number,
|
||||||
fromLng: number,
|
fromLng: number,
|
||||||
toLat: number,
|
toLat: number,
|
||||||
toLng: number,
|
toLng: number,
|
||||||
unit: 'km' | 'miles' = 'km',
|
unit: 'km' | 'miles' = 'km'
|
||||||
options?: Omit<UseQueryOptions<number>, 'queryKey' | 'queryFn'>
|
) {
|
||||||
) => {
|
const [data, setData] = useState<number | null>(null);
|
||||||
return useQuery({
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
queryKey: [...geolocationKeys.all, 'distance', fromLat, fromLng, toLat, toLng, unit],
|
const [error, setError] = useState<string | null>(null);
|
||||||
queryFn: () => geolocationApi.calculateDistance(fromLat, fromLng, toLat, toLng, unit),
|
|
||||||
enabled: !!(fromLat && fromLng && toLat && toLng),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mutations
|
const isEnabled = !!(fromLat && fromLng && toLat && toLng);
|
||||||
export const useCreateGeolocation = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
return useMutation({
|
const fetch = useCallback(async () => {
|
||||||
mutationFn: (data: CreateGeolocationDto) => geolocationApi.create(data),
|
if (!isEnabled) return;
|
||||||
onSuccess: () => {
|
setIsLoading(true);
|
||||||
queryClient.invalidateQueries({ queryKey: geolocationKeys.lists() });
|
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 = () => {
|
useEffect(() => {
|
||||||
const queryClient = useQueryClient();
|
fetch();
|
||||||
|
}, [fetch]);
|
||||||
|
|
||||||
return useMutation({
|
return { data, isLoading, error, refresh: fetch };
|
||||||
mutationFn: ({ id, data }: { id: string; data: UpdateGeolocationDto }) =>
|
}
|
||||||
geolocationApi.update(id, data),
|
|
||||||
onSuccess: (_, { id }) => {
|
|
||||||
queryClient.invalidateQueries({ queryKey: geolocationKeys.lists() });
|
|
||||||
queryClient.invalidateQueries({ queryKey: geolocationKeys.detail(id) });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useDeleteGeolocation = () => {
|
// Función para crear geolocalización (sin hook)
|
||||||
const queryClient = useQueryClient();
|
export async function createGeolocation(data: CreateGeolocationDto): Promise<Geolocation> {
|
||||||
|
return geolocationApi.create(data);
|
||||||
|
}
|
||||||
|
|
||||||
return useMutation({
|
// Función para actualizar geolocalización (sin hook)
|
||||||
mutationFn: (id: string) => geolocationApi.delete(id),
|
export async function updateGeolocation(id: string, data: UpdateGeolocationDto): Promise<Geolocation> {
|
||||||
onSuccess: () => {
|
return geolocationApi.update(id, data);
|
||||||
queryClient.invalidateQueries({ queryKey: geolocationKeys.lists() });
|
}
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useValidatePostalCode = () => {
|
// Función para eliminar geolocalización (sin hook)
|
||||||
return useMutation({
|
export async function deleteGeolocation(id: string): Promise<void> {
|
||||||
mutationFn: ({ postalCode, countryId }: { postalCode: string; countryId?: string }) =>
|
return geolocationApi.delete(id);
|
||||||
geolocationApi.validatePostalCode(postalCode, countryId),
|
}
|
||||||
});
|
|
||||||
};
|
// 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 { mcpApi } from '../api/mcp.api';
|
||||||
import type {
|
import type {
|
||||||
McpServer,
|
McpServer,
|
||||||
@ -10,354 +10,574 @@ import type {
|
|||||||
CreateMcpServerDto,
|
CreateMcpServerDto,
|
||||||
UpdateMcpServerDto,
|
UpdateMcpServerDto,
|
||||||
McpServerStatus,
|
McpServerStatus,
|
||||||
McpExecutionResult,
|
|
||||||
McpToolCall,
|
McpToolCall,
|
||||||
McpResourceAccess,
|
McpResourceAccess,
|
||||||
} from '../../shared/types/api.types';
|
} from '../../shared/types/api.types';
|
||||||
|
|
||||||
// Query Keys
|
// ============================================================================
|
||||||
export const mcpKeys = {
|
// Query Hooks - Using useState/useEffect pattern
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Queries
|
export function useMcpServers(filters?: McpFilters) {
|
||||||
export const useMcpServers = (
|
const [data, setData] = useState<McpServersResponse | null>(null);
|
||||||
filters?: McpFilters,
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
options?: Omit<UseQueryOptions<McpServersResponse>, 'queryKey' | 'queryFn'>
|
const [error, setError] = useState<string | null>(null);
|
||||||
) => {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: mcpKeys.list(filters || {}),
|
|
||||||
queryFn: () => mcpApi.getAll(filters),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useMcpServer = (
|
const fetch = useCallback(async () => {
|
||||||
id: string,
|
setIsLoading(true);
|
||||||
options?: Omit<UseQueryOptions<McpServer>, 'queryKey' | 'queryFn'>
|
setError(null);
|
||||||
) => {
|
try {
|
||||||
return useQuery({
|
const response = await mcpApi.getAll(filters);
|
||||||
queryKey: mcpKeys.detail(id),
|
setData(response);
|
||||||
queryFn: () => mcpApi.getById(id),
|
} catch (err) {
|
||||||
enabled: !!id,
|
setError(err instanceof Error ? err.message : 'Error fetching MCP servers');
|
||||||
...options,
|
} finally {
|
||||||
});
|
setIsLoading(false);
|
||||||
};
|
}
|
||||||
|
}, [
|
||||||
|
filters?.search,
|
||||||
|
filters?.status,
|
||||||
|
filters?.protocol,
|
||||||
|
filters?.isConnected,
|
||||||
|
filters?.page,
|
||||||
|
filters?.limit,
|
||||||
|
filters?.sortBy,
|
||||||
|
filters?.sortOrder,
|
||||||
|
]);
|
||||||
|
|
||||||
export const useMcpServerStatus = (
|
useEffect(() => {
|
||||||
id: string,
|
fetch();
|
||||||
options?: Omit<UseQueryOptions<McpServerStatus>, 'queryKey' | 'queryFn'>
|
}, [fetch]);
|
||||||
) => {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: mcpKeys.status(id),
|
|
||||||
queryFn: () => mcpApi.getStatus(id),
|
|
||||||
enabled: !!id,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useMcpServerTools = (
|
const createServer = useCallback(async (serverData: CreateMcpServerDto): Promise<McpServer> => {
|
||||||
serverId: string,
|
const newServer = await mcpApi.create(serverData);
|
||||||
options?: Omit<UseQueryOptions<McpTool[]>, 'queryKey' | 'queryFn'>
|
await fetch();
|
||||||
) => {
|
return newServer;
|
||||||
return useQuery({
|
}, [fetch]);
|
||||||
queryKey: mcpKeys.tools(serverId),
|
|
||||||
queryFn: () => mcpApi.getTools(serverId),
|
|
||||||
enabled: !!serverId,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useMcpServerTool = (
|
const updateServer = useCallback(async (id: string, serverData: UpdateMcpServerDto): Promise<McpServer> => {
|
||||||
serverId: string,
|
const updatedServer = await mcpApi.update(id, serverData);
|
||||||
toolName: string,
|
await fetch();
|
||||||
options?: Omit<UseQueryOptions<McpTool>, 'queryKey' | 'queryFn'>
|
return updatedServer;
|
||||||
) => {
|
}, [fetch]);
|
||||||
return useQuery({
|
|
||||||
queryKey: mcpKeys.tool(serverId, toolName),
|
|
||||||
queryFn: () => mcpApi.getTool(serverId, toolName),
|
|
||||||
enabled: !!(serverId && toolName),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useMcpServerResources = (
|
const deleteServer = useCallback(async (id: string): Promise<void> => {
|
||||||
serverId: string,
|
await mcpApi.delete(id);
|
||||||
options?: Omit<UseQueryOptions<McpResource[]>, 'queryKey' | 'queryFn'>
|
await fetch();
|
||||||
) => {
|
}, [fetch]);
|
||||||
return useQuery({
|
|
||||||
queryKey: mcpKeys.resources(serverId),
|
|
||||||
queryFn: () => mcpApi.getResources(serverId),
|
|
||||||
enabled: !!serverId,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useMcpServerResource = (
|
const importConfig = useCallback(async (file: File): Promise<McpServer> => {
|
||||||
serverId: string,
|
const importedServer = await mcpApi.importConfig(file);
|
||||||
resourceName: string,
|
await fetch();
|
||||||
options?: Omit<UseQueryOptions<McpResource>, 'queryKey' | 'queryFn'>
|
return importedServer;
|
||||||
) => {
|
}, [fetch]);
|
||||||
return useQuery({
|
|
||||||
queryKey: mcpKeys.resource(serverId, resourceName),
|
|
||||||
queryFn: () => mcpApi.getResource(serverId, resourceName),
|
|
||||||
enabled: !!(serverId && resourceName),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useMcpServerPrompts = (
|
return {
|
||||||
serverId: string,
|
data,
|
||||||
options?: Omit<UseQueryOptions<McpPrompt[]>, 'queryKey' | 'queryFn'>
|
isLoading,
|
||||||
) => {
|
error,
|
||||||
return useQuery({
|
refresh: fetch,
|
||||||
queryKey: mcpKeys.prompts(serverId),
|
createServer,
|
||||||
queryFn: () => mcpApi.getPrompts(serverId),
|
updateServer,
|
||||||
enabled: !!serverId,
|
deleteServer,
|
||||||
...options,
|
importConfig,
|
||||||
});
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export const useMcpServerPrompt = (
|
export function useMcpServer(id: string) {
|
||||||
serverId: string,
|
const [data, setData] = useState<McpServer | null>(null);
|
||||||
promptName: string,
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
options?: Omit<UseQueryOptions<McpPrompt>, 'queryKey' | 'queryFn'>
|
const [error, setError] = useState<string | null>(null);
|
||||||
) => {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: mcpKeys.prompt(serverId, promptName),
|
|
||||||
queryFn: () => mcpApi.getPrompt(serverId, promptName),
|
|
||||||
enabled: !!(serverId && promptName),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useMcpToolHistory = (
|
const fetch = useCallback(async () => {
|
||||||
serverId: string,
|
if (!id) return;
|
||||||
filters?: {
|
setIsLoading(true);
|
||||||
toolName?: string;
|
setError(null);
|
||||||
startDate?: string;
|
try {
|
||||||
endDate?: string;
|
const response = await mcpApi.getById(id);
|
||||||
status?: 'success' | 'error';
|
setData(response);
|
||||||
page?: number;
|
} catch (err) {
|
||||||
limit?: number;
|
setError(err instanceof Error ? err.message : 'Error fetching MCP server');
|
||||||
},
|
} finally {
|
||||||
options?: Omit<UseQueryOptions<{ executions: McpToolCall[]; total: number }>, 'queryKey' | 'queryFn'>
|
setIsLoading(false);
|
||||||
) => {
|
}
|
||||||
return useQuery({
|
}, [id]);
|
||||||
queryKey: [...mcpKeys.toolHistory(serverId), filters],
|
|
||||||
queryFn: () => mcpApi.getToolHistory(serverId, filters),
|
|
||||||
enabled: !!serverId,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useMcpResourceHistory = (
|
useEffect(() => {
|
||||||
serverId: string,
|
fetch();
|
||||||
filters?: {
|
}, [fetch]);
|
||||||
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 = (
|
const update = useCallback(async (serverData: UpdateMcpServerDto): Promise<McpServer> => {
|
||||||
id: string,
|
const updatedServer = await mcpApi.update(id, serverData);
|
||||||
filters?: {
|
setData(updatedServer);
|
||||||
startDate?: string;
|
return updatedServer;
|
||||||
endDate?: string;
|
}, [id]);
|
||||||
level?: 'error' | 'warn' | 'info' | 'debug';
|
|
||||||
page?: number;
|
|
||||||
limit?: number;
|
|
||||||
},
|
|
||||||
options?: Omit<UseQueryOptions<{ logs: any[]; total: number }>, 'queryKey' | 'queryFn'>
|
|
||||||
) => {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: [...mcpKeys.logs(id), filters],
|
|
||||||
queryFn: () => mcpApi.getLogs(id, filters),
|
|
||||||
enabled: !!id,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mutations
|
const connect = useCallback(async (): Promise<McpServer> => {
|
||||||
export const useCreateMcpServer = () => {
|
const connectedServer = await mcpApi.connect(id);
|
||||||
const queryClient = useQueryClient();
|
setData(connectedServer);
|
||||||
|
return connectedServer;
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
return useMutation({
|
const disconnect = useCallback(async (): Promise<McpServer> => {
|
||||||
mutationFn: (data: CreateMcpServerDto) => mcpApi.create(data),
|
const disconnectedServer = await mcpApi.disconnect(id);
|
||||||
onSuccess: () => {
|
setData(disconnectedServer);
|
||||||
queryClient.invalidateQueries({ queryKey: mcpKeys.lists() });
|
return disconnectedServer;
|
||||||
},
|
}, [id]);
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useUpdateMcpServer = () => {
|
const restart = useCallback(async (): Promise<McpServer> => {
|
||||||
const queryClient = useQueryClient();
|
const restartedServer = await mcpApi.restart(id);
|
||||||
|
setData(restartedServer);
|
||||||
|
return restartedServer;
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
return useMutation({
|
const exportConfig = useCallback(async (): Promise<Blob> => {
|
||||||
mutationFn: ({ id, data }: { id: string; data: UpdateMcpServerDto }) =>
|
return await mcpApi.exportConfig(id);
|
||||||
mcpApi.update(id, data),
|
}, [id]);
|
||||||
onSuccess: (_, { id }) => {
|
|
||||||
queryClient.invalidateQueries({ queryKey: mcpKeys.lists() });
|
|
||||||
queryClient.invalidateQueries({ queryKey: mcpKeys.detail(id) });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useDeleteMcpServer = () => {
|
return {
|
||||||
const queryClient = useQueryClient();
|
data,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
refresh: fetch,
|
||||||
|
update,
|
||||||
|
connect,
|
||||||
|
disconnect,
|
||||||
|
restart,
|
||||||
|
exportConfig,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return useMutation({
|
export function useMcpServerStatus(id: string) {
|
||||||
mutationFn: (id: string) => mcpApi.delete(id),
|
const [data, setData] = useState<McpServerStatus | null>(null);
|
||||||
onSuccess: () => {
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
queryClient.invalidateQueries({ queryKey: mcpKeys.lists() });
|
const [error, setError] = useState<string | null>(null);
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useConnectMcpServer = () => {
|
const fetch = useCallback(async () => {
|
||||||
const queryClient = useQueryClient();
|
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]);
|
||||||
|
|
||||||
return useMutation({
|
useEffect(() => {
|
||||||
mutationFn: (id: string) => mcpApi.connect(id),
|
fetch();
|
||||||
onSuccess: (_, id) => {
|
}, [fetch]);
|
||||||
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 = () => {
|
return { data, isLoading, error, refresh: fetch };
|
||||||
const queryClient = useQueryClient();
|
}
|
||||||
|
|
||||||
return useMutation({
|
export function useMcpServerTools(serverId: string) {
|
||||||
mutationFn: (id: string) => mcpApi.disconnect(id),
|
const [data, setData] = useState<McpTool[] | null>(null);
|
||||||
onSuccess: (_, id) => {
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
queryClient.invalidateQueries({ queryKey: mcpKeys.detail(id) });
|
const [error, setError] = useState<string | null>(null);
|
||||||
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 fetch = useCallback(async () => {
|
||||||
const queryClient = useQueryClient();
|
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]);
|
||||||
|
|
||||||
return useMutation({
|
useEffect(() => {
|
||||||
mutationFn: ({
|
fetch();
|
||||||
serverId,
|
}, [fetch]);
|
||||||
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 = () => {
|
return { data, isLoading, error, refresh: fetch };
|
||||||
const queryClient = useQueryClient();
|
}
|
||||||
|
|
||||||
return useMutation({
|
export function useMcpServerTool(serverId: string, toolName: string) {
|
||||||
mutationFn: ({
|
const [data, setData] = useState<McpTool | null>(null);
|
||||||
serverId,
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
resourceName,
|
const [error, setError] = useState<string | null>(null);
|
||||||
uri
|
|
||||||
}: {
|
|
||||||
serverId: string;
|
|
||||||
resourceName: string;
|
|
||||||
uri?: string;
|
|
||||||
}) => mcpApi.readResource(serverId, resourceName, uri),
|
|
||||||
onSuccess: (_, { serverId }) => {
|
|
||||||
queryClient.invalidateQueries({ queryKey: mcpKeys.resourceHistory(serverId) });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useExecuteMcpPrompt = () => {
|
const fetch = useCallback(async () => {
|
||||||
const queryClient = useQueryClient();
|
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]);
|
||||||
|
|
||||||
return useMutation({
|
useEffect(() => {
|
||||||
mutationFn: ({
|
fetch();
|
||||||
serverId,
|
}, [fetch]);
|
||||||
promptName,
|
|
||||||
args
|
|
||||||
}: {
|
|
||||||
serverId: string;
|
|
||||||
promptName: string;
|
|
||||||
args?: Record<string, any>;
|
|
||||||
}) => mcpApi.executePrompt(serverId, promptName, args),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useTestMcpConnection = () => {
|
const execute = useCallback(async (args: Record<string, any>) => {
|
||||||
return useMutation({
|
return await mcpApi.executeTool(serverId, toolName, args);
|
||||||
mutationFn: (serverConfig: CreateMcpServerDto) => mcpApi.testConnection(serverConfig),
|
}, [serverId, toolName]);
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useRestartMcpServer = () => {
|
return { data, isLoading, error, refresh: fetch, execute };
|
||||||
const queryClient = useQueryClient();
|
}
|
||||||
|
|
||||||
return useMutation({
|
export function useMcpServerResources(serverId: string) {
|
||||||
mutationFn: (id: string) => mcpApi.restart(id),
|
const [data, setData] = useState<McpResource[] | null>(null);
|
||||||
onSuccess: (_, id) => {
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
queryClient.invalidateQueries({ queryKey: mcpKeys.detail(id) });
|
const [error, setError] = useState<string | null>(null);
|
||||||
queryClient.invalidateQueries({ queryKey: mcpKeys.status(id) });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useExportMcpConfig = () => {
|
const fetch = useCallback(async () => {
|
||||||
return useMutation({
|
if (!serverId) return;
|
||||||
mutationFn: (id: string) => mcpApi.exportConfig(id),
|
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]);
|
||||||
|
|
||||||
export const useImportMcpConfig = () => {
|
useEffect(() => {
|
||||||
const queryClient = useQueryClient();
|
fetch();
|
||||||
|
}, [fetch]);
|
||||||
|
|
||||||
return useMutation({
|
return { data, isLoading, error, refresh: fetch };
|
||||||
mutationFn: (file: File) => mcpApi.importConfig(file),
|
}
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({ queryKey: mcpKeys.lists() });
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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 { paymentTerminalsApi } from '../api/payment-terminals.api';
|
||||||
import type {
|
import type {
|
||||||
PaymentTerminal,
|
PaymentTerminal,
|
||||||
@ -12,320 +12,420 @@ import type {
|
|||||||
PaymentMethod,
|
PaymentMethod,
|
||||||
} from '../../shared/types/api.types';
|
} from '../../shared/types/api.types';
|
||||||
|
|
||||||
// Query Keys
|
// Types for hook returns
|
||||||
export const paymentTerminalsKeys = {
|
interface UseQueryResult<T> {
|
||||||
all: ['payment-terminals'] as const,
|
data: T | null;
|
||||||
lists: () => [...paymentTerminalsKeys.all, 'list'] as const,
|
isLoading: boolean;
|
||||||
list: (filters: PaymentTerminalFilters) => [...paymentTerminalsKeys.lists(), filters] as const,
|
error: string | null;
|
||||||
details: () => [...paymentTerminalsKeys.all, 'detail'] as const,
|
refresh: () => Promise<void>;
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Queries
|
interface TransactionsResponse {
|
||||||
export const usePaymentTerminals = (
|
transactions: PaymentTerminalTransaction[];
|
||||||
filters?: PaymentTerminalFilters,
|
total: number;
|
||||||
options?: Omit<UseQueryOptions<PaymentTerminalsResponse>, 'queryKey' | 'queryFn'>
|
}
|
||||||
) => {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: paymentTerminalsKeys.list(filters || {}),
|
|
||||||
queryFn: () => paymentTerminalsApi.getAll(filters),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePaymentTerminal = (
|
interface LogsResponse {
|
||||||
id: string,
|
logs: any[];
|
||||||
options?: Omit<UseQueryOptions<PaymentTerminal>, 'queryKey' | 'queryFn'>
|
total: number;
|
||||||
) => {
|
}
|
||||||
return useQuery({
|
|
||||||
queryKey: paymentTerminalsKeys.detail(id),
|
|
||||||
queryFn: () => paymentTerminalsApi.getById(id),
|
|
||||||
enabled: !!id,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePaymentTerminalTransactions = (
|
interface HealthCheckResponse {
|
||||||
|
healthy: boolean;
|
||||||
|
issues: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransactionFilters {
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
|
status?: TransactionStatus;
|
||||||
|
paymentMethod?: PaymentMethod;
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LogsFilters {
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
|
level?: 'error' | 'warn' | 'info' | 'debug';
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PaymentData {
|
||||||
|
amount: number;
|
||||||
|
currency: string;
|
||||||
|
paymentMethod: PaymentMethod;
|
||||||
|
reference?: string;
|
||||||
|
metadata?: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
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,
|
terminalId: string,
|
||||||
filters?: {
|
filters?: TransactionFilters
|
||||||
startDate?: string;
|
): UseQueryResult<TransactionsResponse> & {
|
||||||
endDate?: string;
|
processPayment: (paymentData: PaymentData) => Promise<PaymentTerminalTransaction>;
|
||||||
status?: TransactionStatus;
|
refund: (transactionId: string, amount?: number, reason?: string) => Promise<void>;
|
||||||
paymentMethod?: PaymentMethod;
|
voidTransaction: (transactionId: string, reason?: string) => Promise<void>;
|
||||||
page?: number;
|
exportTransactions: (format: 'json' | 'csv' | 'pdf', exportFilters?: { startDate?: string; endDate?: string; status?: TransactionStatus }) => Promise<Blob>;
|
||||||
limit?: number;
|
} {
|
||||||
},
|
const [data, setData] = useState<TransactionsResponse | null>(null);
|
||||||
options?: Omit<UseQueryOptions<{ transactions: PaymentTerminalTransaction[]; total: number }>, 'queryKey' | 'queryFn'>
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
) => {
|
const [error, setError] = useState<string | null>(null);
|
||||||
return useQuery({
|
|
||||||
queryKey: [...paymentTerminalsKeys.transactions(terminalId), filters],
|
|
||||||
queryFn: () => paymentTerminalsApi.getTransactions(terminalId, filters),
|
|
||||||
enabled: !!terminalId,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePaymentTerminalTransaction = (
|
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,
|
||||||
|
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,
|
terminalId: string,
|
||||||
transactionId: string,
|
transactionId: string
|
||||||
options?: Omit<UseQueryOptions<PaymentTerminalTransaction>, 'queryKey' | 'queryFn'>
|
): UseQueryResult<PaymentTerminalTransaction> {
|
||||||
) => {
|
const [data, setData] = useState<PaymentTerminalTransaction | null>(null);
|
||||||
return useQuery({
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
queryKey: paymentTerminalsKeys.transaction(terminalId, transactionId),
|
const [error, setError] = useState<string | null>(null);
|
||||||
queryFn: () => paymentTerminalsApi.getTransaction(terminalId, transactionId),
|
|
||||||
enabled: !!(terminalId && transactionId),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePaymentTerminalStatus = (
|
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,
|
id: string,
|
||||||
options?: Omit<UseQueryOptions<TerminalStatus>, 'queryKey' | 'queryFn'>
|
filters?: LogsFilters
|
||||||
) => {
|
): UseQueryResult<LogsResponse> {
|
||||||
return useQuery({
|
const [data, setData] = useState<LogsResponse | null>(null);
|
||||||
queryKey: paymentTerminalsKeys.status(id),
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
queryFn: () => paymentTerminalsApi.getStatus(id),
|
const [error, setError] = useState<string | null>(null);
|
||||||
enabled: !!id,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePaymentTerminalConfig = (
|
const fetch = useCallback(async () => {
|
||||||
id: string,
|
if (!id) return;
|
||||||
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
setIsLoading(true);
|
||||||
) => {
|
setError(null);
|
||||||
return useQuery({
|
try {
|
||||||
queryKey: paymentTerminalsKeys.config(id),
|
const response = await paymentTerminalsApi.getLogs(id, filters);
|
||||||
queryFn: () => paymentTerminalsApi.getConfig(id),
|
setData(response);
|
||||||
enabled: !!id,
|
} catch (err) {
|
||||||
...options,
|
setError(err instanceof Error ? err.message : 'Error fetching terminal logs');
|
||||||
});
|
} finally {
|
||||||
};
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
id,
|
||||||
|
filters?.startDate,
|
||||||
|
filters?.endDate,
|
||||||
|
filters?.level,
|
||||||
|
filters?.page,
|
||||||
|
filters?.limit,
|
||||||
|
]);
|
||||||
|
|
||||||
export const usePaymentTerminalHealthCheck = (
|
useEffect(() => {
|
||||||
id: string,
|
fetch();
|
||||||
options?: Omit<UseQueryOptions<{ healthy: boolean; issues: string[] }>, 'queryKey' | 'queryFn'>
|
}, [fetch]);
|
||||||
) => {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: [...paymentTerminalsKeys.detail(id), 'health'],
|
|
||||||
queryFn: () => paymentTerminalsApi.healthCheck(id),
|
|
||||||
enabled: !!id,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePaymentTerminalLogs = (
|
return { data, isLoading, error, refresh: fetch };
|
||||||
id: string,
|
}
|
||||||
filters?: {
|
|
||||||
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 = (
|
// Hook: usePaymentTerminalMetrics
|
||||||
id: string,
|
// Uses status and health check to build metrics data
|
||||||
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>
|
export function usePaymentTerminalMetrics(id: string): UseQueryResult<any> {
|
||||||
) => {
|
const [data, setData] = useState<any | null>(null);
|
||||||
return useQuery({
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
queryKey: paymentTerminalsKeys.metrics(id),
|
const [error, setError] = useState<string | null>(null);
|
||||||
queryFn: () => paymentTerminalsApi.getMetrics(id),
|
|
||||||
enabled: !!id,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mutations
|
const fetch = useCallback(async () => {
|
||||||
export const useCreatePaymentTerminal = () => {
|
if (!id) return;
|
||||||
const queryClient = useQueryClient();
|
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]);
|
||||||
|
|
||||||
return useMutation({
|
useEffect(() => {
|
||||||
mutationFn: (data: CreatePaymentTerminalDto) => paymentTerminalsApi.create(data),
|
fetch();
|
||||||
onSuccess: () => {
|
}, [fetch]);
|
||||||
queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.lists() });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useUpdatePaymentTerminal = () => {
|
return { data, isLoading, error, refresh: fetch };
|
||||||
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: {
|
|
||||||
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();
|
|
||||||
|
|
||||||
return useMutation({
|
|
||||||
mutationFn: ({
|
|
||||||
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) });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useVoidTransaction = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
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),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|||||||
@ -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 { scanningApi } from '../api/scanning.api';
|
||||||
import type {
|
import type {
|
||||||
ScanningSession,
|
ScanningSession,
|
||||||
@ -10,302 +10,381 @@ import type {
|
|||||||
UpdateScanningSessionDto,
|
UpdateScanningSessionDto,
|
||||||
ScanningConfig,
|
ScanningConfig,
|
||||||
ScanningTemplate,
|
ScanningTemplate,
|
||||||
DeviceStatus,
|
|
||||||
ScanningStatus,
|
|
||||||
} from '../../shared/types/api.types';
|
} from '../../shared/types/api.types';
|
||||||
|
|
||||||
// Query Keys
|
// ============================================================================
|
||||||
export const scanningKeys = {
|
// Sessions Hooks
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Queries
|
export function useScanningSessions(filters?: ScanningFilters) {
|
||||||
export const useScanningSessions = (
|
const [data, setData] = useState<ScanningSessionsResponse | null>(null);
|
||||||
filters?: ScanningFilters,
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
options?: Omit<UseQueryOptions<ScanningSessionsResponse>, 'queryKey' | 'queryFn'>
|
const [error, setError] = useState<string | null>(null);
|
||||||
) => {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: scanningKeys.list(filters || {}),
|
|
||||||
queryFn: () => scanningApi.getAll(filters),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useScanningSession = (
|
const fetch = useCallback(async () => {
|
||||||
id: string,
|
setIsLoading(true);
|
||||||
options?: Omit<UseQueryOptions<ScanningSession>, 'queryKey' | 'queryFn'>
|
setError(null);
|
||||||
) => {
|
try {
|
||||||
return useQuery({
|
const response = await scanningApi.getAll(filters);
|
||||||
queryKey: scanningKeys.detail(id),
|
setData(response);
|
||||||
queryFn: () => scanningApi.getById(id),
|
} catch (err) {
|
||||||
enabled: !!id,
|
setError(err instanceof Error ? err.message : 'Error fetching sessions');
|
||||||
...options,
|
} finally {
|
||||||
});
|
setIsLoading(false);
|
||||||
};
|
}
|
||||||
|
}, [
|
||||||
|
filters?.search,
|
||||||
|
filters?.page,
|
||||||
|
filters?.limit,
|
||||||
|
filters?.status,
|
||||||
|
filters?.deviceId,
|
||||||
|
filters?.startDate,
|
||||||
|
filters?.endDate,
|
||||||
|
]);
|
||||||
|
|
||||||
export const useScanningDevices = (
|
useEffect(() => {
|
||||||
options?: Omit<UseQueryOptions<ScanningDevice[]>, 'queryKey' | 'queryFn'>
|
fetch();
|
||||||
) => {
|
}, [fetch]);
|
||||||
return useQuery({
|
|
||||||
queryKey: scanningKeys.devices(),
|
|
||||||
queryFn: () => scanningApi.getDevices(),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useScanningDevice = (
|
const create = useCallback(async (dto: CreateScanningSessionDto): Promise<ScanningSession> => {
|
||||||
id: string,
|
const session = await scanningApi.create(dto);
|
||||||
options?: Omit<UseQueryOptions<ScanningDevice>, 'queryKey' | 'queryFn'>
|
await fetch();
|
||||||
) => {
|
return session;
|
||||||
return useQuery({
|
}, [fetch]);
|
||||||
queryKey: scanningKeys.device(id),
|
|
||||||
queryFn: () => scanningApi.getDevice(id),
|
|
||||||
enabled: !!id,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useScanningTemplates = (
|
const update = useCallback(async (id: string, dto: UpdateScanningSessionDto): Promise<ScanningSession> => {
|
||||||
options?: Omit<UseQueryOptions<ScanningTemplate[]>, 'queryKey' | 'queryFn'>
|
const session = await scanningApi.update(id, dto);
|
||||||
) => {
|
await fetch();
|
||||||
return useQuery({
|
return session;
|
||||||
queryKey: scanningKeys.templates(),
|
}, [fetch]);
|
||||||
queryFn: () => scanningApi.getTemplates(),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useScanningTemplate = (
|
const remove = useCallback(async (id: string): Promise<void> => {
|
||||||
id: string,
|
await scanningApi.delete(id);
|
||||||
options?: Omit<UseQueryOptions<ScanningTemplate>, 'queryKey' | 'queryFn'>
|
await fetch();
|
||||||
) => {
|
}, [fetch]);
|
||||||
return useQuery({
|
|
||||||
queryKey: scanningKeys.template(id),
|
|
||||||
queryFn: () => scanningApi.getTemplate(id),
|
|
||||||
enabled: !!id,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useScanningResults = (
|
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,
|
sessionId: string,
|
||||||
options?: Omit<UseQueryOptions<ScanningResult[]>, 'queryKey' | 'queryFn'>
|
format: 'json' | 'csv' | 'pdf'
|
||||||
) => {
|
): Promise<Blob> {
|
||||||
return useQuery({
|
return scanningApi.exportResults(sessionId, format);
|
||||||
queryKey: scanningKeys.results(sessionId),
|
}
|
||||||
queryFn: () => scanningApi.getResults(sessionId),
|
|
||||||
enabled: !!sessionId,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useScanningConfig = (
|
export async function importScanningData(file: File, sessionId?: string): Promise<void> {
|
||||||
options?: Omit<UseQueryOptions<ScanningConfig>, 'queryKey' | 'queryFn'>
|
await scanningApi.importData(file, sessionId);
|
||||||
) => {
|
}
|
||||||
return useQuery({
|
|
||||||
queryKey: scanningKeys.config(),
|
|
||||||
queryFn: () => scanningApi.getConfig(),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mutations
|
export async function validateScanningData(data: unknown, templateId: string): Promise<unknown> {
|
||||||
export const useCreateScanningSession = () => {
|
return scanningApi.validateData(data, templateId);
|
||||||
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) });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useValidateScanningData = () => {
|
|
||||||
return useMutation({
|
|
||||||
mutationFn: ({ data, templateId }: { data: any; templateId: string }) =>
|
|
||||||
scanningApi.validateData(data, templateId),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user