456 lines
15 KiB
TypeScript
456 lines
15 KiB
TypeScript
import { Notification, toast } from '@/components/ui'
|
||
import { getList } from '@/services/form.service'
|
||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||
import { usePermission } from '@/utils/hooks/usePermission'
|
||
import { FormRef } from 'devextreme-react/form'
|
||
import { captionize } from 'devextreme/core/utils/inflector'
|
||
import CustomStore from 'devextreme/data/custom_store'
|
||
import { GroupItem } from 'devextreme/ui/form'
|
||
import { useEffect, useRef, useState } from 'react'
|
||
import { useNavigate, useSearchParams } from 'react-router-dom'
|
||
import { GridColumnData } from '../list/GridColumnData'
|
||
import { addCss, addJs } from '../list/Utils'
|
||
import { PermissionResults, RowMode, SimpleItemWithColData } from './types'
|
||
import { EditingFormItemDto, GridDto, PlatformEditorTypes } from '@/proxy/form/models'
|
||
import { getAccessDeniedPath } from '@/utils/routing'
|
||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||
import { useLookupDataSource } from './useLookupDataSource'
|
||
import { layoutTypes } from '../admin/listForm/edit/types'
|
||
import { useListFormCustomDataSource } from '../list/useListFormCustomDataSource'
|
||
import { useListFormColumns } from '../list/useListFormColumns'
|
||
|
||
const flattenFormItems = (items: any[] = []): SimpleItemWithColData[] =>
|
||
items.flatMap((item) => [
|
||
...(item?.dataField ? [item] : []),
|
||
...flattenFormItems(item?.items || []),
|
||
...(item?.tabs || []).flatMap((tab: any) => flattenFormItems(tab?.items || [])),
|
||
])
|
||
|
||
const useGridData = (props: {
|
||
mode: RowMode
|
||
listFormCode: string
|
||
id?: string
|
||
level?: number
|
||
isSubForm?: boolean
|
||
onSubmitAction?: () => void
|
||
}) => {
|
||
const { mode, listFormCode, id, isSubForm } = props
|
||
|
||
const [gridReady, setGridReady] = useState(false)
|
||
const [loading, setLoading] = useState(false)
|
||
const [filter, setFilter] = useState<any[]>([])
|
||
const [gridDto, setGridDto] = useState<GridDto>()
|
||
const [dataSource, setDataSource] = useState<CustomStore<any, any>>()
|
||
const [commandColumnData, setCommandColumnData] = useState<GridColumnData>()
|
||
const [formDataOld, setFormDataOld] = useState<any>()
|
||
const [formData, setFormData] = useState<any>()
|
||
const [formItems, setFormItems] = useState<GroupItem[]>([])
|
||
const [permissionResults, setPermissionResults] = useState<PermissionResults>()
|
||
|
||
const refForm = useRef<FormRef>(null)
|
||
const previousFormDataRef = useRef<any>()
|
||
const [searchParams] = useSearchParams()
|
||
const navigate = useNavigate()
|
||
const { translate } = useLocalization()
|
||
const { checkPermission } = usePermission()
|
||
const { getBandedColumns } = useListFormColumns({
|
||
gridDto,
|
||
listFormCode,
|
||
isSubForm,
|
||
gridRef: undefined,
|
||
})
|
||
const { createSelectDataSource } = useListFormCustomDataSource({} as any)
|
||
const { getLookupDataSource } = useLookupDataSource({ listFormCode, isSubForm })
|
||
|
||
const fetchData = async () => {
|
||
setLoading(true)
|
||
try {
|
||
const response: any = await dataSource?.load({
|
||
filter,
|
||
skip: 0,
|
||
take: 1,
|
||
})
|
||
if (response?.data?.length) {
|
||
setFormData(response.data[0])
|
||
setFormDataOld({ ...response.data[0] })
|
||
} else {
|
||
setFormData(undefined)
|
||
setFormDataOld(undefined)
|
||
}
|
||
} catch (error: any) {
|
||
toast.push(<Notification title={error.message} type="danger" />, {
|
||
placement: 'top-end',
|
||
})
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
const handleSubmit = async (e: any) => {
|
||
e.preventDefault()
|
||
if (!dataSource) {
|
||
return
|
||
}
|
||
|
||
const validationResult = refForm.current?.instance().validate()
|
||
if (!validationResult?.isValid) {
|
||
return
|
||
}
|
||
|
||
setLoading(true)
|
||
try {
|
||
const formValues = { ...formData }
|
||
if (mode === 'new') {
|
||
const result = await dataSource.insert(formValues)
|
||
if (result.data) {
|
||
if (!isSubForm) {
|
||
navigate(result.data)
|
||
} else if (props.onSubmitAction) {
|
||
props.onSubmitAction()
|
||
}
|
||
toast.push(
|
||
<Notification type="success" duration={2000}>
|
||
{translate('::ListForms.FormBilgileriKaydedildi')}
|
||
</Notification>,
|
||
{
|
||
placement: 'top-end',
|
||
},
|
||
)
|
||
} else {
|
||
throw new Error(translate('::ListForms.FormBilgileriKaydedilemedi'))
|
||
}
|
||
} else if (mode === 'edit') {
|
||
let data: any = {}
|
||
if (gridDto?.gridOptions.editingOptionDto?.sendOnlyChangedFormValuesUpdate) {
|
||
Object.keys(formValues).forEach((key) => {
|
||
if (formValues[key] !== formDataOld[key]) {
|
||
data[key] = formValues[key]
|
||
}
|
||
})
|
||
} else {
|
||
data = { ...formValues }
|
||
}
|
||
|
||
if (gridDto?.gridOptions.keyFieldName) {
|
||
delete data[gridDto?.gridOptions.keyFieldName]
|
||
}
|
||
|
||
var result = await dataSource.update(id, data)
|
||
if (result.data > 0) {
|
||
if (!isSubForm) {
|
||
navigate(
|
||
ROUTES_ENUM.protected.admin.formView
|
||
.replace(':listFormCode', listFormCode)
|
||
.replace(':id', id!),
|
||
)
|
||
} else if (props.onSubmitAction) {
|
||
props.onSubmitAction()
|
||
}
|
||
toast.push(
|
||
<Notification type="success" duration={2000}>
|
||
{translate('::ListForms.FormBilgileriKaydedildi')}
|
||
</Notification>,
|
||
{
|
||
placement: 'top-end',
|
||
},
|
||
)
|
||
} else {
|
||
throw new Error(translate('::ListForms.FormBilgileriKaydedilemedi'))
|
||
}
|
||
}
|
||
} catch (error: any) {
|
||
toast.push(<Notification title={error.message} type="danger" />, {
|
||
placement: 'top-end',
|
||
})
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
useEffect(() => {
|
||
setGridReady(false)
|
||
const initializeGrid = async () => {
|
||
const response = await getList({ listFormCode })
|
||
setGridDto(response.data)
|
||
}
|
||
|
||
initializeGrid()
|
||
}, [listFormCode])
|
||
|
||
useEffect(() => {
|
||
setGridReady(false)
|
||
if (!gridDto) {
|
||
return
|
||
}
|
||
|
||
setPermissionResults({
|
||
c:
|
||
gridDto?.gridOptions.editingOptionDto.allowAdding === true &&
|
||
checkPermission(gridDto?.gridOptions.permissionDto.c),
|
||
r: checkPermission(gridDto?.gridOptions.permissionDto.r),
|
||
u:
|
||
gridDto?.gridOptions.editingOptionDto.allowUpdating === true &&
|
||
checkPermission(gridDto?.gridOptions.permissionDto.u),
|
||
d:
|
||
gridDto?.gridOptions.editingOptionDto.allowDeleting === true &&
|
||
checkPermission(gridDto?.gridOptions.permissionDto.d),
|
||
e: checkPermission(gridDto?.gridOptions.permissionDto.e),
|
||
i: checkPermission(gridDto?.gridOptions.permissionDto.i),
|
||
})
|
||
|
||
// Set js and css
|
||
const grdOpt = gridDto.gridOptions
|
||
grdOpt.customJsSources.forEach(addJs)
|
||
grdOpt.customStyleSources.forEach(addCss)
|
||
|
||
// Set columns
|
||
const cols = getBandedColumns()
|
||
setCommandColumnData(cols?.find((a) => a.type == 'buttons'))
|
||
|
||
// Set data source
|
||
const dataSource: CustomStore<any, any> = createSelectDataSource(
|
||
gridDto.gridOptions,
|
||
listFormCode,
|
||
searchParams,
|
||
layoutTypes.grid,
|
||
cols,
|
||
)
|
||
setDataSource(dataSource)
|
||
|
||
const items = gridDto?.gridOptions.editingFormDto
|
||
?.sort((a: any, b: any) => {
|
||
return a.order >= b.order ? 1 : -1
|
||
})
|
||
.map((e: any) => {
|
||
return {
|
||
itemType: e.itemType,
|
||
colCount: e.colCount,
|
||
colSpan: e.colSpan,
|
||
caption: e.caption,
|
||
items: e.items
|
||
?.sort((a: any, b: any) => {
|
||
return a.order >= b.order ? 1 : -1
|
||
})
|
||
.map((i: EditingFormItemDto) => {
|
||
let editorOptions: Record<string, any> = {}
|
||
let parsedEditorOptions: Record<string, any> = {}
|
||
const colData = gridDto.columnFormats.find((x) => x.fieldName === i.dataField)
|
||
|
||
try {
|
||
parsedEditorOptions = i.editorOptions ? JSON.parse(i.editorOptions) : {}
|
||
} catch {}
|
||
const lookupEditorOptions = colData?.lookupDto?.dataSourceType
|
||
? {
|
||
dataSource: getLookupDataSource(colData?.editorOptions, colData, formData),
|
||
valueExpr: colData?.lookupDto?.valueExpr?.toLowerCase(),
|
||
displayExpr: colData?.lookupDto?.displayExpr?.toLowerCase(),
|
||
}
|
||
: {}
|
||
editorOptions = {
|
||
...lookupEditorOptions,
|
||
...parsedEditorOptions,
|
||
}
|
||
const item: SimpleItemWithColData = {
|
||
canRead:
|
||
gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canRead ??
|
||
false,
|
||
canUpdate:
|
||
gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canUpdate ??
|
||
false,
|
||
canCreate:
|
||
gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canCreate ??
|
||
false,
|
||
canExport:
|
||
gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canExport ??
|
||
false,
|
||
allowEditing:
|
||
gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)
|
||
?.allowEditing ?? true,
|
||
allowAdding:
|
||
gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)
|
||
?.allowAdding ?? true,
|
||
dataField: i.dataField,
|
||
name: i.dataField,
|
||
editorType2: i.editorType2,
|
||
editorType:
|
||
i.editorType2 == PlatformEditorTypes.dxGridBox
|
||
? 'dxDropDownBox'
|
||
: i.editorType2 == PlatformEditorTypes.dxImageUpload
|
||
? undefined
|
||
: i.editorType2,
|
||
colSpan: i.colSpan,
|
||
isRequired: i.isRequired,
|
||
editorOptions,
|
||
colData,
|
||
tagBoxOptions: i.tagBoxOptions,
|
||
gridBoxOptions: i.gridBoxOptions,
|
||
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
|
||
})
|
||
.filter((a: any) => {
|
||
if (mode === 'view') {
|
||
return a.canRead
|
||
} else if (mode === 'new') {
|
||
return a.canCreate && a.allowAdding
|
||
} else if (mode === 'edit') {
|
||
return a.canUpdate && a.allowEditing
|
||
} else {
|
||
return false
|
||
}
|
||
}),
|
||
} as GroupItem
|
||
})
|
||
setFormItems(items)
|
||
setGridReady(true)
|
||
}, [gridDto])
|
||
|
||
// formData değiştiğinde sadece etkilenen cascading lookup datasource'ları güncelle
|
||
useEffect(() => {
|
||
if (!gridDto || !formItems.length) {
|
||
previousFormDataRef.current = formData
|
||
return
|
||
}
|
||
|
||
const previousFormData = previousFormDataRef.current
|
||
const changedFields = previousFormData
|
||
? Object.keys({ ...(previousFormData || {}), ...(formData || {}) }).filter(
|
||
(field) => !Object.is(previousFormData?.[field], formData?.[field]),
|
||
)
|
||
: []
|
||
|
||
const shouldRefreshLookup = (item: SimpleItemWithColData) => {
|
||
const cascadeParentFields = item.colData?.lookupDto?.cascadeParentFields
|
||
?.split(',')
|
||
.map((field: string) => field.trim())
|
||
.filter(Boolean)
|
||
|
||
return (
|
||
!previousFormData ||
|
||
cascadeParentFields?.some((field: string) => changedFields.includes(field))
|
||
)
|
||
}
|
||
|
||
const updateItems = (items: any[] = []) =>
|
||
items.map((item) => {
|
||
const colData = gridDto.columnFormats.find((x) => x.fieldName === item.dataField)
|
||
if (colData?.lookupDto?.dataSourceType && shouldRefreshLookup(item)) {
|
||
const currentDataSource = item.editorOptions?.dataSource
|
||
const keepCustomDataSource =
|
||
currentDataSource !== undefined && typeof currentDataSource?.load !== 'function'
|
||
|
||
return {
|
||
...item,
|
||
editorOptions: {
|
||
...item.editorOptions,
|
||
// formData null bile olsa getLookupDataSource çağrılmalı (null parametrelerle API çağrısı yapılacak)
|
||
dataSource: keepCustomDataSource
|
||
? currentDataSource
|
||
: getLookupDataSource(colData?.editorOptions, colData, formData || null),
|
||
valueExpr:
|
||
item.editorOptions?.valueExpr ?? colData?.lookupDto?.valueExpr?.toLowerCase(),
|
||
displayExpr:
|
||
item.editorOptions?.displayExpr ?? colData?.lookupDto?.displayExpr?.toLowerCase(),
|
||
},
|
||
}
|
||
}
|
||
|
||
if (item?.items?.length) {
|
||
return {
|
||
...item,
|
||
items: updateItems(item.items),
|
||
}
|
||
}
|
||
|
||
if (item?.tabs?.length) {
|
||
return {
|
||
...item,
|
||
tabs: item.tabs.map((tab: any) => ({
|
||
...tab,
|
||
items: updateItems(tab.items),
|
||
})),
|
||
}
|
||
}
|
||
|
||
return item
|
||
})
|
||
|
||
const hasAffectedLookup =
|
||
!previousFormData ||
|
||
formItems
|
||
.flatMap((group) => flattenFormItems([group]))
|
||
.some((item) => item.colData?.lookupDto?.dataSourceType && shouldRefreshLookup(item))
|
||
|
||
if (!hasAffectedLookup) {
|
||
previousFormDataRef.current = formData
|
||
return
|
||
}
|
||
|
||
const updatedItems = formItems.map((groupItem) => ({
|
||
...groupItem,
|
||
items: updateItems(groupItem.items as any[]),
|
||
tabs: (groupItem as any).tabs?.map((tab: any) => ({
|
||
...tab,
|
||
items: updateItems(tab.items),
|
||
})),
|
||
}))
|
||
|
||
previousFormDataRef.current = formData
|
||
setFormItems(updatedItems)
|
||
}, [formData, gridDto])
|
||
|
||
useEffect(() => {
|
||
if (!gridReady) {
|
||
return
|
||
}
|
||
|
||
if (mode !== 'new') {
|
||
setFilter([gridDto?.gridOptions.keyFieldName ?? 'Id', '=', id])
|
||
}
|
||
}, [id, gridReady])
|
||
|
||
useEffect(() => {
|
||
if (filter?.length) {
|
||
fetchData()
|
||
}
|
||
}, [filter])
|
||
|
||
// Auth check
|
||
useEffect(() => {
|
||
if (!permissionResults) return
|
||
|
||
const noCreate = mode === 'new' && !permissionResults.c
|
||
const noUpdate = mode === 'edit' && !permissionResults.u
|
||
const noRead = mode === 'view' && !permissionResults.r
|
||
|
||
if (noCreate || noUpdate || noRead) {
|
||
navigate(getAccessDeniedPath(location.pathname), { replace: true, state: { from: location } })
|
||
}
|
||
}, [permissionResults])
|
||
|
||
return {
|
||
loading,
|
||
gridDto,
|
||
dataSource,
|
||
commandColumnData,
|
||
filter,
|
||
formItems,
|
||
formData,
|
||
refForm,
|
||
permissionResults,
|
||
fetchData,
|
||
setFormData,
|
||
handleSubmit,
|
||
}
|
||
}
|
||
|
||
export { useGridData }
|