SchedulerView ve TodoBoard komponenti useListFormColumns ile çalıştırıldı
This commit is contained in:
parent
3a03c8e1e8
commit
f8dcf25b26
3 changed files with 139 additions and 61 deletions
|
|
@ -19743,8 +19743,8 @@
|
|||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.TodoBoard.Status",
|
||||
"en": "Kanban / Status",
|
||||
"tr": "Kanban / Durum"
|
||||
"en": "Status",
|
||||
"tr": "Durum"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
|
|
|
|||
|
|
@ -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<string, any>, 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
|
||||
|
|
|
|||
|
|
@ -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<string, Map<string, string>>
|
||||
|
||||
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<DataGridRef<any, any>>()
|
||||
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<CustomStore<any, any>>()
|
||||
const [rows, setRows] = useState<TodoRow[]>([])
|
||||
const [statuses, setStatuses] = useState<string[]>([])
|
||||
|
|
@ -194,6 +235,7 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
|||
const [userOptions, setUserOptions] = useState<UserOption[]>([])
|
||||
const [tenantNames, setTenantNames] = useState<Map<string, string>>(new Map())
|
||||
const [usersLoading, setUsersLoading] = useState(false)
|
||||
const [lookupDisplayValues, setLookupDisplayValues] = useState<LookupDisplayValues>({})
|
||||
const columnConfigRef = useRef<StoredBoardColumns>({ 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<UserOption[]>(() => {
|
||||
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<string, any> = {
|
||||
[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) => {
|
|||
<section className="min-h-[420px] rounded-xl shadow-sm">
|
||||
{renderToolbar(true)}
|
||||
<div className="mb-6 flex justify-end border-b border-gray-200 dark:border-gray-700">
|
||||
{editingOptions?.allowUpdating === true &&
|
||||
!gridDto.gridOptions.updateServiceAddress && (
|
||||
<span className="text-xs text-amber-600">
|
||||
{translate('::ListForms.TodoBoard.EditRequiresUpdateService')}
|
||||
</span>
|
||||
)}
|
||||
{editingOptions?.allowUpdating === true && !gridDto.gridOptions.updateServiceAddress && (
|
||||
<span className="text-xs text-amber-600">
|
||||
{translate('::ListForms.TodoBoard.EditRequiresUpdateService')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="grid gap-5 lg:grid-cols-3">
|
||||
<div className="space-y-5 lg:col-span-2">
|
||||
|
|
@ -941,6 +1025,16 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
|||
/>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<label className={labelClass}>
|
||||
{translate('::App.Listform.ListformField.UserName')}
|
||||
</label>
|
||||
<input
|
||||
readOnly
|
||||
className={`${inputClass} cursor-default`}
|
||||
value={String(draft[userNameField] ?? '')}
|
||||
/>
|
||||
</div>
|
||||
{options.statusExpr && (
|
||||
<div>
|
||||
<label className={labelClass}>{translate('::ListForms.TodoBoard.Status')}</label>
|
||||
|
|
@ -957,7 +1051,7 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
|||
>
|
||||
{statuses.map((status) => (
|
||||
<option key={status} value={status}>
|
||||
{status}
|
||||
{lookupText(options.statusExpr, status)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
|
@ -1176,8 +1270,11 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
|||
</form>
|
||||
) : (
|
||||
<>
|
||||
<h4 className="min-w-0 truncate font-sm" title={status}>
|
||||
{status}
|
||||
<h4
|
||||
className="min-w-0 truncate font-sm"
|
||||
title={lookupText(options.statusExpr, status)}
|
||||
>
|
||||
{lookupText(options.statusExpr, status)}
|
||||
</h4>
|
||||
{canUpdate && (
|
||||
<button
|
||||
|
|
@ -1412,7 +1509,7 @@ const TodoBoard = ({ listFormCode, searchParams, gridDto }: TodoBoardProps) => {
|
|||
className="inline-flex items-center gap-1 rounded-full bg-blue-50 px-2 py-0.5 text-[11px] text-blue-700 dark:bg-blue-950 dark:text-blue-200"
|
||||
>
|
||||
<FaTag />
|
||||
{tag}
|
||||
{lookupText(options.tagExpr, tag)}
|
||||
</span>
|
||||
))}
|
||||
{tags.length > 3 && (
|
||||
|
|
|
|||
Loading…
Reference in a new issue