diff --git a/src/features/dashboard/api/dashboard.api.ts b/src/features/dashboard/api/dashboard.api.ts new file mode 100644 index 0000000..ae11d8f --- /dev/null +++ b/src/features/dashboard/api/dashboard.api.ts @@ -0,0 +1,152 @@ +import { api } from '@services/api/axios-instance'; +import type { + Dashboard, + DashboardWidget, + DashboardFilters, + DashboardsResponse, + CreateDashboardDto, + UpdateDashboardDto, + WidgetConfig, + DashboardMetrics, +} from '../../shared/types/api.types'; + +const BASE_URL = '/api/v1/dashboard'; + +export const dashboardApi = { + // Get all dashboards with filters + getAll: async (filters?: DashboardFilters): Promise => { + const params = new URLSearchParams(); + if (filters?.search) params.append('search', filters.search); + if (filters?.userId) params.append('userId', filters.userId); + if (filters?.isDefault !== undefined) params.append('isDefault', String(filters.isDefault)); + if (filters?.isPublic !== undefined) params.append('isPublic', String(filters.isPublic)); + if (filters?.page) params.append('page', String(filters.page)); + if (filters?.limit) params.append('limit', String(filters.limit)); + if (filters?.sortBy) params.append('sortBy', filters.sortBy); + if (filters?.sortOrder) params.append('sortOrder', filters.sortOrder); + + const response = await api.get(`${BASE_URL}?${params.toString()}`); + return response.data; + }, + + // Get dashboard by ID + getById: async (id: string): Promise => { + const response = await api.get(`${BASE_URL}/${id}`); + return response.data; + }, + + // Get user's default dashboard + getDefault: async (): Promise => { + const response = await api.get(`${BASE_URL}/default`); + return response.data; + }, + + // Create dashboard + create: async (data: CreateDashboardDto): Promise => { + const response = await api.post(BASE_URL, data); + return response.data; + }, + + // Update dashboard + update: async (id: string, data: UpdateDashboardDto): Promise => { + const response = await api.patch(`${BASE_URL}/${id}`, data); + return response.data; + }, + + // Delete dashboard + delete: async (id: string): Promise => { + await api.delete(`${BASE_URL}/${id}`); + }, + + // Duplicate dashboard + duplicate: async (id: string, name: string): Promise => { + const response = await api.post(`${BASE_URL}/${id}/duplicate`, { name }); + return response.data; + }, + + // Set as default dashboard + setAsDefault: async (id: string): Promise => { + const response = await api.patch(`${BASE_URL}/${id}/default`); + return response.data; + }, + + // Share dashboard + share: async (id: string, userIds: string[]): Promise => { + await api.post(`${BASE_URL}/${id}/share`, { userIds }); + }, + + // Unshare dashboard + unshare: async (id: string, userIds: string[]): Promise => { + await api.post(`${BASE_URL}/${id}/unshare`, { userIds }); + }, + + // Get dashboard widgets + getWidgets: async (dashboardId: string): Promise => { + const response = await api.get(`${BASE_URL}/${dashboardId}/widgets`); + return response.data; + }, + + // Add widget to dashboard + addWidget: async (dashboardId: string, widget: CreateDashboardDto): Promise => { + const response = await api.post(`${BASE_URL}/${dashboardId}/widgets`, widget); + return response.data; + }, + + // Update widget + updateWidget: async ( + dashboardId: string, + widgetId: string, + config: WidgetConfig + ): Promise => { + const response = await api.patch( + `${BASE_URL}/${dashboardId}/widgets/${widgetId}`, + config + ); + return response.data; + }, + + // Remove widget from dashboard + removeWidget: async (dashboardId: string, widgetId: string): Promise => { + await api.delete(`${BASE_URL}/${dashboardId}/widgets/${widgetId}`); + }, + + // Get dashboard metrics + getMetrics: async (dashboardId: string, filters?: any): Promise => { + const params = new URLSearchParams(); + if (filters?.startDate) params.append('startDate', filters.startDate); + if (filters?.endDate) params.append('endDate', filters.endDate); + if (filters?.period) params.append('period', filters.period); + + const response = await api.get( + `${BASE_URL}/${dashboardId}/metrics?${params.toString()}` + ); + return response.data; + }, + + // Refresh dashboard data + refresh: async (id: string): Promise => { + const response = await api.post(`${BASE_URL}/${id}/refresh`); + return response.data; + }, + + // Export dashboard + export: async (id: string, format: 'json' | 'pdf' | 'png'): Promise => { + const response = await api.get(`${BASE_URL}/${id}/export?format=${format}`, { + responseType: 'blob', + }); + return response.data; + }, + + // Import dashboard + import: async (file: File): Promise => { + const formData = new FormData(); + formData.append('file', file); + + const response = await api.post(`${BASE_URL}/import`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + return response.data; + }, +}; diff --git a/src/features/dashboard/hooks/useDashboard.ts b/src/features/dashboard/hooks/useDashboard.ts new file mode 100644 index 0000000..5183e6a --- /dev/null +++ b/src/features/dashboard/hooks/useDashboard.ts @@ -0,0 +1,234 @@ +import { useQuery, useMutation, useQueryClient, UseQueryOptions } from '@tanstack/react-query'; +import { dashboardApi } from '../api/dashboard.api'; +import type { + Dashboard, + DashboardWidget, + DashboardFilters, + DashboardsResponse, + CreateDashboardDto, + UpdateDashboardDto, + WidgetConfig, + DashboardMetrics, +} from '../../shared/types/api.types'; + +// Query Keys +export const dashboardKeys = { + all: ['dashboard'] as const, + lists: () => [...dashboardKeys.all, 'list'] as const, + list: (filters: DashboardFilters) => [...dashboardKeys.lists(), filters] as const, + details: () => [...dashboardKeys.all, 'detail'] as const, + detail: (id: string) => [...dashboardKeys.details(), id] as const, + widgets: (id: string) => [...dashboardKeys.detail(id), 'widgets'] as const, + metrics: (id: string) => [...dashboardKeys.detail(id), 'metrics'] as const, +}; + +// Queries +export const useDashboards = ( + filters?: DashboardFilters, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: dashboardKeys.list(filters || {}), + queryFn: () => dashboardApi.getAll(filters), + ...options, + }); +}; + +export const useDashboard = ( + id: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: dashboardKeys.detail(id), + queryFn: () => dashboardApi.getById(id), + enabled: !!id, + ...options, + }); +}; + +export const useDefaultDashboard = ( + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: [...dashboardKeys.all, 'default'], + queryFn: () => dashboardApi.getDefault(), + ...options, + }); +}; + +export const useDashboardWidgets = ( + dashboardId: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: dashboardKeys.widgets(dashboardId), + queryFn: () => dashboardApi.getWidgets(dashboardId), + enabled: !!dashboardId, + ...options, + }); +}; + +export const useDashboardMetrics = ( + dashboardId: string, + filters?: any, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: [...dashboardKeys.metrics(dashboardId), filters], + queryFn: () => dashboardApi.getMetrics(dashboardId, filters), + enabled: !!dashboardId, + ...options, + }); +}; + +// Mutations +export const useCreateDashboard = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (data: CreateDashboardDto) => dashboardApi.create(data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() }); + }, + }); +}; + +export const useUpdateDashboard = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ id, data }: { id: string; data: UpdateDashboardDto }) => + dashboardApi.update(id, data), + onSuccess: (_, { id }) => { + queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() }); + queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(id) }); + }, + }); +}; + +export const useDeleteDashboard = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => dashboardApi.delete(id), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() }); + }, + }); +}; + +export const useDuplicateDashboard = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ id, name }: { id: string; name: string }) => dashboardApi.duplicate(id, name), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() }); + }, + }); +}; + +export const useSetAsDefaultDashboard = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => dashboardApi.setAsDefault(id), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() }); + queryClient.invalidateQueries({ queryKey: [...dashboardKeys.all, 'default'] }); + }, + }); +}; + +export const useShareDashboard = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ id, userIds }: { id: string; userIds: string[] }) => + dashboardApi.share(id, userIds), + onSuccess: (_, { id }) => { + queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(id) }); + }, + }); +}; + +export const useUnshareDashboard = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ id, userIds }: { id: string; userIds: string[] }) => + dashboardApi.unshare(id, userIds), + onSuccess: (_, { id }) => { + queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(id) }); + }, + }); +}; + +export const useAddWidget = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ dashboardId, widget }: { dashboardId: string; widget: CreateDashboardDto }) => + dashboardApi.addWidget(dashboardId, widget), + onSuccess: (_, { dashboardId }) => { + queryClient.invalidateQueries({ queryKey: dashboardKeys.widgets(dashboardId) }); + queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(dashboardId) }); + }, + }); +}; + +export const useUpdateWidget = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ dashboardId, widgetId, config }: { dashboardId: string; widgetId: string; config: WidgetConfig }) => + dashboardApi.updateWidget(dashboardId, widgetId, config), + onSuccess: (_, { dashboardId }) => { + queryClient.invalidateQueries({ queryKey: dashboardKeys.widgets(dashboardId) }); + queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(dashboardId) }); + }, + }); +}; + +export const useRemoveWidget = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ dashboardId, widgetId }: { dashboardId: string; widgetId: string }) => + dashboardApi.removeWidget(dashboardId, widgetId), + onSuccess: (_, { dashboardId }) => { + queryClient.invalidateQueries({ queryKey: dashboardKeys.widgets(dashboardId) }); + queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(dashboardId) }); + }, + }); +}; + +export const useRefreshDashboard = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => dashboardApi.refresh(id), + onSuccess: (_, id) => { + queryClient.invalidateQueries({ queryKey: dashboardKeys.detail(id) }); + queryClient.invalidateQueries({ queryKey: dashboardKeys.metrics(id) }); + }, + }); +}; + +export const useExportDashboard = () => { + return useMutation({ + mutationFn: ({ id, format }: { id: string; format: 'json' | 'pdf' | 'png' }) => + dashboardApi.export(id, format), + }); +}; + +export const useImportDashboard = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (file: File) => dashboardApi.import(file), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: dashboardKeys.lists() }); + }, + }); +}; diff --git a/src/features/geolocation/api/geolocation.api.ts b/src/features/geolocation/api/geolocation.api.ts new file mode 100644 index 0000000..cf6c197 --- /dev/null +++ b/src/features/geolocation/api/geolocation.api.ts @@ -0,0 +1,120 @@ +import { api } from '@services/api/axios-instance'; +import type { + Geolocation, + GeolocationFilters, + GeolocationsResponse, + CreateGeolocationDto, + UpdateGeolocationDto, + GeolocationQuery, +} from '../../shared/types/api.types'; + +const BASE_URL = '/api/v1/geolocation'; + +export const geolocationApi = { + // Get all geolocations with filters + getAll: async (filters?: GeolocationFilters): Promise => { + const params = new URLSearchParams(); + if (filters?.search) params.append('search', filters.search); + if (filters?.countryId) params.append('countryId', filters.countryId); + if (filters?.regionId) params.append('regionId', filters.regionId); + if (filters?.cityId) params.append('cityId', filters.cityId); + if (filters?.postalCode) params.append('postalCode', filters.postalCode); + if (filters?.page) params.append('page', String(filters.page)); + if (filters?.limit) params.append('limit', String(filters.limit)); + if (filters?.sortBy) params.append('sortBy', filters.sortBy); + if (filters?.sortOrder) params.append('sortOrder', filters.sortOrder); + + const response = await api.get(`${BASE_URL}?${params.toString()}`); + return response.data; + }, + + // Get geolocation by ID + getById: async (id: string): Promise => { + const response = await api.get(`${BASE_URL}/${id}`); + return response.data; + }, + + // Create geolocation + create: async (data: CreateGeolocationDto): Promise => { + const response = await api.post(BASE_URL, data); + return response.data; + }, + + // Update geolocation + update: async (id: string, data: UpdateGeolocationDto): Promise => { + const response = await api.patch(`${BASE_URL}/${id}`, data); + return response.data; + }, + + // Delete geolocation + delete: async (id: string): Promise => { + await api.delete(`${BASE_URL}/${id}`); + }, + + // Geocode address + geocode: async (query: GeolocationQuery): Promise => { + const params = new URLSearchParams(); + if (query.address) params.append('address', query.address); + if (query.postalCode) params.append('postalCode', query.postalCode); + if (query.city) params.append('city', query.city); + if (query.country) params.append('country', query.country); + if (query.limit) params.append('limit', String(query.limit)); + + const response = await api.get(`${BASE_URL}/geocode?${params.toString()}`); + return response.data; + }, + + // Reverse geocode (coordinates to address) + reverseGeocode: async (latitude: number, longitude: number): Promise => { + const response = await api.get(`${BASE_URL}/reverse?lat=${latitude}&lng=${longitude}`); + return response.data; + }, + + // Get countries + getCountries: async (): Promise => { + const response = await api.get(`${BASE_URL}/countries`); + return response.data; + }, + + // Get regions/states by country + getRegions: async (countryId: string): Promise => { + const response = await api.get(`${BASE_URL}/countries/${countryId}/regions`); + return response.data; + }, + + // Get cities by region + getCities: async (regionId: string): Promise => { + const response = await api.get(`${BASE_URL}/regions/${regionId}/cities`); + return response.data; + }, + + // Validate postal code + validatePostalCode: async (postalCode: string, countryId?: string): Promise => { + const params = new URLSearchParams(); + params.append('postalCode', postalCode); + if (countryId) params.append('countryId', countryId); + + const response = await api.get(`${BASE_URL}/validate-postal-code?${params.toString()}`); + return response.data; + }, + + // Get timezone by coordinates + getTimezone: async (latitude: number, longitude: number): Promise => { + const response = await api.get(`${BASE_URL}/timezone?lat=${latitude}&lng=${longitude}`); + return response.data; + }, + + // Calculate distance between two points + calculateDistance: async ( + fromLat: number, + fromLng: number, + toLat: number, + toLng: number, + unit: 'km' | 'miles' = 'km' + ): Promise => { + const response = await api.get( + `${BASE_URL}/distance?fromLat=${fromLat}&fromLng=${fromLng}&toLat=${toLat}&toLng=${toLng}&unit=${unit}` + ); + return response.data; + }, +}; diff --git a/src/features/geolocation/hooks/useGeolocation.ts b/src/features/geolocation/hooks/useGeolocation.ts new file mode 100644 index 0000000..bb12345 --- /dev/null +++ b/src/features/geolocation/hooks/useGeolocation.ts @@ -0,0 +1,177 @@ +import { useQuery, useMutation, useQueryClient, UseQueryOptions } from '@tanstack/react-query'; +import { geolocationApi } from '../api/geolocation.api'; +import type { + Geolocation, + GeolocationFilters, + GeolocationsResponse, + CreateGeolocationDto, + UpdateGeolocationDto, + GeolocationQuery, +} from '../types/api.types'; + +// Query Keys +export const geolocationKeys = { + all: ['geolocation'] as const, + lists: () => [...geolocationKeys.all, 'list'] as const, + list: (filters: GeolocationFilters) => [...geolocationKeys.lists(), filters] as const, + details: () => [...geolocationKeys.all, 'detail'] as const, + detail: (id: string) => [...geolocationKeys.details(), id] as const, + countries: () => [...geolocationKeys.all, 'countries'] as const, + regions: (countryId: string) => [...geolocationKeys.all, 'regions', countryId] as const, + cities: (regionId: string) => [...geolocationKeys.all, 'cities', regionId] as const, +}; + +// Queries +export const useGeolocations = ( + filters?: GeolocationFilters, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: geolocationKeys.list(filters || {}), + queryFn: () => geolocationApi.getAll(filters), + ...options, + }); +}; + +export const useGeolocation = ( + id: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: geolocationKeys.detail(id), + queryFn: () => geolocationApi.getById(id), + enabled: !!id, + ...options, + }); +}; + +export const useCountries = ( + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: geolocationKeys.countries(), + queryFn: () => geolocationApi.getCountries(), + ...options, + }); +}; + +export const useRegions = ( + countryId: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: geolocationKeys.regions(countryId), + queryFn: () => geolocationApi.getRegions(countryId), + enabled: !!countryId, + ...options, + }); +}; + +export const useCities = ( + regionId: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: geolocationKeys.cities(regionId), + queryFn: () => geolocationApi.getCities(regionId), + enabled: !!regionId, + ...options, + }); +}; + +export const useGeocode = ( + query: GeolocationQuery, + options?: Omit, '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 = ( + latitude: number, + longitude: number, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: [...geolocationKeys.all, 'reverse', latitude, longitude], + queryFn: () => geolocationApi.reverseGeocode(latitude, longitude), + enabled: !!(latitude && longitude), + ...options, + }); +}; + +export const useTimezone = ( + latitude: number, + longitude: number, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: [...geolocationKeys.all, 'timezone', latitude, longitude], + queryFn: () => geolocationApi.getTimezone(latitude, longitude), + enabled: !!(latitude && longitude), + ...options, + }); +}; + +export const useDistance = ( + fromLat: number, + fromLng: number, + toLat: number, + toLng: number, + unit: 'km' | 'miles' = 'km', + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: [...geolocationKeys.all, 'distance', fromLat, fromLng, toLat, toLng, unit], + queryFn: () => geolocationApi.calculateDistance(fromLat, fromLng, toLat, toLng, unit), + enabled: !!(fromLat && fromLng && toLat && toLng), + ...options, + }); +}; + +// Mutations +export const useCreateGeolocation = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (data: CreateGeolocationDto) => geolocationApi.create(data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: geolocationKeys.lists() }); + }, + }); +}; + +export const useUpdateGeolocation = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ id, data }: { id: string; data: UpdateGeolocationDto }) => + geolocationApi.update(id, data), + onSuccess: (_, { id }) => { + queryClient.invalidateQueries({ queryKey: geolocationKeys.lists() }); + queryClient.invalidateQueries({ queryKey: geolocationKeys.detail(id) }); + }, + }); +}; + +export const useDeleteGeolocation = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => geolocationApi.delete(id), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: geolocationKeys.lists() }); + }, + }); +}; + +export const useValidatePostalCode = () => { + return useMutation({ + mutationFn: ({ postalCode, countryId }: { postalCode: string; countryId?: string }) => + geolocationApi.validatePostalCode(postalCode, countryId), + }); +}; diff --git a/src/features/mcp/api/mcp.api.ts b/src/features/mcp/api/mcp.api.ts new file mode 100644 index 0000000..2fcafc8 --- /dev/null +++ b/src/features/mcp/api/mcp.api.ts @@ -0,0 +1,277 @@ +import { api } from '@services/api/axios-instance'; +import type { + McpServer, + McpTool, + McpResource, + McpPrompt, + McpFilters, + McpServersResponse, + CreateMcpServerDto, + UpdateMcpServerDto, + McpServerStatus, + McpExecutionResult, + McpToolCall, + McpResourceAccess, +} from '../../shared/types/api.types'; + +const BASE_URL = '/api/v1/mcp'; + +export const mcpApi = { + // Get all MCP servers with filters + getAll: async (filters?: McpFilters): Promise => { + const params = new URLSearchParams(); + if (filters?.search) params.append('search', filters.search); + if (filters?.status) params.append('status', filters.status); + if (filters?.protocol) params.append('protocol', filters.protocol); + if (filters?.isConnected !== undefined) params.append('isConnected', String(filters.isConnected)); + if (filters?.page) params.append('page', String(filters.page)); + if (filters?.limit) params.append('limit', String(filters.limit)); + if (filters?.sortBy) params.append('sortBy', filters.sortBy); + if (filters?.sortOrder) params.append('sortOrder', filters.sortOrder); + + const response = await api.get(`${BASE_URL}?${params.toString()}`); + return response.data; + }, + + // Get MCP server by ID + getById: async (id: string): Promise => { + const response = await api.get(`${BASE_URL}/${id}`); + return response.data; + }, + + // Create MCP server + create: async (data: CreateMcpServerDto): Promise => { + const response = await api.post(BASE_URL, data); + return response.data; + }, + + // Update MCP server + update: async (id: string, data: UpdateMcpServerDto): Promise => { + const response = await api.patch(`${BASE_URL}/${id}`, data); + return response.data; + }, + + // Delete MCP server + delete: async (id: string): Promise => { + await api.delete(`${BASE_URL}/${id}`); + }, + + // Connect to MCP server + connect: async (id: string): Promise => { + const response = await api.post(`${BASE_URL}/${id}/connect`); + return response.data; + }, + + // Disconnect from MCP server + disconnect: async (id: string): Promise => { + const response = await api.post(`${BASE_URL}/${id}/disconnect`); + return response.data; + }, + + // Get server status + getStatus: async (id: string): Promise => { + const response = await api.get(`${BASE_URL}/${id}/status`); + return response.data; + }, + + // Get available tools for server + getTools: async (serverId: string): Promise => { + const response = await api.get(`${BASE_URL}/${serverId}/tools`); + return response.data; + }, + + // Get tool details + getTool: async (serverId: string, toolName: string): Promise => { + const response = await api.get(`${BASE_URL}/${serverId}/tools/${toolName}`); + return response.data; + }, + + // Execute tool + executeTool: async ( + serverId: string, + toolName: string, + args: Record + ): Promise => { + const response = await api.post( + `${BASE_URL}/${serverId}/tools/${toolName}/execute`, + { args } + ); + return response.data; + }, + + // Get available resources for server + getResources: async (serverId: string): Promise => { + const response = await api.get(`${BASE_URL}/${serverId}/resources`); + return response.data; + }, + + // Get resource details + getResource: async (serverId: string, resourceName: string): Promise => { + const response = await api.get(`${BASE_URL}/${serverId}/resources/${resourceName}`); + return response.data; + }, + + // Read resource content + readResource: async ( + serverId: string, + resourceName: string, + uri?: string + ): Promise<{ contents: any[] }> => { + const params = new URLSearchParams(); + if (uri) params.append('uri', uri); + + const response = await api.get<{ contents: any[] }>( + `${BASE_URL}/${serverId}/resources/${resourceName}/read?${params.toString()}` + ); + return response.data; + }, + + // Get available prompts for server + getPrompts: async (serverId: string): Promise => { + const response = await api.get(`${BASE_URL}/${serverId}/prompts`); + return response.data; + }, + + // Get prompt details + getPrompt: async (serverId: string, promptName: string): Promise => { + const response = await api.get(`${BASE_URL}/${serverId}/prompts/${promptName}`); + return response.data; + }, + + // Execute prompt + executePrompt: async ( + serverId: string, + promptName: string, + args?: Record + ): Promise<{ messages: any[] }> => { + const response = await api.post<{ messages: any[] }>( + `${BASE_URL}/${serverId}/prompts/${promptName}/execute`, + { args } + ); + return response.data; + }, + + // Get tool execution history + getToolHistory: async ( + serverId: string, + filters?: { + toolName?: string; + startDate?: string; + endDate?: string; + status?: 'success' | 'error'; + page?: number; + limit?: number; + } + ): Promise<{ executions: McpToolCall[]; total: number }> => { + const params = new URLSearchParams(); + if (filters?.toolName) params.append('toolName', filters.toolName); + if (filters?.startDate) params.append('startDate', filters.startDate); + if (filters?.endDate) params.append('endDate', filters.endDate); + if (filters?.status) params.append('status', filters.status); + if (filters?.page) params.append('page', String(filters.page)); + if (filters?.limit) params.append('limit', String(filters.limit)); + + const response = await api.get<{ executions: McpToolCall[]; total: number }>( + `${BASE_URL}/${serverId}/tools/history?${params.toString()}` + ); + return response.data; + }, + + // Get resource access history + getResourceHistory: async ( + serverId: string, + filters?: { + resourceName?: string; + startDate?: string; + endDate?: string; + page?: number; + limit?: number; + } + ): Promise<{ accesses: McpResourceAccess[]; total: number }> => { + const params = new URLSearchParams(); + if (filters?.resourceName) params.append('resourceName', filters.resourceName); + if (filters?.startDate) params.append('startDate', filters.startDate); + if (filters?.endDate) params.append('endDate', filters.endDate); + if (filters?.page) params.append('page', String(filters.page)); + if (filters?.limit) params.append('limit', String(filters.limit)); + + const response = await api.get<{ accesses: McpResourceAccess[]; total: number }>( + `${BASE_URL}/${serverId}/resources/history?${params.toString()}` + ); + return response.data; + }, + + // Test server connection + testConnection: async (serverConfig: CreateMcpServerDto): Promise<{ success: boolean; error?: string }> => { + const response = await api.post<{ success: boolean; error?: string }>( + `${BASE_URL}/test-connection`, + serverConfig + ); + return response.data; + }, + + // Get server logs + getLogs: async ( + id: string, + filters?: { + startDate?: string; + endDate?: string; + level?: 'error' | 'warn' | 'info' | 'debug'; + page?: number; + limit?: number; + } + ): Promise<{ logs: any[]; total: number }> => { + const params = new URLSearchParams(); + if (filters?.startDate) params.append('startDate', filters.startDate); + if (filters?.endDate) params.append('endDate', filters.endDate); + if (filters?.level) params.append('level', filters.level); + if (filters?.page) params.append('page', String(filters.page)); + if (filters?.limit) params.append('limit', String(filters.limit)); + + const response = await api.get<{ logs: any[]; total: number }>( + `${BASE_URL}/${id}/logs?${params.toString()}` + ); + return response.data; + }, + + // Restart server + restart: async (id: string): Promise => { + const response = await api.post(`${BASE_URL}/${id}/restart`); + return response.data; + }, + + // Get server metrics + getMetrics: async (id: string): Promise<{ + uptime: number; + totalRequests: number; + successfulRequests: number; + failedRequests: number; + averageResponseTime: number; + memoryUsage: number; + cpuUsage: number; + }> => { + const response = await api.get(`${BASE_URL}/${id}/metrics`); + return response.data; + }, + + // Export server configuration + exportConfig: async (id: string): Promise => { + const response = await api.get(`${BASE_URL}/${id}/export`, { + responseType: 'blob', + }); + return response.data; + }, + + // Import server configuration + importConfig: async (file: File): Promise => { + const formData = new FormData(); + formData.append('file', file); + + const response = await api.post(`${BASE_URL}/import`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + return response.data; + }, +}; diff --git a/src/features/mcp/hooks/useMcp.ts b/src/features/mcp/hooks/useMcp.ts new file mode 100644 index 0000000..da28650 --- /dev/null +++ b/src/features/mcp/hooks/useMcp.ts @@ -0,0 +1,363 @@ +import { useQuery, useMutation, useQueryClient, UseQueryOptions } from '@tanstack/react-query'; +import { mcpApi } from '../api/mcp.api'; +import type { + McpServer, + McpTool, + McpResource, + McpPrompt, + McpFilters, + McpServersResponse, + CreateMcpServerDto, + UpdateMcpServerDto, + McpServerStatus, + McpExecutionResult, + McpToolCall, + McpResourceAccess, +} from '../../shared/types/api.types'; + +// Query Keys +export const mcpKeys = { + all: ['mcp'] as const, + lists: () => [...mcpKeys.all, 'list'] as const, + list: (filters: McpFilters) => [...mcpKeys.lists(), filters] as const, + details: () => [...mcpKeys.all, 'detail'] as const, + detail: (id: string) => [...mcpKeys.details(), id] as const, + status: (id: string) => [...mcpKeys.detail(id), 'status'] as const, + tools: (serverId: string) => [...mcpKeys.detail(serverId), 'tools'] as const, + tool: (serverId: string, toolName: string) => + [...mcpKeys.tools(serverId), toolName] as const, + resources: (serverId: string) => [...mcpKeys.detail(serverId), 'resources'] as const, + resource: (serverId: string, resourceName: string) => + [...mcpKeys.resources(serverId), resourceName] as const, + prompts: (serverId: string) => [...mcpKeys.detail(serverId), 'prompts'] as const, + prompt: (serverId: string, promptName: string) => + [...mcpKeys.prompts(serverId), promptName] as const, + toolHistory: (serverId: string) => [...mcpKeys.detail(serverId), 'toolHistory'] as const, + resourceHistory: (serverId: string) => [...mcpKeys.detail(serverId), 'resourceHistory'] as const, + logs: (id: string) => [...mcpKeys.detail(id), 'logs'] as const, + metrics: (id: string) => [...mcpKeys.detail(id), 'metrics'] as const, +}; + +// Queries +export const useMcpServers = ( + filters?: McpFilters, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: mcpKeys.list(filters || {}), + queryFn: () => mcpApi.getAll(filters), + ...options, + }); +}; + +export const useMcpServer = ( + id: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: mcpKeys.detail(id), + queryFn: () => mcpApi.getById(id), + enabled: !!id, + ...options, + }); +}; + +export const useMcpServerStatus = ( + id: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: mcpKeys.status(id), + queryFn: () => mcpApi.getStatus(id), + enabled: !!id, + ...options, + }); +}; + +export const useMcpServerTools = ( + serverId: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: mcpKeys.tools(serverId), + queryFn: () => mcpApi.getTools(serverId), + enabled: !!serverId, + ...options, + }); +}; + +export const useMcpServerTool = ( + serverId: string, + toolName: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: mcpKeys.tool(serverId, toolName), + queryFn: () => mcpApi.getTool(serverId, toolName), + enabled: !!(serverId && toolName), + ...options, + }); +}; + +export const useMcpServerResources = ( + serverId: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: mcpKeys.resources(serverId), + queryFn: () => mcpApi.getResources(serverId), + enabled: !!serverId, + ...options, + }); +}; + +export const useMcpServerResource = ( + serverId: string, + resourceName: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: mcpKeys.resource(serverId, resourceName), + queryFn: () => mcpApi.getResource(serverId, resourceName), + enabled: !!(serverId && resourceName), + ...options, + }); +}; + +export const useMcpServerPrompts = ( + serverId: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: mcpKeys.prompts(serverId), + queryFn: () => mcpApi.getPrompts(serverId), + enabled: !!serverId, + ...options, + }); +}; + +export const useMcpServerPrompt = ( + serverId: string, + promptName: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: mcpKeys.prompt(serverId, promptName), + queryFn: () => mcpApi.getPrompt(serverId, promptName), + enabled: !!(serverId && promptName), + ...options, + }); +}; + +export const useMcpToolHistory = ( + serverId: string, + filters?: { + toolName?: string; + startDate?: string; + endDate?: string; + status?: 'success' | 'error'; + page?: number; + limit?: number; + }, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: [...mcpKeys.toolHistory(serverId), filters], + queryFn: () => mcpApi.getToolHistory(serverId, filters), + enabled: !!serverId, + ...options, + }); +}; + +export const useMcpResourceHistory = ( + serverId: string, + filters?: { + resourceName?: string; + startDate?: string; + endDate?: string; + page?: number; + limit?: number; + }, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: [...mcpKeys.resourceHistory(serverId), filters], + queryFn: () => mcpApi.getResourceHistory(serverId, filters), + enabled: !!serverId, + ...options, + }); +}; + +export const useMcpServerLogs = ( + id: string, + filters?: { + startDate?: string; + endDate?: string; + level?: 'error' | 'warn' | 'info' | 'debug'; + page?: number; + limit?: number; + }, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: [...mcpKeys.logs(id), filters], + queryFn: () => mcpApi.getLogs(id, filters), + enabled: !!id, + ...options, + }); +}; + +// Mutations +export const useCreateMcpServer = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (data: CreateMcpServerDto) => mcpApi.create(data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: mcpKeys.lists() }); + }, + }); +}; + +export const useUpdateMcpServer = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ id, data }: { id: string; data: UpdateMcpServerDto }) => + mcpApi.update(id, data), + onSuccess: (_, { id }) => { + queryClient.invalidateQueries({ queryKey: mcpKeys.lists() }); + queryClient.invalidateQueries({ queryKey: mcpKeys.detail(id) }); + }, + }); +}; + +export const useDeleteMcpServer = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => mcpApi.delete(id), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: mcpKeys.lists() }); + }, + }); +}; + +export const useConnectMcpServer = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => mcpApi.connect(id), + onSuccess: (_, id) => { + queryClient.invalidateQueries({ queryKey: mcpKeys.detail(id) }); + queryClient.invalidateQueries({ queryKey: mcpKeys.status(id) }); + queryClient.invalidateQueries({ queryKey: mcpKeys.tools(id) }); + queryClient.invalidateQueries({ queryKey: mcpKeys.resources(id) }); + queryClient.invalidateQueries({ queryKey: mcpKeys.prompts(id) }); + }, + }); +}; + +export const useDisconnectMcpServer = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => mcpApi.disconnect(id), + onSuccess: (_, id) => { + queryClient.invalidateQueries({ queryKey: mcpKeys.detail(id) }); + queryClient.invalidateQueries({ queryKey: mcpKeys.status(id) }); + queryClient.invalidateQueries({ queryKey: mcpKeys.tools(id) }); + queryClient.invalidateQueries({ queryKey: mcpKeys.resources(id) }); + queryClient.invalidateQueries({ queryKey: mcpKeys.prompts(id) }); + }, + }); +}; + +export const useExecuteMcpTool = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ + serverId, + toolName, + args + }: { + serverId: string; + toolName: string; + args: Record; + }) => mcpApi.executeTool(serverId, toolName, args), + onSuccess: (_, { serverId }) => { + queryClient.invalidateQueries({ queryKey: mcpKeys.toolHistory(serverId) }); + }, + }); +}; + +export const useReadMcpResource = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ + serverId, + resourceName, + uri + }: { + serverId: string; + resourceName: string; + uri?: string; + }) => mcpApi.readResource(serverId, resourceName, uri), + onSuccess: (_, { serverId }) => { + queryClient.invalidateQueries({ queryKey: mcpKeys.resourceHistory(serverId) }); + }, + }); +}; + +export const useExecuteMcpPrompt = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ + serverId, + promptName, + args + }: { + serverId: string; + promptName: string; + args?: Record; + }) => mcpApi.executePrompt(serverId, promptName, args), + }); +}; + +export const useTestMcpConnection = () => { + return useMutation({ + mutationFn: (serverConfig: CreateMcpServerDto) => mcpApi.testConnection(serverConfig), + }); +}; + +export const useRestartMcpServer = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => mcpApi.restart(id), + onSuccess: (_, id) => { + queryClient.invalidateQueries({ queryKey: mcpKeys.detail(id) }); + queryClient.invalidateQueries({ queryKey: mcpKeys.status(id) }); + }, + }); +}; + +export const useExportMcpConfig = () => { + return useMutation({ + mutationFn: (id: string) => mcpApi.exportConfig(id), + }); +}; + +export const useImportMcpConfig = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (file: File) => mcpApi.importConfig(file), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: mcpKeys.lists() }); + }, + }); +}; diff --git a/src/features/payment-terminals/api/payment-terminals.api.ts b/src/features/payment-terminals/api/payment-terminals.api.ts new file mode 100644 index 0000000..7314cf0 --- /dev/null +++ b/src/features/payment-terminals/api/payment-terminals.api.ts @@ -0,0 +1,234 @@ +import { api } from '@services/api/axios-instance'; +import type { + PaymentTerminal, + PaymentTerminalTransaction, + PaymentTerminalFilters, + PaymentTerminalsResponse, + CreatePaymentTerminalDto, + UpdatePaymentTerminalDto, + TerminalStatus, + TransactionStatus, + PaymentMethod, +} from '../../shared/types/api.types'; + +const BASE_URL = '/api/v1/payment-terminals'; + +export const paymentTerminalsApi = { + // Get all payment terminals with filters + getAll: async (filters?: PaymentTerminalFilters): Promise => { + const params = new URLSearchParams(); + if (filters?.search) params.append('search', filters.search); + if (filters?.storeId) params.append('storeId', filters.storeId); + if (filters?.status) params.append('status', filters.status); + if (filters?.terminalType) params.append('terminalType', filters.terminalType); + if (filters?.paymentMethod) params.append('paymentMethod', filters.paymentMethod); + if (filters?.isActive !== undefined) params.append('isActive', String(filters.isActive)); + if (filters?.page) params.append('page', String(filters.page)); + if (filters?.limit) params.append('limit', String(filters.limit)); + if (filters?.sortBy) params.append('sortBy', filters.sortBy); + if (filters?.sortOrder) params.append('sortOrder', filters.sortOrder); + + const response = await api.get(`${BASE_URL}?${params.toString()}`); + return response.data; + }, + + // Get payment terminal by ID + getById: async (id: string): Promise => { + const response = await api.get(`${BASE_URL}/${id}`); + return response.data; + }, + + // Create payment terminal + create: async (data: CreatePaymentTerminalDto): Promise => { + const response = await api.post(BASE_URL, data); + return response.data; + }, + + // Update payment terminal + update: async (id: string, data: UpdatePaymentTerminalDto): Promise => { + const response = await api.patch(`${BASE_URL}/${id}`, data); + return response.data; + }, + + // Delete payment terminal + delete: async (id: string): Promise => { + await api.delete(`${BASE_URL}/${id}`); + }, + + // Activate terminal + activate: async (id: string): Promise => { + const response = await api.post(`${BASE_URL}/${id}/activate`); + return response.data; + }, + + // Deactivate terminal + deactivate: async (id: string): Promise => { + const response = await api.post(`${BASE_URL}/${id}/deactivate`); + return response.data; + }, + + // Get terminal status + getStatus: async (id: string): Promise => { + const response = await api.get(`${BASE_URL}/${id}/status`); + return response.data; + }, + + // Get terminal transactions + getTransactions: async ( + terminalId: string, + filters?: { + startDate?: string; + endDate?: string; + status?: TransactionStatus; + paymentMethod?: PaymentMethod; + page?: number; + limit?: number; + } + ): Promise<{ transactions: PaymentTerminalTransaction[]; total: number }> => { + const params = new URLSearchParams(); + if (filters?.startDate) params.append('startDate', filters.startDate); + if (filters?.endDate) params.append('endDate', filters.endDate); + if (filters?.status) params.append('status', filters.status); + if (filters?.paymentMethod) params.append('paymentMethod', filters.paymentMethod); + if (filters?.page) params.append('page', String(filters.page)); + if (filters?.limit) params.append('limit', String(filters.limit)); + + const response = await api.get<{ transactions: PaymentTerminalTransaction[]; total: number }>( + `${BASE_URL}/${terminalId}/transactions?${params.toString()}` + ); + return response.data; + }, + + // Process payment through terminal + processPayment: async ( + terminalId: string, + paymentData: { + amount: number; + currency: string; + paymentMethod: PaymentMethod; + reference?: string; + metadata?: Record; + } + ): Promise => { + const response = await api.post( + `${BASE_URL}/${terminalId}/process-payment`, + paymentData + ); + return response.data; + }, + + // Refund transaction + refundTransaction: async ( + terminalId: string, + transactionId: string, + amount?: number, + reason?: string + ): Promise => { + const response = await api.post( + `${BASE_URL}/${terminalId}/transactions/${transactionId}/refund`, + { amount, reason } + ); + return response.data; + }, + + // Void transaction + voidTransaction: async ( + terminalId: string, + transactionId: string, + reason?: string + ): Promise => { + const response = await api.post( + `${BASE_URL}/${terminalId}/transactions/${transactionId}/void`, + { reason } + ); + return response.data; + }, + + // Get transaction by ID + getTransaction: async ( + terminalId: string, + transactionId: string + ): Promise => { + const response = await api.get( + `${BASE_URL}/${terminalId}/transactions/${transactionId}` + ); + return response.data; + }, + + // Sync terminal with provider + sync: async (id: string): Promise => { + const response = await api.post(`${BASE_URL}/${id}/sync`); + return response.data; + }, + + // Get terminal configuration + getConfig: async (id: string): Promise => { + const response = await api.get(`${BASE_URL}/${id}/config`); + return response.data; + }, + + // Update terminal configuration + updateConfig: async (id: string, config: any): Promise => { + const response = await api.patch(`${BASE_URL}/${id}/config`, config); + return response.data; + }, + + // Get terminal health check + healthCheck: async (id: string): Promise<{ healthy: boolean; issues: string[] }> => { + const response = await api.get<{ healthy: boolean; issues: string[] }>( + `${BASE_URL}/${id}/health-check` + ); + return response.data; + }, + + // Reboot terminal + reboot: async (id: string): Promise => { + await api.post(`${BASE_URL}/${id}/reboot`); + }, + + // Get terminal logs + getLogs: async ( + id: string, + filters?: { + startDate?: string; + endDate?: string; + level?: 'error' | 'warn' | 'info' | 'debug'; + page?: number; + limit?: number; + } + ): Promise<{ logs: any[]; total: number }> => { + const params = new URLSearchParams(); + if (filters?.startDate) params.append('startDate', filters.startDate); + if (filters?.endDate) params.append('endDate', filters.endDate); + if (filters?.level) params.append('level', filters.level); + if (filters?.page) params.append('page', String(filters.page)); + if (filters?.limit) params.append('limit', String(filters.limit)); + + const response = await api.get<{ logs: any[]; total: number }>( + `${BASE_URL}/${id}/logs?${params.toString()}` + ); + return response.data; + }, + + // Export transactions + exportTransactions: async ( + terminalId: string, + format: 'json' | 'csv' | 'pdf', + filters?: { + startDate?: string; + endDate?: string; + status?: TransactionStatus; + } + ): Promise => { + const params = new URLSearchParams(); + params.append('format', format); + if (filters?.startDate) params.append('startDate', filters.startDate); + if (filters?.endDate) params.append('endDate', filters.endDate); + if (filters?.status) params.append('status', filters.status); + + const response = await api.get(`${BASE_URL}/${terminalId}/transactions/export?${params.toString()}`, { + responseType: 'blob', + }); + return response.data; + }, +}; diff --git a/src/features/payment-terminals/hooks/usePaymentTerminals.ts b/src/features/payment-terminals/hooks/usePaymentTerminals.ts new file mode 100644 index 0000000..a851165 --- /dev/null +++ b/src/features/payment-terminals/hooks/usePaymentTerminals.ts @@ -0,0 +1,331 @@ +import { useQuery, useMutation, useQueryClient, UseQueryOptions } from '@tanstack/react-query'; +import { paymentTerminalsApi } from '../api/payment-terminals.api'; +import type { + PaymentTerminal, + PaymentTerminalTransaction, + PaymentTerminalFilters, + PaymentTerminalsResponse, + CreatePaymentTerminalDto, + UpdatePaymentTerminalDto, + TerminalStatus, + TransactionStatus, + PaymentMethod, +} from '../../shared/types/api.types'; + +// Query Keys +export const paymentTerminalsKeys = { + all: ['payment-terminals'] as const, + lists: () => [...paymentTerminalsKeys.all, 'list'] as const, + list: (filters: PaymentTerminalFilters) => [...paymentTerminalsKeys.lists(), filters] as const, + details: () => [...paymentTerminalsKeys.all, 'detail'] as const, + detail: (id: string) => [...paymentTerminalsKeys.details(), id] as const, + transactions: (terminalId: string) => [...paymentTerminalsKeys.detail(terminalId), 'transactions'] as const, + transaction: (terminalId: string, transactionId: string) => + [...paymentTerminalsKeys.transactions(terminalId), transactionId] as const, + status: (id: string) => [...paymentTerminalsKeys.detail(id), 'status'] as const, + config: (id: string) => [...paymentTerminalsKeys.detail(id), 'config'] as const, + logs: (id: string) => [...paymentTerminalsKeys.detail(id), 'logs'] as const, + metrics: (id: string) => [...paymentTerminalsKeys.detail(id), 'metrics'] as const, +}; + +// Queries +export const usePaymentTerminals = ( + filters?: PaymentTerminalFilters, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: paymentTerminalsKeys.list(filters || {}), + queryFn: () => paymentTerminalsApi.getAll(filters), + ...options, + }); +}; + +export const usePaymentTerminal = ( + id: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: paymentTerminalsKeys.detail(id), + queryFn: () => paymentTerminalsApi.getById(id), + enabled: !!id, + ...options, + }); +}; + +export const usePaymentTerminalTransactions = ( + terminalId: string, + filters?: { + startDate?: string; + endDate?: string; + status?: TransactionStatus; + paymentMethod?: PaymentMethod; + page?: number; + limit?: number; + }, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: [...paymentTerminalsKeys.transactions(terminalId), filters], + queryFn: () => paymentTerminalsApi.getTransactions(terminalId, filters), + enabled: !!terminalId, + ...options, + }); +}; + +export const usePaymentTerminalTransaction = ( + terminalId: string, + transactionId: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: paymentTerminalsKeys.transaction(terminalId, transactionId), + queryFn: () => paymentTerminalsApi.getTransaction(terminalId, transactionId), + enabled: !!(terminalId && transactionId), + ...options, + }); +}; + +export const usePaymentTerminalStatus = ( + id: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: paymentTerminalsKeys.status(id), + queryFn: () => paymentTerminalsApi.getStatus(id), + enabled: !!id, + ...options, + }); +}; + +export const usePaymentTerminalConfig = ( + id: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: paymentTerminalsKeys.config(id), + queryFn: () => paymentTerminalsApi.getConfig(id), + enabled: !!id, + ...options, + }); +}; + +export const usePaymentTerminalHealthCheck = ( + id: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: [...paymentTerminalsKeys.detail(id), 'health'], + queryFn: () => paymentTerminalsApi.healthCheck(id), + enabled: !!id, + ...options, + }); +}; + +export const usePaymentTerminalLogs = ( + id: string, + filters?: { + startDate?: string; + endDate?: string; + level?: 'error' | 'warn' | 'info' | 'debug'; + page?: number; + limit?: number; + }, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: [...paymentTerminalsKeys.logs(id), filters], + queryFn: () => paymentTerminalsApi.getLogs(id, filters), + enabled: !!id, + ...options, + }); +}; + +export const usePaymentTerminalMetrics = ( + id: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: paymentTerminalsKeys.metrics(id), + queryFn: () => paymentTerminalsApi.getMetrics(id), + enabled: !!id, + ...options, + }); +}; + +// Mutations +export const useCreatePaymentTerminal = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (data: CreatePaymentTerminalDto) => paymentTerminalsApi.create(data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.lists() }); + }, + }); +}; + +export const useUpdatePaymentTerminal = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ id, data }: { id: string; data: UpdatePaymentTerminalDto }) => + paymentTerminalsApi.update(id, data), + onSuccess: (_, { id }) => { + queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.lists() }); + queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.detail(id) }); + }, + }); +}; + +export const useDeletePaymentTerminal = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => paymentTerminalsApi.delete(id), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.lists() }); + }, + }); +}; + +export const useActivatePaymentTerminal = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => paymentTerminalsApi.activate(id), + onSuccess: (_, id) => { + queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.detail(id) }); + queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.lists() }); + }, + }); +}; + +export const useDeactivatePaymentTerminal = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => paymentTerminalsApi.deactivate(id), + onSuccess: (_, id) => { + queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.detail(id) }); + queryClient.invalidateQueries({ queryKey: paymentTerminalsKeys.lists() }); + }, + }); +}; + +export const useProcessPayment = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ + terminalId, + paymentData + }: { + terminalId: string; + paymentData: { + amount: number; + currency: string; + paymentMethod: PaymentMethod; + reference?: string; + metadata?: Record; + }; + }) => 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), + }); +}; diff --git a/src/features/scanning/api/scanning.api.ts b/src/features/scanning/api/scanning.api.ts new file mode 100644 index 0000000..b6616bb --- /dev/null +++ b/src/features/scanning/api/scanning.api.ts @@ -0,0 +1,188 @@ +import { api } from '@services/api/axios-instance'; +import type { + ScanningSession, + ScanningDevice, + ScanningResult, + ScanningFilters, + ScanningSessionsResponse, + CreateScanningSessionDto, + UpdateScanningSessionDto, + ScanningConfig, + ScanningTemplate, +} from '../../shared/types/api.types'; + +const BASE_URL = '/api/v1/scanning'; + +export const scanningApi = { + // Get all scanning sessions with filters + getAll: async (filters?: ScanningFilters): Promise => { + const params = new URLSearchParams(); + if (filters?.search) params.append('search', filters.search); + if (filters?.deviceId) params.append('deviceId', filters.deviceId); + if (filters?.status) params.append('status', filters.status); + if (filters?.userId) params.append('userId', filters.userId); + if (filters?.startDate) params.append('startDate', filters.startDate); + if (filters?.endDate) params.append('endDate', filters.endDate); + if (filters?.page) params.append('page', String(filters.page)); + if (filters?.limit) params.append('limit', String(filters.limit)); + if (filters?.sortBy) params.append('sortBy', filters.sortBy); + if (filters?.sortOrder) params.append('sortOrder', filters.sortOrder); + + const response = await api.get(`${BASE_URL}?${params.toString()}`); + return response.data; + }, + + // Get scanning session by ID + getById: async (id: string): Promise => { + const response = await api.get(`${BASE_URL}/${id}`); + return response.data; + }, + + // Create scanning session + create: async (data: CreateScanningSessionDto): Promise => { + const response = await api.post(BASE_URL, data); + return response.data; + }, + + // Update scanning session + update: async (id: string, data: UpdateScanningSessionDto): Promise => { + const response = await api.patch(`${BASE_URL}/${id}`, data); + return response.data; + }, + + // Delete scanning session + delete: async (id: string): Promise => { + await api.delete(`${BASE_URL}/${id}`); + }, + + // Start scanning session + start: async (id: string): Promise => { + const response = await api.post(`${BASE_URL}/${id}/start`); + return response.data; + }, + + // Pause scanning session + pause: async (id: string): Promise => { + const response = await api.post(`${BASE_URL}/${id}/pause`); + return response.data; + }, + + // Resume scanning session + resume: async (id: string): Promise => { + const response = await api.post(`${BASE_URL}/${id}/resume`); + return response.data; + }, + + // Stop scanning session + stop: async (id: string): Promise => { + const response = await api.post(`${BASE_URL}/${id}/stop`); + return response.data; + }, + + // Get scanning results for session + getResults: async (sessionId: string): Promise => { + const response = await api.get(`${BASE_URL}/${sessionId}/results`); + return response.data; + }, + + // Get scanning devices + getDevices: async (): Promise => { + const response = await api.get(`${BASE_URL}/devices`); + return response.data; + }, + + // Get device by ID + getDevice: async (id: string): Promise => { + const response = await api.get(`${BASE_URL}/devices/${id}`); + return response.data; + }, + + // Register scanning device + registerDevice: async (device: Partial): Promise => { + const response = await api.post(`${BASE_URL}/devices`, device); + return response.data; + }, + + // Update device + updateDevice: async (id: string, device: Partial): Promise => { + const response = await api.patch(`${BASE_URL}/devices/${id}`, device); + return response.data; + }, + + // Remove device + removeDevice: async (id: string): Promise => { + await api.delete(`${BASE_URL}/devices/${id}`); + }, + + // Get scanning templates + getTemplates: async (): Promise => { + const response = await api.get(`${BASE_URL}/templates`); + return response.data; + }, + + // Get template by ID + getTemplate: async (id: string): Promise => { + const response = await api.get(`${BASE_URL}/templates/${id}`); + return response.data; + }, + + // Create scanning template + createTemplate: async (template: Partial): Promise => { + const response = await api.post(`${BASE_URL}/templates`, template); + return response.data; + }, + + // Update template + updateTemplate: async (id: string, template: Partial): Promise => { + const response = await api.patch(`${BASE_URL}/templates/${id}`, template); + return response.data; + }, + + // Delete template + deleteTemplate: async (id: string): Promise => { + await api.delete(`${BASE_URL}/templates/${id}`); + }, + + // Get scanning configuration + getConfig: async (): Promise => { + const response = await api.get(`${BASE_URL}/config`); + return response.data; + }, + + // Update scanning configuration + updateConfig: async (config: Partial): Promise => { + const response = await api.patch(`${BASE_URL}/config`, config); + return response.data; + }, + + // Export scanning results + exportResults: async (sessionId: string, format: 'json' | 'csv' | 'pdf'): Promise => { + const response = await api.get(`${BASE_URL}/${sessionId}/export?format=${format}`, { + responseType: 'blob', + }); + return response.data; + }, + + // Import scanning data + importData: async (file: File, sessionId?: string): Promise => { + const formData = new FormData(); + formData.append('file', file); + if (sessionId) formData.append('sessionId', sessionId); + + const response = await api.post(`${BASE_URL}/import`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + return response.data; + }, + + // Validate scanned data + validateData: async (data: any, templateId: string): Promise<{ valid: boolean; errors: string[] }> => { + const response = await api.post<{ valid: boolean; errors: string[] }>( + `${BASE_URL}/validate`, + { data, templateId } + ); + return response.data; + }, +}; diff --git a/src/features/scanning/hooks/useScanning.ts b/src/features/scanning/hooks/useScanning.ts new file mode 100644 index 0000000..d563386 --- /dev/null +++ b/src/features/scanning/hooks/useScanning.ts @@ -0,0 +1,311 @@ +import { useQuery, useMutation, useQueryClient, UseQueryOptions } from '@tanstack/react-query'; +import { scanningApi } from '../api/scanning.api'; +import type { + ScanningSession, + ScanningDevice, + ScanningResult, + ScanningFilters, + ScanningSessionsResponse, + CreateScanningSessionDto, + UpdateScanningSessionDto, + ScanningConfig, + ScanningTemplate, + DeviceStatus, + ScanningStatus, +} from '../../shared/types/api.types'; + +// Query Keys +export const scanningKeys = { + all: ['scanning'] as const, + lists: () => [...scanningKeys.all, 'list'] as const, + list: (filters: ScanningFilters) => [...scanningKeys.lists(), filters] as const, + details: () => [...scanningKeys.all, 'detail'] as const, + detail: (id: string) => [...scanningKeys.details(), id] as const, + devices: () => [...scanningKeys.all, 'devices'] as const, + device: (id: string) => [...scanningKeys.devices(), id] as const, + templates: () => [...scanningKeys.all, 'templates'] as const, + template: (id: string) => [...scanningKeys.templates(), id] as const, + results: (sessionId: string) => [...scanningKeys.detail(sessionId), 'results'] as const, + config: () => [...scanningKeys.all, 'config'] as const, +}; + +// Queries +export const useScanningSessions = ( + filters?: ScanningFilters, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: scanningKeys.list(filters || {}), + queryFn: () => scanningApi.getAll(filters), + ...options, + }); +}; + +export const useScanningSession = ( + id: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: scanningKeys.detail(id), + queryFn: () => scanningApi.getById(id), + enabled: !!id, + ...options, + }); +}; + +export const useScanningDevices = ( + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: scanningKeys.devices(), + queryFn: () => scanningApi.getDevices(), + ...options, + }); +}; + +export const useScanningDevice = ( + id: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: scanningKeys.device(id), + queryFn: () => scanningApi.getDevice(id), + enabled: !!id, + ...options, + }); +}; + +export const useScanningTemplates = ( + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: scanningKeys.templates(), + queryFn: () => scanningApi.getTemplates(), + ...options, + }); +}; + +export const useScanningTemplate = ( + id: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: scanningKeys.template(id), + queryFn: () => scanningApi.getTemplate(id), + enabled: !!id, + ...options, + }); +}; + +export const useScanningResults = ( + sessionId: string, + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: scanningKeys.results(sessionId), + queryFn: () => scanningApi.getResults(sessionId), + enabled: !!sessionId, + ...options, + }); +}; + +export const useScanningConfig = ( + options?: Omit, 'queryKey' | 'queryFn'> +) => { + return useQuery({ + queryKey: scanningKeys.config(), + queryFn: () => scanningApi.getConfig(), + ...options, + }); +}; + +// Mutations +export const useCreateScanningSession = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (data: CreateScanningSessionDto) => scanningApi.create(data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: scanningKeys.lists() }); + }, + }); +}; + +export const useUpdateScanningSession = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ id, data }: { id: string; data: UpdateScanningSessionDto }) => + scanningApi.update(id, data), + onSuccess: (_, { id }) => { + queryClient.invalidateQueries({ queryKey: scanningKeys.lists() }); + queryClient.invalidateQueries({ queryKey: scanningKeys.detail(id) }); + }, + }); +}; + +export const useDeleteScanningSession = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => scanningApi.delete(id), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: scanningKeys.lists() }); + }, + }); +}; + +export const useStartScanningSession = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => scanningApi.start(id), + onSuccess: (_, id) => { + queryClient.invalidateQueries({ queryKey: scanningKeys.detail(id) }); + queryClient.invalidateQueries({ queryKey: scanningKeys.lists() }); + }, + }); +}; + +export const usePauseScanningSession = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => scanningApi.pause(id), + onSuccess: (_, id) => { + queryClient.invalidateQueries({ queryKey: scanningKeys.detail(id) }); + }, + }); +}; + +export const useResumeScanningSession = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => scanningApi.resume(id), + onSuccess: (_, id) => { + queryClient.invalidateQueries({ queryKey: scanningKeys.detail(id) }); + }, + }); +}; + +export const useStopScanningSession = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: string) => scanningApi.stop(id), + onSuccess: (_, id) => { + queryClient.invalidateQueries({ queryKey: scanningKeys.detail(id) }); + queryClient.invalidateQueries({ queryKey: scanningKeys.results(id) }); + }, + }); +}; + +export const useRegisterScanningDevice = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (device: Partial) => scanningApi.registerDevice(device), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: scanningKeys.devices() }); + }, + }); +}; + +export const useUpdateScanningDevice = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ id, device }: { id: string; device: Partial }) => + 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) => scanningApi.createTemplate(template), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: scanningKeys.templates() }); + }, + }); +}; + +export const useUpdateScanningTemplate = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ id, template }: { id: string; template: Partial }) => + 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) => 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), + }); +}; diff --git a/src/features/shared/types/api.types.ts b/src/features/shared/types/api.types.ts new file mode 100644 index 0000000..8f3fdc0 --- /dev/null +++ b/src/features/shared/types/api.types.ts @@ -0,0 +1,561 @@ +// Geolocation Types +export interface Geolocation { + id: string; + country: string; + countryCode: string; + region?: string; + regionCode?: string; + city?: string; + postalCode?: string; + latitude?: number; + longitude?: number; + timezone?: string; + isActive: boolean; + createdAt: string; + updatedAt: string; +} + +export interface GeolocationFilters { + search?: string; + countryId?: string; + regionId?: string; + cityId?: string; + postalCode?: string; + page?: number; + limit?: number; + sortBy?: string; + sortOrder?: 'asc' | 'desc'; +} + +export interface GeolocationsResponse { + data: Geolocation[]; + total: number; + page: number; + limit: number; + totalPages: number; +} + +export interface CreateGeolocationDto { + country: string; + countryCode: string; + region?: string; + regionCode?: string; + city?: string; + postalCode?: string; + latitude?: number; + longitude?: number; + timezone?: string; +} + +export interface UpdateGeolocationDto { + country?: string; + countryCode?: string; + region?: string; + regionCode?: string; + city?: string; + postalCode?: string; + latitude?: number; + longitude?: number; + timezone?: string; + isActive?: boolean; +} + +export interface GeolocationQuery { + address?: string; + postalCode?: string; + city?: string; + country?: string; + limit?: number; +} + +// Dashboard Types +export interface Dashboard { + id: string; + name: string; + description?: string; + userId: string; + isDefault: boolean; + isPublic: boolean; + layout: DashboardLayout; + widgets: DashboardWidget[]; + createdAt: string; + updatedAt: string; +} + +export interface DashboardLayout { + columns: number; + gap: number; + breakpoints: { + lg: number; + md: number; + sm: number; + xs: number; + }; +} + +export interface DashboardWidget { + id: string; + dashboardId: string; + type: WidgetType; + title: string; + config: WidgetConfig; + position: { + x: number; + y: number; + w: number; + h: number; + }; + createdAt: string; + updatedAt: string; +} + +export type WidgetType = + | 'chart' + | 'metric' + | 'table' + | 'list' + | 'calendar' + | 'map' + | 'text' + | 'image' + | 'iframe'; + +export interface WidgetConfig { + [key: string]: any; + dataSource?: string; + refreshInterval?: number; + filters?: Record; + chartType?: 'line' | 'bar' | 'pie' | 'area' | 'scatter'; + metricConfig?: { + label: string; + format: string; + showTrend: boolean; + }; +} + +export interface DashboardFilters { + search?: string; + userId?: string; + isDefault?: boolean; + isPublic?: boolean; + page?: number; + limit?: number; + sortBy?: string; + sortOrder?: 'asc' | 'desc'; +} + +export interface DashboardsResponse { + data: Dashboard[]; + total: number; + page: number; + limit: number; + totalPages: number; +} + +export interface CreateDashboardDto { + name: string; + description?: string; + isDefault?: boolean; + isPublic?: boolean; + layout?: DashboardLayout; +} + +export interface UpdateDashboardDto { + name?: string; + description?: string; + isDefault?: boolean; + isPublic?: boolean; + layout?: DashboardLayout; +} + +export interface DashboardMetrics { + totalWidgets: number; + activeWidgets: number; + lastUpdated: string; + refreshRate: number; + dataPoints: number; +} + +// Scanning Types +export interface ScanningSession { + id: string; + name: string; + deviceId: string; + userId: string; + status: ScanningStatus; + templateId?: string; + config: ScanningConfig; + results: ScanningResult[]; + startedAt?: string; + pausedAt?: string; + stoppedAt?: string; + createdAt: string; + updatedAt: string; +} + +export type ScanningStatus = + | 'idle' + | 'scanning' + | 'paused' + | 'completed' + | 'error' + | 'cancelled'; + +export interface ScanningDevice { + id: string; + name: string; + type: string; + model: string; + serialNumber?: string; + ipAddress?: string; + port?: number; + protocol: string; + config: Record; + status: DeviceStatus; + lastSeen?: string; + isActive: boolean; + createdAt: string; + updatedAt: string; +} + +export type DeviceStatus = + | 'online' + | 'offline' + | 'error' + | 'maintenance'; + +export interface ScanningResult { + id: string; + sessionId: string; + scanIndex: number; + data: Record; + isValid: boolean; + errors?: string[]; + warnings?: string[]; + scannedAt: string; + processedAt?: string; +} + +export interface ScanningFilters { + search?: string; + deviceId?: string; + status?: ScanningStatus; + userId?: string; + startDate?: string; + endDate?: string; + page?: number; + limit?: number; + sortBy?: string; + sortOrder?: 'asc' | 'desc'; +} + +export interface ScanningSessionsResponse { + data: ScanningSession[]; + total: number; + page: number; + limit: number; + totalPages: number; +} + +export interface CreateScanningSessionDto { + name: string; + deviceId: string; + templateId?: string; + config?: ScanningConfig; +} + +export interface UpdateScanningSessionDto { + name?: string; + deviceId?: string; + templateId?: string; + config?: ScanningConfig; +} + +export interface ScanningConfig { + scanInterval: number; + batchSize: number; + maxRetries: number; + timeout: number; + validationRules: ValidationRule[]; + outputFormat: 'json' | 'xml' | 'csv'; + compression: boolean; +} + +export interface ValidationRule { + field: string; + type: 'required' | 'format' | 'range' | 'custom'; + value?: any; + message: string; +} + +export interface ScanningTemplate { + id: string; + name: string; + description?: string; + fields: TemplateField[]; + config: ScanningConfig; + isActive: boolean; + createdAt: string; + updatedAt: string; +} + +export interface TemplateField { + name: string; + type: 'string' | 'number' | 'boolean' | 'date' | 'object' | 'array'; + required: boolean; + validation?: ValidationRule[]; + defaultValue?: any; +} + +// Payment Terminals Types +export interface PaymentTerminal { + id: string; + name: string; + storeId: string; + terminalType: string; + serialNumber: string; + model: string; + manufacturer: string; + ipAddress?: string; + port?: number; + paymentMethods: PaymentMethod[]; + status: TerminalStatus; + isActive: boolean; + config: PaymentTerminalConfig; + lastHeartbeat?: string; + createdAt: string; + updatedAt: string; +} + +export type TerminalStatus = + | 'online' + | 'offline' + | 'error' + | 'maintenance' + | 'initializing'; + +export type PaymentMethod = + | 'credit_card' + | 'debit_card' + | 'cash' + | 'mobile_payment' + | 'bank_transfer' + | 'check' + | 'cryptocurrency'; + +export interface PaymentTerminalTransaction { + id: string; + terminalId: string; + amount: number; + currency: string; + paymentMethod: PaymentMethod; + status: TransactionStatus; + reference?: string; + authorizationCode?: string; + errorCode?: string; + errorMessage?: string; + metadata?: Record; + createdAt: string; + updatedAt: string; +} + +export type TransactionStatus = + | 'pending' + | 'approved' + | 'declined' + | 'cancelled' + | 'refunded' + | 'voided' + | 'error'; + +export interface PaymentTerminalFilters { + search?: string; + storeId?: string; + status?: TerminalStatus; + terminalType?: string; + paymentMethod?: PaymentMethod; + isActive?: boolean; + page?: number; + limit?: number; + sortBy?: string; + sortOrder?: 'asc' | 'desc'; +} + +export interface PaymentTerminalsResponse { + data: PaymentTerminal[]; + total: number; + page: number; + limit: number; + totalPages: number; +} + +export interface CreatePaymentTerminalDto { + name: string; + storeId: string; + terminalType: string; + serialNumber: string; + model: string; + manufacturer: string; + ipAddress?: string; + port?: number; + paymentMethods: PaymentMethod[]; + config?: PaymentTerminalConfig; +} + +export interface UpdatePaymentTerminalDto { + name?: string; + storeId?: string; + terminalType?: string; + model?: string; + manufacturer?: string; + ipAddress?: string; + port?: number; + paymentMethods?: PaymentMethod[]; + config?: PaymentTerminalConfig; + isActive?: boolean; +} + +export interface PaymentTerminalConfig { + connectionTimeout: number; + responseTimeout: number; + maxRetries: number; + retryDelay: number; + enableLogging: boolean; + logLevel: 'error' | 'warn' | 'info' | 'debug'; + customSettings: Record; +} + +// MCP (Model Context Protocol) Types +export interface McpServer { + id: string; + name: string; + description?: string; + protocol: string; + version: string; + endpoint: string; + config: McpServerConfig; + status: McpServerStatus; + isConnected: boolean; + lastConnectedAt?: string; + tools: McpTool[]; + resources: McpResource[]; + prompts: McpPrompt[]; + createdAt: string; + updatedAt: string; +} + +export interface McpServerConfig { + timeout: number; + retries: number; + headers?: Record; + auth?: { + type: 'bearer' | 'basic' | 'api_key'; + token?: string; + username?: string; + password?: string; + apiKey?: string; + }; + customSettings?: Record; +} + +export interface McpServerStatus { + healthy: boolean; + uptime: number; + version: string; + capabilities: string[]; + lastError?: string; +} + +export interface McpTool { + name: string; + description: string; + inputSchema: Record; + isAsync: boolean; +} + +export interface McpResource { + uri: string; + name: string; + description?: string; + mimeType?: string; +} + +export interface McpPrompt { + name: string; + description?: string; + arguments?: McpPromptArgument[]; +} + +export interface McpPromptArgument { + name: string; + description?: string; + required?: boolean; +} + +export interface McpFilters { + search?: string; + status?: string; + protocol?: string; + isConnected?: boolean; + page?: number; + limit?: number; + sortBy?: string; + sortOrder?: 'asc' | 'desc'; +} + +export interface McpServersResponse { + data: McpServer[]; + total: number; + page: number; + limit: number; + totalPages: number; +} + +export interface CreateMcpServerDto { + name: string; + description?: string; + protocol: string; + version: string; + endpoint: string; + config: McpServerConfig; +} + +export interface UpdateMcpServerDto { + name?: string; + description?: string; + protocol?: string; + version?: string; + endpoint?: string; + config?: McpServerConfig; +} + +export interface McpExecutionResult { + success: boolean; + result?: any; + error?: string; + executionTime: number; +} + +export interface McpToolCall { + id: string; + serverId: string; + toolName: string; + args: Record; + status: 'success' | 'error'; + result?: any; + error?: string; + executionTime: number; + createdAt: string; +} + +export interface McpResourceAccess { + id: string; + serverId: string; + resourceUri: string; + action: 'read' | 'write' | 'delete'; + status: 'success' | 'error'; + result?: any; + error?: string; + executionTime: number; + createdAt: string; +}