feat(5.22): Saved Searches / Smart Lists — reusable filters for list views
- SavedFilter model with TenantMixin (name, entity_type, filter_criteria JSONB, user_id)
- Routes: GET/POST /saved-filters, DELETE /saved-filters/{id} with RBAC
- Alembic migration 0029_saved_filters creates saved_filters table
- Frontend: SavedFilters.tsx component with save/load/delete UI
- Frontend: api/savedFilters.ts with React Query hooks
- Integrated into ContactsListPage as example
- i18n keys for savedFilters.* in de.json and en.json
- Tests: test_saved_filters.py (9 tests) + SavedFilters.test.tsx (3 tests)
- Registered SavedFilter in conftest.py
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Saved filters API hooks — CRUD for reusable filter criteria.
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiGet, apiPost, apiDelete } from './client';
|
||||
|
||||
export interface SavedFilter {
|
||||
id: string;
|
||||
name: string;
|
||||
entity_type: string;
|
||||
filter_criteria: Record<string, any>;
|
||||
user_id: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface SavedFilterCreateInput {
|
||||
name: string;
|
||||
entity_type: string;
|
||||
filter_criteria: Record<string, any>;
|
||||
}
|
||||
|
||||
export function useSavedFilters(entityType?: string) {
|
||||
const params = new URLSearchParams();
|
||||
if (entityType) params.set('entity_type', entityType);
|
||||
return useQuery({
|
||||
queryKey: ['saved-filters', entityType],
|
||||
queryFn: () => apiGet<SavedFilter[]>(`/saved-filters?${params.toString()}`),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateSavedFilter() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: SavedFilterCreateInput) => apiPost<SavedFilter>('/saved-filters', data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['saved-filters'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteSavedFilter() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiDelete(`/saved-filters/${id}`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['saved-filters'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user