From f8dcf25b269d7d903b4412b64a0ce2570b3dc270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96zt=C3=BCrk?= Date: Fri, 31 Jul 2026 22:39:52 +0300 Subject: [PATCH] =?UTF-8?q?SchedulerView=20ve=20TodoBoard=20komponenti=20u?= =?UTF-8?q?seListFormColumns=20ile=20=C3=A7al=C4=B1=C5=9Ft=C4=B1r=C4=B1ld?= =?UTF-8?q?=C4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Seeds/LanguagesData.json | 4 +- ui/src/views/list/SchedulerView.tsx | 71 ++++------ ui/src/views/list/TodoBoard.tsx | 125 ++++++++++++++++-- 3 files changed, 139 insertions(+), 61 deletions(-) diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json index e05e4094..560aed92 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json @@ -19743,8 +19743,8 @@ { "resourceName": "Platform", "key": "ListForms.TodoBoard.Status", - "en": "Kanban / Status", - "tr": "Kanban / Durum" + "en": "Status", + "tr": "Durum" }, { "resourceName": "Platform", diff --git a/ui/src/views/list/SchedulerView.tsx b/ui/src/views/list/SchedulerView.tsx index b6c943ea..6ec033c6 100644 --- a/ui/src/views/list/SchedulerView.tsx +++ b/ui/src/views/list/SchedulerView.tsx @@ -7,7 +7,6 @@ import { FieldCustomValueTypeEnum, GridDto, PlatformEditorTypes, - UiLookupDataSourceTypeEnum, } from '@/proxy/form/models' import { useLocalization } from '@/utils/hooks/useLocalization' import Scheduler, { @@ -38,6 +37,8 @@ import type { ValidationRule } from 'devextreme/common' import { FaAlignLeft, FaClock, FaHeading } from 'react-icons/fa' import { Avatar } from '@/components/ui' import { getUsers } from '@/services/identity.service' +import { useListFormColumns } from './useListFormColumns' +import type { GridColumnData } from './GridColumnData' interface SchedulerViewProps { listFormCode: string @@ -82,6 +83,11 @@ const getValueByField = (data: Record, field?: string) => { return resolvedField ? data[resolvedField] : undefined } +const flattenColumns = (columns: GridColumnData[] = []): GridColumnData[] => + columns.flatMap((column) => + column.columns?.length ? flattenColumns(column.columns as GridColumnData[]) : [column], + ) + const SchedulerView = (props: SchedulerViewProps) => { const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props const { translate } = useLocalization() @@ -158,6 +164,13 @@ const SchedulerView = (props: SchedulerViewProps) => { }, [listFormCode]) const { createSelectDataSource } = useListFormCustomDataSource({ gridRef: schedulerRef }) + const { getBandedColumns } = useListFormColumns({ + gridDto, + listFormCode, + isSubForm, + gridRef: schedulerRef, + }) + const listFormColumns = useMemo(() => flattenColumns(getBandedColumns()), [getBandedColumns]) useEffect(() => { if (!gridDto) { @@ -643,8 +656,10 @@ const SchedulerView = (props: SchedulerViewProps) => { console.log(err) } - const fieldName = i.fieldName! const listFormField = i + const listFormColumn = listFormColumns.find( + (column) => column.dataField?.toLowerCase() === i.fieldName?.toLowerCase(), + ) if (listFormField?.sourceDbType === DbTypeEnum.Date) { editorOptions = { @@ -678,48 +693,14 @@ const SchedulerView = (props: SchedulerViewProps) => { editorType = i.editorType2 } - // Lookup DataSource oluştur - if (listFormField?.lookupDto) { - const lookup = listFormField.lookupDto - - if (lookup.dataSourceType === UiLookupDataSourceTypeEnum.Query) { - 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: fieldName, - 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 [] - } - }, - }) - - editorOptions.valueExpr = 'key' - editorOptions.displayExpr = 'name' - } else if (lookup.dataSourceType === UiLookupDataSourceTypeEnum.StaticData) { - if (lookup.lookupQuery) { - try { - const staticData = JSON.parse(lookup.lookupQuery) - editorOptions.dataSource = staticData - editorOptions.valueExpr = lookup.valueExpr || 'key' - editorOptions.displayExpr = lookup.displayExpr || 'name' - } catch (error) { - console.error('Static data parse error:', error) - } - } - } + const lookup = listFormColumn?.lookup + if (lookup) { + editorOptions.dataSource = + typeof lookup.dataSource === 'function' + ? lookup.dataSource({ data: e.appointmentData ?? {} }) + : lookup.dataSource + editorOptions.valueExpr = lookup.valueExpr + editorOptions.displayExpr = lookup.displayExpr } const item: SimpleItemWithColData = { @@ -822,7 +803,7 @@ const SchedulerView = (props: SchedulerViewProps) => { e.form.option('showValidationSummary', false) e.form.option('items', result) }, - [gridDto, translate, isPopupFullScreen, listFormCode, useMobileEditPopup], + [gridDto, translate, isPopupFullScreen, listFormColumns, useMobileEditPopup], ) const configuredEditPopup = gridDto?.gridOptions.editingOptionDto?.popup diff --git a/ui/src/views/list/TodoBoard.tsx b/ui/src/views/list/TodoBoard.tsx index aac49f69..df0f8aa2 100644 --- a/ui/src/views/list/TodoBoard.tsx +++ b/ui/src/views/list/TodoBoard.tsx @@ -45,6 +45,9 @@ import { getTenants } from '@/services/tenant.service' import { useLocalization } from '@/utils/hooks/useLocalization' import { usePermission } from '@/utils/hooks/usePermission' import { usePWA } from '@/utils/hooks/usePWA' +import { useListFormColumns } from './useListFormColumns' +import type { GridColumnData } from './GridColumnData' +import { useStoreState } from '@/store/store' dayjs.extend(relativeTime) @@ -78,6 +81,13 @@ interface UserOption { avatarUrl?: string } +type LookupDisplayValues = Record> + +const flattenColumns = (columns: GridColumnData[] = []): GridColumnData[] => + columns.flatMap((column) => + column.columns?.length ? flattenColumns(column.columns as GridColumnData[]) : [column], + ) + const fieldValue = (row: TodoRow, field?: string) => (field ? row[field] : undefined) const tenantIdValue = (row: TodoRow) => row.TenantId ?? row.tenantId @@ -157,7 +167,7 @@ const priorityLabel = ( : translate('::ListForms.TodoBoard.NoPriority') } -const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => { +const TodoBoard = ({ listFormCode, searchParams, isSubForm, gridDto }: TodoBoardProps) => { const options = gridDto.gridOptions.todoOptionDto as TodoOptionDto const editingOptions = gridDto.gridOptions.editingOptionDto const canAdd = @@ -167,9 +177,40 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => { const canDelete = editingOptions?.allowDeleting === true && Boolean(gridDto.gridOptions.deleteServiceAddress) const keyField = gridDto.gridOptions.keyFieldName ?? 'id' + const userNameField = + gridDto.columnFormats?.find((column) => column.fieldName?.toLowerCase() === 'username') + ?.fieldName ?? 'UserName' const stateStoring = gridDto.gridOptions.stateStoringDto + const currentUserName = useStoreState((state) => state.auth.user.userName) const gridRef = useRef>() const { createSelectDataSource } = useListFormCustomDataSource({ gridRef }) + const { getBandedColumns, loadLookupDisplayValues } = useListFormColumns({ + gridDto, + listFormCode, + isSubForm, + gridRef, + }) + const getBandedColumnsRef = useRef(getBandedColumns) + const loadLookupDisplayValuesRef = useRef(loadLookupDisplayValues) + getBandedColumnsRef.current = getBandedColumns + loadLookupDisplayValuesRef.current = loadLookupDisplayValues + const listFormColumnsCacheRef = useRef<{ + gridDto?: GridDto + listFormCode: string + columns: GridColumnData[] + }>() + if ( + !listFormColumnsCacheRef.current || + listFormColumnsCacheRef.current.gridDto !== gridDto || + listFormColumnsCacheRef.current.listFormCode !== listFormCode + ) { + listFormColumnsCacheRef.current = { + gridDto, + listFormCode, + columns: flattenColumns(getBandedColumnsRef.current()), + } + } + const listFormColumns = listFormColumnsCacheRef.current.columns const [store, setStore] = useState>() const [rows, setRows] = useState([]) const [statuses, setStatuses] = useState([]) @@ -194,6 +235,7 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => { const [userOptions, setUserOptions] = useState([]) const [tenantNames, setTenantNames] = useState>(new Map()) const [usersLoading, setUsersLoading] = useState(false) + const [lookupDisplayValues, setLookupDisplayValues] = useState({}) const columnConfigRef = useRef({ columns: [], retired: [] }) const dragActiveRef = useRef(false) const { translate } = useLocalization() @@ -201,6 +243,23 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => { const isPwaMode = usePWA() const uncategorized = translate('::ListForms.TodoBoard.Uncategorized') + const lookupValuesForField = useCallback( + (field?: string) => { + if (!field) return undefined + const dataField = listFormColumns.find( + (column) => column.dataField?.toLowerCase() === field.toLowerCase(), + )?.dataField + return dataField ? lookupDisplayValues[dataField] : undefined + }, + [listFormColumns, lookupDisplayValues], + ) + + const lookupText = useCallback( + (field: string | undefined, value: unknown) => + lookupValuesForField(field)?.get(String(value)) ?? String(value ?? ''), + [lookupValuesForField], + ) + const clearDragState = useCallback(() => { dragActiveRef.current = false setDraggedKey(undefined) @@ -267,6 +326,22 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => { } }, []) + useEffect(() => { + let active = true + setLookupDisplayValues({}) + + loadLookupDisplayValuesRef + .current(listFormColumns) + .then((values) => { + if (active) setLookupDisplayValues(values) + }) + .catch((error) => console.error('TodoBoard lookup load error:', error)) + + return () => { + active = false + } + }, [listFormColumns]) + useEffect(() => { let cancelled = false @@ -384,14 +459,23 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => { [options.assigneeExpr, rows], ) const assigneeOptions = useMemo(() => { - const knownUsers = new Set(userOptions.map((option) => option.value)) + const lookupValues = lookupValuesForField(options.assigneeExpr) + const avatarUrls = new Map(userOptions.map((option) => [option.value, option.avatarUrl])) + const configuredOptions = lookupValues + ? Array.from(lookupValues, ([value, label]) => ({ + value, + label, + avatarUrl: avatarUrls.get(value), + })) + : userOptions + const knownUsers = new Set(configuredOptions.map((option) => option.value)) return [ - ...userOptions, + ...configuredOptions, ...assigneeSuggestions .filter((value) => !knownUsers.has(value)) .map((value) => ({ value, label: value })), ] - }, [assigneeSuggestions, userOptions]) + }, [assigneeSuggestions, lookupValuesForField, options.assigneeExpr, userOptions]) const assigneeOptionMap = useMemo( () => new Map(assigneeOptions.map((option) => [option.value, option])), [assigneeOptions], @@ -681,6 +765,7 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => { const values: Record = { [options.titleExpr]: title, [options.statusExpr]: status, + [userNameField]: currentUserName, } if (options.completedExpr) values[options.completedExpr] = false if (options.orderExpr) values[options.orderExpr] = itemsForStatus(status).length @@ -778,12 +863,11 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
{renderToolbar(true)}
- {editingOptions?.allowUpdating === true && - !gridDto.gridOptions.updateServiceAddress && ( - - {translate('::ListForms.TodoBoard.EditRequiresUpdateService')} - - )} + {editingOptions?.allowUpdating === true && !gridDto.gridOptions.updateServiceAddress && ( + + {translate('::ListForms.TodoBoard.EditRequiresUpdateService')} + + )}
@@ -941,6 +1025,16 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => { />
)} +
+ + +
{options.statusExpr && (
@@ -957,7 +1051,7 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => { > {statuses.map((status) => ( ))} @@ -1176,8 +1270,11 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => { ) : ( <> -

- {status} +

+ {lookupText(options.statusExpr, status)}

{canUpdate && (