import { MotionButton } from '@/common/components/molecules/MotionButton/MotionButton'; import { checkIsIndividual } from '@/common/utils/check-is-individual/check-is-individual'; import { ctw } from '@/common/utils/ctw/ctw'; import { useApproveDocumentByIdMutation } from '@/domains/documents/hooks/mutations/useApproveDocumentByIdMutation/useApproveDocumentByIdMutation'; import { useRejectDocumentByIdMutation } from '@/domains/documents/hooks/mutations/useRejectDocumentByIdMutation/useRejectDocumentByIdMutation'; import { useRemoveDocumentDecisionByIdMutation } from '@/domains/documents/hooks/mutations/useRemoveDocumentDecisionByIdMutation/useRemoveDocumentDecisionByIdMutation'; import { useApproveTaskByIdMutation } from '@/domains/entities/hooks/mutations/useApproveTaskByIdMutation/useApproveTaskByIdMutation'; import { useDocumentOcr } from '@/domains/entities/hooks/mutations/useDocumentOcr/useDocumentOcr'; import { useRejectTaskByIdMutation } from '@/domains/entities/hooks/mutations/useRejectTaskByIdMutation/useRejectTaskByIdMutation'; import { useRemoveTaskDecisionByIdMutation } from '@/domains/entities/hooks/mutations/useRemoveTaskDecisionByIdMutation/useRemoveTaskDecisionByIdMutation'; import { TWorkflowById } from '@/domains/workflows/fetchers'; import { createBlocksTyped } from '@/lib/blocks/create-blocks-typed/create-blocks-typed'; import { motionButtonProps } from '@/lib/blocks/hooks/useAssosciatedCompaniesBlock/useAssociatedCompaniesBlock'; import { useCommentInputLogic } from '@/lib/blocks/hooks/useDocumentBlocks/hooks/useCommentInputLogic/useCommentInputLogic'; import { checkCanApprove } from '@/lib/blocks/hooks/useDocumentBlocks/utils/check-can-approve/check-can-approve'; import { checkCanReject } from '@/lib/blocks/hooks/useDocumentBlocks/utils/check-can-reject/check-can-reject'; import { checkCanRevision } from '@/lib/blocks/hooks/useDocumentBlocks/utils/check-can-revision/check-can-revision'; import { motionBadgeProps } from '@/lib/blocks/motion-badge-props'; import { useCaseState } from '@/pages/Entity/components/Case/hooks/useCaseState/useCaseState'; import { composePickableCategoryType, isExistingSchemaForDocument, } from '@/pages/Entity/hooks/useEntityLogic/utils'; import { CommonWorkflowStates, StateTag, TDocument, valueOrNA } from '@ballerine/common'; import { Button, TextArea } from '@ballerine/ui'; import { X } from 'lucide-react'; import * as React from 'react'; import { FunctionComponent, useCallback } from 'react'; import { capitalize, titleCase } from 'string-ts'; import { useDocuments } from './hooks/useDocuments'; import { keyFactory } from '@/common/utils/key-factory/key-factory'; import { ExtractCellProps } from '@ballerine/blocks'; import { systemCreatedIconCell, userCreatedIconCell } from '@/lib/blocks/utils/constants'; import { Separator } from '@/common/components/atoms/Separator/Separator'; export const useDocumentBlocks = ({ workflow, parentMachine, noAction, caseState, withEntityNameInHeader, onReuploadNeeded, isLoadingReuploadNeeded, dialog, actions, }: { workflow: TWorkflowById; parentMachine: TWorkflowById['context']['parentMachine']; noAction: boolean; caseState: ReturnType; withEntityNameInHeader: boolean; onReuploadNeeded: ({ workflowId, documentId, reason, }: { workflowId: string; documentId: string; reason?: string; }) => () => void; isLoadingReuploadNeeded: boolean; dialog: { reupload: { Description: FunctionComponent; }; }; actions?: { reuploadNeeded?: { isDisabled?: boolean; }; }; }) => { const { businessDocuments, directorsDocuments, ubosDocuments, businessDocumentsSchemas, directorsDocumentsSchemas, ubosDocumentsSchemas, isLoading: isLoadingDocuments, } = useDocuments(workflow); const { mutate: mutateApproveTaskById, isLoading: isLoadingApproveTaskById } = useApproveTaskByIdMutation(workflow?.id); const { mutate: mutateApproveDocumentById, isLoading: isLoadingApproveDocumentById } = useApproveDocumentByIdMutation(); const { mutate: mutateOCRDocument, isLoading: isLoadingOCRDocument, data: ocrResult, } = useDocumentOcr({ workflowId: workflow?.id, }); const { isLoading: isLoadingRejectTaskById } = useRejectTaskByIdMutation(workflow?.id); const { isLoading: isLoadingRejectDocumentById } = useRejectDocumentByIdMutation(); const { comment, onClearComment, onCommentChange } = useCommentInputLogic(); const onMutateApproveTaskById = useCallback( ({ taskId, contextUpdateMethod, comment, }: { taskId: string; contextUpdateMethod: 'base' | 'director'; comment?: string; }) => () => { if (!workflow?.workflowDefinition?.config?.isDocumentsV2) { mutateApproveTaskById({ documentId: taskId, contextUpdateMethod, comment }); } if (workflow?.workflowDefinition?.config?.isDocumentsV2) { mutateApproveDocumentById({ documentId: taskId, decisionReason: '', comment }); } onClearComment(); }, [ mutateApproveDocumentById, mutateApproveTaskById, onClearComment, workflow?.workflowDefinition?.config?.isDocumentsV2, ], ); const { mutate: mutateRemoveTaskDecisionById } = useRemoveTaskDecisionByIdMutation(workflow?.id); const { mutate: mutateRemoveDocumentDecisionById } = useRemoveDocumentDecisionByIdMutation(); const onMutateRemoveDecisionById = useCallback( ({ documentId, contextUpdateMethod, }: { documentId: string; contextUpdateMethod: 'base' | 'director'; }) => { if (workflow?.workflowDefinition?.config?.isDocumentsV2) { mutateRemoveDocumentDecisionById({ documentId }); return; } mutateRemoveTaskDecisionById({ documentId, contextUpdateMethod }); }, [ mutateRemoveDocumentDecisionById, mutateRemoveTaskDecisionById, workflow?.workflowDefinition?.config?.isDocumentsV2, ], ); const formatDocument = ({ documents, documentsSchemas, }: { documents: TDocument[]; documentsSchemas: | typeof businessDocumentsSchemas | typeof directorsDocumentsSchemas | typeof ubosDocumentsSchemas; }) => ({ id, type: docType, category, properties, propertiesSchema, decision, details, entityType, entity, }: TDocument) => { const additionalProperties = isExistingSchemaForDocument(documentsSchemas ?? []) ? composePickableCategoryType( category, docType, documentsSchemas ?? [], workflow?.workflowDefinition?.config, ) : {}; const isDoneWithRevision = decision?.status === 'revised' && parentMachine?.status === 'completed'; const isDocumentRevision = decision?.status === CommonWorkflowStates.REVISION && (!isDoneWithRevision || noAction); const isLegacyReject = workflow?.workflowDefinition?.config?.isLegacyReject; const canRevision = checkCanRevision({ caseState, noAction, workflow, decision, isLoadingRevision: isLoadingReuploadNeeded, }); const canReject = checkCanReject({ caseState, noAction, workflow, decision, isLoadingReject: isLoadingRejectTaskById || isLoadingRejectDocumentById, }); const canApprove = checkCanApprove({ caseState, noAction, workflow, decision, isLoadingApprove: isLoadingApproveTaskById || isLoadingApproveDocumentById, }); const getDecisionStatusOrAction = (isDocumentRevision: boolean) => { const badgeClassNames = 'text-sm font-bold'; if (isDocumentRevision && workflow?.tags?.includes(StateTag.REVISION)) { return createBlocksTyped() .addBlock() .addCell({ type: 'badge', value: 'Pending re-upload', props: { ...motionBadgeProps, variant: 'warning', className: badgeClassNames, }, }) .build() .flat(1); } if (isDocumentRevision && !workflow?.tags?.includes(StateTag.REVISION)) { return createBlocksTyped() .addBlock() .addCell({ type: 'badge', value: ( Re-upload needed {!isLegacyReject && ( onMutateRemoveDecisionById({ documentId: id, contextUpdateMethod: 'base', }) } /> )} ), props: { ...motionBadgeProps, variant: 'warning', className: `gap-x-1 text-white bg-warning ${badgeClassNames}`, }, }) .buildFlat(); } if (decision?.status === StateTag.APPROVED) { return createBlocksTyped() .addBlock() .addCell({ type: 'badge', value: 'Approved', props: { ...motionBadgeProps, variant: 'success', className: `${badgeClassNames} bg-success/20`, }, }) .buildFlat(); } if (decision?.status === StateTag.REJECTED) { return createBlocksTyped() .addBlock() .addCell({ type: 'badge', value: 'Rejected', props: { ...motionBadgeProps, variant: 'destructive', className: badgeClassNames, }, }) .buildFlat(); } const revisionReasons = workflow?.workflowDefinition?.contextSchema?.schema?.properties?.documents?.items?.properties?.decision?.properties?.revisionReason?.anyOf?.find( ({ enum: enum_ }) => !!enum_, )?.enum; const rejectionReasons = workflow?.workflowDefinition?.contextSchema?.schema?.properties?.documents?.items?.properties?.decision?.properties?.rejectionReason?.anyOf?.find( ({ enum: enum_ }) => !!enum_, )?.enum; return createBlocksTyped() .addBlock() .addCell({ type: 'callToActionLegacy', // 'Reject' displays the dialog with both "block" and "ask for re-upload" options value: { text: isLegacyReject ? 'Reject' : 'Re-upload needed', props: { revisionReasons, rejectionReasons, id, workflow, disabled: actions?.reuploadNeeded?.isDisabled || (isLegacyReject ? !canReject && !canRevision : !canRevision), onReuploadNeeded, isLoadingReuploadNeeded, decision: 'reject', dialog, }, }, }) .addCell({ type: 'dialog', value: { trigger: ( Approve ), title: `Approval confirmation`, description:

Are you sure you want to approve?

, content: (