sozsoft-platform/ui/src/views/form/FormButtons.tsx

389 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Button, Dialog, Notification, toast } from '@/components/ui'
import { useLocalization } from '@/utils/hooks/useLocalization'
import CustomStore from 'devextreme/data/custom_store'
import Toolbar from 'devextreme-react/toolbar'
import type { dxToolbarItem as DxToolbarItem } from 'devextreme/ui/toolbar'
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { GridColumnData } from '../list/GridColumnData'
import { isWorkflowToolbarItemDisabled, useToolbar } from '../list/useToolbar'
import { orderListToolbarItems } from '../list/toolbarOrder'
import { PermissionResults, RowMode } from './types'
import { GridDto } from '@/proxy/form/models'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { usePermission } from '@/utils/hooks/usePermission'
import { usePWA } from '@/utils/hooks/usePWA'
import { layoutTypes } from '../admin/listForm/edit/types'
import { useStoreState } from '@/store'
import FormFieldOrderPanel from './FormFieldOrderPanel'
import type { FormFieldOrder } from './shared/formItems'
/** Form sayfasının kullanıcı bazlı alan sırası (`useGridData`). */
type FormFieldOrderControl = {
value?: FormFieldOrder
preview: (order: FormFieldOrder | undefined) => void
save: (order: FormFieldOrder | undefined) => Promise<void>
}
const FormButtons = (props: {
isSubForm?: boolean
mode: RowMode
listFormCode: string
id?: string
gridDto: GridDto
commandColumnData: GridColumnData
dataSource: CustomStore<any, any>
permissions: PermissionResults
handleSubmit: (e: any) => void
refreshData: () => void
getSelectedRowKeys: () => any[] | Promise<any[]>
getSelectedRowsData: () => any[]
getFilter: () => void
onActionEdit?: () => void
onActionNew?: () => void
onActionView?: () => void
/** Verilirse araç çubuğunda alan sırası paneli açılır (ekranda state saklama açıksa). */
fieldOrder?: FormFieldOrderControl
}) => {
const {
isSubForm,
mode,
listFormCode,
id,
gridDto,
commandColumnData,
dataSource,
permissions,
handleSubmit,
refreshData,
getSelectedRowKeys,
getSelectedRowsData,
getFilter,
onActionEdit,
onActionNew,
onActionView,
fieldOrder,
} = props
const [deleteRowId, setDeleteRowId] = useState<string>()
const [fieldOrderOpen, setFieldOrderOpen] = useState(false)
const { checkPermission } = usePermission()
const isPwaMode = usePWA()
const navigate = useNavigate()
const { translate } = useLocalization()
const layout = layoutTypes.grid
const { toolbarData, toolbarModalData, setToolbarModalData } = useToolbar({
gridDto,
listFormCode,
getSelectedRowKeys,
getSelectedRowsData,
refreshData,
getFilter,
layout,
})
const currentUser = useStoreState((state) => state.auth.user)
const selectedRowData = getSelectedRowsData()?.[0]
const rowEvent = {
row: {
data: selectedRowData,
key: id,
rowType: 'data',
isEditing: mode === 'edit',
},
}
const resolveVisible = (visible: unknown) => {
if (typeof visible !== 'function') {
return visible !== false
}
try {
return Boolean(visible(rowEvent))
} catch (error) {
console.error('Button visibility evaluation error:', error)
return false
}
}
const isToolbarButtonVisible = (item: any) =>
item.widget === 'dxButton' &&
!toolbarExcludedNames.includes(item.name) &&
resolveVisible(item.visible) &&
resolveVisible(item.options?.visible)
const isCommandButtonVisible = (item: any) =>
typeof item !== 'string' &&
!commandExcludedNames.includes(item.name) &&
resolveVisible(item.visible)
const isToolbarButtonDisabled = (item: any) => {
const workflowDisabled = isWorkflowToolbarItemDisabled(
item.name,
gridDto.gridOptions.workflowDto,
selectedRowData ? [selectedRowData] : [],
currentUser,
)
return workflowDisabled ?? Boolean(item.options?.disabled)
}
const openDynamicGridReport = () => {
if (!id) return
const reportUrl = `/admin/reports/DynamicGrid/view/${encodeURIComponent(id)}/${encodeURIComponent(listFormCode)}`
window.open(reportUrl, isPwaMode ? '_self' : '_blank')
}
const handleDelete = async (e: any) => {
//auth check
e.preventDefault()
if (!dataSource || !id) {
return
}
try {
await dataSource.remove(id)
setDeleteRowId(undefined)
if (props.onActionNew) {
props.onActionNew()
} else {
navigate(-1)
}
toast.push(
<Notification type="success" duration={2000}>
{translate('::App.Platform.RecordDeleted')}
</Notification>,
{
placement: 'bottom-end',
},
)
} catch (error: any) {
toast.push(<Notification title={error.message} type="danger" />, {
placement: 'bottom-end',
})
}
}
const toolbarExcludedNames = ['deleteAllRecords', 'deleteSelectedRecords', 'refreshButton']
const commandExcludedNames = ['edit', 'delete', 'detail']
/** Grid toolbar sozlesmesiyle ayni bicimde bir dxButton ogesi uretir. */
const buildButtonItem = (
icon: string,
text: string,
onClick: (e: any) => void,
disabled = false,
): DxToolbarItem => ({
location: 'after',
widget: 'dxButton',
locateInMenu: 'auto',
showText: 'always',
options: { icon, text, hint: text, disabled, onClick },
})
const toolbarItems: DxToolbarItem[] = [
// Liste toolbar'indan gelen ozel butonlar; grid ile ayni sirayi korur.
...orderListToolbarItems(toolbarData?.filter(isToolbarButtonVisible) ?? []).map(
(item): DxToolbarItem => ({
location: 'after',
widget: 'dxButton',
locateInMenu: 'auto',
showText: 'always',
options: {
...item.options,
// Gorunurluk zaten filtrelendi; DX'e fonksiyon gecilmemeli.
visible: true,
disabled: isToolbarButtonDisabled(item),
onClick: (e: any) => item.options?.onClick?.({ ...e, ...rowEvent }),
},
}),
),
// Komut kolonundan toolbar'a tasinan butonlar.
...(commandColumnData?.buttons?.filter(isCommandButtonVisible) ?? []).map(
(item: any): DxToolbarItem => ({
location: 'after',
widget: 'dxButton',
locateInMenu: 'auto',
showText: 'always',
options: {
icon: item.icon,
text: item.text,
hint: item.hint ?? item.text,
onClick: (e: any) => item.onClick?.({ ...e, ...rowEvent }),
},
}),
),
]
if (!isSubForm) {
toolbarItems.push(
buildButtonItem('back', translate('::App.Platform.Cancel'), () => {
if (onActionView && id) {
onActionView()
} else {
navigate(-1)
}
}),
)
}
if (mode !== 'new') {
toolbarItems.push(
buildButtonItem(
'trash',
translate('::App.Platform.Delete'),
() => setDeleteRowId(id),
!permissions.d,
),
buildButtonItem(
'add',
translate('::App.Platform.AddNew'),
() => {
if (onActionNew) {
onActionNew()
} else {
navigate(
ROUTES_ENUM.protected.admin.formNew.replace(
':listFormCode',
encodeURIComponent(listFormCode),
),
)
}
},
!permissions.c,
),
)
}
if (mode === 'view') {
toolbarItems.push(
buildButtonItem(
'edit',
translate('::App.Platform.Edit'),
() => {
if (onActionEdit) {
onActionEdit()
} else {
navigate(
ROUTES_ENUM.protected.admin.formEdit
.replace(':listFormCode', encodeURIComponent(listFormCode))
.replace(':id', encodeURIComponent(id!)),
)
}
},
!permissions.u,
),
)
}
if (mode === 'edit' || (onActionView && mode === 'new')) {
toolbarItems.push(
buildButtonItem(
'eyeopen',
translate('::App.Platform.Detail'),
() => {
if (onActionView) {
onActionView()
} else {
navigate(
ROUTES_ENUM.protected.admin.formView
.replace(':listFormCode', encodeURIComponent(listFormCode))
.replace(':id', encodeURIComponent(id!)),
)
}
},
!permissions.r,
),
)
}
if (mode === 'edit' || mode === 'new') {
toolbarItems.push(
buildButtonItem(
'save',
translate('::App.Platform.Save'),
handleSubmit,
!(permissions.c || permissions.u),
),
)
}
if (id && !isSubForm) {
toolbarItems.push(
buildButtonItem('doc', translate('::App.Reports.Form'), openDynamicGridReport),
)
}
if (fieldOrder) {
toolbarItems.push(
buildButtonItem('sorted', translate('::App.ListForm.FormFieldOrder'), () =>
setFieldOrderOpen(true),
),
)
}
if (checkPermission('App.Listforms.Listform.Update') && !isSubForm) {
toolbarItems.push(
buildButtonItem('preferences', translate('::App.Platform.Manage'), () => {
window.open(
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(':listFormCode', listFormCode),
isPwaMode ? '_self' : '_blank',
)
}),
)
}
return (
<>
<Toolbar className="list-view-toolbar-host flex-1" items={toolbarItems} />
{fieldOrder && (
<FormFieldOrderPanel
isOpen={fieldOrderOpen}
gridDto={gridDto}
fieldOrder={fieldOrder.value}
onClose={() => setFieldOrderOpen(false)}
onPreview={fieldOrder.preview}
onSave={fieldOrder.save}
/>
)}
<Dialog
isOpen={toolbarModalData?.open || false}
onClose={() => setToolbarModalData(undefined)}
onRequestClose={() => setToolbarModalData(undefined)}
>
{toolbarModalData?.content}
</Dialog>
<Dialog
id="confirmDelete"
isOpen={!!deleteRowId}
onClose={() => setDeleteRowId(undefined)}
onRequestClose={() => setDeleteRowId(undefined)}
>
<h5 className="mb-4">{translate('::App.Platform.Delete')}</h5>
<p>{translate('::App.Platform.DeleteConfirmation')}</p>
<div className="text-right mt-6">
<Button
className="ltr:mr-2 rtl:ml-2"
variant="plain"
onClick={() => {
setDeleteRowId(undefined)
}}
>
{translate('::App.Platform.Cancel')}
</Button>
<Button variant="solid" onClick={handleDelete}>
{translate('::App.Platform.Delete')}
</Button>
</div>
</Dialog>
</>
)
}
export default FormButtons