diff --git a/api/src/Kurs.Platform.Domain/ListForms/ListFormFieldManager.cs b/api/src/Kurs.Platform.Domain/ListForms/ListFormFieldManager.cs index 251e410a..ebc32c09 100644 --- a/api/src/Kurs.Platform.Domain/ListForms/ListFormFieldManager.cs +++ b/api/src/Kurs.Platform.Domain/ListForms/ListFormFieldManager.cs @@ -101,6 +101,9 @@ public class ListFormFieldManager : PlatformDomainService, IListFormFieldManager fields.AddRange(formFieldsRole.Where(a => !fields.Any(b => b.FieldName == a.FieldName))); fields.AddRange(formFieldsDefault.Where(a => !fields.Any(b => b.FieldName == a.FieldName))); + if (fields.Count == 0) + return null; + var field = fields.FirstOrDefault(); return await SetPermissionsAsync(field, listFormCode); @@ -125,7 +128,7 @@ public class ListFormFieldManager : PlatformDomainService, IListFormFieldManager ? await AuthManager.CanAccess(listFormCode, AuthorizationTypeEnum.Update) : await authorizationService.IsGrantedAsync(permissions.U); field.CanExport = permissions.E; - + if (!field.CanRead && !field.CanCreate && !field.CanUpdate) { return null; diff --git a/ui/dev-dist/sw.js b/ui/dev-dist/sw.js index bca95733..0869ddba 100644 --- a/ui/dev-dist/sw.js +++ b/ui/dev-dist/sw.js @@ -82,7 +82,7 @@ define(['./workbox-a959eb95'], (function (workbox) { 'use strict'; "revision": "3ca0b8505b4bec776b69afdba2768812" }, { "url": "/index.html", - "revision": "0.el21c3n5m38" + "revision": "0.7curt4sl6" }], {}); workbox.cleanupOutdatedCaches(); workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/index.html"), { diff --git a/ui/src/index.css b/ui/src/index.css index 68f07acd..10201f3b 100644 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -1,5 +1,10 @@ @import './assets/styles/app.css'; +.dx-toolbar .dx-toolbar-item.no-default { + all: unset; /* tüm default style'ları sıfırla */ + display: block; /* tekrar görünür yap */ +} + /* React Modal prevent bg scroll */ .dialog-open { overflow: hidden; diff --git a/ui/src/shared/useListFormCustomDataSource.ts b/ui/src/shared/useListFormCustomDataSource.ts index 2c6a5138..ea9021cb 100644 --- a/ui/src/shared/useListFormCustomDataSource.ts +++ b/ui/src/shared/useListFormCustomDataSource.ts @@ -23,9 +23,9 @@ const useListFormCustomDataSource = ({ listFormCode: string, searchParams?: URLSearchParams, cols?: GridColumnData[], - setWidgetGroups?: (widgetGroups: WidgetGroupDto[]) => void, + // setWidgetGroups?: (widgetGroups: WidgetGroupDto[]) => void, ) => { - return new CustomStore({ + const store: any = new CustomStore({ key: gridOptions.keyFieldName, useDefaultSearch: true, load: async (loadOptions) => { @@ -82,7 +82,7 @@ const useListFormCustomDataSource = ({ } } } - + if (combinedFilter && combinedFilter.length > 0) { parameters.filter = JSON.stringify(combinedFilter) } else { @@ -92,8 +92,6 @@ const useListFormCustomDataSource = ({ //parameters.filter = JSON.stringify(parameters.filter) const response = await dynamicFetch('list-form-select/select', 'GET', parameters) - if (setWidgetGroups) setWidgetGroups(response.data.widgets ?? []) - // Column format multiValue ise, gelen stringi array yapmaliyiz if (columns) { columns.forEach((col: any) => { @@ -141,12 +139,13 @@ const useListFormCustomDataSource = ({ : 'transparent', ) + //Widgets bilgisini store'un custom propertysinde tutuyoruz ki, + store._widgets = response.data.widgets ?? [] const retValue = { data: response.data.data, totalCount: response.data.totalCount, summary: response.data.summary, groupCount: response.data.groupCount, - widgets: response.data.widgets, } return retValue } catch (error: any) { @@ -185,10 +184,10 @@ const useListFormCustomDataSource = ({ } if (combinedFilter && combinedFilter.length > 0) { - parameters.filter = JSON.stringify(combinedFilter) - } else { - delete parameters.filter // hiç göndermesin - } + parameters.filter = JSON.stringify(combinedFilter) + } else { + delete parameters.filter // hiç göndermesin + } try { const response = await dynamicFetch('list-form-select/select', 'GET', parameters) @@ -206,29 +205,29 @@ const useListFormCustomDataSource = ({ return null } }, - // byKey: async (key) => { - // const parameters = getLoadOptions( - // { key }, - // { listFormCode, filter: '', createDeleteQuery: '' }, - // ) - - // parameters.filter = JSON.stringify(parameters.filter) - // try { - // const response = await dynamicFetch('list-form-select/select', 'GET', parameters) - // return response.data.data[0] - // } catch (error: any) { - // // toast.push( - // // - // // ByKey error - // // {error.toString()} - // // , - // // { - // // placement: 'top-end', - // // }, - // // ) - // return null - // } - // }, + byKey: async (key) => { + const parameters = getLoadOptions( + { key }, + { listFormCode, filter: '', createDeleteQuery: '' }, + ) + + parameters.filter = JSON.stringify(parameters.filter) + try { + const response = await dynamicFetch('list-form-select/select', 'GET', parameters) + return response.data.data[0] + } catch (error: any) { + // toast.push( + // + // ByKey error + // {error.toString()} + // , + // { + // placement: 'top-end', + // }, + // ) + return null + } + }, remove: function (key) { if (!gridOptions.deleteServiceAddress) { return @@ -265,6 +264,8 @@ const useListFormCustomDataSource = ({ console.log(error.message) }, }) + + return store }, [], ) diff --git a/ui/src/views/admin/listForm/edit/FormEdit.tsx b/ui/src/views/admin/listForm/edit/FormEdit.tsx index b352cba8..4de79178 100644 --- a/ui/src/views/admin/listForm/edit/FormEdit.tsx +++ b/ui/src/views/admin/listForm/edit/FormEdit.tsx @@ -188,12 +188,12 @@ const FormEdit = () => { {translate('::ListForms.ListFormEdit.TabColumnsPivots')} {translate('::ListForms.ListFormEdit.TabPaging')} {translate('::ListForms.ListFormEdit.TabState')} - {translate('::ListForms.ListFormEdit.TabSubForms')} - {translate('::ListForms.ListFormEdit.TabWidgets')} {translate('::ListForms.ListFormEdit.TabFields')} {translate('::ListForms.ListFormEdit.TabCustomization')} + {translate('::ListForms.ListFormEdit.TabSubForms')} + {translate('::ListForms.ListFormEdit.TabWidgets')} {translate('::ListForms.ListFormEdit.ExtraFilters')} diff --git a/ui/src/views/form/SubForms.tsx b/ui/src/views/form/SubForms.tsx index e35ecac0..7f4a1d6f 100644 --- a/ui/src/views/form/SubForms.tsx +++ b/ui/src/views/form/SubForms.tsx @@ -3,7 +3,7 @@ import TabContent from '@/components/ui/Tabs/TabContent' import TabList from '@/components/ui/Tabs/TabList' import TabNav from '@/components/ui/Tabs/TabNav' import { useEffect, useState } from 'react' -import { FaChartBar, FaList } from 'react-icons/fa'; +import { FaChartBar, FaList } from 'react-icons/fa' import { useLocation, useNavigate } from 'react-router-dom' import Grid from '../list/Grid' import Chart from '../chart/Chart' @@ -41,18 +41,22 @@ const SubForms = (props: { for (const subForm of gridDto.gridOptions.subFormsDto) { if (formData && subForm.relation?.length) { - subForm.searchParams = new URLSearchParams( - subForm.relation.reduce( - (acc, a) => { - if (formData?.[a.parentFieldName]) { - acc[a.childFieldName] = formData[a.parentFieldName] - } - return acc - }, - {} as Record, - ), - ) - subForm.id = formData[subForm.relation[0].parentFieldName] + // sadece ilk eşleşmeyi al + const rel = subForm.relation.find((a) => formData?.[a.parentFieldName]) + + if (rel) { + const filter: [string, string, string] = [ + rel.childFieldName, + '=', + formData[rel.parentFieldName], + ] + + subForm.searchParams = new URLSearchParams({ + filter: JSON.stringify(filter), + }) + + subForm.id = formData[rel.parentFieldName] + } } } diff --git a/ui/src/views/list/Grid.tsx b/ui/src/views/list/Grid.tsx index e3410aaf..0da9aaa4 100644 --- a/ui/src/views/list/Grid.tsx +++ b/ui/src/views/list/Grid.tsx @@ -103,6 +103,15 @@ const Grid = (props: GridProps) => { import('devextreme/pdf_exporter') } + const defaultSearchParamsFilter = useRef(null) + + useEffect(() => { + // sadece 1 kere açılışta al + if (!defaultSearchParamsFilter.current) { + defaultSearchParamsFilter.current = searchParams?.get('filter') ?? null + } + }, [searchParams]) + const { toolbarData, toolbarModalData, setToolbarModalData } = useToolbar({ gridDto, listFormCode, @@ -242,33 +251,37 @@ const Grid = (props: GridProps) => { if (!searchParams) { continue } - if (searchParams.has(colFormat.fieldName)) { - const dType = colFormat.dataType as DataType - switch (dType) { - case 'date': - case 'datetime': - e.data[colFormat.fieldName] = new Date(searchParams.get(colFormat.fieldName)!) - break - case 'number': - e.data[colFormat.fieldName] = Number(searchParams.get(colFormat.fieldName)) - break - case 'boolean': - if (searchParams.get(colFormat.fieldName) === 'true') { - e.data[colFormat.fieldName] = true - } else if (searchParams.get(colFormat.fieldName) === 'false') { - e.data[colFormat.fieldName] = false + + const filterValue = searchParams.get('filter') + + if (filterValue) { + if (filterValue.includes(colFormat.fieldName)) { + const parsed = JSON.parse(filterValue) as [string, string, any] + const [field, op, val] = parsed + + if (field === colFormat.fieldName) { + const dType = colFormat.dataType as DataType + switch (dType) { + case 'date': + case 'datetime': + e.data[colFormat.fieldName] = new Date(val) + break + case 'number': + e.data[colFormat.fieldName] = Number(val) + break + case 'boolean': + e.data[colFormat.fieldName] = val === true || val === 'true' + break + case 'object': + try { + e.data[colFormat.fieldName] = JSON.parse(val) + } catch {} + break + default: + e.data[colFormat.fieldName] = val + break } - break - case 'object': - try { - e.data[colFormat.fieldName] = JSON.parse( - searchParams.get(colFormat.fieldName) as string, - ) - } catch (error) {} - break - default: - e.data[colFormat.fieldName] = searchParams.get(colFormat.fieldName) - break + } } } } @@ -366,6 +379,10 @@ const Grid = (props: GridProps) => { gridRef.current.instance.option('remoteOperations', false) gridRef.current.instance.option('dataSource', undefined) gridRef.current.instance.state(null) + + //bir önceki gridin filtreleri kalmasın + setExtraFilters([]) + setWidgetGroups([]) } const initializeGrid = async () => { @@ -395,6 +412,17 @@ const Grid = (props: GridProps) => { addCss(css) } } + + if (gridDto?.gridOptions.extraFilterDto) { + setExtraFilters( + gridDto.gridOptions.extraFilterDto.map((f) => ({ + fieldName: f.fieldName, + operator: f.operator, + controlType: f.controlType, + value: f.defaultValue ?? '', + })), + ) + } }, [gridDto]) useEffect(() => { @@ -403,48 +431,42 @@ const Grid = (props: GridProps) => { const cols = getBandedColumns() setColumnData(cols) - // 1. extraFilters'tan gelenleri ekle - const filterArray: any[] = [] - extraFilters.forEach((f) => { - if (f.value) { - if (filterArray.length > 0) filterArray.push('and') - filterArray.push([f.fieldName, f.operator, f.value]) - } - }) - - // 2. searchParams içindeki kolonları filtreye çevir - const params = new URLSearchParams(searchParams) - if (cols?.length) { - cols.forEach((col) => { - if (!col.dataField) return - const sValue = params.get(col.dataField) - if (sValue) { - if (filterArray.length > 0) filterArray.push('and') - filterArray.push([col.dataField, '=', sValue]) - // orijinal paramı kaldır - params.delete(col.dataField) - } - }) - } - - // 3. oluşan filtreleri `filter` paramına yaz - if (filterArray.length > 0) { - params.set('filter', JSON.stringify(filterArray)) - } else { - params.delete('filter') - } - - // 4. datasource oluştur - const dataSource = createSelectDataSource( - gridDto.gridOptions, - listFormCode, - params, - cols, - setWidgetGroups, - ) - + const dataSource = createSelectDataSource(gridDto.gridOptions, listFormCode, searchParams, cols) setGridDataSource(dataSource) - }, [gridDto, searchParams, extraFilters]) + }, [gridDto, searchParams]) + + useEffect(() => { + const activeFilters = extraFilters.filter((f) => f.value) + + let filter: any = null + + if (activeFilters.length === 1) { + filter = [activeFilters[0].fieldName, activeFilters[0].operator, activeFilters[0].value] + } else if (activeFilters.length > 1) { + filter = activeFilters.reduce((acc, f, idx) => { + if (idx === 0) return [f.fieldName, f.operator, f.value] + return [acc, 'and', [f.fieldName, f.operator, f.value]] + }, null as any) + } + + if (filter) { + // hem defaultFilter hem extraFilter birleştir + if (defaultSearchParamsFilter.current) { + const base = JSON.parse(defaultSearchParamsFilter.current) + filter = [base, 'and', filter] + } + searchParams?.set('filter', JSON.stringify(filter)) + } else { + // sadece default filter kalsın + if (defaultSearchParamsFilter.current) { + searchParams?.set('filter', defaultSearchParamsFilter.current) + } else { + searchParams?.delete('filter') + } + } + + gridRef.current?.instance.refresh() + }, [extraFilters]) useEffect(() => { refListFormCode.current = listFormCode @@ -540,85 +562,10 @@ const Grid = (props: GridProps) => { } } - useEffect(() => { - if (gridDto?.gridOptions.extraFilterDto.length === 0) return - - if (gridDto?.gridOptions.extraFilterDto) { - setExtraFilters( - gridDto.gridOptions.extraFilterDto.map((f) => ({ - fieldName: f.fieldName, - operator: f.operator, - controlType: f.controlType, - value: f.defaultValue ?? '', - })), - ) - } - }, [gridDto]) - - useEffect(() => { - if (!searchParams) return - if (extraFilters.length === 0) return - - // aktif filtreleri al - const activeFilters = extraFilters.filter((f) => f.value) - - let filter: any = null - if (activeFilters.length === 1) { - // tek filtre -> düz array - const f = activeFilters[0] - filter = [f.fieldName, f.operator, f.value] - } else if (activeFilters.length > 1) { - // birden fazla filtre -> and ile bağla - filter = activeFilters.reduce((acc, f, idx) => { - if (idx === 0) return [f.fieldName, f.operator, f.value] - return [acc, 'and', [f.fieldName, f.operator, f.value]] - }, null as any) - } - - if (filter) { - searchParams.set('filter', JSON.stringify(filter)) - } else { - searchParams.delete('filter') - } - - gridRef.current?.instance.refresh() - }, [extraFilters, searchParams]) - return ( <> - {/* {gridDto?.gridOptions.extraFilterDto && ( - - {gridDto?.gridOptions.extraFilterDto?.map((fs) => { - const current = extraFilters.find((f) => f.fieldName === fs.fieldName) - - return ( - - {fs.caption} - - setExtraFilters((prev) => - prev.map((f) => - f.fieldName === fs.fieldName ? { ...f, value: e.target.value } : f, - ), - ) - } - > - Seçiniz - {fs.items.map((item) => ( - - {item.text} - - ))} - - - ) - })} - - )} */} - {!isSubForm && ( { focusedRowEnabled={gridDto.gridOptions.columnOptionDto?.focusedRowEnabled} showColumnHeaders={gridDto.gridOptions.columnOptionDto?.showColumnHeaders} filterSyncEnabled={true} + onContentReady={(e) => { + const ds = e.component.getDataSource() + if (!ds) return + + const store = ds.store() as any + if (store._widgets) { + setWidgetGroups(store._widgets) + } + }} onSelectionChanged={onSelectionChanged} onInitNewRow={onInitNewRow} onCellPrepared={onCellPrepared} @@ -726,10 +682,19 @@ const Grid = (props: GridProps) => { editorOptions = i.editorOptions && JSON.parse(i.editorOptions) // Eğer default value varsa, bu editörü readonly yapıyoruz - if (searchParams?.has(i.dataField)) { - editorOptions = { - ...editorOptions, - readOnly: true, + const rawFilter = searchParams?.get('filter') + if (rawFilter) { + const parsed = JSON.parse(rawFilter) as [ + string, + string, + any, + ] + const [field, op, val] = parsed + if (field === i.dataField) { + editorOptions = { + ...editorOptions, + readOnly: true, + } } } } catch {} @@ -826,7 +791,7 @@ const Grid = (props: GridProps) => { ))} {/* burada özel filtre alanını Template ile bağla */} {gridDto?.gridOptions.extraFilterDto?.length ? ( - + ) : null} @@ -900,21 +865,11 @@ const Grid = (props: GridProps) => { /> ))} - {/* - - */} {gridDto?.gridOptions?.subFormsDto?.length > 0 && ( <> - + > )} diff --git a/ui/src/views/list/useToolbar.tsx b/ui/src/views/list/useToolbar.tsx index 8ebdce51..ad6be9fd 100644 --- a/ui/src/views/list/useToolbar.tsx +++ b/ui/src/views/list/useToolbar.tsx @@ -8,7 +8,7 @@ import { ToolbarItem } from 'devextreme/ui/data_grid_types' import { useEffect, useState } from 'react' import { useDialogContext } from '../shared/DialogContext' import { usePWA } from '@/utils/hooks/usePWA' -import { GridExtraFilterState } from './GridColumnData' +import { GridExtraFilterState } from './Utils' type ToolbarModalData = { open: boolean