sozsoft-platform/ui/src/views/form/shared/FormPageShell.tsx

150 lines
4.2 KiB
TypeScript
Raw Normal View History

/**
* FormView / FormEdit / FormNew ekranlarının ortak sayfa iskeleti.
*
* Üç bileşen de aynı düzeni (Helmet + başlık + FormButtons + FormDevExpress +
* SubForms + NotePanel) satır satır kopyalıyordu. Tek noktadan yönetilir.
*/
import type { ReactNode, RefObject } from 'react'
import { useState } from 'react'
import { Container, Loading } from '@/components/shared'
import { Badge } from '@/components/ui'
import { APP_NAME } from '@/constants/app.constant'
import type { GridDto } from '@/proxy/form/models'
import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { usePermission } from '@/utils/hooks/usePermission'
import type { FormRef } from 'devextreme-react/form'
import type { GroupItem } from 'devextreme/ui/form'
import { Helmet } from 'react-helmet'
import FormDevExpress from '../FormDevExpress'
import SubForms from '../SubForms'
import { NotePanel } from '../notes/NotePanel'
import type { RowMode } from '../types'
type FormPageShellProps = {
mode: RowMode
listFormCode: string
isSubForm?: boolean
level?: number
loading: boolean
gridDto?: GridDto
formData: any
formItems: GroupItem[]
refForm: RefObject<FormRef>
setFormData: (data: any) => void
/** Başlığın sağındaki aksiyon butonları (FormButtons). */
actions?: ReactNode
/** Not paneli kimliği; verilmezse panel gösterilmez. */
noteEntityId?: string
/** Alt form sekmelerini yenileme geri çağrısı. */
refreshData?: () => Promise<void>
/** FormNew'de alt form gösterilmez. */
showSubForms?: boolean
/** Form gövdesinin sarmalayıcı sınıfı (FormView farklı dolgu kullanır). */
contentClassName?: string
}
export const FormPageShell = ({
mode,
listFormCode,
isSubForm,
level,
loading,
gridDto,
formData,
formItems,
refForm,
setFormData,
actions,
noteEntityId,
refreshData,
showSubForms = true,
contentClassName = 'px-2',
}: FormPageShellProps) => {
const { translate } = useLocalization()
const { checkPermission } = usePermission()
const MenuIcon = useCurrentMenuIcon('w-5 h-5')
const [isNotePanelVisible, setIsNotePanelVisible] = useState(false)
const showNotePanel = Boolean(
listFormCode &&
noteEntityId &&
!isSubForm &&
gridDto?.gridOptions?.showNote &&
checkPermission(gridDto?.gridOptions.permissionDto.n),
)
if (!listFormCode) {
return null
}
if (loading) {
return <Loading type="default" loading />
}
if (!formData) {
return <>{translate('::App.NoResults')}</>
}
const title = translate('::' + gridDto?.gridOptions.title)
return (
<>
<Container
className={`${
isNotePanelVisible && !isSubForm ? 'lg:mr-[375px]' : ''
} transition-all duration-300`}
>
{!isSubForm && (
<Helmet titleTemplate={`%s | ${APP_NAME}`} title={title} defaultTitle={APP_NAME} />
)}
<div
className={`flex items-center pb-2 px-2 ${isSubForm ? 'justify-end' : 'justify-between'}`}
>
<div className="flex items-center gap-2">
{MenuIcon}
{!isSubForm && (
<>
<h4 className="text-slate-700 text-sm font-medium leading-none">{title}</h4>
<Badge content={mode} />
</>
)}
</div>
{actions}
</div>
<div className={contentClassName}>
<FormDevExpress
mode={mode}
refForm={refForm}
formData={formData}
formItems={formItems}
setFormData={setFormData}
listFormCode={listFormCode}
/>
</div>
{showSubForms && gridDto && (
<SubForms
gridDto={gridDto}
formData={formData}
level={level ?? 0}
refreshData={refreshData}
/>
)}
</Container>
{/* Not paneli yalnızca ana formda gösterilir. */}
{showNotePanel && (
<NotePanel
entityName={listFormCode}
entityId={noteEntityId!}
isVisible={isNotePanelVisible}
onToggle={() => setIsNotePanelVisible((visible) => !visible)}
/>
)}
</>
)
}