Phase 0.4: split hooks.ts into separate API modules
This commit is contained in:
+1
-1
@@ -12,7 +12,7 @@
|
||||
| 0.1 | ✅ done | 2026-07-23 | codebase-vs-requirements.md neu geschrieben, security-review-phase2.md Resolution Summary, architecture.md Implementation Status, MASTER-PLAN.md + PROGRESS.md erstellt |
|
||||
| 0.2 | ✅ done | 2026-07-23 | lucide-react installieren + Icons migrieren |
|
||||
| 0.3 | ✅ done | 2026-07-23 | date-fns installieren + Datum-Formatierung |
|
||||
| 0.4 | ⬜ pending | | hooks.ts aufteilen |
|
||||
| 0.4 | ✅ done | 2026-07-23 | hooks.ts aufteilen — 1298 Zeilen → 12 Module + Re-Export-Hub |
|
||||
| 0.5 | ⬜ pending | | Store-Verzeichnis konsolidieren |
|
||||
| 0.6 | ⬜ pending | | Frontend-Bestandsanalyse als Dokument speichern |
|
||||
| 0.7 | ⬜ pending | | UI-Design-Richtlinien erstellen |
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Attachment and address hooks.
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiGet, apiPost, apiPatch, apiDelete, apiClient } from './client';
|
||||
|
||||
export interface Attachment {
|
||||
id: string;
|
||||
entity_type: string;
|
||||
entity_id: string;
|
||||
filename: string;
|
||||
file_path: string;
|
||||
mime_type: string;
|
||||
file_size: number;
|
||||
uploaded_by?: string | null;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface Address {
|
||||
id: string;
|
||||
entity_type: string;
|
||||
entity_id: string;
|
||||
label: string;
|
||||
address_type: string;
|
||||
street: string | null;
|
||||
street_number: string | null;
|
||||
city: string | null;
|
||||
zip: string | null;
|
||||
state: string | null;
|
||||
country: string | null;
|
||||
is_default: boolean;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
// Attachments
|
||||
export function useAttachments(entityType?: string, entityId?: string) {
|
||||
return useQuery({
|
||||
queryKey: ['attachments', entityType, entityId],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams();
|
||||
if (entityType) params.set('entity_type', entityType);
|
||||
if (entityId) params.set('entity_id', entityId);
|
||||
return apiGet<{ items: Attachment[]; total: number }>(`/attachments?${params.toString()}`);
|
||||
},
|
||||
enabled: !!entityType && !!entityId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useUploadAttachment() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ file, entityType, entityId }: { file: File; entityType: string; entityId: string }) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('entity_type', entityType);
|
||||
formData.append('entity_id', entityId);
|
||||
const response = await apiClient.post('/attachments', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['attachments'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteAttachment() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiDelete(`/attachments/${id}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['attachments'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Addresses
|
||||
export function useAddresses(entityType?: string, entityId?: string) {
|
||||
return useQuery({
|
||||
queryKey: ['addresses', entityType, entityId],
|
||||
queryFn: () =>
|
||||
apiGet<{ items: Address[]; total: number }>(
|
||||
`/addresses?entity_type=${entityType}&entity_id=${entityId}`
|
||||
),
|
||||
enabled: !!entityType && !!entityId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateAddress() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: Partial<Address> & { entity_type: string; entity_id: string }) =>
|
||||
apiPost('/addresses', data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['addresses'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateAddress() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: Partial<Address> }) =>
|
||||
apiPatch(`/addresses/${id}`, data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['addresses'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteAddress() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiDelete(`/addresses/${id}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['addresses'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Audit log hooks.
|
||||
*/
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { apiGet } from './client';
|
||||
import { PaginatedResponse } from './types';
|
||||
|
||||
export interface AuditLogEntry {
|
||||
id: string;
|
||||
timestamp: string;
|
||||
user: string;
|
||||
action: string;
|
||||
entity: string;
|
||||
entity_id?: string;
|
||||
details?: string;
|
||||
}
|
||||
|
||||
export function useAuditLog(page = 1, pageSize = 25, filters?: { user?: string; action?: string; entity?: string; dateFrom?: string; dateTo?: string }) {
|
||||
const params = new URLSearchParams({ page: String(page), page_size: String(pageSize) });
|
||||
if (filters?.user) params.set('user', filters.user);
|
||||
if (filters?.action) params.set('action', filters.action);
|
||||
if (filters?.entity) params.set('entity', filters.entity);
|
||||
if (filters?.dateFrom) params.set('date_from', filters.dateFrom);
|
||||
if (filters?.dateTo) params.set('date_to', filters.dateTo);
|
||||
return useQuery({
|
||||
queryKey: ['auditLog', page, pageSize, filters],
|
||||
queryFn: () =>
|
||||
apiGet<PaginatedResponse<AuditLogEntry>>(`/audit-log?${params.toString()}`),
|
||||
retry: false,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Authentication hooks: login, logout, current user, password reset, tenant switching.
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiPost, apiGet, setCsrfToken } from './client';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
|
||||
export interface LoginPayload {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface PasswordResetRequestPayload {
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface PasswordResetConfirmPayload {
|
||||
token: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export function useLogin() {
|
||||
const { setUser, setError } = useAuthStore();
|
||||
return useMutation({
|
||||
mutationFn: (payload: LoginPayload) =>
|
||||
apiPost('/auth/login', payload),
|
||||
onSuccess: (data: any) => {
|
||||
setUser(data.user || data);
|
||||
setError(null);
|
||||
// Store CSRF token for subsequent unsafe requests
|
||||
if (data.csrf_token) {
|
||||
setCsrfToken(data.csrf_token);
|
||||
}
|
||||
},
|
||||
onError: (error: any) => {
|
||||
setError(error.message || 'Login failed');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useLogout() {
|
||||
const { logout } = useAuthStore();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: () => apiPost('/auth/logout'),
|
||||
onSettled: () => {
|
||||
logout();
|
||||
setCsrfToken(null);
|
||||
queryClient.clear();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCurrentUser() {
|
||||
const { setUser, setAuthenticated } = useAuthStore();
|
||||
return useQuery({
|
||||
queryKey: ['currentUser'],
|
||||
queryFn: async () => {
|
||||
const data = await apiGet<any>('/auth/me');
|
||||
setUser(data.user || data);
|
||||
setAuthenticated(true);
|
||||
return data;
|
||||
},
|
||||
retry: false,
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
}
|
||||
|
||||
export function usePasswordResetRequest() {
|
||||
return useMutation({
|
||||
mutationFn: (payload: PasswordResetRequestPayload) =>
|
||||
apiPost('/auth/password-reset/request', payload),
|
||||
});
|
||||
}
|
||||
|
||||
export function usePasswordResetConfirm() {
|
||||
return useMutation({
|
||||
mutationFn: (payload: PasswordResetConfirmPayload) =>
|
||||
apiPost('/auth/password-reset/confirm', payload),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSwitchTenant() {
|
||||
const { setTenant } = useAuthStore();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (tenantId: string) =>
|
||||
apiPost('/auth/switch-tenant', { tenant_id: tenantId }),
|
||||
onSuccess: (data: any) => {
|
||||
setTenant(data.tenant || data);
|
||||
queryClient.invalidateQueries();
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Contact CRUD hooks (legacy contact model — not unified contacts).
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiGet, apiPost, apiPatch, apiDelete, apiPut } from './client';
|
||||
import { PaginatedResponse, Contact, ContactDetail } from './types';
|
||||
import { ContactFolder } from './contactFolders';
|
||||
|
||||
export function useContacts(page = 1, pageSize = 25, search?: string) {
|
||||
const params = new URLSearchParams({ page: String(page), page_size: String(pageSize) });
|
||||
if (search) params.set('search', search);
|
||||
return useQuery({
|
||||
queryKey: ['contacts', page, pageSize, search],
|
||||
queryFn: () =>
|
||||
apiGet<PaginatedResponse<Contact>>(`/contacts?${params.toString()}`),
|
||||
});
|
||||
}
|
||||
|
||||
export function useContact(id?: string) {
|
||||
return useQuery({
|
||||
queryKey: ['contacts', id],
|
||||
queryFn: () => apiGet<ContactDetail>(`/contacts/${id}`),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateContact() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: Partial<Contact> & { company_ids?: string[] }) =>
|
||||
apiPost('/contacts', data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['contacts'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateContact() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: Partial<Contact> & { company_ids?: string[] } }) =>
|
||||
apiPatch(`/contacts/${id}`, data),
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['contacts'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['contacts', variables.id] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteContact() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiDelete(`/contacts/${id}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['contacts'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ── Contact Folders ──
|
||||
|
||||
export function useContactFolders() {
|
||||
return useQuery({
|
||||
queryKey: ['contactFolders'],
|
||||
queryFn: () => apiGet<ContactFolder[]>('/contact-folders'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateContactFolder() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: { name: string; parent_id?: string }) => apiPost('/contact-folders', data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['contactFolders'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateContactFolder() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: { name?: string; parent_id?: string | null; sort_order?: number } }) =>
|
||||
apiPut(`/contact-folders/${id}`, data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['contactFolders'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteContactFolder() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiDelete(`/contact-folders/${id}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['contactFolders'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useMoveContactToFolder() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ contactId, folderId }: { contactId: string; folderId: string | null }) =>
|
||||
apiPut(`/contact-folders/contacts/${contactId}/move`, { folder_id: folderId }),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['unifiedContacts'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['contactFolders'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Group and group member hooks.
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiGet, apiPost, apiPatch, apiDelete } from './client';
|
||||
|
||||
export interface Group {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
permissions: Record<string, any>;
|
||||
denied_permissions: string[];
|
||||
field_permissions: Record<string, any>;
|
||||
permission_version: number;
|
||||
}
|
||||
|
||||
export interface GroupMember {
|
||||
user_id: string;
|
||||
email: string;
|
||||
name: string;
|
||||
is_active: boolean;
|
||||
}
|
||||
|
||||
export function useGroups() {
|
||||
return useQuery({
|
||||
queryKey: ['groups'],
|
||||
queryFn: () => apiGet<{ items: Group[] }>('/groups'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateGroup() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: Partial<Group>) => apiPost('/groups', data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['groups'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateGroup() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: Partial<Group> }) =>
|
||||
apiPatch(`/groups/${id}`, data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['groups'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteGroup() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiDelete(`/groups/${id}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['groups'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useGroupMembers(groupId: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ['groupMembers', groupId],
|
||||
queryFn: () => apiGet<{ items: GroupMember[] }>(`/groups/${groupId}/members`),
|
||||
enabled: !!groupId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useAddGroupMember() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ groupId, userId }: { groupId: string; userId: string }) =>
|
||||
apiPost(`/groups/${groupId}/members`, { user_id: userId }),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['groupMembers'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useRemoveGroupMember() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ groupId, userId }: { groupId: string; userId: string }) =>
|
||||
apiDelete(`/groups/${groupId}/members/${userId}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['groupMembers'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUserGroups(userId: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ['userGroups', userId],
|
||||
queryFn: () => apiGet<{ items: Group[] }>(`/groups/user/${userId}`),
|
||||
enabled: !!userId,
|
||||
});
|
||||
}
|
||||
+35
-1197
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Notification hooks: list, unread count, mark read, delete, types, preferences.
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiGet, apiPatch, apiDelete } from './client';
|
||||
import { PaginatedResponse } from './types';
|
||||
|
||||
export interface NotificationTypeItem {
|
||||
type_key: string;
|
||||
plugin_name: string;
|
||||
category: string;
|
||||
label: string;
|
||||
description: string | null;
|
||||
is_enabled_by_default: boolean;
|
||||
is_enabled: boolean;
|
||||
}
|
||||
|
||||
export function useNotifications() {
|
||||
return useQuery({
|
||||
queryKey: ['notifications'],
|
||||
queryFn: () => apiGet<PaginatedResponse<any>>('/notifications'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUnreadNotificationCount() {
|
||||
return useQuery({
|
||||
queryKey: ['notifications', 'unread-count'],
|
||||
queryFn: () => apiGet<number>('/notifications/unread-count'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useMarkNotificationRead() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiPatch(`/notifications/${id}/read`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['notifications'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteNotification() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiDelete(`/notifications/${id}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['notifications'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useNotificationTypes() {
|
||||
return useQuery({
|
||||
queryKey: ['notification-types'],
|
||||
queryFn: () => apiGet<{ items: NotificationTypeItem[] }>('/notifications/types'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useNotificationPreferences() {
|
||||
return useQuery({
|
||||
queryKey: ['notification-preferences'],
|
||||
queryFn: () => apiGet<{ items: { type_key: string; is_enabled: boolean }[] }>('/notifications/preferences'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateNotificationPreference() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ typeKey, isEnabled }: { typeKey: string; isEnabled: boolean }) =>
|
||||
apiPatch(`/notifications/preferences/${typeKey}`, { is_enabled: isEnabled }),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['notification-preferences'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['notification-types'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Plugin management hooks: list, install, activate, deactivate, uninstall.
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiGet, apiPost, apiDelete } from './client';
|
||||
|
||||
export interface Plugin {
|
||||
name: string;
|
||||
display_name?: string;
|
||||
description?: string;
|
||||
version?: string;
|
||||
status: 'discovered' | 'installed' | 'active' | 'inactive';
|
||||
installed?: boolean;
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
export function usePlugins() {
|
||||
return useQuery({
|
||||
queryKey: ['plugins'],
|
||||
queryFn: async () => {
|
||||
const data = await apiGet<any>('/plugins');
|
||||
return data;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useInstallPlugin() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (name: string) => apiPost(`/plugins/${name}/install`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['plugins'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useActivatePlugin() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (name: string) => apiPost(`/plugins/${name}/activate`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['plugins'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeactivatePlugin() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (name: string) => apiPost(`/plugins/${name}/deactivate`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['plugins'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUninstallPlugin() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ name, removeData }: { name: string; removeData: boolean }) =>
|
||||
apiDelete(`/plugins/${name}?remove_data=${removeData}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['plugins'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Role and permission hooks.
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiGet, apiPost, apiPatch, apiDelete } from './client';
|
||||
|
||||
export interface Role {
|
||||
id: string;
|
||||
name: string;
|
||||
permissions: Record<string, any>;
|
||||
field_permissions?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface PermissionItem {
|
||||
key: string;
|
||||
label: string;
|
||||
category: string;
|
||||
plugin_name?: string;
|
||||
}
|
||||
|
||||
export interface FieldDefinition {
|
||||
module: string;
|
||||
field: string;
|
||||
label: string;
|
||||
sensitivity: string;
|
||||
}
|
||||
|
||||
export interface PermissionsResponse {
|
||||
system: PermissionItem[];
|
||||
plugins: PermissionItem[];
|
||||
all: PermissionItem[];
|
||||
field_definitions?: FieldDefinition[];
|
||||
}
|
||||
|
||||
export function useRoles() {
|
||||
return useQuery({
|
||||
queryKey: ['roles'],
|
||||
queryFn: async () => {
|
||||
const data = await apiGet<any>('/roles');
|
||||
return data;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function usePermissions() {
|
||||
return useQuery({
|
||||
queryKey: ['permissions'],
|
||||
queryFn: () => apiGet<PermissionsResponse>('/roles/permissions'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateRole() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: { name: string; permissions: Record<string, any>; field_permissions: Record<string, any> }) =>
|
||||
apiPost('/roles', data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['roles'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateRole() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: { name?: string; permissions?: Record<string, any>; field_permissions?: Record<string, any> } }) =>
|
||||
apiPatch(`/roles/${id}`, data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['roles'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteRole() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiDelete(`/roles/${id}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['roles'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* System settings, currency, tax, and sequence hooks.
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiGet, apiPost, apiPatch, apiDelete, apiClient } from './client';
|
||||
|
||||
export interface Currency {
|
||||
id: string;
|
||||
code: string;
|
||||
name: string;
|
||||
symbol: string;
|
||||
is_default: boolean;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface TaxRate {
|
||||
id: string;
|
||||
name: string;
|
||||
rate: number;
|
||||
is_default: boolean;
|
||||
country?: string | null;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface Sequence {
|
||||
id: string;
|
||||
name: string;
|
||||
prefix: string;
|
||||
next_number: number;
|
||||
padding: number;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface SystemSettings {
|
||||
id?: string;
|
||||
company_name: string;
|
||||
company_legal_form?: string | null;
|
||||
company_street: string;
|
||||
company_city: string;
|
||||
company_zip: string;
|
||||
company_country: string;
|
||||
tax_number?: string | null;
|
||||
vat_id?: string | null;
|
||||
iban?: string | null;
|
||||
bic?: string | null;
|
||||
bank_name?: string | null;
|
||||
ceo?: string | null;
|
||||
trade_register?: string | null;
|
||||
default_currency_id?: string | null;
|
||||
default_tax_id?: string | null;
|
||||
invoice_prefix: string;
|
||||
quote_prefix: string;
|
||||
payment_terms_days: number;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
// System Settings
|
||||
export function useSystemSettings() {
|
||||
return useQuery({
|
||||
queryKey: ['systemSettings'],
|
||||
queryFn: () => apiGet<SystemSettings>('/system-settings'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateSystemSettings() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: Partial<SystemSettings>) => apiClient.put('/system-settings', data).then(r => r.data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['systemSettings'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Currencies
|
||||
export function useCurrencies() {
|
||||
return useQuery({
|
||||
queryKey: ['currencies'],
|
||||
queryFn: () => apiGet<{ items: Currency[]; total: number }>('/currencies'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateCurrency() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: Partial<Currency>) => apiPost('/currencies', data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['currencies'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateCurrency() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: Partial<Currency> }) =>
|
||||
apiPatch(`/currencies/${id}`, data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['currencies'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteCurrency() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiDelete(`/currencies/${id}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['currencies'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Taxes
|
||||
export function useTaxes() {
|
||||
return useQuery({
|
||||
queryKey: ['taxes'],
|
||||
queryFn: () => apiGet<{ items: TaxRate[]; total: number }>('/taxes'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateTax() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: Partial<TaxRate>) => apiPost('/taxes', data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['taxes'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateTax() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: Partial<TaxRate> }) =>
|
||||
apiPatch(`/taxes/${id}`, data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['taxes'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteTax() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiDelete(`/taxes/${id}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['taxes'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Sequences
|
||||
export function useSequences() {
|
||||
return useQuery({
|
||||
queryKey: ['sequences'],
|
||||
queryFn: () => apiGet<{ items: Sequence[]; total: number }>('/sequences'),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateSequence() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: Partial<Sequence>) => apiPost('/sequences', data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['sequences'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateSequence() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: Partial<Sequence> }) =>
|
||||
apiPatch(`/sequences/${id}`, data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['sequences'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteSequence() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiDelete(`/sequences/${id}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['sequences'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Shared type definitions used across multiple API modules.
|
||||
*/
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
items: T[];
|
||||
total: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
}
|
||||
|
||||
export interface Company {
|
||||
id: string;
|
||||
name: string;
|
||||
account_number?: string | null;
|
||||
industry?: string | null;
|
||||
phone?: string | null;
|
||||
email?: string | null;
|
||||
website?: string | null;
|
||||
description?: string | null;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface CompanyDetail extends Company {
|
||||
contacts?: Contact[];
|
||||
}
|
||||
|
||||
export interface Contact {
|
||||
id: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
email: string;
|
||||
phone?: string | null;
|
||||
position?: string | null;
|
||||
company_ids?: string[];
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface ContactDetail extends Contact {
|
||||
companies?: Company[];
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
/**
|
||||
* Unified Contact (Rentman-style) hooks and standalone API functions.
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiGet, apiPost, apiPut, apiDelete } from './client';
|
||||
import { PaginatedResponse } from './types';
|
||||
|
||||
export interface ContactPerson {
|
||||
id: string;
|
||||
contact_id: string;
|
||||
displayname: string;
|
||||
firstname?: string | null;
|
||||
middle_name?: string | null;
|
||||
lastname?: string | null;
|
||||
function?: string | null;
|
||||
phone?: string | null;
|
||||
mobilephone?: string | null;
|
||||
email?: string | null;
|
||||
street?: string | null;
|
||||
number?: string | null;
|
||||
postalcode?: string | null;
|
||||
city?: string | null;
|
||||
state?: string | null;
|
||||
country?: string | null;
|
||||
tags?: string | null;
|
||||
custom?: Record<string, any> | null;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface UnifiedContact {
|
||||
id: string;
|
||||
type: 'company' | 'person';
|
||||
displayname: string;
|
||||
name?: string | null;
|
||||
firstname?: string | null;
|
||||
surname?: string | null;
|
||||
surfix?: string | null;
|
||||
ext_name_line?: string | null;
|
||||
gender?: string | null;
|
||||
code?: string | null;
|
||||
accounting_code?: string | null;
|
||||
vendor_accounting_code?: string | null;
|
||||
// Mailing address
|
||||
mailing_street?: string | null;
|
||||
mailing_number?: string | null;
|
||||
mailing_unit_number?: string | null;
|
||||
mailing_district?: string | null;
|
||||
mailing_extra_address_line?: string | null;
|
||||
mailing_postalcode?: string | null;
|
||||
mailing_city?: string | null;
|
||||
mailing_state?: string | null;
|
||||
mailing_country?: string | null;
|
||||
// Visit address
|
||||
visit_street?: string | null;
|
||||
visit_number?: string | null;
|
||||
visit_unit_number?: string | null;
|
||||
visit_district?: string | null;
|
||||
visit_extra_address_line?: string | null;
|
||||
visit_postalcode?: string | null;
|
||||
visit_city?: string | null;
|
||||
visit_state?: string | null;
|
||||
// Invoice address
|
||||
invoice_street?: string | null;
|
||||
invoice_number?: string | null;
|
||||
invoice_unit_number?: string | null;
|
||||
invoice_district?: string | null;
|
||||
invoice_extra_address_line?: string | null;
|
||||
invoice_postalcode?: string | null;
|
||||
invoice_city?: string | null;
|
||||
invoice_state?: string | null;
|
||||
invoice_country?: string | null;
|
||||
country?: string | null;
|
||||
// Communication
|
||||
phone_1?: string | null;
|
||||
phone_2?: string | null;
|
||||
email_1?: string | null;
|
||||
email_2?: string | null;
|
||||
website?: string | null;
|
||||
// Financial
|
||||
vat_code?: string | null;
|
||||
fiscal_code?: string | null;
|
||||
commerce_code?: string | null;
|
||||
purchase_number?: string | null;
|
||||
bic?: string | null;
|
||||
bank_account?: string | null;
|
||||
// Discounts
|
||||
discount_crew: number;
|
||||
discount_transport: number;
|
||||
discount_rental: number;
|
||||
discount_sale: number;
|
||||
discount_subrent: number;
|
||||
discount_total: number;
|
||||
// Geo
|
||||
latitude?: number | null;
|
||||
longitude?: number | null;
|
||||
// Notes
|
||||
projectnote?: string | null;
|
||||
projectnote_title?: string | null;
|
||||
contact_warning?: string | null;
|
||||
tags?: string | null;
|
||||
image?: string | null;
|
||||
custom?: Record<string, any> | null;
|
||||
folder_id?: string | null;
|
||||
default_person_id?: string | null;
|
||||
admin_contactperson_id?: string | null;
|
||||
contact_persons?: ContactPerson[];
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export function useUnifiedContacts(
|
||||
page = 1,
|
||||
pageSize = 25,
|
||||
search?: string,
|
||||
contactType?: string,
|
||||
sortBy?: string,
|
||||
sortOrder?: string,
|
||||
folderId?: string,
|
||||
) {
|
||||
const params = new URLSearchParams({ page: String(page), page_size: String(pageSize) });
|
||||
if (search) params.set('search', search);
|
||||
if (contactType) params.set('type', contactType);
|
||||
if (sortBy) params.set('sort_by', sortBy);
|
||||
if (sortOrder) params.set('sort_order', sortOrder);
|
||||
if (folderId) params.set('folder_id', folderId);
|
||||
return useQuery({
|
||||
queryKey: ['unifiedContacts', page, pageSize, search, contactType, sortBy, sortOrder, folderId],
|
||||
queryFn: () =>
|
||||
apiGet<PaginatedResponse<UnifiedContact>>(`/contacts?${params.toString()}`),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUnifiedContact(id?: string) {
|
||||
return useQuery({
|
||||
queryKey: ['unifiedContacts', id],
|
||||
queryFn: () => apiGet<UnifiedContact & { contact_persons: ContactPerson[] }>(`/contacts/${id}`),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateUnifiedContact() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: Partial<UnifiedContact>) => apiPost('/contacts', data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['unifiedContacts'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateUnifiedContact() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: Partial<UnifiedContact> }) =>
|
||||
apiPut(`/contacts/${id}`, data),
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['unifiedContacts'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['unifiedContacts', variables.id] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteUnifiedContact() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, hard }: { id: string; hard?: boolean }) =>
|
||||
apiDelete(`/contacts/${id}${hard ? '?hard=true' : ''}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['unifiedContacts'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useContactPersons(contactId?: string) {
|
||||
return useQuery({
|
||||
queryKey: ['unifiedContacts', contactId, 'persons'],
|
||||
queryFn: () => apiGet<{ items: ContactPerson[] }>(`/contacts/${contactId}/persons`),
|
||||
enabled: !!contactId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateContactPerson() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ contactId, data }: { contactId: string; data: Partial<ContactPerson> }) =>
|
||||
apiPost(`/contacts/${contactId}/persons`, data),
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['unifiedContacts'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['unifiedContacts', variables.contactId] });
|
||||
queryClient.invalidateQueries({ queryKey: ['unifiedContacts', variables.contactId, 'persons'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateContactPerson() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ contactId, personId, data }: { contactId: string; personId: string; data: Partial<ContactPerson> }) =>
|
||||
apiPut(`/contacts/${contactId}/persons/${personId}`, data),
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['unifiedContacts'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['unifiedContacts', variables.contactId] });
|
||||
queryClient.invalidateQueries({ queryKey: ['unifiedContacts', variables.contactId, 'persons'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteContactPerson() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ contactId, personId }: { contactId: string; personId: string }) =>
|
||||
apiDelete(`/contacts/${contactId}/persons/${personId}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['unifiedContacts'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Standalone API functions (for non-hook usage)
|
||||
export async function fetchContacts(
|
||||
page = 1,
|
||||
pageSize = 25,
|
||||
search?: string,
|
||||
contactType?: string,
|
||||
sortBy?: string,
|
||||
sortOrder?: string,
|
||||
): Promise<PaginatedResponse<UnifiedContact>> {
|
||||
const params = new URLSearchParams({ page: String(page), page_size: String(pageSize) });
|
||||
if (search) params.set('search', search);
|
||||
if (contactType) params.set('type', contactType);
|
||||
if (sortBy) params.set('sort_by', sortBy);
|
||||
if (sortOrder) params.set('sort_order', sortOrder);
|
||||
return apiGet<PaginatedResponse<UnifiedContact>>(`/contacts?${params.toString()}`);
|
||||
}
|
||||
|
||||
export async function fetchContact(id: string): Promise<UnifiedContact & { contact_persons: ContactPerson[] }> {
|
||||
return apiGet<UnifiedContact & { contact_persons: ContactPerson[] }>(`/contacts/${id}`);
|
||||
}
|
||||
|
||||
export async function createContact(data: Partial<UnifiedContact>): Promise<UnifiedContact> {
|
||||
return apiPost<UnifiedContact>('/contacts', data);
|
||||
}
|
||||
|
||||
export async function updateContact(id: string, data: Partial<UnifiedContact>): Promise<UnifiedContact> {
|
||||
return apiPut<UnifiedContact>(`/contacts/${id}`, data);
|
||||
}
|
||||
|
||||
export async function deleteContact(id: string, hard?: boolean): Promise<void> {
|
||||
await apiDelete(`/contacts/${id}${hard ? '?hard=true' : ''}`);
|
||||
}
|
||||
|
||||
export async function fetchContactPersons(contactId: string): Promise<{ items: ContactPerson[] }> {
|
||||
return apiGet<{ items: ContactPerson[] }>(`/contacts/${contactId}/persons`);
|
||||
}
|
||||
|
||||
export async function createContactPerson(contactId: string, data: Partial<ContactPerson>): Promise<ContactPerson> {
|
||||
return apiPost<ContactPerson>(`/contacts/${contactId}/persons`, data);
|
||||
}
|
||||
|
||||
export async function updateContactPerson(contactId: string, personId: string, data: Partial<ContactPerson>): Promise<ContactPerson> {
|
||||
return apiPut<ContactPerson>(`/contacts/${contactId}/persons/${personId}`, data);
|
||||
}
|
||||
|
||||
export async function deleteContactPerson(contactId: string, personId: string): Promise<void> {
|
||||
await apiDelete(`/contacts/${contactId}/persons/${personId}`);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* User CRUD hooks.
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiGet, apiPost, apiPatch, apiDelete } from './client';
|
||||
import { PaginatedResponse } from './types';
|
||||
|
||||
export function useUsers(page = 1, pageSize = 25) {
|
||||
return useQuery({
|
||||
queryKey: ['users', page, pageSize],
|
||||
queryFn: () =>
|
||||
apiGet<PaginatedResponse<any>>(`/users?page=${page}&page_size=${pageSize}`),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUser(id?: string) {
|
||||
return useQuery({
|
||||
queryKey: ['users', id],
|
||||
queryFn: () => apiGet<any>(`/users/${id}`),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateUser() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: any) => apiPost('/users', data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['users'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateUser() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: any }) =>
|
||||
apiPatch(`/users/${id}`, data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['users'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteUser() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiDelete(`/users/${id}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['users'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user