Form içerisindeki Workflow custom butonlar
This commit is contained in:
parent
b53a4243ab
commit
1d05d24e41
5 changed files with 122 additions and 23 deletions
|
|
@ -15,13 +15,14 @@ import {
|
|||
} from 'react-icons/fa'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { GridColumnData } from '../list/GridColumnData'
|
||||
import { useToolbar } from '../list/useToolbar'
|
||||
import { isWorkflowToolbarItemDisabled, useToolbar } from '../list/useToolbar'
|
||||
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'
|
||||
|
||||
const FormButtons = (props: {
|
||||
isSubForm?: boolean
|
||||
|
|
@ -68,7 +69,7 @@ const FormButtons = (props: {
|
|||
const { translate } = useLocalization()
|
||||
|
||||
const layout = layoutTypes.grid
|
||||
const { toolbarData } = useToolbar({
|
||||
const { toolbarData, toolbarModalData, setToolbarModalData } = useToolbar({
|
||||
gridDto,
|
||||
listFormCode,
|
||||
getSelectedRowKeys,
|
||||
|
|
@ -78,6 +79,52 @@ const FormButtons = (props: {
|
|||
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
|
||||
|
||||
|
|
@ -116,22 +163,16 @@ const FormButtons = (props: {
|
|||
}
|
||||
|
||||
const toolbarExcludedNames = ['deleteAllRecords', 'deleteSelectedRecords', 'refreshButton']
|
||||
const hasVisibleToolbarButtons = toolbarData?.some(
|
||||
(item) => item.widget == 'dxButton' && !toolbarExcludedNames.includes(item.name!),
|
||||
)
|
||||
const hasVisibleToolbarButtons = toolbarData?.some(isToolbarButtonVisible)
|
||||
|
||||
const commandExcludedNames = ['edit', 'delete', 'detail']
|
||||
const hasVisibleCommandButtons = commandColumnData?.buttons?.some(
|
||||
(item) => typeof item !== 'string' && !commandExcludedNames.includes(item.name!),
|
||||
)
|
||||
const hasVisibleCommandButtons = commandColumnData?.buttons?.some(isCommandButtonVisible)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row items-center gap-1">
|
||||
{toolbarData
|
||||
?.filter(
|
||||
(item) => item.widget == 'dxButton' && !toolbarExcludedNames.includes(item.name!),
|
||||
)
|
||||
?.filter(isToolbarButtonVisible)
|
||||
.map((item, i) => {
|
||||
const IconComp = navigationIcon[item.options?.icon]
|
||||
const hasValidIcon =
|
||||
|
|
@ -145,7 +186,8 @@ const FormButtons = (props: {
|
|||
variant="default"
|
||||
size="sm"
|
||||
icon={hasValidIcon ? <IconComp className="text-gray-400" /> : null}
|
||||
onClick={item.options?.onClick}
|
||||
disabled={isToolbarButtonDisabled(item)}
|
||||
onClick={(e) => item.options?.onClick?.({ ...e, ...rowEvent })}
|
||||
>
|
||||
{item.options?.text}
|
||||
</Button>
|
||||
|
|
@ -155,7 +197,7 @@ const FormButtons = (props: {
|
|||
{hasVisibleToolbarButtons && <Badge innerClass="bg-blue-500" />}
|
||||
|
||||
{commandColumnData?.buttons
|
||||
?.filter((item) => typeof item !== 'string' && !commandExcludedNames.includes(item.name!))
|
||||
?.filter(isCommandButtonVisible)
|
||||
.map((item: any, i) => {
|
||||
return (
|
||||
<Button
|
||||
|
|
@ -165,9 +207,7 @@ const FormButtons = (props: {
|
|||
title={item.hint}
|
||||
onClick={(e: any) => {
|
||||
if (item.onClick) {
|
||||
const [rowData] = getSelectedRowsData()
|
||||
e.row = { data: rowData }
|
||||
item.onClick(e)
|
||||
item.onClick({ ...e, ...rowEvent })
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
|
@ -308,6 +348,13 @@ const FormButtons = (props: {
|
|||
></Button>
|
||||
)}
|
||||
</div>
|
||||
<Dialog
|
||||
isOpen={toolbarModalData?.open || false}
|
||||
onClose={() => setToolbarModalData(undefined)}
|
||||
onRequestClose={() => setToolbarModalData(undefined)}
|
||||
>
|
||||
{toolbarModalData?.content}
|
||||
</Dialog>
|
||||
<Dialog
|
||||
id="confirmDelete"
|
||||
isOpen={!!deleteRowId}
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ const FormView = (
|
|||
dataSource={dataSource!}
|
||||
permissions={permissionResults}
|
||||
handleSubmit={() => {}}
|
||||
refreshData={() => {}}
|
||||
refreshData={fetchData}
|
||||
getSelectedRowKeys={() => [id]}
|
||||
getSelectedRowsData={() => [formData]}
|
||||
getFilter={() => filter}
|
||||
|
|
|
|||
|
|
@ -655,6 +655,53 @@ function getCurrentUserWorkflowIdentities(currentUser?: {
|
|||
return [currentUser?.email, currentUser?.userName, currentUser?.name].filter(Boolean) as string[]
|
||||
}
|
||||
|
||||
export function isWorkflowToolbarItemDisabled(
|
||||
itemName: string | undefined,
|
||||
workflowOptions: WorkflowDto | undefined,
|
||||
selectedRowsData: Record<string, unknown>[] = [],
|
||||
currentUser?: {
|
||||
userName?: string
|
||||
email?: string
|
||||
name?: string
|
||||
},
|
||||
) {
|
||||
if (!itemName || !workflowOptions?.approvalStatusFieldName) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (itemName === 'workflowStart') {
|
||||
return (
|
||||
selectedRowsData.length === 0 ||
|
||||
!selectedRowsData.every((row) => isWorkflowNotStarted(row, workflowOptions))
|
||||
)
|
||||
}
|
||||
|
||||
if (!itemName.startsWith('workflowApproval_')) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const criteriaId = itemName.replace('workflowApproval_', '')
|
||||
const criteria = workflowOptions.criteria?.find(
|
||||
(item) => item.kind === 'Approval' && item.id === criteriaId,
|
||||
)
|
||||
if (!criteria) {
|
||||
return true
|
||||
}
|
||||
|
||||
const currentUserIdentities = getCurrentUserWorkflowIdentities(currentUser)
|
||||
return (
|
||||
selectedRowsData.length === 0 ||
|
||||
!selectedRowsData.every((row) =>
|
||||
isWorkflowApprovalCriteriaActive(
|
||||
row,
|
||||
workflowOptions,
|
||||
criteria.title,
|
||||
currentUserIdentities,
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export function updateWorkflowApprovalToolbarItems(
|
||||
component: any,
|
||||
workflowOptions: WorkflowDto | undefined,
|
||||
|
|
|
|||
|
|
@ -108,11 +108,15 @@ export const MenuManager = () => {
|
|||
Design Mode
|
||||
</span>
|
||||
<Button
|
||||
type="button"
|
||||
variant="plain"
|
||||
shape="none"
|
||||
role="switch"
|
||||
aria-checked={isDesignMode}
|
||||
aria-label="Toggle design mode"
|
||||
className={`
|
||||
relative !inline-flex !h-6 !w-11 !min-w-11 !justify-start !rounded-full !border-0 !px-0 !items-center transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
|
||||
${isDesignMode ? 'bg-blue-600 hover:!bg-blue-600 dark:bg-blue-700 dark:hover:!bg-blue-700' : 'bg-gray-200 hover:!bg-gray-200 dark:bg-gray-700 dark:hover:!bg-gray-700'}
|
||||
relative !inline-flex !h-6 !w-11 !min-w-11 !items-center !justify-start !rounded-full !border-0 !px-0 shadow-inner transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
|
||||
${isDesignMode ? '!bg-blue-600 hover:!bg-blue-600 dark:!bg-blue-700 dark:hover:!bg-blue-700' : '!bg-gray-300 hover:!bg-gray-300 dark:!bg-gray-700 dark:hover:!bg-gray-700'}
|
||||
`}
|
||||
onClick={handleToggleDesignMode}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@ export default defineConfig(async ({ mode }) => {
|
|||
mode === 'production'
|
||||
? {
|
||||
globDirectory: 'dist',
|
||||
// Precache only the application shell. Feature chunks and the
|
||||
// many optional DevExtreme themes are cached on first use.
|
||||
globPatterns: ['index.html', 'assets/css/index-*.css', 'assets/js/index-*.js'],
|
||||
// Eski bir sekme yeni deploy sırasında henüz açmadığı lazy
|
||||
// chunk'ları da çalıştırabilsin; tüm uygulama dosyalarını sakla.
|
||||
globPatterns: ['**/*.{js,css,html,wasm}'],
|
||||
|
||||
// Büyük asset'leri de cache'leyebil
|
||||
maximumFileSizeToCacheInBytes: 15 * 1024 * 1024,
|
||||
|
|
@ -52,9 +52,10 @@ export default defineConfig(async ({ mode }) => {
|
|||
runtimeCaching: [
|
||||
{
|
||||
urlPattern: /\.(?:js|css|wasm)$/,
|
||||
handler: 'CacheFirst',
|
||||
handler: 'NetworkFirst',
|
||||
options: {
|
||||
cacheName: 'static-resources-v2',
|
||||
networkTimeoutSeconds: 5,
|
||||
expiration: {
|
||||
maxEntries: 200,
|
||||
maxAgeSeconds: 30 * 24 * 60 * 60,
|
||||
|
|
|
|||
Loading…
Reference in a new issue