a7e3890634
- Tags UI: TagsPage (CRUD, color picker), TagBadge, TagSelector (multi-select, inline creation) - Custom Fields Backend: model, schema, service, routes, migration 0041 - Custom Fields Frontend: CustomFieldsPage (definitions CRUD), CustomFieldRenderer (dynamic field rendering) - Custom Fields: _collect_custom_field_definitions() extended to merge DB definitions with plugin definitions - Notifications Bell: NotificationBell (30s polling, unread badge), NotificationDropdown, NotificationItem - NotificationBell integrated into TopBar - Routes: /tags, /settings/custom-fields registered - Settings nav: Custom Fields entry added - Menu items: Tags added to automation plugin manifest
110 lines
2.9 KiB
TypeScript
110 lines
2.9 KiB
TypeScript
/**
|
|
* Custom field definitions API hooks — CRUD for entity-level field definitions.
|
|
*/
|
|
|
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import { apiGet, apiPost, apiPatch, apiDelete } from './client';
|
|
|
|
export type CustomFieldType = 'text' | 'number' | 'date' | 'select' | 'multiselect' | 'boolean';
|
|
|
|
export type CustomFieldEntity = 'contact' | 'company';
|
|
|
|
export interface CustomFieldDefinition {
|
|
id: string;
|
|
tenant_id: string;
|
|
entity: string;
|
|
name: string;
|
|
label: string;
|
|
field_type: CustomFieldType;
|
|
options: string[];
|
|
default_value: any;
|
|
required: boolean;
|
|
is_active: boolean;
|
|
sort_order: number;
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
export interface CustomFieldDefinitionCreate {
|
|
entity: string;
|
|
name: string;
|
|
label: string;
|
|
field_type: CustomFieldType;
|
|
options?: string[];
|
|
default_value?: any;
|
|
required?: boolean;
|
|
is_active?: boolean;
|
|
sort_order?: number;
|
|
}
|
|
|
|
export interface CustomFieldDefinitionUpdate {
|
|
name?: string;
|
|
label?: string;
|
|
field_type?: CustomFieldType;
|
|
options?: string[];
|
|
default_value?: any;
|
|
required?: boolean;
|
|
is_active?: boolean;
|
|
sort_order?: number;
|
|
}
|
|
|
|
export interface CustomFieldDefinitionsResponse {
|
|
items: CustomFieldDefinition[];
|
|
total: number;
|
|
}
|
|
|
|
/**
|
|
* Fetch custom field definitions, optionally filtered by entity.
|
|
*/
|
|
export function useCustomFieldDefinitions(entity?: string) {
|
|
return useQuery({
|
|
queryKey: ['custom-field-definitions', entity ?? 'all'],
|
|
queryFn: () => {
|
|
const params = entity ? `?entity=${encodeURIComponent(entity)}` : '';
|
|
return apiGet<CustomFieldDefinitionsResponse>(`/custom-fields/definitions${params}`);
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create a new custom field definition.
|
|
*/
|
|
export function useCreateCustomFieldDefinition() {
|
|
const queryClient = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: (data: CustomFieldDefinitionCreate) =>
|
|
apiPost<CustomFieldDefinition>('/custom-fields/definitions', data),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['custom-field-definitions'] });
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update an existing custom field definition.
|
|
*/
|
|
export function useUpdateCustomFieldDefinition() {
|
|
const queryClient = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: ({ id, data }: { id: string; data: CustomFieldDefinitionUpdate }) =>
|
|
apiPatch<CustomFieldDefinition>(`/custom-fields/definitions/${id}`, data),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['custom-field-definitions'] });
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Delete a custom field definition.
|
|
*/
|
|
export function useDeleteCustomFieldDefinition() {
|
|
const queryClient = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: (id: string) =>
|
|
apiDelete<void>(`/custom-fields/definitions/${id}`),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['custom-field-definitions'] });
|
|
},
|
|
});
|
|
}
|