namirasoft-account-react 1.5.0 → 1.5.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.
Files changed (45) hide show
  1. package/dist/NSARouterMaker.js +4 -2
  2. package/dist/NSARouterMaker.js.map +1 -1
  3. package/dist/components/NSATable.d.ts +5 -0
  4. package/dist/components/NSATable.js +45 -11
  5. package/dist/components/NSATable.js.map +1 -1
  6. package/dist/components/quickfilter/NSAFilterBoxBase.d.ts +9 -0
  7. package/dist/components/quickfilter/NSAFilterBoxBase.js +25 -0
  8. package/dist/components/quickfilter/NSAFilterBoxBase.js.map +1 -1
  9. package/dist/components/quickfilter/NSAFilterBoxBoolean.d.ts +1 -0
  10. package/dist/components/quickfilter/NSAFilterBoxBoolean.js +12 -2
  11. package/dist/components/quickfilter/NSAFilterBoxBoolean.js.map +1 -1
  12. package/dist/components/quickfilter/NSAFilterBoxDate.js +7 -5
  13. package/dist/components/quickfilter/NSAFilterBoxDate.js.map +1 -1
  14. package/dist/components/quickfilter/NSAFilterBoxDateTime.js +7 -5
  15. package/dist/components/quickfilter/NSAFilterBoxDateTime.js.map +1 -1
  16. package/dist/components/quickfilter/NSAFilterBoxEnum.js +2 -2
  17. package/dist/components/quickfilter/NSAFilterBoxEnum.js.map +1 -1
  18. package/dist/components/quickfilter/NSAFilterBoxNumber.js +11 -5
  19. package/dist/components/quickfilter/NSAFilterBoxNumber.js.map +1 -1
  20. package/dist/components/quickfilter/NSAFilterBoxString.js +3 -3
  21. package/dist/components/quickfilter/NSAFilterBoxString.js.map +1 -1
  22. package/dist/components/quickfilter/NSAFilterBoxTime.js +6 -5
  23. package/dist/components/quickfilter/NSAFilterBoxTime.js.map +1 -1
  24. package/dist/components/quickfilter/NSAQuickFilterBar.d.ts +2 -0
  25. package/dist/components/quickfilter/NSAQuickFilterBar.js +26 -2
  26. package/dist/components/quickfilter/NSAQuickFilterBar.js.map +1 -1
  27. package/dist/layouts/NSALayout.js +1 -4
  28. package/dist/layouts/NSALayout.js.map +1 -1
  29. package/dist/pages/NSAHomePage.d.ts +6 -2
  30. package/dist/pages/NSAHomePage.js +4 -4
  31. package/dist/pages/NSAHomePage.js.map +1 -1
  32. package/package.json +4 -4
  33. package/src/NSARouterMaker.tsx +4 -2
  34. package/src/components/NSATable.tsx +503 -455
  35. package/src/components/quickfilter/NSAFilterBoxBase.tsx +34 -0
  36. package/src/components/quickfilter/NSAFilterBoxBoolean.tsx +13 -1
  37. package/src/components/quickfilter/NSAFilterBoxDate.tsx +8 -2
  38. package/src/components/quickfilter/NSAFilterBoxDateTime.tsx +8 -3
  39. package/src/components/quickfilter/NSAFilterBoxEnum.tsx +2 -2
  40. package/src/components/quickfilter/NSAFilterBoxNumber.tsx +11 -2
  41. package/src/components/quickfilter/NSAFilterBoxString.tsx +3 -1
  42. package/src/components/quickfilter/NSAFilterBoxTime.tsx +7 -2
  43. package/src/components/quickfilter/NSAQuickFilterBar.tsx +30 -2
  44. package/src/layouts/NSALayout.tsx +1 -4
  45. package/src/pages/NSAHomePage.tsx +26 -5
@@ -1,7 +1,7 @@
1
- import { FilterItem, FilterItemColumnType, NamingConvention, ObjectService, SearchOperation, SortItem } from "namirasoft-core";
1
+ import { EncodingOperation, FilterItem, FilterItemColumnType, NamingConvention, ObjectService, SearchOperation, SortItem } from "namirasoft-core";
2
2
  import { CategoryRow, FieldRow } from "namirasoft-field";
3
3
  import { FilterItemColumnTypeBuilder, VariableType } from "namirasoft-schema";
4
- import { IBaseComponentProps, NSButtonProps, NSFilterBox, NSFilterBoxItems, NSFilterBoxProps_Table, NSTable, TableColumnInfo, TableRowInfo } from "namirasoft-site-react";
4
+ import { IBaseComponentProps, NSButtonProps, NSFilterBox, NSFilterBoxItems, NSFilterBoxProps_Table, NSSpace, NSSpaceSizeType, NSTable, TableColumnInfo, TableRowInfo } from "namirasoft-site-react";
5
5
  import { Component, ReactNode, createRef } from "react";
6
6
  import { IEntityInfo } from "../IEntityInfo";
7
7
  import { NSACacheService } from "../NSACacheService";
@@ -12,488 +12,536 @@ import { NSASortDialog } from "./NSASortDialog";
12
12
 
13
13
  export interface NSATableUIProps<RowType>
14
14
  {
15
- getRowAttributes?: (row_value: RowType, row_index: number, res: { rows: RowType[]; count: number; } | null) => { [key: string]: any };
16
- getColumnAttributes?: (column: TableColumnInfo) => { [key: string]: any };
17
- getCellFormattedValue?: (value: any, column: TableColumnInfo, row: TableRowInfo<RowType>) => any;
18
- search_box?: {
19
- exclude?: {
20
- show?: {
21
- table?: (table: string) => boolean;
22
- column?: (table: string, column: string) => boolean;
23
- },
24
- load_value?: (table: string, column: string) => boolean;
25
- }
26
- };
15
+ getRowAttributes?: (row_value: RowType, row_index: number, res: { rows: RowType[]; count: number; } | null) => { [key: string]: any };
16
+ getColumnAttributes?: (column: TableColumnInfo) => { [key: string]: any };
17
+ getCellFormattedValue?: (value: any, column: TableColumnInfo, row: TableRowInfo<RowType>) => any;
18
+ search_box?: {
19
+ exclude?: {
20
+ show?: {
21
+ table?: (table: string) => boolean;
22
+ column?: (table: string, column: string) => boolean;
23
+ },
24
+ load_value?: (table: string, column: string) => boolean;
25
+ }
26
+ };
27
27
  }
28
28
 
29
29
  export interface NSATableProps<RowType extends { id: string }, RowTypeInput = RowType> extends IBaseComponentProps
30
30
  {
31
- entity: IEntityInfo<RowType, RowTypeInput>;
32
- ctf: {
33
- enabled: boolean;
34
- fields: FieldRow[] | null;
35
- categories: CategoryRow[] | null;
36
- };
37
- url: { getQuery(name: string, base64?: boolean, remove?: boolean): string };
38
- ui?: NSATableUIProps<RowType>;
39
- checkbox?: boolean;
40
- buttons?: NSButtonProps[];
41
- onSelectionChanged?: () => void;
31
+ entity: IEntityInfo<RowType, RowTypeInput>;
32
+ ctf: {
33
+ enabled: boolean;
34
+ fields: FieldRow[] | null;
35
+ categories: CategoryRow[] | null;
36
+ };
37
+ url: { getQuery(name: string, base64?: boolean, remove?: boolean): string };
38
+ ui?: NSATableUIProps<RowType>;
39
+ checkbox?: boolean;
40
+ buttons?: NSButtonProps[];
41
+ onSelectionChanged?: () => void;
42
42
  }
43
43
 
44
44
  interface NSATableState<RowType>
45
45
  {
46
- res: { rows: RowType[]; count: number; } | null;
47
- sorts: SortItem[];
48
- visible_column_keys: string[];
49
- quick_filter_configs: QuickFilterConfig[];
50
- show_sort_dialog: boolean;
51
- show_reorder_dialog: boolean;
52
- show_quick_filter_dialog: boolean;
53
- filter_revision: number;
46
+ res: { rows: RowType[]; count: number; } | null;
47
+ sorts: SortItem[];
48
+ visible_column_keys: string[];
49
+ quick_filter_configs: QuickFilterConfig[];
50
+ show_sort_dialog: boolean;
51
+ show_reorder_dialog: boolean;
52
+ show_quick_filter_dialog: boolean;
53
+ filter_revision: number;
54
54
  }
55
55
 
56
56
  export class NSATable<RowType extends { id: string }, RowTypeInput = RowType>
57
- extends Component<NSATableProps<RowType, RowTypeInput>, NSATableState<RowType>>
57
+ extends Component<NSATableProps<RowType, RowTypeInput>, NSATableState<RowType>>
58
58
  {
59
- public NSTable_Main = createRef<NSTable<RowType>>();
60
- public NSFilterBox_Top = createRef<NSFilterBox>();
61
- public NSAQuickFilterBar_Ref = createRef<NSAQuickFilterBar>();
59
+ public NSTable_Main = createRef<NSTable<RowType>>();
60
+ public NSFilterBox_Top = createRef<NSFilterBox>();
61
+ public NSAQuickFilterBar_Ref = createRef<NSAQuickFilterBar>();
62
62
 
63
- constructor(props: NSATableProps<RowType, RowTypeInput>)
64
- {
65
- super(props);
66
- this.state = {
67
- res: null,
68
- sorts: [],
69
- visible_column_keys: [],
70
- quick_filter_configs: [],
71
- show_sort_dialog: false,
72
- show_reorder_dialog: false,
73
- show_quick_filter_dialog: false,
74
- filter_revision: 0,
75
- };
76
- }
63
+ constructor(props: NSATableProps<RowType, RowTypeInput>)
64
+ {
65
+ super(props);
77
66
 
78
- override componentDidMount()
79
- {
80
- const visible = this.NSTable_Main.current?.getVisibleColumns() ?? [];
81
- if (visible.length > 0)
82
- this.setState({ visible_column_keys: visible.map(v => v.table + "." + v.column) });
67
+ let quick_filter_configs = NSACacheService.loadQuickFilters(props.entity.name);
68
+ if (quick_filter_configs.length === 0)
69
+ quick_filter_configs = this.buildDefaultQuickFilters();
70
+ this.state = {
71
+ res: null,
72
+ sorts: [],
73
+ visible_column_keys: [],
74
+ quick_filter_configs,
75
+ show_sort_dialog: false,
76
+ show_reorder_dialog: false,
77
+ show_quick_filter_dialog: false,
78
+ filter_revision: 0,
79
+ };
80
+ }
83
81
 
84
- let quick_filter_configs = NSACacheService.loadQuickFilters(this.props.entity.name);
85
- if (quick_filter_configs.length === 0)
86
- quick_filter_configs = this.buildDefaultQuickFilters();
87
- // Re-render so the filter chips reflect any incoming filter items.
88
- this.setState(prev => ({ quick_filter_configs, filter_revision: prev.filter_revision + 1 }));
89
- }
82
+ override componentDidMount()
83
+ {
84
+ const visible = this.NSTable_Main.current?.getVisibleColumns() ?? [];
85
+ if (visible.length > 0)
86
+ this.setState({ visible_column_keys: visible.map(v => v.table + "." + v.column) });
90
87
 
91
- private getColumns(): TableColumnInfo[]
92
- {
93
- let columns = Object.values(this.props.entity.client.getColumns()).flat();
94
- columns.forEach(col =>
95
- {
96
- col.text = NamingConvention.auto.convert(col.text, NamingConvention.Pascal_Case_Space);
97
- });
98
- return columns;
99
- }
88
+ this.setState(prev => ({ filter_revision: prev.filter_revision + 1 }));
89
+ }
100
90
 
101
- private buildDefaultQuickFilters(): QuickFilterConfig[]
102
- {
103
- const getDefaults = this.props.entity.client.getQuickFilters;
104
- if (!getDefaults)
105
- return [];
91
+ private getColumns(): TableColumnInfo[]
92
+ {
93
+ let columns = Object.values(this.props.entity.client.getColumns()).flat();
94
+ columns.forEach(col =>
95
+ {
96
+ col.text = NamingConvention.auto.convert(col.text, NamingConvention.Pascal_Case_Space);
97
+ });
98
+ return columns;
99
+ }
106
100
 
107
- const columns = this.getColumns();
108
- const ans: QuickFilterConfig[] = [];
109
- for (const def of getDefaults())
110
- {
111
- const col = columns.find(c => c.name === def.column);
112
- if (!col)
113
- continue;
114
- const columnType = new ObjectService(col.type).getEnum(VariableType, VariableType.Any);
115
- ans.push({
116
- id: `default-${col.table.name}-${col.name}`,
117
- column: {
118
- table: { name: col.table.name, text: col.table.text },
119
- name: col.name,
120
- text: col.text,
121
- type: col.type,
122
- columnType,
123
- },
124
- operatorName: (def as { operator?: string }).operator,
125
- });
126
- }
127
- return ans;
128
- }
101
+ private buildDefaultQuickFilters(): QuickFilterConfig[]
102
+ {
103
+ const getDefaults = this.props.entity.client.getQuickFilters;
104
+ if (!getDefaults)
105
+ return [];
129
106
 
130
- private filterItemTypeConverter(type: string): FilterItemColumnType
131
- {
132
- let t = new ObjectService(type).getEnum(VariableType, VariableType.Any);
133
- let builder = new FilterItemColumnTypeBuilder(t);
134
- return builder.runSynch(() => { }) ?? FilterItemColumnType.Unknown;
135
- }
107
+ const columns = this.getColumns();
108
+ const ans: QuickFilterConfig[] = [];
109
+ for (const def of getDefaults())
110
+ {
111
+ const col = columns.find(c => c.name === def.column);
112
+ if (!col)
113
+ continue;
114
+ const columnType = new ObjectService(col.type).getEnum(VariableType, VariableType.Any);
115
+ ans.push({
116
+ id: `default-${col.table.name}-${col.name}`,
117
+ column: {
118
+ table: { name: col.table.name, text: col.table.text },
119
+ name: col.name,
120
+ text: col.text,
121
+ type: col.type,
122
+ columnType,
123
+ },
124
+ operatorName: (def as { operator?: string }).operator,
125
+ });
126
+ }
127
+ return ans;
128
+ }
136
129
 
137
- private getIncomingFilterItems(): FilterItem[] | undefined
138
- {
139
- try
140
- {
141
- let encoded = this.props.url.getQuery("filters");
142
- if (encoded)
143
- return FilterItem.decode(encoded) ?? undefined;
144
- } catch (error)
145
- {
146
- }
147
- return undefined;
148
- }
130
+ private filterItemTypeConverter(type: string): FilterItemColumnType
131
+ {
132
+ let t = new ObjectService(type).getEnum(VariableType, VariableType.Any);
133
+ let builder = new FilterItemColumnTypeBuilder(t);
134
+ return builder.runSynch(() => { }) ?? FilterItemColumnType.Unknown;
135
+ }
149
136
 
150
- public reload(page: number | null, size: number | null): Promise<void> | undefined
151
- {
152
- return this.NSTable_Main.current?.reload(page, size);
153
- }
154
- public getSelectedIDs(): string[]
155
- {
156
- return this.NSTable_Main.current?.getSelectedIDs() ?? [];
157
- }
158
- public getSelectedRows(): RowType[]
159
- {
160
- return this.NSTable_Main.current?.getSelectedRows() ?? [];
161
- }
162
- public setSelectedIDs(ids: string[], rows: RowType[], callback?: () => void)
163
- {
164
- this.NSTable_Main.current?.setSelectedIDs(ids, rows, callback);
165
- }
166
- public getSortItems(): SortItem[]
167
- {
168
- return this.NSTable_Main.current?.getSortItems() ?? [];
169
- }
170
- public setSortItems(items: SortItem[] | null)
171
- {
172
- this.NSTable_Main.current?.setSortItems(items);
173
- }
174
- public getVisibleColumns(): { table: string, column: string }[]
175
- {
176
- return this.NSTable_Main.current?.getVisibleColumns() ?? [];
177
- }
178
- public setVisibleColumns(visible_columns: { table: string, column: string }[])
179
- {
180
- this.NSTable_Main.current?.setVisibleColumns(visible_columns);
181
- }
182
- public getFilterBoxItems(): FilterItem[]
183
- {
184
- return this.NSFilterBox_Top.current?.getFilterItems() ?? [];
185
- }
186
- public getQuickFilterItems(): FilterItem[]
187
- {
188
- return this.NSAQuickFilterBar_Ref.current?.getFilterItems() ?? [];
189
- }
190
- public getFilterItems(): FilterItem[]
191
- {
192
- return [...this.getFilterBoxItems(), ...this.getQuickFilterItems()];
193
- }
137
+ private getIncomingFilterItems(): FilterItem[] | undefined
138
+ {
139
+ try
140
+ {
141
+ let encoded = this.props.url.getQuery("filters");
142
+ if (encoded)
143
+ return FilterItem.decode(encoded) ?? undefined;
144
+ } catch (error)
145
+ {
146
+ }
147
+ return undefined;
148
+ }
194
149
 
195
- private reloadFromFilters()
196
- {
197
- this.NSTable_Main.current?.setSelectedIDs([], []);
198
- this.NSTable_Main.current?.reload(null, null);
199
- }
150
+ private itemMatchesQuickFilter(item: FilterItem): boolean
151
+ {
152
+ return this.state.quick_filter_configs.some(c =>
153
+ c.column.table.name === item.table.name && c.column.name === item.column.name);
154
+ }
155
+ private getIncomingBoxItems(): FilterItem[] | undefined
156
+ {
157
+ let items = this.getIncomingFilterItems();
158
+ if (!items)
159
+ return undefined;
160
+ return items.filter(i => !this.itemMatchesQuickFilter(i));
161
+ }
162
+ private getIncomingQuickItems(): FilterItem[]
163
+ {
164
+ let items = this.getIncomingFilterItems();
165
+ if (!items)
166
+ return [];
167
+ return items.filter(i => this.itemMatchesQuickFilter(i));
168
+ }
200
169
 
201
- private onFilterBoxChanged = () =>
202
- {
203
- this.reloadFromFilters();
204
- this.setState(prev => ({ filter_revision: prev.filter_revision + 1 }));
205
- };
170
+ private encodeFilters(items: FilterItem[]): string
171
+ {
172
+ let command = items.map(item =>
173
+ {
174
+ let key = item.operator.name.replace(/\s+/g, "").toLowerCase();
175
+ let values = item.values.map(v => (v ?? "").toString().replace(/;/g, "\\;"));
176
+ return [item.table.name, item.column.name, item.not ? "1" : "0", key, ...values].join(";");
177
+ }).join(";");
178
+ return EncodingOperation.Base64Encode(command);
179
+ }
206
180
 
207
- private buildButtons(): NSButtonProps[]
208
- {
209
- const buttons: NSButtonProps[] = [
210
- {
211
- title: "",
212
- icon: { src: "https://static.namirasoft.com/image/concept/grip-vertical/blue.svg", alt: "reorder" },
213
- attributes: { title: "Reorder" },
214
- onClick: {
215
- action: () => this.setState({ show_reorder_dialog: true, visible_column_keys: this.getVisibleColumns().map(c => c.table + "." + c.column) }),
216
- showLoading: false,
217
- },
218
- },
219
- {
220
- title: "",
221
- icon: { src: "https://static.namirasoft.com/image/concept/arrow-down-short-wide/blue.svg", alt: "sort" },
222
- attributes: { title: "Sort" },
223
- onClick: {
224
- action: () => this.setState({ show_sort_dialog: true, sorts: this.getSortItems() }),
225
- showLoading: false,
226
- },
227
- },
228
- {
229
- title: "",
230
- icon: { src: "https://static.namirasoft.com/image/concept/sliders/blue.svg", alt: "filter" },
231
- attributes: { title: "Quick Filters" },
232
- onClick: {
233
- action: () => this.setState({ show_quick_filter_dialog: true }),
234
- showLoading: false,
235
- },
236
- },
237
- ];
238
- return [...buttons, ...(this.props.buttons ?? [])];
239
- }
181
+ private syncQueryFromFilters()
182
+ {
183
+ let items = this.getFilterItems();
184
+ let url = new URL(window.location.href);
185
+ let encoded = items.length > 0 ? this.encodeFilters(items) : "";
186
+ if (encoded)
187
+ url.searchParams.set("filters", encoded);
188
+ else
189
+ url.searchParams.delete("filters");
190
+ window.history.replaceState({}, "", url.toString());
191
+ }
240
192
 
241
- private renderFilterHeader(): ReactNode
242
- {
243
- return (
244
- <NSFilterBox
245
- ref={this.NSFilterBox_Top}
246
- filterItems={this.getIncomingFilterItems()}
247
- getTables={() =>
248
- {
249
- return this.props.entity.tables.map(table =>
250
- {
251
- let columns = table.table.getColumns().map(c => ({ id: c.name, ...c, type: this.filterItemTypeConverter(c.type) }));
252
- columns = columns.filter(c =>
253
- {
254
- if (this.props.ui?.search_box?.exclude?.show?.column)
255
- if (this.props.ui?.search_box?.exclude.show?.column(table.table.name, c.name))
256
- return false;
257
- return true;
258
- });
259
- return { name: table.table.name, text: table.table.text, columns, required: table.required };
260
- }).filter(t =>
261
- {
262
- if (this.props.ui?.search_box?.exclude?.show?.table)
263
- if (this.props.ui?.search_box?.exclude.show.table(t.name))
264
- return false;
265
- return true;
266
- });
267
- }}
268
- getMoreTables={async () =>
269
- {
270
- let ans: NSFilterBoxProps_Table[] = [];
271
- if (this.props.ctf.enabled)
272
- try
273
- {
274
- if (this.props.ctf.fields)
275
- if (this.props.ctf.fields.length > 0)
276
- {
277
- let name = this.props.entity.name + "_field";
278
- ans.push({
279
- name,
280
- text: "Fields",
281
- columns: [{
282
- id: "field_id",
283
- name: "field_id",
284
- text: "ID",
285
- type: FilterItemColumnType.Unknown,
286
- getValues: async (search) =>
287
- {
288
- return this.props.ctf.fields?.filter(c => SearchOperation.match(c.id, search)).map(c => { return { value: c.id, title: c.id } }) ?? [];
289
- }
290
- }, {
291
- id: "field_name",
292
- name: "field_id",
293
- text: "Name",
294
- type: FilterItemColumnType.Unknown,
295
- getValues: async (search) =>
296
- {
297
- return this.props.ctf.fields?.filter(c => SearchOperation.match(c.name, search)).map(c => { return { value: c.id, title: c.name } }) ?? [];
298
- }
299
- }, {
300
- id: "field_value",
301
- name: "value",
302
- text: "Value",
303
- type: FilterItemColumnType.Unknown
304
- },
305
- ...this.props.ctf.fields.map(f =>
306
- {
307
- return {
308
- id: f.id,
309
- name: f.id,
310
- text: f.name,
311
- type: this.filterItemTypeConverter(f.type.type.type),
312
- getValues: async (search: string) =>
313
- {
314
- let values = await this.props.entity.server.value_list(name, "value", search, f.id, null);
315
- return values.map(v => { return { value: v, title: v } });
316
- }
317
- };
318
- }),],
319
- required: false
320
- });
321
- }
193
+ public reload(page: number | null, size: number | null): Promise<void> | undefined
194
+ {
195
+ return this.NSTable_Main.current?.reload(page, size);
196
+ }
197
+ public getSelectedIDs(): string[]
198
+ {
199
+ return this.NSTable_Main.current?.getSelectedIDs() ?? [];
200
+ }
201
+ public getSelectedRows(): RowType[]
202
+ {
203
+ return this.NSTable_Main.current?.getSelectedRows() ?? [];
204
+ }
205
+ public setSelectedIDs(ids: string[], rows: RowType[], callback?: () => void)
206
+ {
207
+ this.NSTable_Main.current?.setSelectedIDs(ids, rows, callback);
208
+ }
209
+ public getSortItems(): SortItem[]
210
+ {
211
+ return this.NSTable_Main.current?.getSortItems() ?? [];
212
+ }
213
+ public setSortItems(items: SortItem[] | null)
214
+ {
215
+ this.NSTable_Main.current?.setSortItems(items);
216
+ }
217
+ public getVisibleColumns(): { table: string, column: string }[]
218
+ {
219
+ return this.NSTable_Main.current?.getVisibleColumns() ?? [];
220
+ }
221
+ public setVisibleColumns(visible_columns: { table: string, column: string }[])
222
+ {
223
+ this.NSTable_Main.current?.setVisibleColumns(visible_columns);
224
+ }
225
+ public getFilterBoxItems(): FilterItem[]
226
+ {
227
+ return this.NSFilterBox_Top.current?.getFilterItems() ?? [];
228
+ }
229
+ public getQuickFilterItems(): FilterItem[]
230
+ {
231
+ return this.NSAQuickFilterBar_Ref.current?.getFilterItems() ?? [];
232
+ }
233
+ public getFilterItems(): FilterItem[]
234
+ {
235
+ return [...this.getFilterBoxItems(), ...this.getQuickFilterItems()];
236
+ }
322
237
 
323
- if (this.props.ctf.categories)
324
- if (this.props.ctf.categories.length > 0)
325
- ans.push({
326
- name: this.props.entity.name + "_category",
327
- text: "Categories",
328
- columns: [{
329
- id: "category_id",
330
- name: "category_id",
331
- text: "ID",
332
- type: FilterItemColumnType.Unknown,
333
- getValues: async (search) =>
334
- {
335
- return this.props.ctf.categories?.filter(c => SearchOperation.match(c.id, search)).map(c => { return { value: c.id, title: c.id } }) ?? [];
336
- }
337
- }, {
338
- id: "category_name",
339
- name: "category_id",
340
- text: "Name",
341
- type: FilterItemColumnType.Unknown,
342
- getValues: async (search) =>
343
- {
344
- return this.props.ctf.categories?.filter(c => SearchOperation.match(c.name, search)).map(c => { return { value: c.id, title: c.name } }) ?? [];
345
- }
346
- }],
347
- required: false
348
- });
238
+ private reloadFromFilters()
239
+ {
240
+ this.syncQueryFromFilters();
241
+ this.NSTable_Main.current?.setSelectedIDs([], []);
242
+ this.NSTable_Main.current?.reload(null, null);
243
+ }
349
244
 
350
- } catch (error)
351
- {
352
- }
353
- // todo add tags
354
- return ans;
355
- }}
356
- getValues={async (table, column, value) =>
357
- {
358
- if (this.props.ui?.search_box?.exclude?.load_value)
359
- if (this.props.ui?.search_box?.exclude?.load_value(table.name, column.name))
360
- return [];
361
- if (column.type === FilterItemColumnType.Boolean)
362
- {
363
- const col = this.getColumns().find(c => c.table.name === table.name && c.name === column.name);
364
- return [true, false].map(b =>
365
- {
366
- const formatted = col ? col.formatter.format(b, col, { value: {} } as any, true) : null;
367
- return { value: String(b), title: formatted != null ? String(formatted) : String(b) };
368
- });
369
- }
370
- let values = await this.props.entity.server.value_list(table.name, column.name, value, null, null);
371
- return values.map(v => { return { value: v, title: v } });
372
- }}
373
- onChanged={this.onFilterBoxChanged}
374
- />
375
- );
376
- }
245
+ private onFilterBoxChanged = () =>
246
+ {
247
+ this.reloadFromFilters();
248
+ this.setState(prev => ({ filter_revision: prev.filter_revision + 1 }));
249
+ };
377
250
 
378
- private getOrderedColumns(): TableColumnInfo[]
379
- {
380
- const all = this.getColumns();
381
- const vk = this.state.visible_column_keys;
382
- if (vk.length === 0)
383
- return all;
384
- const ordered = vk.map(k => all.find(c => c.table.name + "." + c.name === k)).filter((c): c is TableColumnInfo => !!c);
385
- const rest = all.filter(c => !vk.includes(c.table.name + "." + c.name));
386
- return [...ordered, ...rest];
387
- }
251
+ private buildButtons(): NSButtonProps[]
252
+ {
253
+ const buttons: NSButtonProps[] = [
254
+ {
255
+ title: "",
256
+ icon: { src: "https://static.namirasoft.com/image/concept/grip-vertical/blue.svg", alt: "reorder" },
257
+ attributes: { title: "Reorder" },
258
+ onClick: {
259
+ action: () => this.setState({ show_reorder_dialog: true, visible_column_keys: this.getVisibleColumns().map(c => c.table + "." + c.column) }),
260
+ showLoading: false,
261
+ },
262
+ },
263
+ {
264
+ title: "",
265
+ icon: { src: "https://static.namirasoft.com/image/concept/arrow-down-short-wide/blue.svg", alt: "sort" },
266
+ attributes: { title: "Sort" },
267
+ onClick: {
268
+ action: () => this.setState({ show_sort_dialog: true, sorts: this.getSortItems() }),
269
+ showLoading: false,
270
+ },
271
+ },
272
+ {
273
+ title: "",
274
+ icon: { src: "https://static.namirasoft.com/image/concept/sliders/blue.svg", alt: "filter" },
275
+ attributes: { title: "Quick Filters" },
276
+ onClick: {
277
+ action: () => this.setState({ show_quick_filter_dialog: true }),
278
+ showLoading: false,
279
+ },
280
+ },
281
+ ];
282
+ return [...buttons, ...(this.props.buttons ?? [])];
283
+ }
388
284
 
389
- override render(): ReactNode
390
- {
391
- const filterBoxItems = this.NSFilterBox_Top.current?.getFilterItems() ?? [];
392
- const filterChips = filterBoxItems.length > 0 && (
393
- <NSFilterBoxItems
394
- filterItems={filterBoxItems}
395
- onClearFilterItems={() => this.NSFilterBox_Top.current?.setFilterItems([])}
396
- onRemoveFilterItem={(item) => this.NSFilterBox_Top.current?.removeFilterItem(item)}
397
- />
398
- );
285
+ private renderFilterHeader(): ReactNode
286
+ {
287
+ return (
288
+ <NSFilterBox
289
+ ref={this.NSFilterBox_Top}
290
+ filterItems={this.getIncomingBoxItems()}
291
+ getTables={() =>
292
+ {
293
+ return this.props.entity.tables.map(table =>
294
+ {
295
+ let columns = table.table.getColumns().map(c => ({ id: c.name, ...c, type: this.filterItemTypeConverter(c.type) }));
296
+ columns = columns.filter(c =>
297
+ {
298
+ if (this.props.ui?.search_box?.exclude?.show?.column)
299
+ if (this.props.ui?.search_box?.exclude.show?.column(table.table.name, c.name))
300
+ return false;
301
+ return true;
302
+ });
303
+ return { name: table.table.name, text: table.table.text, columns, required: table.required };
304
+ }).filter(t =>
305
+ {
306
+ if (this.props.ui?.search_box?.exclude?.show?.table)
307
+ if (this.props.ui?.search_box?.exclude.show.table(t.name))
308
+ return false;
309
+ return true;
310
+ });
311
+ }}
312
+ getMoreTables={async () =>
313
+ {
314
+ let ans: NSFilterBoxProps_Table[] = [];
315
+ if (this.props.ctf.enabled)
316
+ try
317
+ {
318
+ if (this.props.ctf.fields)
319
+ if (this.props.ctf.fields.length > 0)
320
+ {
321
+ let name = this.props.entity.name + "_field";
322
+ ans.push({
323
+ name,
324
+ text: "Fields",
325
+ columns: [{
326
+ id: "field_id",
327
+ name: "field_id",
328
+ text: "ID",
329
+ type: FilterItemColumnType.Unknown,
330
+ getValues: async (search) =>
331
+ {
332
+ return this.props.ctf.fields?.filter(c => SearchOperation.match(c.id, search)).map(c => { return { value: c.id, title: c.id } }) ?? [];
333
+ }
334
+ }, {
335
+ id: "field_name",
336
+ name: "field_id",
337
+ text: "Name",
338
+ type: FilterItemColumnType.Unknown,
339
+ getValues: async (search) =>
340
+ {
341
+ return this.props.ctf.fields?.filter(c => SearchOperation.match(c.name, search)).map(c => { return { value: c.id, title: c.name } }) ?? [];
342
+ }
343
+ }, {
344
+ id: "field_value",
345
+ name: "value",
346
+ text: "Value",
347
+ type: FilterItemColumnType.Unknown
348
+ },
349
+ ...this.props.ctf.fields.map(f =>
350
+ {
351
+ return {
352
+ id: f.id,
353
+ name: f.id,
354
+ text: f.name,
355
+ type: this.filterItemTypeConverter(f.type.type.type),
356
+ getValues: async (search: string) =>
357
+ {
358
+ let values = await this.props.entity.server.value_list(name, "value", search, f.id, null);
359
+ return values.map(v => { return { value: v, title: v } });
360
+ }
361
+ };
362
+ }),],
363
+ required: false
364
+ });
365
+ }
399
366
 
400
- const quickFilterBar = this.state.quick_filter_configs.length > 0 && (
401
- <NSAQuickFilterBar
402
- key={this.state.quick_filter_configs.map(c => c.id).join("-")}
403
- ref={this.NSAQuickFilterBar_Ref}
404
- configs={this.state.quick_filter_configs}
405
- getValues={async (tableName, columnName, search) =>
406
- {
407
- const values = await this.props.entity.server.value_list(tableName, columnName, search, null, null);
408
- const col = this.getColumns().find(c => c.table.name === tableName && c.name === columnName);
409
- return values.map(v =>
410
- {
411
- const formatted = col ? col.formatter.format(v, col, { value: {} } as any, true) : null;
412
- return { value: v, title: formatted != null ? String(formatted) : v };
413
- });
414
- }}
415
- onChanged={() => this.reloadFromFilters()}
416
- />
417
- );
367
+ if (this.props.ctf.categories)
368
+ if (this.props.ctf.categories.length > 0)
369
+ ans.push({
370
+ name: this.props.entity.name + "_category",
371
+ text: "Categories",
372
+ columns: [{
373
+ id: "category_id",
374
+ name: "category_id",
375
+ text: "ID",
376
+ type: FilterItemColumnType.Unknown,
377
+ getValues: async (search) =>
378
+ {
379
+ return this.props.ctf.categories?.filter(c => SearchOperation.match(c.id, search)).map(c => { return { value: c.id, title: c.id } }) ?? [];
380
+ }
381
+ }, {
382
+ id: "category_name",
383
+ name: "category_id",
384
+ text: "Name",
385
+ type: FilterItemColumnType.Unknown,
386
+ getValues: async (search) =>
387
+ {
388
+ return this.props.ctf.categories?.filter(c => SearchOperation.match(c.name, search)).map(c => { return { value: c.id, title: c.name } }) ?? [];
389
+ }
390
+ }],
391
+ required: false
392
+ });
418
393
 
419
- return (
420
- <>
421
- <NSTable<RowType>
422
- id={this.props.id}
423
- classList={this.props.classList}
424
- style={this.props.style}
425
- ref={this.NSTable_Main}
426
- name={NamingConvention.lower_case_dash.convert(this.props.entity.name, NamingConvention.Pascal_Case_Space)}
427
- checkbox={this.props.checkbox ?? true}
428
- headerChildren={this.renderFilterHeader()}
429
- buttons={this.buildButtons()}
430
- columns={this.getOrderedColumns()}
431
- getRows={async (page, size, sorts) =>
432
- {
433
- let res: { rows: RowType[], count: number } = { rows: [], count: 0 };
434
- if (this.props.entity.server.list)
435
- res = await this.props.entity.server.list(this.getFilterItems(), page, size, sorts, null);
436
- this.setState({ res });
437
- return res;
438
- }}
439
- getRowKey={(row) => row.value.id.toString()}
440
- getRowAttributes={(row_value, row_index) => this.props.ui?.getRowAttributes?.(row_value, row_index, this.state.res) ?? {}}
441
- getColumnAttributes={this.props.ui?.getColumnAttributes}
442
- getCellFormattedValue={this.props.ui?.getCellFormattedValue}
443
- onVisibleColumnsChanged={(visible_columns) => this.setState({ visible_column_keys: visible_columns.map(c => c.table + "." + c.column) })}
444
- onSelectedIDsChanged={() => this.props.onSelectionChanged?.()}
445
- >
446
- {filterChips}
447
- {quickFilterBar}
448
- </NSTable>
449
- {
450
- this.state.show_sort_dialog &&
451
- <NSASortDialog
452
- sorts={this.state.sorts}
453
- getColumns={() => this.getColumns()}
454
- onCancel={() => this.setState({ show_sort_dialog: false })}
455
- onApply={sorts =>
456
- {
457
- this.setSortItems(sorts);
458
- this.setState({ show_sort_dialog: false, sorts: sorts ?? [] });
459
- }}
460
- />
461
- }
462
- {
463
- this.state.show_reorder_dialog &&
464
- <NSAReorderDialog
465
- visibleColumnKeys={this.state.visible_column_keys}
466
- getColumns={() => this.getColumns()}
467
- onCancel={() => this.setState({ show_reorder_dialog: false })}
468
- onApply={orderedKeys =>
469
- {
470
- const cs_all = this.getColumns();
471
- const cs = orderedKeys.map((v: string) => cs_all.find((c: TableColumnInfo) => v === c.table.name + "." + c.name)).filter(Boolean) as TableColumnInfo[];
472
- this.setVisibleColumns(cs.map(c => ({ table: c.table.name, column: c.name })));
473
- this.setState({ show_reorder_dialog: false, visible_column_keys: orderedKeys });
474
- }}
475
- />
476
- }
477
- {
478
- this.state.show_quick_filter_dialog &&
479
- <NSAQuickFilterDialog
480
- configs={this.state.quick_filter_configs}
481
- getColumns={() => this.getColumns()}
482
- typeConverter={(type: string) => new ObjectService(type).getEnum(VariableType, VariableType.Any)}
483
- onSave={configs =>
484
- {
485
- NSACacheService.saveQuickFilters(this.props.entity.name, configs);
486
- this.setState({ quick_filter_configs: configs }, () => this.reloadFromFilters());
487
- }}
488
- onConfirm={configs =>
489
- {
490
- NSACacheService.saveQuickFilters(this.props.entity.name, configs);
491
- this.setState({ quick_filter_configs: configs, show_quick_filter_dialog: false }, () => this.reloadFromFilters());
492
- }}
493
- onClose={() => this.setState({ show_quick_filter_dialog: false })}
494
- />
495
- }
496
- </>
497
- );
498
- }
394
+ } catch (error)
395
+ {
396
+ }
397
+ // todo add tags
398
+ return ans;
399
+ }}
400
+ getValues={async (table, column, value) =>
401
+ {
402
+ if (this.props.ui?.search_box?.exclude?.load_value)
403
+ if (this.props.ui?.search_box?.exclude?.load_value(table.name, column.name))
404
+ return [];
405
+ if (column.type === FilterItemColumnType.Boolean)
406
+ {
407
+ const col = this.getColumns().find(c => c.table.name === table.name && c.name === column.name);
408
+ return [true, false].map(b =>
409
+ {
410
+ const formatted = col ? col.formatter.format(b, col, { value: {} } as any, true) : null;
411
+ return { value: String(b), title: formatted != null ? String(formatted) : String(b) };
412
+ });
413
+ }
414
+ let values = await this.props.entity.server.value_list(table.name, column.name, value, null, null);
415
+ return values.map(v => { return { value: v, title: v } });
416
+ }}
417
+ onChanged={this.onFilterBoxChanged}
418
+ />
419
+ );
420
+ }
421
+
422
+ private getOrderedColumns(): TableColumnInfo[]
423
+ {
424
+ const all = this.getColumns();
425
+ const vk = this.state.visible_column_keys;
426
+ if (vk.length === 0)
427
+ return all;
428
+ const ordered = vk.map(k => all.find(c => c.table.name + "." + c.name === k)).filter((c): c is TableColumnInfo => !!c);
429
+ const rest = all.filter(c => !vk.includes(c.table.name + "." + c.name));
430
+ return [...ordered, ...rest];
431
+ }
432
+
433
+ override render(): ReactNode
434
+ {
435
+ const filterBoxItems = this.NSFilterBox_Top.current?.getFilterItems() ?? [];
436
+ const filterChips = filterBoxItems.length > 0 && (
437
+ <>
438
+ <NSFilterBoxItems
439
+ filterItems={filterBoxItems}
440
+ onClearFilterItems={() => this.NSFilterBox_Top.current?.setFilterItems([])}
441
+ onRemoveFilterItem={(item) => this.NSFilterBox_Top.current?.removeFilterItem(item)}
442
+ />
443
+ <NSSpace size={NSSpaceSizeType.MINI} />
444
+ </>
445
+ );
446
+
447
+ const quickFilterBar = this.state.quick_filter_configs.length > 0 && (
448
+ <NSAQuickFilterBar
449
+ key={this.state.quick_filter_configs.map(c => c.id).join("-")}
450
+ ref={this.NSAQuickFilterBar_Ref}
451
+ configs={this.state.quick_filter_configs}
452
+ initialItems={this.getIncomingQuickItems()}
453
+ getValues={async (tableName, columnName, search) =>
454
+ {
455
+ const values = await this.props.entity.server.value_list(tableName, columnName, search, null, null);
456
+ const col = this.getColumns().find(c => c.table.name === tableName && c.name === columnName);
457
+ return values.map(v =>
458
+ {
459
+ const formatted = col ? col.formatter.format(v, col, { value: {} } as any, true) : null;
460
+ return { value: v, title: formatted != null ? String(formatted) : v };
461
+ });
462
+ }}
463
+ onChanged={() => this.reloadFromFilters()}
464
+ />
465
+ );
466
+
467
+ return (
468
+ <>
469
+ <NSTable<RowType>
470
+ id={this.props.id}
471
+ classList={this.props.classList}
472
+ style={this.props.style}
473
+ ref={this.NSTable_Main}
474
+ name={NamingConvention.lower_case_dash.convert(this.props.entity.name, NamingConvention.Pascal_Case_Space)}
475
+ checkbox={this.props.checkbox ?? true}
476
+ headerChildren={this.renderFilterHeader()}
477
+ buttons={this.buildButtons()}
478
+ columns={this.getOrderedColumns()}
479
+ getRows={async (page, size, sorts) =>
480
+ {
481
+ let res: { rows: RowType[], count: number } = { rows: [], count: 0 };
482
+ if (this.props.entity.server.list)
483
+ res = await this.props.entity.server.list(this.getFilterItems(), page, size, sorts, null);
484
+ this.setState({ res });
485
+ return res;
486
+ }}
487
+ getRowKey={(row) => row.value.id.toString()}
488
+ getRowAttributes={(row_value, row_index) => this.props.ui?.getRowAttributes?.(row_value, row_index, this.state.res) ?? {}}
489
+ getColumnAttributes={this.props.ui?.getColumnAttributes}
490
+ getCellFormattedValue={this.props.ui?.getCellFormattedValue}
491
+ onVisibleColumnsChanged={(visible_columns) => this.setState({ visible_column_keys: visible_columns.map(c => c.table + "." + c.column) })}
492
+ onSelectedIDsChanged={() => this.props.onSelectionChanged?.()}
493
+ >
494
+ {filterChips}
495
+ {quickFilterBar}
496
+ </NSTable>
497
+ {
498
+ this.state.show_sort_dialog &&
499
+ <NSASortDialog
500
+ sorts={this.state.sorts}
501
+ getColumns={() => this.getColumns()}
502
+ onCancel={() => this.setState({ show_sort_dialog: false })}
503
+ onApply={sorts =>
504
+ {
505
+ this.setSortItems(sorts);
506
+ this.setState({ show_sort_dialog: false, sorts: sorts ?? [] });
507
+ }}
508
+ />
509
+ }
510
+ {
511
+ this.state.show_reorder_dialog &&
512
+ <NSAReorderDialog
513
+ visibleColumnKeys={this.state.visible_column_keys}
514
+ getColumns={() => this.getColumns()}
515
+ onCancel={() => this.setState({ show_reorder_dialog: false })}
516
+ onApply={orderedKeys =>
517
+ {
518
+ const cs_all = this.getColumns();
519
+ const cs = orderedKeys.map((v: string) => cs_all.find((c: TableColumnInfo) => v === c.table.name + "." + c.name)).filter(Boolean) as TableColumnInfo[];
520
+ this.setVisibleColumns(cs.map(c => ({ table: c.table.name, column: c.name })));
521
+ this.setState({ show_reorder_dialog: false, visible_column_keys: orderedKeys });
522
+ }}
523
+ />
524
+ }
525
+ {
526
+ this.state.show_quick_filter_dialog &&
527
+ <NSAQuickFilterDialog
528
+ configs={this.state.quick_filter_configs}
529
+ getColumns={() => this.getColumns()}
530
+ typeConverter={(type: string) => new ObjectService(type).getEnum(VariableType, VariableType.Any)}
531
+ onSave={configs =>
532
+ {
533
+ NSACacheService.saveQuickFilters(this.props.entity.name, configs);
534
+ this.setState({ quick_filter_configs: configs }, () => this.reloadFromFilters());
535
+ }}
536
+ onConfirm={configs =>
537
+ {
538
+ NSACacheService.saveQuickFilters(this.props.entity.name, configs);
539
+ this.setState({ quick_filter_configs: configs, show_quick_filter_dialog: false }, () => this.reloadFromFilters());
540
+ }}
541
+ onClose={() => this.setState({ show_quick_filter_dialog: false })}
542
+ />
543
+ }
544
+ </>
545
+ );
546
+ }
499
547
  }