@startsimpli/hooks 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,223 @@
1
+ import { QueryKey, UseMutationResult } from '@tanstack/react-query';
2
+
3
+ interface TableFilterBase {
4
+ page: number;
5
+ pageSize: number;
6
+ search?: string;
7
+ sortField?: string;
8
+ sortDirection?: 'asc' | 'desc';
9
+ }
10
+ type TableFilters<TFilters extends Record<string, unknown>> = TFilters & TableFilterBase;
11
+ interface UseTableFiltersReturn<TFilters extends Record<string, unknown>> {
12
+ filters: TableFilters<TFilters>;
13
+ setFilter: <K extends keyof TFilters>(key: K, value: TFilters[K]) => void;
14
+ setPage: (page: number) => void;
15
+ setPageSize: (pageSize: number) => void;
16
+ setSearch: (search: string) => void;
17
+ setSort: (field: string, direction: 'asc' | 'desc') => void;
18
+ resetFilters: () => void;
19
+ }
20
+ declare function useTableFilters<TFilters extends Record<string, unknown>>(initial: TFilters & {
21
+ page?: number;
22
+ pageSize?: number;
23
+ sortField?: string;
24
+ sortDirection?: 'asc' | 'desc';
25
+ }): UseTableFiltersReturn<TFilters>;
26
+
27
+ /**
28
+ * Generic filter types for table/list filtering across StartSimpli apps.
29
+ *
30
+ * These types define the shape of a filter configuration that supports
31
+ * AND/OR logic, multiple operators, and nested groups.
32
+ *
33
+ * Domain-specific field types (e.g. FirmFilterField) extend these in each app.
34
+ */
35
+ type FilterOperator = 'equals' | 'not_equals' | 'in' | 'not_in' | 'contains' | 'not_contains' | 'starts_with' | 'ends_with' | 'greater_than' | 'greater_than_or_equal' | 'less_than' | 'less_than_or_equal' | 'is_null' | 'is_not_null' | 'between' | 'not_between' | 'fuzzy_search';
36
+ type FilterValue = string | number | boolean | null | string[] | number[] | [number, number] | [string, string];
37
+ interface FilterCondition {
38
+ field: string;
39
+ operator: FilterOperator;
40
+ value: FilterValue;
41
+ }
42
+ interface FilterGroup {
43
+ logic: 'AND' | 'OR';
44
+ conditions: FilterCondition[];
45
+ groups?: FilterGroup[];
46
+ }
47
+ interface FilterConfig {
48
+ groups: FilterGroup[];
49
+ globalLogic?: 'AND' | 'OR';
50
+ metadata?: {
51
+ name?: string;
52
+ description?: string;
53
+ tags?: string[];
54
+ };
55
+ }
56
+ /** URL-encoded representation of table state */
57
+ interface EncodedFilterState {
58
+ f?: string;
59
+ s?: string;
60
+ d?: string;
61
+ p?: number;
62
+ l?: number;
63
+ }
64
+ interface FilterValidationError {
65
+ field?: string;
66
+ operator?: string;
67
+ value?: FilterValue;
68
+ message: string;
69
+ code: string;
70
+ }
71
+ interface FilterValidationResult {
72
+ isValid: boolean;
73
+ errors: FilterValidationError[];
74
+ warnings: string[];
75
+ }
76
+
77
+ /**
78
+ * URL filter encoding utilities.
79
+ *
80
+ * Provides base64 encode/decode for FilterConfig (so filter state can be
81
+ * stored in URL query params as compact strings) plus helpers for common
82
+ * filter operations.
83
+ *
84
+ * These are pure functions with no React or Next.js dependencies.
85
+ */
86
+
87
+ /**
88
+ * Encode a FilterConfig to a URL-safe base64 string.
89
+ */
90
+ declare function encodeFilterConfig(config: FilterConfig): string;
91
+ /**
92
+ * Decode a URL-safe base64 string back to a FilterConfig.
93
+ * Returns null if the string is invalid or cannot be parsed.
94
+ */
95
+ declare function decodeFilterConfig(encoded: string): FilterConfig | null;
96
+ /**
97
+ * Parse URLSearchParams into an EncodedFilterState.
98
+ */
99
+ declare function parseUrlFilters(params: URLSearchParams): EncodedFilterState;
100
+ /**
101
+ * Build a single-condition FilterConfig.
102
+ */
103
+ declare function createSimpleFilter(field: string, operator: FilterOperator, value: FilterValue): FilterConfig;
104
+ /**
105
+ * Merge multiple FilterConfigs with AND logic.
106
+ */
107
+ declare function mergeFilters(...configs: FilterConfig[]): FilterConfig;
108
+ /**
109
+ * Get a human-readable description of a FilterConfig.
110
+ */
111
+ declare function getFilterDescription(config: FilterConfig): string;
112
+
113
+ interface UseCRUDMutationOptions<TInput, TOutput> {
114
+ invalidateKeys: QueryKey[];
115
+ onSuccess?: (data: TOutput, variables: TInput) => void;
116
+ onError?: (error: Error, variables: TInput) => void;
117
+ }
118
+ declare function useCRUDMutation<TInput, TOutput>(mutationFn: (input: TInput) => Promise<TOutput>, opts: UseCRUDMutationOptions<TInput, TOutput>): UseMutationResult<TOutput, Error, TInput>;
119
+
120
+ interface SavedView {
121
+ id: string;
122
+ name: string;
123
+ isDefault?: boolean;
124
+ createdAt?: string;
125
+ [key: string]: unknown;
126
+ }
127
+ interface UseSavedViewsOptions<T extends SavedView> {
128
+ resource: string;
129
+ loadFn: (resource: string) => Promise<T[]>;
130
+ saveFn: (resource: string, view: Omit<T, 'id' | 'createdAt'>) => Promise<T>;
131
+ updateFn: (resource: string, viewId: string, updates: Partial<T>) => Promise<T>;
132
+ deleteFn: (resource: string, viewId: string) => Promise<void>;
133
+ }
134
+ declare function useSavedViews<T extends SavedView>({ resource, loadFn, saveFn, updateFn, deleteFn, }: UseSavedViewsOptions<T>): {
135
+ views: T[];
136
+ currentViewId: string | null;
137
+ loading: boolean;
138
+ error: string | null;
139
+ saveView: (viewData: Omit<T, "id" | "createdAt">) => Promise<T>;
140
+ updateView: (viewId: string, updates: Partial<T>) => Promise<void>;
141
+ deleteView: (viewId: string) => Promise<void>;
142
+ loadView: (viewId: string) => void;
143
+ getCurrentView: () => T | null;
144
+ refreshViews: () => Promise<void>;
145
+ };
146
+
147
+ declare function useRecentlyViewed<T extends {
148
+ id: string;
149
+ }>(storageKey: string, maxItems?: number): {
150
+ items: T[];
151
+ timestamps: {
152
+ id: string;
153
+ viewedAt: number;
154
+ }[];
155
+ trackView: (item: T) => void;
156
+ clear: () => void;
157
+ };
158
+
159
+ interface WizardState<TStep extends string> {
160
+ currentStep: TStep;
161
+ stepIndex: number;
162
+ totalSteps: number;
163
+ isFirstStep: boolean;
164
+ isLastStep: boolean;
165
+ canGoBack: boolean;
166
+ canGoNext: boolean;
167
+ goTo: (step: TStep) => void;
168
+ next: () => void;
169
+ prev: () => void;
170
+ reset: () => void;
171
+ }
172
+ declare function useWizard<TStep extends string>(steps: readonly TStep[], initialStep?: TStep): WizardState<TStep>;
173
+
174
+ interface CSVColumnMapping {
175
+ csv_column: string;
176
+ target_field: string;
177
+ }
178
+ interface CSVPreviewResult<TField extends string = string> {
179
+ columns: string[];
180
+ sample_rows: Record<string, string>[];
181
+ suggested_mappings?: CSVColumnMapping[];
182
+ total_rows?: number;
183
+ }
184
+ interface CSVImportResult {
185
+ total_rows: number;
186
+ successful: number;
187
+ failed: number;
188
+ errors: Array<{
189
+ row: number;
190
+ message: string;
191
+ }>;
192
+ }
193
+ type CSVImportStep = 'upload' | 'mapping' | 'importing' | 'complete';
194
+ interface UseCSVImportOptions<TField extends string = string> {
195
+ previewFn: (file: File) => Promise<CSVPreviewResult<TField>>;
196
+ importFn: (file: File, mappings: CSVColumnMapping[]) => Promise<CSVImportResult>;
197
+ onSuccess?: (result: CSVImportResult) => void;
198
+ }
199
+ interface UseCSVImportState {
200
+ step: CSVImportStep;
201
+ file: File | null;
202
+ preview: CSVPreviewResult | null;
203
+ mappings: CSVColumnMapping[];
204
+ result: CSVImportResult | null;
205
+ isLoading: boolean;
206
+ error: string | null;
207
+ handleFileSelect: (file: File) => Promise<void>;
208
+ updateMapping: (csvColumn: string, targetField: string) => void;
209
+ startImport: () => Promise<void>;
210
+ reset: () => void;
211
+ goBack: () => void;
212
+ }
213
+ declare function useCSVImport<TField extends string = string>({ previewFn, importFn, onSuccess, }: UseCSVImportOptions<TField>): UseCSVImportState;
214
+ interface UseCSVExportOptions {
215
+ exportFn: () => Promise<Blob | string>;
216
+ filename?: string;
217
+ }
218
+ declare function useCSVExport({ exportFn, filename }: UseCSVExportOptions): {
219
+ exportCSV: () => Promise<void>;
220
+ isExporting: boolean;
221
+ };
222
+
223
+ export { type CSVColumnMapping, type CSVImportResult, type CSVImportStep, type CSVPreviewResult, type EncodedFilterState, type FilterCondition, type FilterConfig, type FilterGroup, type FilterOperator, type FilterValidationError, type FilterValidationResult, type FilterValue, type SavedView as SavedViewEntry, type UseCSVImportState, type WizardState, createSimpleFilter, decodeFilterConfig, encodeFilterConfig, getFilterDescription, mergeFilters, parseUrlFilters, useCRUDMutation, useCSVExport, useCSVImport, useRecentlyViewed, useSavedViews, useTableFilters, useWizard };
@@ -0,0 +1,223 @@
1
+ import { QueryKey, UseMutationResult } from '@tanstack/react-query';
2
+
3
+ interface TableFilterBase {
4
+ page: number;
5
+ pageSize: number;
6
+ search?: string;
7
+ sortField?: string;
8
+ sortDirection?: 'asc' | 'desc';
9
+ }
10
+ type TableFilters<TFilters extends Record<string, unknown>> = TFilters & TableFilterBase;
11
+ interface UseTableFiltersReturn<TFilters extends Record<string, unknown>> {
12
+ filters: TableFilters<TFilters>;
13
+ setFilter: <K extends keyof TFilters>(key: K, value: TFilters[K]) => void;
14
+ setPage: (page: number) => void;
15
+ setPageSize: (pageSize: number) => void;
16
+ setSearch: (search: string) => void;
17
+ setSort: (field: string, direction: 'asc' | 'desc') => void;
18
+ resetFilters: () => void;
19
+ }
20
+ declare function useTableFilters<TFilters extends Record<string, unknown>>(initial: TFilters & {
21
+ page?: number;
22
+ pageSize?: number;
23
+ sortField?: string;
24
+ sortDirection?: 'asc' | 'desc';
25
+ }): UseTableFiltersReturn<TFilters>;
26
+
27
+ /**
28
+ * Generic filter types for table/list filtering across StartSimpli apps.
29
+ *
30
+ * These types define the shape of a filter configuration that supports
31
+ * AND/OR logic, multiple operators, and nested groups.
32
+ *
33
+ * Domain-specific field types (e.g. FirmFilterField) extend these in each app.
34
+ */
35
+ type FilterOperator = 'equals' | 'not_equals' | 'in' | 'not_in' | 'contains' | 'not_contains' | 'starts_with' | 'ends_with' | 'greater_than' | 'greater_than_or_equal' | 'less_than' | 'less_than_or_equal' | 'is_null' | 'is_not_null' | 'between' | 'not_between' | 'fuzzy_search';
36
+ type FilterValue = string | number | boolean | null | string[] | number[] | [number, number] | [string, string];
37
+ interface FilterCondition {
38
+ field: string;
39
+ operator: FilterOperator;
40
+ value: FilterValue;
41
+ }
42
+ interface FilterGroup {
43
+ logic: 'AND' | 'OR';
44
+ conditions: FilterCondition[];
45
+ groups?: FilterGroup[];
46
+ }
47
+ interface FilterConfig {
48
+ groups: FilterGroup[];
49
+ globalLogic?: 'AND' | 'OR';
50
+ metadata?: {
51
+ name?: string;
52
+ description?: string;
53
+ tags?: string[];
54
+ };
55
+ }
56
+ /** URL-encoded representation of table state */
57
+ interface EncodedFilterState {
58
+ f?: string;
59
+ s?: string;
60
+ d?: string;
61
+ p?: number;
62
+ l?: number;
63
+ }
64
+ interface FilterValidationError {
65
+ field?: string;
66
+ operator?: string;
67
+ value?: FilterValue;
68
+ message: string;
69
+ code: string;
70
+ }
71
+ interface FilterValidationResult {
72
+ isValid: boolean;
73
+ errors: FilterValidationError[];
74
+ warnings: string[];
75
+ }
76
+
77
+ /**
78
+ * URL filter encoding utilities.
79
+ *
80
+ * Provides base64 encode/decode for FilterConfig (so filter state can be
81
+ * stored in URL query params as compact strings) plus helpers for common
82
+ * filter operations.
83
+ *
84
+ * These are pure functions with no React or Next.js dependencies.
85
+ */
86
+
87
+ /**
88
+ * Encode a FilterConfig to a URL-safe base64 string.
89
+ */
90
+ declare function encodeFilterConfig(config: FilterConfig): string;
91
+ /**
92
+ * Decode a URL-safe base64 string back to a FilterConfig.
93
+ * Returns null if the string is invalid or cannot be parsed.
94
+ */
95
+ declare function decodeFilterConfig(encoded: string): FilterConfig | null;
96
+ /**
97
+ * Parse URLSearchParams into an EncodedFilterState.
98
+ */
99
+ declare function parseUrlFilters(params: URLSearchParams): EncodedFilterState;
100
+ /**
101
+ * Build a single-condition FilterConfig.
102
+ */
103
+ declare function createSimpleFilter(field: string, operator: FilterOperator, value: FilterValue): FilterConfig;
104
+ /**
105
+ * Merge multiple FilterConfigs with AND logic.
106
+ */
107
+ declare function mergeFilters(...configs: FilterConfig[]): FilterConfig;
108
+ /**
109
+ * Get a human-readable description of a FilterConfig.
110
+ */
111
+ declare function getFilterDescription(config: FilterConfig): string;
112
+
113
+ interface UseCRUDMutationOptions<TInput, TOutput> {
114
+ invalidateKeys: QueryKey[];
115
+ onSuccess?: (data: TOutput, variables: TInput) => void;
116
+ onError?: (error: Error, variables: TInput) => void;
117
+ }
118
+ declare function useCRUDMutation<TInput, TOutput>(mutationFn: (input: TInput) => Promise<TOutput>, opts: UseCRUDMutationOptions<TInput, TOutput>): UseMutationResult<TOutput, Error, TInput>;
119
+
120
+ interface SavedView {
121
+ id: string;
122
+ name: string;
123
+ isDefault?: boolean;
124
+ createdAt?: string;
125
+ [key: string]: unknown;
126
+ }
127
+ interface UseSavedViewsOptions<T extends SavedView> {
128
+ resource: string;
129
+ loadFn: (resource: string) => Promise<T[]>;
130
+ saveFn: (resource: string, view: Omit<T, 'id' | 'createdAt'>) => Promise<T>;
131
+ updateFn: (resource: string, viewId: string, updates: Partial<T>) => Promise<T>;
132
+ deleteFn: (resource: string, viewId: string) => Promise<void>;
133
+ }
134
+ declare function useSavedViews<T extends SavedView>({ resource, loadFn, saveFn, updateFn, deleteFn, }: UseSavedViewsOptions<T>): {
135
+ views: T[];
136
+ currentViewId: string | null;
137
+ loading: boolean;
138
+ error: string | null;
139
+ saveView: (viewData: Omit<T, "id" | "createdAt">) => Promise<T>;
140
+ updateView: (viewId: string, updates: Partial<T>) => Promise<void>;
141
+ deleteView: (viewId: string) => Promise<void>;
142
+ loadView: (viewId: string) => void;
143
+ getCurrentView: () => T | null;
144
+ refreshViews: () => Promise<void>;
145
+ };
146
+
147
+ declare function useRecentlyViewed<T extends {
148
+ id: string;
149
+ }>(storageKey: string, maxItems?: number): {
150
+ items: T[];
151
+ timestamps: {
152
+ id: string;
153
+ viewedAt: number;
154
+ }[];
155
+ trackView: (item: T) => void;
156
+ clear: () => void;
157
+ };
158
+
159
+ interface WizardState<TStep extends string> {
160
+ currentStep: TStep;
161
+ stepIndex: number;
162
+ totalSteps: number;
163
+ isFirstStep: boolean;
164
+ isLastStep: boolean;
165
+ canGoBack: boolean;
166
+ canGoNext: boolean;
167
+ goTo: (step: TStep) => void;
168
+ next: () => void;
169
+ prev: () => void;
170
+ reset: () => void;
171
+ }
172
+ declare function useWizard<TStep extends string>(steps: readonly TStep[], initialStep?: TStep): WizardState<TStep>;
173
+
174
+ interface CSVColumnMapping {
175
+ csv_column: string;
176
+ target_field: string;
177
+ }
178
+ interface CSVPreviewResult<TField extends string = string> {
179
+ columns: string[];
180
+ sample_rows: Record<string, string>[];
181
+ suggested_mappings?: CSVColumnMapping[];
182
+ total_rows?: number;
183
+ }
184
+ interface CSVImportResult {
185
+ total_rows: number;
186
+ successful: number;
187
+ failed: number;
188
+ errors: Array<{
189
+ row: number;
190
+ message: string;
191
+ }>;
192
+ }
193
+ type CSVImportStep = 'upload' | 'mapping' | 'importing' | 'complete';
194
+ interface UseCSVImportOptions<TField extends string = string> {
195
+ previewFn: (file: File) => Promise<CSVPreviewResult<TField>>;
196
+ importFn: (file: File, mappings: CSVColumnMapping[]) => Promise<CSVImportResult>;
197
+ onSuccess?: (result: CSVImportResult) => void;
198
+ }
199
+ interface UseCSVImportState {
200
+ step: CSVImportStep;
201
+ file: File | null;
202
+ preview: CSVPreviewResult | null;
203
+ mappings: CSVColumnMapping[];
204
+ result: CSVImportResult | null;
205
+ isLoading: boolean;
206
+ error: string | null;
207
+ handleFileSelect: (file: File) => Promise<void>;
208
+ updateMapping: (csvColumn: string, targetField: string) => void;
209
+ startImport: () => Promise<void>;
210
+ reset: () => void;
211
+ goBack: () => void;
212
+ }
213
+ declare function useCSVImport<TField extends string = string>({ previewFn, importFn, onSuccess, }: UseCSVImportOptions<TField>): UseCSVImportState;
214
+ interface UseCSVExportOptions {
215
+ exportFn: () => Promise<Blob | string>;
216
+ filename?: string;
217
+ }
218
+ declare function useCSVExport({ exportFn, filename }: UseCSVExportOptions): {
219
+ exportCSV: () => Promise<void>;
220
+ isExporting: boolean;
221
+ };
222
+
223
+ export { type CSVColumnMapping, type CSVImportResult, type CSVImportStep, type CSVPreviewResult, type EncodedFilterState, type FilterCondition, type FilterConfig, type FilterGroup, type FilterOperator, type FilterValidationError, type FilterValidationResult, type FilterValue, type SavedView as SavedViewEntry, type UseCSVImportState, type WizardState, createSimpleFilter, decodeFilterConfig, encodeFilterConfig, getFilterDescription, mergeFilters, parseUrlFilters, useCRUDMutation, useCSVExport, useCSVImport, useRecentlyViewed, useSavedViews, useTableFilters, useWizard };