@solidstarters/solid-core-ui 1.1.61 → 1.1.63
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.
- package/dist/components/core/common/SolidGlobalSearchElement.d.ts +1 -0
- package/dist/components/core/common/SolidGlobalSearchElement.d.ts.map +1 -1
- package/dist/components/core/common/SolidGlobalSearchElement.js +292 -56
- package/dist/components/core/common/SolidGlobalSearchElement.js.map +1 -1
- package/dist/components/core/common/SolidSaveCustomFilterForm.d.ts +9 -0
- package/dist/components/core/common/SolidSaveCustomFilterForm.d.ts.map +1 -0
- package/dist/components/core/common/SolidSaveCustomFilterForm.js +37 -0
- package/dist/components/core/common/SolidSaveCustomFilterForm.js.map +1 -0
- package/dist/components/core/filter/fields/SolidBooleanField.js +1 -1
- package/dist/components/core/filter/fields/SolidBooleanField.js.map +1 -1
- package/dist/components/core/kanban/SolidKanbanView.d.ts.map +1 -1
- package/dist/components/core/kanban/SolidKanbanView.js +205 -167
- package/dist/components/core/kanban/SolidKanbanView.js.map +1 -1
- package/dist/components/core/kanban/kanban-fields/SolidMediaSingleKanbanField.d.ts.map +1 -1
- package/dist/components/core/kanban/kanban-fields/SolidMediaSingleKanbanField.js +6 -2
- package/dist/components/core/kanban/kanban-fields/SolidMediaSingleKanbanField.js.map +1 -1
- package/dist/components/core/model/CreateModel.js +3 -3
- package/dist/components/core/model/CreateModel.js.map +1 -1
- package/dist/components/core/model/FieldMetaDataForm.d.ts.map +1 -1
- package/dist/components/core/model/FieldMetaDataForm.js.map +1 -1
- package/dist/components/core/model/ModelMetaData.d.ts.map +1 -1
- package/dist/components/core/model/ModelMetaData.js +68 -15
- package/dist/components/core/model/ModelMetaData.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/resources/globals.css +8 -0
- package/package.json +1 -1
- package/src/components/core/common/SolidGlobalSearchElement.tsx +425 -123
- package/src/components/core/common/SolidSaveCustomFilterForm.tsx +74 -0
- package/src/components/core/filter/fields/SolidBooleanField.tsx +1 -1
- package/src/components/core/kanban/SolidKanbanView.tsx +176 -158
- package/src/components/core/kanban/kanban-fields/SolidMediaSingleKanbanField.tsx +45 -2
- package/src/components/core/model/CreateModel.tsx +2 -2
- package/src/components/core/model/FieldMetaDataForm.tsx +0 -2
- package/src/components/core/model/ModelMetaData.tsx +204 -121
- package/src/index.ts +1 -0
- package/src/resources/globals.css +8 -0
|
@@ -6,9 +6,13 @@ import FilterComponent, { FilterOperator, FilterRule, FilterRuleType } from "@/c
|
|
|
6
6
|
import { Button } from "primereact/button";
|
|
7
7
|
import { OverlayPanel } from "primereact/overlaypanel";
|
|
8
8
|
import { Divider } from "primereact/divider";
|
|
9
|
-
import { useSearchParams } from "next/navigation";
|
|
9
|
+
import { useRouter, useSearchParams } from "next/navigation";
|
|
10
10
|
import { queryStringToQueryObject } from "../list/SolidListView";
|
|
11
11
|
import { InputText } from "primereact/inputtext";
|
|
12
|
+
import { createSolidEntityApi } from "@/redux/api/solidEntityApi";
|
|
13
|
+
import qs from "qs";
|
|
14
|
+
import { useSelector } from "react-redux";
|
|
15
|
+
import { SolidSaveCustomFilterForm } from "./SolidSaveCustomFilterForm";
|
|
12
16
|
|
|
13
17
|
const getRandomInt = (min: number, max: number) => {
|
|
14
18
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
@@ -17,7 +21,6 @@ const getRandomInt = (min: number, max: number) => {
|
|
|
17
21
|
|
|
18
22
|
const transformFiltersToRules = (filter: any, parentRule: number | null = null): FilterRule => {
|
|
19
23
|
const currentId = idCounter++;
|
|
20
|
-
|
|
21
24
|
if (filter["$or"]) {
|
|
22
25
|
return {
|
|
23
26
|
id: currentId,
|
|
@@ -173,6 +176,50 @@ const tranformSearchToFilters = (input: any) => {
|
|
|
173
176
|
};
|
|
174
177
|
}
|
|
175
178
|
|
|
179
|
+
export const mergeSearchAndCustomFilters = (transformedFilter: any, newFilter: any, transformedFilterName: string, newFilterName: string) => {
|
|
180
|
+
const filters: any = {};
|
|
181
|
+
|
|
182
|
+
// Add only non-null filters
|
|
183
|
+
if (transformedFilter && Object.keys(transformedFilter).length > 0) {
|
|
184
|
+
filters[transformedFilterName] = transformedFilter;
|
|
185
|
+
}
|
|
186
|
+
if (newFilter && Object.keys(newFilter).length > 0) {
|
|
187
|
+
filters[newFilterName] = newFilter;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Return the combined filters object
|
|
191
|
+
return filters;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
const SavedFilterList = ({ savedfilter, activeSavedFilter, applySavedFilter, openSavedCustomFilter, setSavedFilterTobeDeleted, setIsDeleteSQDialogVisible }: any) => {
|
|
196
|
+
return (
|
|
197
|
+
<div className="flex align-items-center justify-content-between gap-2">
|
|
198
|
+
<Button text size="small" className="text-base py-1 w-full" severity={Number(activeSavedFilter) == savedfilter.id ? "secondary" : "contrast" } onClick={() => applySavedFilter(savedfilter)}>{savedfilter.name}</Button>
|
|
199
|
+
<div className="flex align-items-center gap-2">
|
|
200
|
+
<Button
|
|
201
|
+
icon="pi pi-pencil"
|
|
202
|
+
style={{ fontSize: 10 }}
|
|
203
|
+
severity="secondary"
|
|
204
|
+
outlined size="small"
|
|
205
|
+
onClick={() => openSavedCustomFilter(savedfilter)}
|
|
206
|
+
/>
|
|
207
|
+
<Button
|
|
208
|
+
icon="pi pi-trash"
|
|
209
|
+
style={{ fontSize: 10 }}
|
|
210
|
+
severity="secondary"
|
|
211
|
+
outlined size="small"
|
|
212
|
+
onClick={() => {
|
|
213
|
+
setSavedFilterTobeDeleted(savedfilter.id),
|
|
214
|
+
setIsDeleteSQDialogVisible(true);
|
|
215
|
+
|
|
216
|
+
}}
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
176
223
|
export const SolidGlobalSearchElement = forwardRef(({ viewData, handleApplyCustomFilter, filters, clearFilter }: any, ref) => {
|
|
177
224
|
const defaultState: FilterRule[] = [
|
|
178
225
|
{
|
|
@@ -204,8 +251,10 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, handleApplyCusto
|
|
|
204
251
|
];
|
|
205
252
|
const [initialState, setInitialState] = useState(defaultState);
|
|
206
253
|
|
|
207
|
-
const searchParams = useSearchParams()
|
|
254
|
+
const searchParams = useSearchParams() // Converts the query params to a string
|
|
255
|
+
const activeSavedFilter = searchParams?.get("savedQuery");
|
|
208
256
|
|
|
257
|
+
const router = useRouter();
|
|
209
258
|
|
|
210
259
|
const chipsRef = useRef<HTMLDivElement | null | any>(null);
|
|
211
260
|
|
|
@@ -219,6 +268,85 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, handleApplyCusto
|
|
|
219
268
|
const [searchFilter, setSearchFilter] = useState<any | null>(null);
|
|
220
269
|
const [customFilter, setCustomFilter] = useState<any | null>(null);
|
|
221
270
|
const [hasSearched, setHasSearched] = useState<boolean>(false);
|
|
271
|
+
const [showSaveFilterPopup, setShowSaveFilterPopup] = useState<boolean>(false);
|
|
272
|
+
const [currentSavedFilterData, setCurrentSavedFilterData] = useState<any>();
|
|
273
|
+
const [savedFilterTobeDeleted, setSavedFilterTobeDeleted] = useState<any>();
|
|
274
|
+
const [isDeleteSQDialogVisible, setIsDeleteSQDialogVisible] = useState<boolean>(false);
|
|
275
|
+
const [savedFilterQueryString, setSavedFilterQueryString] = useState<string>();
|
|
276
|
+
const { user } = useSelector((state: any) => state.auth);
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
const [savedFilters, setSavedFilters] = useState([]);
|
|
280
|
+
|
|
281
|
+
const entityApi = createSolidEntityApi("savedFilters");
|
|
282
|
+
const {
|
|
283
|
+
useCreateSolidEntityMutation,
|
|
284
|
+
useDeleteSolidEntityMutation,
|
|
285
|
+
useGetSolidEntityByIdQuery,
|
|
286
|
+
useUpdateSolidEntityMutation,
|
|
287
|
+
useLazyGetSolidEntitiesQuery
|
|
288
|
+
} = entityApi;
|
|
289
|
+
|
|
290
|
+
const [
|
|
291
|
+
createEntity,
|
|
292
|
+
{ isSuccess: isEntityCreateSuccess, isError: isEntityCreateError, error: entityCreateError },
|
|
293
|
+
] = useCreateSolidEntityMutation();
|
|
294
|
+
|
|
295
|
+
const [
|
|
296
|
+
updateEntity,
|
|
297
|
+
{ isSuccess: isEntityUpdateSuceess, isError: isEntityUpdateError, error: entityUpdateError },
|
|
298
|
+
] = useUpdateSolidEntityMutation();
|
|
299
|
+
|
|
300
|
+
const [
|
|
301
|
+
deleteEntity,
|
|
302
|
+
{ isSuccess: isEntityDeleteSuceess, isError: isEntityDeleteError, error: entityDeleteError },
|
|
303
|
+
] = useDeleteSolidEntityMutation();
|
|
304
|
+
|
|
305
|
+
const [triggerGetSolidEntities, { data: solidEntityListViewData, isLoading, error }] = useLazyGetSolidEntitiesQuery();
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
useEffect(() => {
|
|
309
|
+
|
|
310
|
+
const filters = {
|
|
311
|
+
$or:[
|
|
312
|
+
{
|
|
313
|
+
$and: [
|
|
314
|
+
{ model: { $in: [viewData?.data?.solidView?.model?.id] } },
|
|
315
|
+
{ view: { $in: [viewData?.solidView?.id] } },
|
|
316
|
+
{ user: { $in: [user?.user?.id] } }
|
|
317
|
+
]
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
$and: [
|
|
321
|
+
{ model: { $in: [viewData?.data?.solidView?.model?.id] } },
|
|
322
|
+
{ view: { $in: [viewData?.solidView?.id] } },
|
|
323
|
+
{ isPrivate: { $eq: true } }
|
|
324
|
+
]
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
]
|
|
328
|
+
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const queryData: any = {
|
|
332
|
+
offset: 0,
|
|
333
|
+
limit: 10,
|
|
334
|
+
filters: filters,
|
|
335
|
+
populate: ["model", "view", "user"],
|
|
336
|
+
sort: ["id:desc"],
|
|
337
|
+
};
|
|
338
|
+
const queryString = qs.stringify(queryData, { encodeValuesOnly: true });
|
|
339
|
+
setSavedFilterQueryString(queryString)
|
|
340
|
+
triggerGetSolidEntities(queryString);
|
|
341
|
+
|
|
342
|
+
}, [searchParams])
|
|
343
|
+
|
|
344
|
+
useEffect(() => {
|
|
345
|
+
if (solidEntityListViewData) {
|
|
346
|
+
setSavedFilters(solidEntityListViewData.records)
|
|
347
|
+
}
|
|
348
|
+
}, [solidEntityListViewData])
|
|
349
|
+
|
|
222
350
|
useImperativeHandle(ref, () => ({
|
|
223
351
|
clearFilter: () => {
|
|
224
352
|
setFilterRules(initialState);
|
|
@@ -226,43 +354,112 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, handleApplyCusto
|
|
|
226
354
|
}));
|
|
227
355
|
|
|
228
356
|
useEffect(() => {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
357
|
+
let searchChips: any;
|
|
358
|
+
let customChips: any;
|
|
359
|
+
let parsedSearchParams = searchParams;
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
const savedQuery = parsedSearchParams?.get("savedQuery");
|
|
363
|
+
if (savedQuery && savedFilters.length > 0) {
|
|
364
|
+
const currentSavedFilterId = Number(savedQuery);
|
|
365
|
+
const currentSavedFilterData: any = savedFilters.find((savedFilter: any) => savedFilter.id === currentSavedFilterId);
|
|
366
|
+
setCurrentSavedFilterData(currentSavedFilterData)
|
|
367
|
+
if (currentSavedFilterData) {
|
|
368
|
+
const filterJson = JSON.parse(currentSavedFilterData?.filterQueryJson);
|
|
369
|
+
if (filterJson) {
|
|
370
|
+
searchChips = filterJson?.s_filter || null;
|
|
371
|
+
customChips = filterJson?.c_filter || null;
|
|
242
372
|
}
|
|
243
|
-
);
|
|
244
|
-
setSearchChips(formattedChips);
|
|
245
373
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
374
|
+
} else {
|
|
375
|
+
const queryObject = queryStringToQueryObject();
|
|
376
|
+
if (queryObject) {
|
|
377
|
+
searchChips = queryObject?.s_filter || null;
|
|
378
|
+
customChips = queryObject?.c_filter || null;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (searchChips) {
|
|
382
|
+
const formattedChips = searchChips?.$and.map((chip: any, key: any) => {
|
|
383
|
+
const chipKey = Object.keys(chip)[0]; // Get the key, e.g., "displayName"
|
|
384
|
+
const chipValue = chip[chipKey]?.$containsi; // Get the value of "$containsi"
|
|
385
|
+
const chipdata = {
|
|
386
|
+
columnName: chipKey,
|
|
387
|
+
value: chipValue
|
|
388
|
+
};
|
|
389
|
+
return chipdata
|
|
390
|
+
}
|
|
391
|
+
);
|
|
392
|
+
setSearchChips(formattedChips);
|
|
393
|
+
setSearchFilter(searchChips);
|
|
394
|
+
setHasSearched(true);
|
|
395
|
+
}
|
|
396
|
+
if (customChips) {
|
|
397
|
+
setCustomFilter(customChips);
|
|
398
|
+
const formatedCustomChips: FilterRule = transformFiltersToRules(customChips);
|
|
399
|
+
setFilterRules([formatedCustomChips]);
|
|
400
|
+
setHasSearched(true);
|
|
401
|
+
}
|
|
402
|
+
}, [searchParams, savedFilters])
|
|
403
|
+
|
|
404
|
+
function findSearchableField(node: any, targetName: string): boolean {
|
|
405
|
+
if (
|
|
406
|
+
node?.type === 'field' &&
|
|
407
|
+
node?.attrs?.name === targetName &&
|
|
408
|
+
node?.attrs?.isSearchable
|
|
409
|
+
) {
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (Array.isArray(node?.children)) {
|
|
414
|
+
return node.children.some((child: any) => findSearchableField(child, targetName));
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
function collectLeafFieldTypes(layoutArray: any) {
|
|
422
|
+
const result: any = [];
|
|
423
|
+
|
|
424
|
+
function recurse(node: any) {
|
|
425
|
+
if (!node || typeof node !== 'object') return;
|
|
426
|
+
|
|
427
|
+
// If it's a field and has no children (leaf field node)
|
|
428
|
+
if (node.type === 'field' && !node.children) {
|
|
429
|
+
result.push(node);
|
|
430
|
+
}
|
|
251
431
|
|
|
432
|
+
// If it has children, recurse into them
|
|
433
|
+
if (Array.isArray(node.children)) {
|
|
434
|
+
node.children.forEach(recurse);
|
|
252
435
|
}
|
|
253
436
|
}
|
|
254
|
-
|
|
437
|
+
|
|
438
|
+
// Input is an array of nodes
|
|
439
|
+
layoutArray.forEach(recurse);
|
|
440
|
+
|
|
441
|
+
return result;
|
|
442
|
+
}
|
|
443
|
+
|
|
255
444
|
|
|
256
445
|
|
|
257
446
|
useEffect(() => {
|
|
258
447
|
if (viewData?.data?.solidFieldsMetadata) {
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
const fieldsData = viewData?.data?.solidFieldsMetadata;
|
|
448
|
+
let fieldsData = viewData?.data?.solidFieldsMetadata;
|
|
262
449
|
const fieldsList = Object.entries(fieldsData).map(([key, value]: any) => ({ name: value.displayName, value: key, type: value.type }));
|
|
263
450
|
setFields(fieldsList);
|
|
264
451
|
const searchableFieldsList = fieldsList.filter((field: any) => field.type === "longText" || field.type === "shortText");
|
|
265
|
-
|
|
452
|
+
let finalsearchableFieldsList: any = [];
|
|
453
|
+
if (typeof window !== "undefined" && window.location.href.includes("list")) {
|
|
454
|
+
finalsearchableFieldsList = searchableFieldsList.filter((field: any) => field.value && viewData?.data?.solidView?.layout?.children?.some((child: any) => child?.attrs?.name === field.value && child?.attrs?.isSearchable)).map((field: any) => field.value);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (typeof window !== "undefined" && window.location.href.includes("kanban")) {
|
|
458
|
+
const result = collectLeafFieldTypes(viewData?.data?.solidView?.layout?.children);
|
|
459
|
+
finalsearchableFieldsList = searchableFieldsList.filter((field: any) => field.value && result?.some((child: any) => child?.attrs?.name === field.value && child?.attrs?.isSearchable)).map((field: any) => field.value);
|
|
460
|
+
|
|
461
|
+
}
|
|
462
|
+
|
|
266
463
|
setSearchableFields(finalsearchableFieldsList);
|
|
267
464
|
}
|
|
268
465
|
}, [])
|
|
@@ -278,15 +475,15 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, handleApplyCusto
|
|
|
278
475
|
}
|
|
279
476
|
}, []);
|
|
280
477
|
|
|
281
|
-
useEffect(() => {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}, [filters]);
|
|
478
|
+
// useEffect(() => {
|
|
479
|
+
// // Get the last valid filter
|
|
480
|
+
// const validFilters = filters?.$or?.filter((filter: any) => filter !== undefined) || [];
|
|
481
|
+
// if (validFilters.length > 0) {
|
|
482
|
+
// setCustomChip(validFilters.length.toString()); // Store only the number
|
|
483
|
+
// } else {
|
|
484
|
+
// setCustomChip(""); // Reset when no filters are present
|
|
485
|
+
// }
|
|
486
|
+
// }, [filters]);
|
|
290
487
|
|
|
291
488
|
const firstFilterableFieldName = searchableFields[0]; // First searchable field
|
|
292
489
|
|
|
@@ -374,6 +571,67 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, handleApplyCusto
|
|
|
374
571
|
}
|
|
375
572
|
}, [searchChips]);
|
|
376
573
|
|
|
574
|
+
|
|
575
|
+
// Saved Filter related
|
|
576
|
+
|
|
577
|
+
const applySavedFilter = (savedfilter: any) => {
|
|
578
|
+
// push the savedQuery=1 in url
|
|
579
|
+
if (savedfilter?.id) {
|
|
580
|
+
router.push(`?savedQuery=${savedfilter.id}`);
|
|
581
|
+
} else {
|
|
582
|
+
console.error("Saved filter ID is undefined or null.");
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
}
|
|
586
|
+
const openSavedCustomFilter = (savedfilter: any) => {
|
|
587
|
+
//Open custom filter popup
|
|
588
|
+
router.push(`?savedQuery=${savedfilter.id}`);
|
|
589
|
+
setShowGlobalSearchElement(true);
|
|
590
|
+
// dont refetch the data yet
|
|
591
|
+
const customFilter = JSON.parse(savedfilter.filterQueryJson).c_filter;
|
|
592
|
+
setCustomFilter(customFilter ? customFilter : null);
|
|
593
|
+
if (customFilter) {
|
|
594
|
+
const formatedCustomChips: FilterRule = transformFiltersToRules(customFilter ? customFilter : null);
|
|
595
|
+
setFilterRules(formatedCustomChips ? [formatedCustomChips] : initialState);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const deleteSavedFilter = () => {
|
|
599
|
+
// delte the saved filter with id
|
|
600
|
+
deleteEntity(savedFilterTobeDeleted);
|
|
601
|
+
triggerGetSolidEntities(savedFilterQueryString);
|
|
602
|
+
setIsDeleteSQDialogVisible(false);
|
|
603
|
+
let parsedSearchParams = searchParams;
|
|
604
|
+
const savedQuery = parsedSearchParams?.get("savedQuery");
|
|
605
|
+
if (savedFilterTobeDeleted === savedQuery) {
|
|
606
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
607
|
+
urlParams.delete("savedQuery");
|
|
608
|
+
router.push(`?${urlParams.toString()}`);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
const handleSaveFilter = async (formValues: any) => {
|
|
613
|
+
const filterJson = mergeSearchAndCustomFilters(customFilter, searchFilter, "c_filter", "s_filter");
|
|
614
|
+
setShowSaveFilterPopup(false)
|
|
615
|
+
|
|
616
|
+
const formData = new FormData();
|
|
617
|
+
formData.append("name", formValues.name);
|
|
618
|
+
formData.append("filterQueryJson", JSON.stringify(filterJson));
|
|
619
|
+
formData.append("modelId", viewData?.data?.solidView?.model?.id);
|
|
620
|
+
formData.append("viewId", viewData?.data?.solidView?.id);
|
|
621
|
+
formData.append("isPrivate", formValues.isPrivate);
|
|
622
|
+
formData.append("userId", user?.user?.id);
|
|
623
|
+
if (formValues.id) {
|
|
624
|
+
await updateEntity({ id: +formValues.id, data: formData }).unwrap();
|
|
625
|
+
router.push(`?savedQuery=${formValues.id}`);
|
|
626
|
+
|
|
627
|
+
} else {
|
|
628
|
+
const result = await createEntity(formData).unwrap();
|
|
629
|
+
router.push(`?savedQuery=${result.data.id}`);
|
|
630
|
+
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
|
|
377
635
|
const groupedSearchChips = searchChips.reduce((acc, chip) => {
|
|
378
636
|
const key = chip.columnName || firstFilterableFieldName;
|
|
379
637
|
if (!acc[key]) {
|
|
@@ -400,7 +658,7 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, handleApplyCusto
|
|
|
400
658
|
<path d="M8.66667 15V13.3333H11.3333V15H8.66667ZM6 10.8333V9.16667H14V10.8333H6ZM4 6.66667V5H16V6.66667H4Z"
|
|
401
659
|
fill="white" />
|
|
402
660
|
</svg>
|
|
403
|
-
<span><strong>{customFilter
|
|
661
|
+
<span><strong>{customFilter?.$or && customFilter?.$or?.length > 0 ? `${customFilter?.$or?.length}` : customFilter.$and.length}</strong> rules applied</span>
|
|
404
662
|
</div>
|
|
405
663
|
|
|
406
664
|
{/* button to clear filter */}
|
|
@@ -433,102 +691,146 @@ export const SolidGlobalSearchElement = forwardRef(({ viewData, handleApplyCusto
|
|
|
433
691
|
<i className="pi pi-times ml-1"
|
|
434
692
|
style={{ cursor: "pointer" }}
|
|
435
693
|
onClick={() => handleRemoveChipGroup(column)}
|
|
436
|
-
>
|
|
694
|
+
>
|
|
695
|
+
</i>
|
|
437
696
|
</div>
|
|
438
697
|
</li>
|
|
439
698
|
))}
|
|
440
699
|
</>
|
|
441
700
|
);
|
|
442
701
|
|
|
443
|
-
console.log("custom chip", customFilter);
|
|
444
702
|
|
|
445
703
|
const [showOverlay, setShowOverlay] = useState(false);
|
|
446
704
|
|
|
447
|
-
|
|
448
705
|
return (
|
|
449
|
-
|
|
450
|
-
<div className="solid-
|
|
451
|
-
<
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
<
|
|
456
|
-
<
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
</li>
|
|
483
|
-
</ul>
|
|
484
|
-
</div>
|
|
485
|
-
{showOverlay && inputValue?.trim() && (
|
|
486
|
-
<div className="absolute w-full z-5 bg-white border-round border-1 border-300 shadow-2" style={{ top: 35 }}>
|
|
487
|
-
{inputValue ? (
|
|
488
|
-
<>
|
|
489
|
-
<div className="custom-filter-search-options px-2 py-2 flex flex-column">
|
|
490
|
-
{
|
|
491
|
-
searchableFields.map((value: any, index: any) => (
|
|
492
|
-
<Button
|
|
493
|
-
key={index}
|
|
494
|
-
className="p-2 flex gap-1 text-color"
|
|
495
|
-
onClick={() => handleAddChip(value)}
|
|
496
|
-
text
|
|
497
|
-
severity="secondary"
|
|
498
|
-
size="small"
|
|
499
|
-
>
|
|
500
|
-
Search <strong>{value}</strong> for :
|
|
501
|
-
<span className="font-bold" style={{ color: '#000' }}>{inputValue}</span>
|
|
502
|
-
</Button>
|
|
503
|
-
))
|
|
504
|
-
}
|
|
706
|
+
<>
|
|
707
|
+
<div className="flex justify-content-center solid-custom-filter-wrapper relative">
|
|
708
|
+
<div className="solid-global-search-element">
|
|
709
|
+
<ul className="">
|
|
710
|
+
{customFilter && <CustomChip />}
|
|
711
|
+
<SearchChip />
|
|
712
|
+
<li ref={chipsRef}>
|
|
713
|
+
<div className="relative">
|
|
714
|
+
<InputText
|
|
715
|
+
value={inputValue || ""}
|
|
716
|
+
placeholder="Search..."
|
|
717
|
+
onChange={(e) => {
|
|
718
|
+
setInputValue(e.target.value);
|
|
719
|
+
setShowOverlay(true);
|
|
720
|
+
}}
|
|
721
|
+
onFocus={() => {
|
|
722
|
+
if (inputValue?.trim()) setShowOverlay(true);
|
|
723
|
+
}}
|
|
724
|
+
onBlur={() => {
|
|
725
|
+
// Delay so you can click buttons inside overlay
|
|
726
|
+
setTimeout(() => setShowOverlay(false), 150);
|
|
727
|
+
}}
|
|
728
|
+
|
|
729
|
+
onKeyDown={handleKeyDown}
|
|
730
|
+
/>
|
|
731
|
+
<Button
|
|
732
|
+
icon="pi pi-search"
|
|
733
|
+
style={{ fontSize: 10 }}
|
|
734
|
+
severity="secondary"
|
|
735
|
+
outlined size="small"
|
|
736
|
+
onClick={() => setShowOverlay(true)}
|
|
737
|
+
className="custom-filter-button"
|
|
738
|
+
/>
|
|
505
739
|
</div>
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
) :
|
|
509
|
-
<>
|
|
510
|
-
<div className="p-3 text-base">Search Here...</div>
|
|
511
|
-
<Divider className="m-0" />
|
|
512
|
-
</>
|
|
513
|
-
}
|
|
514
|
-
<div className="px-2 py-1">
|
|
515
|
-
<Button text size="small" label="Custom Filter" iconPos="left" icon='pi pi-plus' onClick={() => setShowGlobalSearchElement(true)} className="font-bold" />
|
|
516
|
-
</div>
|
|
740
|
+
</li>
|
|
741
|
+
</ul>
|
|
517
742
|
</div>
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
743
|
+
|
|
744
|
+
{showOverlay && (
|
|
745
|
+
<div className="absolute w-full z-5 bg-white border-round border-1 border-300 shadow-2" style={{ top: 35 }}>
|
|
746
|
+
{inputValue ? (
|
|
747
|
+
<>
|
|
748
|
+
<div className="custom-filter-search-options px-2 py-2 flex flex-column">
|
|
749
|
+
{
|
|
750
|
+
searchableFields.map((value: any, index: any) => (
|
|
751
|
+
<Button
|
|
752
|
+
key={index}
|
|
753
|
+
className="p-2 flex gap-1 text-color"
|
|
754
|
+
onClick={() => handleAddChip(value)}
|
|
755
|
+
text
|
|
756
|
+
severity="secondary"
|
|
757
|
+
size="small"
|
|
758
|
+
>
|
|
759
|
+
Search <strong>{value}</strong> for :
|
|
760
|
+
<span className="font-bold" style={{ color: '#000' }}>{inputValue}</span>
|
|
761
|
+
</Button>
|
|
762
|
+
))
|
|
763
|
+
}
|
|
764
|
+
</div>
|
|
765
|
+
<Divider className="m-0" />
|
|
766
|
+
</>
|
|
767
|
+
) :
|
|
768
|
+
<>
|
|
769
|
+
<div className="p-3 text-base">Search Here...</div>
|
|
770
|
+
<Divider className="m-0" />
|
|
771
|
+
</>
|
|
772
|
+
}
|
|
773
|
+
{savedFilters.length > 0 &&
|
|
774
|
+
<>
|
|
775
|
+
<div className="p-3">
|
|
776
|
+
<p className="font-medium">Saved Filters</p>
|
|
777
|
+
<div className="flex flex-column gap-2">
|
|
778
|
+
{savedFilters.map((savedfilter: any) =>
|
|
779
|
+
<SavedFilterList savedfilter={savedfilter} activeSavedFilter={activeSavedFilter} applySavedFilter={applySavedFilter} openSavedCustomFilter={openSavedCustomFilter} setSavedFilterTobeDeleted={setSavedFilterTobeDeleted} setIsDeleteSQDialogVisible={setIsDeleteSQDialogVisible}></SavedFilterList>
|
|
780
|
+
)}
|
|
781
|
+
</div>
|
|
782
|
+
</div>
|
|
783
|
+
<Divider className="m-0" />
|
|
784
|
+
</>
|
|
785
|
+
}
|
|
786
|
+
<div className="px-2 py-1">
|
|
787
|
+
<Button text size="small" label="Custom Filter" iconPos="left" icon='pi pi-plus' onClick={() => setShowGlobalSearchElement(true)} className="font-bold" />
|
|
788
|
+
</div>
|
|
789
|
+
</div>
|
|
790
|
+
)
|
|
791
|
+
}
|
|
792
|
+
<Dialog header={false} className="solid-global-search-filter" showHeader={false} visible={showGlobalSearchElement} style={{ width: '65vw' }} onHide={() => { if (!showGlobalSearchElement) return; setShowGlobalSearchElement(false); }}>
|
|
793
|
+
<div className="flex align-items-center justify-content-between px-3">
|
|
794
|
+
<h5 className="solid-custom-title m-0">Add Custom Filter</h5>
|
|
795
|
+
<Button icon="pi pi-times" rounded text aria-label="Cancel" type="reset" size="small" onClick={() => setShowGlobalSearchElement(false)} />
|
|
796
|
+
</div>
|
|
797
|
+
<Divider className="m-0" />
|
|
798
|
+
<div className="p-4">
|
|
799
|
+
{fields.length > 0 &&
|
|
800
|
+
<FilterComponent viewData={viewData} fields={fields} filterRules={filterRules} setFilterRules={setFilterRules} transformFilterRules={transformFilterRules} closeDialog={() => setShowGlobalSearchElement(false)}></FilterComponent>
|
|
801
|
+
}
|
|
802
|
+
</div>
|
|
803
|
+
</Dialog>
|
|
804
|
+
<Dialog header="Add Custom Filter" visible={showSaveFilterPopup} style={{ width: 500 }} onHide={() => { if (!showSaveFilterPopup) return; setShowSaveFilterPopup(false); }}>
|
|
805
|
+
<SolidSaveCustomFilterForm currentSavedFilterData={currentSavedFilterData} handleSaveFilter={handleSaveFilter} closeDialog={setShowSaveFilterPopup}></SolidSaveCustomFilterForm>
|
|
806
|
+
</Dialog>
|
|
807
|
+
|
|
808
|
+
<Dialog
|
|
809
|
+
visible={isDeleteSQDialogVisible}
|
|
810
|
+
header="Confirm Delete"
|
|
811
|
+
modal
|
|
812
|
+
footer={() => (
|
|
813
|
+
<div className="flex justify-content-center">
|
|
814
|
+
<Button label="Yes" icon="pi pi-check" className='small-button' severity="danger" autoFocus onClick={deleteSavedFilter} />
|
|
815
|
+
<Button label="No" icon="pi pi-times" className='small-button' onClick={() => setIsDeleteSQDialogVisible(false)} />
|
|
816
|
+
</div>
|
|
817
|
+
)}
|
|
818
|
+
onHide={() => setIsDeleteSQDialogVisible(false)}
|
|
819
|
+
>
|
|
820
|
+
<p>Are you sure you want to delete the {currentSavedFilterData?.name} saved query?</p>
|
|
821
|
+
</Dialog>
|
|
822
|
+
</div>
|
|
823
|
+
<div>
|
|
824
|
+
<Button
|
|
825
|
+
icon="pi pi-save"
|
|
826
|
+
style={{ fontSize: 10 }}
|
|
827
|
+
severity="secondary"
|
|
828
|
+
outlined size="small"
|
|
829
|
+
onClick={() => {
|
|
830
|
+
setShowSaveFilterPopup(true)
|
|
831
|
+
}}
|
|
832
|
+
/>
|
|
833
|
+
</div>
|
|
834
|
+
</>
|
|
533
835
|
)
|
|
534
836
|
});
|