From 8b162ac64110181b3ef8f2d9237e537137ab7665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96zt=C3=BCrk?= Date: Sun, 1 Feb 2026 12:38:14 +0300 Subject: [PATCH] =?UTF-8?q?Card=20Layout=20kald=C4=B1r=C4=B1ld=C4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ListForms/GridOptionsDto/LayoutDto.cs | 2 - .../Seeds/LanguagesData.json | 14 +- .../Seeds/ListFormSeeder_Hr.cs | 73 +- .../Seeds/SeederDefaults.cs | 2 - ui/src/proxy/admin/list-form/models.ts | 1 - ui/src/proxy/form/models.ts | 2 - .../admin/listForm/edit/FormTabDetails.tsx | 35 - ui/src/views/admin/listForm/edit/options.ts | 3 +- ui/src/views/admin/listForm/edit/types.ts | 3 +- ui/src/views/list/CardView.tsx | 1214 ----------------- ui/src/views/list/List.tsx | 23 - 11 files changed, 40 insertions(+), 1332 deletions(-) delete mode 100644 ui/src/views/list/CardView.tsx diff --git a/api/src/Erp.Platform.Application.Contracts/ListForms/GridOptionsDto/LayoutDto.cs b/api/src/Erp.Platform.Application.Contracts/ListForms/GridOptionsDto/LayoutDto.cs index 9222eff8..e8d87fda 100644 --- a/api/src/Erp.Platform.Application.Contracts/ListForms/GridOptionsDto/LayoutDto.cs +++ b/api/src/Erp.Platform.Application.Contracts/ListForms/GridOptionsDto/LayoutDto.cs @@ -3,13 +3,11 @@ public class LayoutDto { public bool Grid { get; set; } = true; - public bool Card { get; set; } = true; public bool Pivot { get; set; } = true; public bool Chart { get; set; } = true; public bool Tree { get; set; } = true; public bool Gantt { get; set; } = true; public bool Scheduler { get; set; } = true; public string DefaultLayout { get; set; } = "grid"; - public int CardLayoutColumn { get; set; } = 4; } diff --git a/api/src/Erp.Platform.DbMigrator/Seeds/LanguagesData.json b/api/src/Erp.Platform.DbMigrator/Seeds/LanguagesData.json index 77f4ad7e..4cdde616 100644 --- a/api/src/Erp.Platform.DbMigrator/Seeds/LanguagesData.json +++ b/api/src/Erp.Platform.DbMigrator/Seeds/LanguagesData.json @@ -3588,12 +3588,6 @@ "en": "Grid Layout", "tr": "Liste Düzeni" }, - { - "resourceName": "Platform", - "key": "ListForms.ListFormEdit.DetailsLayoutDto.CardLayout", - "en": "Card Layout", - "tr": "Kart Düzeni" - }, { "resourceName": "Platform", "key": "ListForms.ListFormEdit.DetailsLayoutDto.PivotLayout", @@ -3618,12 +3612,6 @@ "en": "Default Layout", "tr": "Varsayılan Düzen" }, - { - "resourceName": "Platform", - "key": "ListForms.ListFormEdit.DetailsLayoutDto.CardLayoutColumn", - "en": "Card Layout Column", - "tr": "Kart Düzeni Sütunu" - }, { "resourceName": "Platform", "key": "ListForms.ListFormEdit.DatabaseDataSource", @@ -15432,7 +15420,7 @@ "resourceName": "Platform", "key": "App.Listform.ListformField.Township", "en": "Township", - "tr": "İlçe" + "tr": "Mahalle" }, { "resourceName": "Platform", diff --git a/api/src/Erp.Platform.DbMigrator/Seeds/ListFormSeeder_Hr.cs b/api/src/Erp.Platform.DbMigrator/Seeds/ListFormSeeder_Hr.cs index 8598731e..0b0c6f3d 100644 --- a/api/src/Erp.Platform.DbMigrator/Seeds/ListFormSeeder_Hr.cs +++ b/api/src/Erp.Platform.DbMigrator/Seeds/ListFormSeeder_Hr.cs @@ -1516,39 +1516,6 @@ public class ListFormSeeder_Hr : IDataSeedContributor, ITransientDependency PermissionJson = DefaultFieldPermissionJson(listForm.Name), PivotSettingsJson = DefaultPivotSettingsJson }, - - new() { - ListFormCode = listForm.ListFormCode, - CultureName = LanguageCodes.En, - SourceDbType = DbType.String, - FieldName = "Address1", - CaptionName = "App.Listform.ListformField.Address1", - Width = 200, - ListOrderNo = 10, - Visible = true, - IsActive = true, - IsDeleted = false, - AllowSearch = true, - ColumnCustomizationJson = DefaultColumnCustomizationJson, - PermissionJson = DefaultFieldPermissionJson(listForm.Name), - PivotSettingsJson = DefaultPivotSettingsJson - }, - new() { - ListFormCode = listForm.ListFormCode, - CultureName = LanguageCodes.En, - SourceDbType = DbType.String, - FieldName = "Address2", - CaptionName = "App.Listform.ListformField.Address2", - Width = 200, - ListOrderNo = 11, - Visible = true, - IsActive = true, - IsDeleted = false, - AllowSearch = true, - ColumnCustomizationJson = DefaultColumnCustomizationJson, - PermissionJson = DefaultFieldPermissionJson(listForm.Name), - PivotSettingsJson = DefaultPivotSettingsJson - }, new() { ListFormCode = listForm.ListFormCode, CultureName = LanguageCodes.En, @@ -1556,7 +1523,7 @@ public class ListFormSeeder_Hr : IDataSeedContributor, ITransientDependency FieldName = "Country", CaptionName = "App.Listform.ListformField.Country", Width = 100, - ListOrderNo = 12, + ListOrderNo = 10, Visible = true, IsActive = true, IsDeleted = false, @@ -1581,7 +1548,7 @@ public class ListFormSeeder_Hr : IDataSeedContributor, ITransientDependency FieldName = "City", CaptionName = "App.Listform.ListformField.City", Width = 100, - ListOrderNo = 13, + ListOrderNo = 11, Visible = true, IsActive = true, IsDeleted = false, @@ -1609,7 +1576,7 @@ public class ListFormSeeder_Hr : IDataSeedContributor, ITransientDependency FieldName = "District", CaptionName = "App.Listform.ListformField.District", Width = 100, - ListOrderNo = 14, + ListOrderNo = 12, Visible = true, IsActive = true, IsDeleted = false, @@ -1637,7 +1604,7 @@ public class ListFormSeeder_Hr : IDataSeedContributor, ITransientDependency FieldName = "Township", CaptionName = "App.Listform.ListformField.Township", Width = 100, - ListOrderNo = 15, + ListOrderNo = 13, Visible = true, IsActive = true, IsDeleted = false, @@ -1657,6 +1624,38 @@ public class ListFormSeeder_Hr : IDataSeedContributor, ITransientDependency PermissionJson = DefaultFieldPermissionJson(listForm.Name), PivotSettingsJson = DefaultPivotSettingsJson }, + new() { + ListFormCode = listForm.ListFormCode, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "Address1", + CaptionName = "App.Listform.ListformField.Address1", + Width = 200, + ListOrderNo = 14, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + ColumnCustomizationJson = DefaultColumnCustomizationJson, + PermissionJson = DefaultFieldPermissionJson(listForm.Name), + PivotSettingsJson = DefaultPivotSettingsJson + }, + new() { + ListFormCode = listForm.ListFormCode, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "Address2", + CaptionName = "App.Listform.ListformField.Address2", + Width = 200, + ListOrderNo = 15, + Visible = true, + IsActive = true, + IsDeleted = false, + AllowSearch = true, + ColumnCustomizationJson = DefaultColumnCustomizationJson, + PermissionJson = DefaultFieldPermissionJson(listForm.Name), + PivotSettingsJson = DefaultPivotSettingsJson + }, new() { ListFormCode = listForm.ListFormCode, CultureName = LanguageCodes.En, diff --git a/api/src/Erp.Platform.DbMigrator/Seeds/SeederDefaults.cs b/api/src/Erp.Platform.DbMigrator/Seeds/SeederDefaults.cs index 4c009509..b22d9d6b 100644 --- a/api/src/Erp.Platform.DbMigrator/Seeds/SeederDefaults.cs +++ b/api/src/Erp.Platform.DbMigrator/Seeds/SeederDefaults.cs @@ -65,14 +65,12 @@ public static class SeederDefaults public static string DefaultLayoutJson(string DefaultLayout = "grid") => JsonSerializer.Serialize(new LayoutDto() { Grid = true, - Card = true, Pivot = true, Chart = true, Tree = true, Gantt = true, Scheduler = true, DefaultLayout = DefaultLayout, - CardLayoutColumn = 4 }); public static readonly string DefaultExportJson = JsonSerializer.Serialize(new ChartExportDto { diff --git a/ui/src/proxy/admin/list-form/models.ts b/ui/src/proxy/admin/list-form/models.ts index db57c912..91b145a6 100644 --- a/ui/src/proxy/admin/list-form/models.ts +++ b/ui/src/proxy/admin/list-form/models.ts @@ -55,5 +55,4 @@ export interface ListFormJsonRowDto { export interface ListState { listFormCode: string layout: ListViewLayoutType - cardLayoutColumn?: number } diff --git a/ui/src/proxy/form/models.ts b/ui/src/proxy/form/models.ts index 9f9b5d58..1ebfd340 100644 --- a/ui/src/proxy/form/models.ts +++ b/ui/src/proxy/form/models.ts @@ -893,14 +893,12 @@ export interface WidgetEditDto { export interface LayoutDto { grid: boolean - card: boolean pivot: boolean tree: boolean chart: boolean gantt: boolean scheduler: boolean defaultLayout: ListViewLayoutType - cardLayoutColumn: number } export interface ExtraFilterDto { diff --git a/ui/src/views/admin/listForm/edit/FormTabDetails.tsx b/ui/src/views/admin/listForm/edit/FormTabDetails.tsx index cd91a35d..d6ce2047 100644 --- a/ui/src/views/admin/listForm/edit/FormTabDetails.tsx +++ b/ui/src/views/admin/listForm/edit/FormTabDetails.tsx @@ -23,13 +23,11 @@ const schema = Yup.object().shape({ showNote: Yup.boolean(), layoutDto: Yup.object().shape({ grid: Yup.boolean(), - card: Yup.boolean(), pivot: Yup.boolean(), chart: Yup.boolean(), tree: Yup.boolean(), gantt: Yup.boolean(), defaultLayout: Yup.string(), - cardLayoutColumn: Yup.number(), }), }) @@ -259,25 +257,6 @@ function FormTabDetails( )} - - - - )} @@ -296,20 +275,6 @@ function FormTabDetails( /> - - - - Promise - gridDto?: GridDto - refreshGridDto?: () => Promise -} - -const statedGridPanelColor = 'rgba(50, 200, 200, 0.5)' - -const CardView = (props: CardViewProps) => { - const { listFormCode, searchParams, isSubForm, gridDto: extGridDto, refreshGridDto } = props - const { translate } = useLocalization() - const { checkPermission } = usePermission() - const isPwaMode = usePWA() - const config = useStoreState((state) => state.abpConfig.config) - - const cardViewRef = useRef>(null) - const refListFormCode = useRef('') - const widgetGroupRef = useRef(null) - - const [cardViewDataSource, setCardViewDataSource] = useState>() - const [columnData, setColumnData] = useState() - const [formData, setFormData] = useState() - const [mode, setMode] = useState('view') - const [extraFilters, setExtraFilters] = useState([]) - const [gridDto, setGridDto] = useState() - const [isPopupFullScreen, setIsPopupFullScreen] = useState(false) - const [widgetGroupHeight, setWidgetGroupHeight] = useState(0) - const [cardsPerRow, setCardsPerRow] = useState(0) - - // Store'dan admin.lists.states'e erişim - const adminStates = useStoreState((state: any) => state.admin?.lists?.states || []) - const setAdminStates = useStoreActions((actions: any) => actions.admin?.lists?.setStates) - const [pageSize, setPageSize] = useState(20) - - type EditorOptionsWithButtons = { - buttons?: any[] - } & Record - - const defaultSearchParams = useRef(null) - - const settingButtonClick = useCallback(() => { - window.open( - ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(':listFormCode', listFormCode), - isPwaMode ? '_self' : '_blank', - ) - }, [listFormCode, isPwaMode]) - - useEffect(() => { - const initializeCardView = async () => { - const response = await getList({ listFormCode }) - setGridDto(response.data) - } - - if (extGridDto === undefined) { - initializeCardView() - } else { - setGridDto(extGridDto) - } - }, [listFormCode, extGridDto]) - - useEffect(() => { - if (!defaultSearchParams.current) { - defaultSearchParams.current = searchParams?.get('filter') ?? null - } - }, [searchParams]) - - const { filterToolbarData, ...filterData } = useFilters({ - gridDto, - gridRef: cardViewRef as any, - listFormCode, - }) - - const { createSelectDataSource } = useListFormCustomDataSource({ gridRef: cardViewRef as any }) - const { getBandedColumns } = useListFormColumns({ - gridDto, - listFormCode, - isSubForm, - gridRef: cardViewRef as any, - }) - - function refreshData() { - const instance = cardViewRef.current?.instance() - if (instance) { - instance.getDataSource()?.reload() - } - } - - // CardView specific events - function onSelectionChanged(data: any) { - const grdOpt = gridDto?.gridOptions - const grd = cardViewRef.current?.instance() - if (!grdOpt || !grd) { - return - } - - // kullanicinin yetkisi varsa ve birden fazla kayit secili ise coklu silme gorunsun - if (grdOpt.editingOptionDto?.allowDeleting) { - // && abp.auth.isGranted(grdOpt.permissionDto?.d) - // kullanicinin silme yetkisi var ise - const opt = grd.option('toolbar') - const deleteSelectedRecordsIndex = opt?.items - ?.map((e: any) => e.name) - .indexOf('deleteSelectedRecords') - // deleteSelectedRecords ismindeki custom butonun index degerini bul - - grd.option( - `toolbar.items[${deleteSelectedRecordsIndex}].options.visible`, - data.selectedRowsData.length > 1, - ) // birden fazla kayit secilmis ise gorunsun - } - - // SubForm'ları gösterebilmek için secili satiri formData'ya at - if (data.selectedRowsData.length) { - setFormData(data.selectedRowsData[0]) - } - } - - function onInitNewRow(e: any) { - if (!gridDto?.columnFormats) { - return - } - - setMode('new') - setIsPopupFullScreen(gridDto?.gridOptions.editingOptionDto?.popup?.fullScreen ?? false) - - for (const colFormat of gridDto?.columnFormats) { - if (!colFormat.fieldName) { - continue - } - - // Grid'den gelen columnFormat'ları kullanarak default değerleri set et - if (colFormat.defaultValue != null) { - if ( - typeof colFormat.defaultValue === 'string' && - colFormat.defaultValue === '@AUTONUMBER' - ) { - e.data[colFormat.fieldName] = autoNumber() - } else { - e.data[colFormat.fieldName] = colFormat.defaultValue - } - } - - // ExtraFilters içerisinde ilgili Field varsa, default değerleri set etme - if (extraFilters.some((f) => f.fieldName === colFormat.fieldName)) { - continue - } - - // URL'den veya Component Prop'dan gelen parametreleri set et - if (!searchParams) { - continue - } - - const rawFilter = searchParams?.get('filter') - if (rawFilter) { - const parsed = JSON.parse(rawFilter) - const filters = extractSearchParamsFields(parsed) - const fieldMatch = filters.find(([field]) => field === colFormat.fieldName) - - if (fieldMatch) { - const val = fieldMatch[2] //value - 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 - } - } - } - } - } - - function onRowInserting(e: CardInsertingEvent) { - e.data = setFormEditingExtraItemValues(e.data) - } - - function onRowUpdating(e: CardUpdatingEvent) { - if (gridDto?.gridOptions.editingOptionDto?.sendOnlyChangedFormValuesUpdate) { - if (Object.keys(e.newData).some((a) => a.includes(':'))) { - Object.keys(e.oldData).forEach((col) => { - if (col.includes(':')) { - e.newData[col] = e.newData[col] ?? e.oldData[col] - } - }) - } - e.newData = setFormEditingExtraItemValues(e.newData) - } else { - let newData = { ...e.oldData, ...e.newData } - newData = setFormEditingExtraItemValues(newData) - Object.keys(newData).forEach((key) => { - if (key.includes(':')) { - delete newData[key] - } - }) - e.newData = newData - } - - if (gridDto?.gridOptions.keyFieldName) { - delete e.newData[gridDto?.gridOptions.keyFieldName] - } - } - - function onEditingStart(e: EditingStartEvent) { - setMode('edit') - setIsPopupFullScreen(gridDto?.gridOptions.editingOptionDto?.popup?.fullScreen ?? false) - const columns = e.component.option('columns') as GridColumnData[] - // FormEditingExtraItem field ise datayı doldur - columns?.forEach((col) => { - if (!col.dataField?.includes(':')) { - return - } - const field = col.dataField.split(':') - if (!e.data[field[0]]) { - return - } - const json = JSON.parse(e.data[field[0]]) - e.data[col.dataField] = json[field[1]] - }) - } - - useEffect(() => { - if (cardViewRef?.current) { - const instance = cardViewRef?.current?.instance() - if (instance) { - instance.option('columns', undefined) - instance.option('remoteOperations', false) - instance.option('dataSource', undefined) - } - } - - if (refListFormCode.current !== listFormCode) { - setExtraFilters([]) - } - }, [listFormCode]) - - // cardsPerRow başlangıç değeri: önce admin.lists.states'ten, yoksa gridDto'dan - useEffect(() => { - let foundState = adminStates.find((s: any) => s.listFormCode === listFormCode) - if (foundState) { - setCardsPerRow(foundState.cardLayoutColumn) - } else if (gridDto?.gridOptions.layoutDto?.cardLayoutColumn) { - setCardsPerRow(gridDto.gridOptions.layoutDto.cardLayoutColumn) - } else { - setCardsPerRow(0) - } - }, [listFormCode, adminStates]) - - useEffect(() => { - if (!gridDto) { - return - } - - // Set js and css - const grdOpt = gridDto.gridOptions - if (grdOpt.customJsSources.length) { - for (const js of grdOpt.customJsSources) { - addJs(js) - } - } - if (grdOpt.customStyleSources.length) { - for (const css of grdOpt.customStyleSources) { - addCss(css) - } - } - - if (gridDto?.gridOptions.extraFilterDto) { - setExtraFilters( - gridDto.gridOptions.extraFilterDto.map((f) => ({ - fieldName: f.fieldName, - operator: f.operator, - controlType: f.controlType, - value: f.defaultValue ?? '', - })), - ) - } - - if (gridDto.gridOptions.editingOptionDto?.popup) { - setIsPopupFullScreen(gridDto.gridOptions.editingOptionDto?.popup?.fullScreen ?? false) - } - }, [gridDto]) - - // Kolonları oluştur - dil değiştiğinde güncelle - useEffect(() => { - if (!gridDto || !config) return - - //butonlar kaldırılarak CardView ekleniyor. - const cols = getBandedColumns()?.filter((c) => c.type !== 'buttons') - - cols?.forEach((col) => { - const eo = col.editorOptions - - // Lookup desteği ekle (SchedulerView'daki gibi) - // gridDto'dan ilgili columnFormat'ı bul - const listFormField = gridDto?.columnFormats?.find((x: any) => x.fieldName === col.dataField) - if (listFormField?.lookupDto) { - const lookup = listFormField.lookupDto - if (lookup.dataSourceType === UiLookupDataSourceTypeEnum.Query) { - col.editorOptions = { - ...col.editorOptions, - dataSource: new CustomStore({ - key: 'key', - loadMode: 'raw', - load: async () => { - try { - const { dynamicFetch } = await import('@/services/form.service') - const response = await dynamicFetch('list-form-select/lookup', 'POST', null, { - listFormCode, - listFormFieldName: col.dataField, - filters: [], - }) - return (response.data ?? []).map((a: any) => ({ - key: a.Key, - name: a.Name, - group: a.Group, - })) - } catch (error) { - console.error('Lookup load error:', error) - return [] - } - }, - }), - valueExpr: 'key', - displayExpr: 'name', - } - } else if (lookup.dataSourceType === UiLookupDataSourceTypeEnum.StaticData) { - if (lookup.lookupQuery) { - try { - const staticData = JSON.parse(lookup.lookupQuery) - col.editorOptions = { - ...col.editorOptions, - dataSource: staticData, - valueExpr: lookup.valueExpr || 'key', - displayExpr: lookup.displayExpr || 'name', - } - } catch (error) { - console.error('Static data parse error:', error) - } - } - } - } - - // Sadece phoneGlobal formatlı kolonlarda çalış - if (eo?.format === 'phoneGlobal') { - // DevExtreme bazen string tipinde formatter'ı çağırmaz - // Bu yüzden her durumda çalışması için customizeText ekleyeceğiz - col.dataType = 'string' - - const formatter = (value: any) => { - if (!value) return '' - - // string'e dönüştür ve sadece rakamları al - let digits = String(value).replace(/\D/g, '') - - // +90, 0090, 0 gibi ülke kodu veya ön ekleri atla - if (digits.startsWith('90') && digits.length > 10) digits = digits.slice(-10) - if (digits.startsWith('0') && digits.length > 10) digits = digits.slice(-10) - if (digits.length > 10) digits = digits.slice(-10) - - // 🔒 Eğer 10 haneli değilse geçersiz → boş göster - if (digits.length !== 10) return '' - - // (XXX) XXX-XXXX formatında göster - const match = digits.match(/^(\d{3})(\d{3})(\d{4})$/) - return match ? `(${match[1]}) ${match[2]}-${match[3]}` : digits - } - - // 1️⃣ Normal format nesnesi - col.format = { formatter } - - // 2️⃣ CustomizeText fallback — bazı durumlarda zorunlu - col.customizeText = (cellInfo: any) => formatter(cellInfo?.value) - } - }) - - setColumnData(cols as any) - }, [gridDto, config]) - - // DataSource oluştur - useEffect(() => { - if (!gridDto) return - - const dataSource = createSelectDataSource( - gridDto.gridOptions, - listFormCode, - searchParams, - layoutTypes.grid, - columnData, - ) - - setCardViewDataSource(dataSource) - }, [gridDto, searchParams]) - - useEffect(() => { - if (!columnData) return - - refListFormCode.current = listFormCode - if (!cardViewRef?.current) { - return - } - - const instance = cardViewRef?.current?.instance() - if (instance) { - instance.option('columns', columnData) - instance.option('remoteOperations', { - filtering: true, - sorting: true, - paging: true, - grouping: true, - }) - instance.option('dataSource', cardViewDataSource) - } - }, [columnData]) - - // extraFilters değişikliğini izlemek için ref - const extraFiltersInitialized = useRef(false) - - useEffect(() => { - // İlk yüklemede reload yapma, sadece kullanıcı filtre değiştirdiğinde - if (!extraFiltersInitialized.current) { - extraFiltersInitialized.current = true - return - } - - const instance = cardViewRef.current?.instance() - if (!instance) return - - const activeFilters = extraFilters.filter((f) => f.value) - - let base: any = null - if (defaultSearchParams.current) { - base = JSON.parse(defaultSearchParams.current) - } - - const baseTriplets = extractSearchParamsFields(base) - const extraTriplets = activeFilters.map( - (f) => [f.fieldName, f.operator, f.value] as [string, string, any], - ) - - const merged = [...baseTriplets, ...extraTriplets].reduce( - (acc, cur) => { - const idx = acc.findIndex((a) => a[0] === cur[0] && a[1] === cur[1]) - if (idx >= 0) { - acc[idx] = cur - } else { - acc.push(cur) - } - return acc - }, - [] as [string, string, any][], - ) - - let filter: any = null - if (merged.length === 1) { - filter = merged[0] - } else if (merged.length > 1) { - filter = merged.reduce((acc, f, idx) => { - if (idx === 0) return f - return [acc, 'and', f] - }, null as any) - } - - if (filter) { - instance?.option('filterValue', filter) - } else { - instance?.option('filterValue', undefined) - } - - instance?.getDataSource()?.reload() - }, [extraFilters]) - - useEffect(() => { - refListFormCode.current = listFormCode - }, [listFormCode]) - - // WidgetGroup yüksekliğini hesapla - useEffect(() => { - const calculateWidgetHeight = () => { - if (widgetGroupRef.current) { - const height = widgetGroupRef.current.offsetHeight - setWidgetGroupHeight(height) - } - } - - calculateWidgetHeight() - - const resizeObserver = new ResizeObserver(calculateWidgetHeight) - if (widgetGroupRef.current) { - resizeObserver.observe(widgetGroupRef.current) - } - - return () => { - resizeObserver.disconnect() - } - }, [gridDto?.widgets]) - - useEffect(() => { - if (!gridDto) { - return - } - - // Set js and css - const grdOpt = gridDto.gridOptions - if (grdOpt.customJsSources.length) { - for (const js of grdOpt.customJsSources) { - addJs(js) - } - } - if (grdOpt.customStyleSources.length) { - for (const css of grdOpt.customStyleSources) { - addCss(css) - } - } - - if (gridDto?.gridOptions.extraFilterDto) { - const extras = gridDto.gridOptions.extraFilterDto.map((f) => ({ - fieldName: f.fieldName, - caption: f.caption, - operator: f.operator || '=', - value: f.defaultValue || '', - controlType: f.controlType, - })) - // Sadece ilk yüklemede extraFilters'ı set et, her gridDto değişiminde değil - setExtraFilters((prev) => { - if (prev.length === 0) return extras - return prev - }) - } - - if (gridDto?.gridOptions.editingOptionDto?.popup) { - setIsPopupFullScreen(gridDto.gridOptions.editingOptionDto.popup.fullScreen) - } - - // pageSize başlangıç değeri sadece gridDto'dan alınır - if (gridDto?.gridOptions.pageSize) { - setPageSize(gridDto.gridOptions.pageSize) - } - }, [gridDto, listFormCode]) - - // Toolbar items - const toolbarItems = useMemo(() => { - if (!gridDto) return [] - const items: any[] = [ - { name: 'addCardButton' }, - { name: 'searchPanel' }, - { - location: 'after', - widget: 'dxButtonGroup', - options: { - items: [ - { text: 'Auto', value: 0, hint: 'Otomatik' }, - { text: '1', value: 1, hint: '1 Kolon' }, - { text: '2', value: 2, hint: '2 Kolon' }, - { text: '3', value: 3, hint: '3 Kolon' }, - { text: '4', value: 4, hint: '4 Kolon' }, - { text: '5', value: 5, hint: '5 Kolon' }, - ], - keyExpr: 'value', - selectedItemKeys: [cardsPerRow || 0], - selectionMode: 'single', - stylingMode: 'outlined', - onItemClick: (e: any) => { - if (e.itemData) { - onCardsPerRowChanged(e.itemData.value) - } - }, - }, - }, - { - location: 'after', - widget: 'dxButton', - options: { - icon: 'refresh', - hint: translate('::Refresh'), - stylingMode: 'text', - onClick: () => refreshData(), - }, - }, - ] - - // Column Chooser butonu için permission kontrolü - if (checkPermission(gridDto?.gridOptions.permissionDto?.u)) { - items.push({ - location: 'after', - widget: 'dxButton', - options: { - icon: 'columnchooser', - hint: translate('::ColumnChooser'), - stylingMode: 'text', - onClick: () => { - const instance = cardViewRef.current?.instance() - instance?.showColumnChooser() - }, - }, - }) - } - - // Settings butonu için permission kontrolü - if (checkPermission(gridDto?.gridOptions.permissionDto?.u)) { - items.push({ - location: 'after', - widget: 'dxButton', - options: { - icon: 'preferences', - hint: translate('::ListForms.ListForm.Manage'), - stylingMode: 'text', - onClick: settingButtonClick, - }, - }) - } - - return items - }, [gridDto, cardsPerRow]) - - // Kolon sayısı değiştiğinde hem state'e hem store'a kaydet - const onCardsPerRowChanged = useCallback( - (value: number) => { - setCardsPerRow(value) - // admin.lists.states'e kaydet - setAdminStates && - setAdminStates({ - listFormCode, - layout: 'card', - cardLayoutColumn: value, - }) - }, - [listFormCode, setAdminStates], - ) - - // Paging ayarları - const pagingConfig = useMemo( - () => ({ - enabled: gridDto?.gridOptions.pagerOptionDto?.visible !== false, - pageSize: pageSize, - }), - [gridDto?.gridOptions.pagerOptionDto?.visible, pageSize], - ) - - // Pager ayarları - const pagerConfig = useMemo(() => { - const allowedSizes = gridDto?.gridOptions.pagerOptionDto?.allowedPageSizes - ?.split(',') - .map((s: string) => Number(s.trim())) - .filter((n: number) => !isNaN(n) && n > 0) || [10, 20, 50, 100] - - return { - visible: gridDto?.gridOptions.pagerOptionDto?.visible !== false, - showPageSizeSelector: gridDto?.gridOptions.pagerOptionDto?.showPageSizeSelector !== false, - showInfo: gridDto?.gridOptions.pagerOptionDto?.showInfo !== false, - showNavigationButtons: gridDto?.gridOptions.pagerOptionDto?.showNavigationButtons !== false, - allowedPageSizes: allowedSizes, - displayMode: gridDto?.gridOptions.pagerOptionDto?.displayMode || 'full', - infoText: gridDto?.gridOptions.pagerOptionDto?.infoText, - } - }, [gridDto?.gridOptions.pagerOptionDto]) - - return ( - <> -
- -
- - {gridDto?.gridOptions.extraFilterDto && gridDto?.gridOptions.extraFilterDto.length > 0 && ( - - )} - - - {!isSubForm && ( - - )} - - {!gridDto && ( -
- Loading CardView configuration... -
- )} - - {gridDto && !cardViewDataSource && ( -
- Loading data source... -
- )} - - {gridDto && cardViewDataSource && ( -
- 0 - ? gridDto.gridOptions.height - : gridDto.gridOptions.fullHeight - ? `calc(100vh - ${170 + widgetGroupHeight}px)` - : undefined - } - onSelectionChanged={onSelectionChanged} - onInitNewCard={onInitNewRow} - onCardInserting={onRowInserting} - onCardUpdating={onRowUpdating} - onEditingStart={onEditingStart} - onEditCanceled={() => { - setMode('view') - setIsPopupFullScreen(false) - }} - onCardInserted={() => { - setMode('view') - setIsPopupFullScreen(false) - // Küçük bir gecikme ile reload - server transaction commit bekle - refreshData() - props.refreshData?.() - }} - onCardUpdated={() => { - setMode('view') - setIsPopupFullScreen(false) - refreshData() - props.refreshData?.() - }} - onCardRemoved={() => { - refreshData() - props.refreshData?.() - }} - > - {/* Selection */} - - - {/* Sorting */} - - - {/* Header Filter */} - - - {/* Filter Panel */} - - - {/* Search Panel */} - - - {/* Column Chooser */} - - - {/* Paging */} - - - {/* Pager */} - - - {/* Editing */} - { - const cardView = cardViewRef.current?.instance() - if (cardView) { - // Form validasyonu yap - const editForm = (cardView as any) - .getController?.('validating') - ?.validate?.() - - // Eğer validate fonksiyonu yoksa direkt kaydet - if (!editForm) { - cardView.saveEditData() - return - } - - // Validasyon hatası varsa kaydetme - if (editForm && !editForm.brokenRules?.length) { - cardView.saveEditData() - } - } - }, - }, - }, - { - widget: 'dxButton', - toolbar: 'bottom', - location: 'after', - options: { - text: translate('::Cancel'), - onClick: () => { - const cardView = cardViewRef.current?.instance() - cardView?.cancelEditData() - }, - }, - }, - { - widget: 'dxButton', - toolbar: 'top', - location: 'after', - options: { - icon: isPopupFullScreen ? 'collapse' : 'fullscreen', - hint: isPopupFullScreen - ? translate('::Normal Boyut') - : translate('::Tam Ekran'), - stylingMode: 'text', - onClick: () => setIsPopupFullScreen(!isPopupFullScreen), - }, - }, - ], - }} - form={{ - colCount: 1, - onFieldDataChanged: (e) => { - if (e.dataField) { - const formItem = gridDto.gridOptions.editingFormDto - .flatMap((group) => group.items || []) - .find((i) => i.dataField === e.dataField) - if (formItem?.editorScript) { - try { - eval(formItem.editorScript) - } catch (err) { - console.error('Script exec error', err) - } - } - } - }, - items: - gridDto.gridOptions.editingFormDto?.length > 0 - ? (() => { - const sortedFormDto = gridDto.gridOptions.editingFormDto - .slice() - .sort((a: any, b: any) => (a.order >= b.order ? 1 : -1)) - - // Tabbed item'ları grupla - const tabbedItems = sortedFormDto.filter( - (e: any) => e.itemType === 'tabbed', - ) - const result: any[] = [] - - // Helper function: item mapper - const mapFormItem = (i: EditingFormItemDto) => { - let editorOptions: EditorOptionsWithButtons = {} - try { - editorOptions = i.editorOptions && JSON.parse(i.editorOptions) - - if (editorOptions?.buttons) { - editorOptions.buttons = (editorOptions?.buttons || []).map( - (btn: any) => { - if ( - btn?.options?.onClick && - typeof btn.options.onClick === 'string' - ) { - btn.options.onClick = eval(`(${btn.options.onClick})`) - } - return btn - }, - ) - } - - const rawFilter = searchParams?.get('filter') - if (rawFilter) { - const parsed = JSON.parse(rawFilter) - const filters = extractSearchParamsFields(parsed) - - const hasFilter = filters.some( - ([field, op, val]) => field === i.dataField, - ) - - if (hasFilter) { - const existsInExtra = extraFilters.some( - (f) => f.fieldName === i.dataField && !!f.value, - ) - - if (!existsInExtra) { - editorOptions = { - ...editorOptions, - readOnly: true, - } - } - } - } - } catch {} - - const fieldName = i.dataField.split(':')[0] - const listFormField = gridDto.columnFormats.find( - (x: any) => x.fieldName === fieldName, - ) - - if (listFormField?.sourceDbType === DbTypeEnum.Date) { - editorOptions = { - ...{ - type: 'date', - dateSerializationFormat: 'yyyy-MM-dd', - displayFormat: 'shortDate', - }, - ...editorOptions, - } - } else if ( - listFormField?.sourceDbType === DbTypeEnum.DateTime || - listFormField?.sourceDbType === DbTypeEnum.DateTime2 || - listFormField?.sourceDbType === DbTypeEnum.DateTimeOffset - ) { - editorOptions = { - ...{ - type: 'datetime', - dateSerializationFormat: 'yyyy-MM-ddTHH:mm:ss', - displayFormat: 'shortDateShortTime', - }, - ...editorOptions, - } - } - - // Set defaultValue for @AUTONUMBER fields - if ( - typeof listFormField?.defaultValue === 'string' && - listFormField?.defaultValue === '@AUTONUMBER' && - mode === 'new' - ) { - editorOptions = { - ...editorOptions, - value: autoNumber(), - } - } - - const item: SimpleItemWithColData = { - canRead: listFormField?.canRead ?? false, - canUpdate: listFormField?.canUpdate ?? false, - canCreate: listFormField?.canCreate ?? false, - canExport: listFormField?.canExport ?? false, - dataField: i.dataField, - name: i.dataField, - editorType2: i.editorType2, - editorType: - i.editorType2 == PlatformEditorTypes.dxGridBox - ? 'dxDropDownBox' - : i.editorType2, - colSpan: i.colSpan, - isRequired: i.isRequired, - editorOptions, - editorScript: i.editorScript, - } - - if (i.dataField.indexOf(':') >= 0) { - item.label = { text: captionize(i.dataField.split(':')[1]) } - } - - if ( - (mode == 'edit' && !item.canUpdate) || - (mode == 'new' && !item.canCreate) - ) { - item.editorOptions = { - ...item.editorOptions, - readOnly: true, - } - } - - return item - } - - sortedFormDto.forEach((e: any) => { - if (e.itemType !== 'tabbed') { - // Backend'den gelen colCount ve colSpan değerlerini kullan - const effectiveColCount = e.colCount || 1 - const effectiveColSpan = e.colSpan || 1 - - result.push({ - itemType: e.itemType, - colCount: effectiveColCount, - colSpan: effectiveColSpan, - caption: e.caption, // Group'larda caption olmalı - items: e.items - ?.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1)) - .map(mapFormItem) - .filter((a: any) => { - if (mode === 'view') { - return a.canRead - } else if (mode === 'new') { - return a.canCreate || a.canRead - } else if (mode === 'edit') { - return a.canUpdate || a.canRead - } else { - return false - } - }), - }) - } else if (tabbedItems.length > 0 && e === tabbedItems[0]) { - // Tabbed için caption OLMAMALI - sadece tabs array içinde title kullan - result.push({ - itemType: 'tabbed', - colCount: 1, - colSpan: 1, - // caption kullanma! Tabs içindeki title'lar yeterli - tabs: tabbedItems.map((tabbedItem: any) => { - // Backend'den gelen colCount ve colSpan değerlerini kullan - const effectiveColCount = tabbedItem.colCount || 1 - - return { - title: tabbedItem.caption, // Her tab'ın title'ı - colCount: effectiveColCount, - items: tabbedItem.items - ?.sort((a: any, b: any) => (a.order >= b.order ? 1 : -1)) - .map(mapFormItem) - .filter((a: any) => { - if (mode === 'view') { - return a.canRead - } else if (mode === 'new') { - return a.canCreate || a.canRead - } else if (mode === 'edit') { - return a.canUpdate || a.canRead - } else { - return false - } - }), - } - }), - }) - } - }) - - return result - })() - : undefined, - }} - /> -