@@ -4,17 +4,22 @@ import { DataGrid } from "@mui/x-data-grid";
44import Paper from "@mui/material/Paper" ;
55import { useTranslation } from "react-i18next" ;
66import { push } from "connected-react-router" ;
7- import { setBPMFormLimit , setBPMFormListPage , setBpmFormSort } from "../../../actions/formActions" ;
8- import { resetFormProcessData } from "../../../apiManager/services/processServices" ;
7+ import { setBPMFormLimit , setBPMFormListPage , setBpmFormSort , setFormDeleteStatus } from "../../../actions/formActions" ;
8+ import { resetFormProcessData , unPublishForm , getApplicationCount , getProcessDetails } from "../../../apiManager/services/processServices" ;
99import { fetchBPMFormList } from "../../../apiManager/services/bpmFormServices" ;
1010import { setFormSearchLoading } from "../../../actions/checkListActions" ;
1111import userRoles from "../../../constants/permissions" ;
12- import { HelperServices , StyleServices } from "@formsflow/service" ;
12+ import { HelperServices , StyleServices } from "@formsflow/service" ;
1313import { MULTITENANCY_ENABLED } from "../../../constants/constants" ;
14- import { V8CustomButton , RefreshIcon , NewSortDownIcon , V8CustomDropdownButton } from "@formsflow/components" ;
14+ import { V8CustomButton , RefreshIcon , NewSortDownIcon , V8CustomDropdownButton , PromptModal } from "@formsflow/components" ;
15+ import { deleteForm } from "@aot-technologies/formio-react" ;
16+ import { fetchFormById } from "../../../apiManager/services/bpmFormServices" ;
17+ import { formCreate } from "../../../apiManager/services/FormServices" ;
18+ import { manipulatingFormData } from "../../../apiManager/services/formFormatterService" ;
19+ import _cloneDeep from "lodash/cloneDeep" ;
20+ import { toast } from "react-toastify" ;
1521
16-
17- function FormTable ( ) {
22+ function FormTable ( { isDuplicating, setIsDuplicating, setDuplicateProgress } ) {
1823 const dispatch = useDispatch ( ) ;
1924 const tenantKey = useSelector ( state => state . tenants ?. tenantId ) ;
2025 const bpmForms = useSelector ( state => state . bpmForms ) ;
@@ -26,10 +31,22 @@ function FormTable() {
2631 const searchFormLoading = useSelector ( state => state . formCheckList . searchFormLoading ) ;
2732 const isApplicationCountLoading = useSelector ( state => state . process . isApplicationCountLoading ) ;
2833 const searchText = useSelector ( state => state . bpmForms . searchText ) ;
34+ const applicationCount = useSelector ( ( state ) => state . process ?. applicationCount ) ;
35+
36+ // Get form access data from Redux store
37+ const formAccess = useSelector ( ( state ) => state . user ?. formAccess || [ ] ) ;
38+ const submissionAccess = useSelector ( ( state ) => state . user ?. submissionAccess || [ ] ) ;
39+
2940 const { createDesigns, viewDesigns } = userRoles ( ) ;
3041 const { t } = useTranslation ( ) ;
3142 const redirectUrl = MULTITENANCY_ENABLED ? `/tenant/${ tenantKey } /` : "/" ;
3243 const iconColor = StyleServices . getCSSVariable ( '--ff-gray-medium-dark' ) ;
44+
45+ const [ showDeleteModal , setShowDeleteModal ] = React . useState ( false ) ;
46+ const [ selectedRow , setSelectedRow ] = React . useState ( null ) ;
47+ const [ deleteMessage , setDeleteMessage ] = React . useState ( "" ) ;
48+ const [ disableDelete , setDisableDelete ] = React . useState ( false ) ;
49+ const [ isAppCountLoading , setIsAppCountLoading ] = React . useState ( false ) ;
3350
3451 // Mapping between DataGrid field names and reducer sort keys
3552 const gridFieldToSortKey = {
@@ -46,6 +63,128 @@ function FormTable() {
4663 status : "status" ,
4764 } ;
4865
66+ // 🔹 Delete flow
67+ const deleteAction = ( row ) => {
68+ setSelectedRow ( row ) ;
69+ setShowDeleteModal ( true ) ;
70+ setIsAppCountLoading ( true ) ; // start loading state
71+
72+ dispatch (
73+ getApplicationCount ( row . mapperId , ( ) => {
74+ setIsAppCountLoading ( false ) ; // stop loading once response arrives
75+ } )
76+ ) ;
77+ } ;
78+
79+ const handleDelete = ( ) => {
80+ if ( ! selectedRow ) return ;
81+ const formId = selectedRow . _id ;
82+ const mapperId = selectedRow . mapperId ;
83+ if ( ! applicationCount || applicationCount === 0 ) {
84+ dispatch (
85+ deleteForm ( "form" , formId , ( ) => {
86+ dispatch ( fetchBPMFormList ( { pageNo, limit, formSort : formsort } ) ) ;
87+ } )
88+ ) ;
89+
90+ if ( mapperId ) {
91+ dispatch ( unPublishForm ( mapperId ) ) ;
92+ }
93+ dispatch ( setFormDeleteStatus ( { modalOpen : false , formId : "" , formName : "" } ) ) ;
94+ toast . success ( t ( "Form deleted successfully" ) ) ;
95+ }
96+ handleCloseDelete ( ) ;
97+ } ;
98+
99+ const handleCloseDelete = ( ) => {
100+ setShowDeleteModal ( false ) ;
101+ setSelectedRow ( null ) ;
102+ } ;
103+
104+ // 🔹 Duplicate flow
105+ const handleDuplicate = async ( row ) => {
106+ try {
107+ setIsDuplicating ( true ) ;
108+ setDuplicateProgress ( 0 ) ;
109+ // Fetch the full form data
110+ setDuplicateProgress ( 20 ) ;
111+ const formResponse = await fetchFormById ( row . _id ) ;
112+
113+ setDuplicateProgress ( 40 ) ;
114+ const diagramResponse = await getProcessDetails ( {
115+ processKey : row . processKey ,
116+ tenantKey,
117+ mapperId : row . mapperId
118+ } ) ;
119+
120+ setDuplicateProgress ( 60 ) ;
121+ const originalForm = formResponse . data ;
122+
123+ // Create duplicated form data
124+ const duplicatedForm = _cloneDeep ( originalForm ) ;
125+
126+ // Modify title, name, and path
127+ duplicatedForm . title = `${ originalForm . title } -copy` ;
128+ duplicatedForm . name = `${ originalForm . name } -copy` ;
129+ duplicatedForm . path = `${ originalForm . path } -copy` ;
130+
131+ duplicatedForm . processData = diagramResponse . data . processData ;
132+ duplicatedForm . processType = diagramResponse . data . processType ;
133+
134+ // Remove _id and other fields that should not be copied
135+ delete duplicatedForm . _id ;
136+ delete duplicatedForm . machineName ;
137+ delete duplicatedForm . parentFormId ;
138+
139+ // Set as new version
140+ duplicatedForm . componentChanged = true ;
141+ duplicatedForm . newVersion = true ;
142+
143+ setDuplicateProgress ( 80 ) ;
144+ // Manipulate form data with proper formatting
145+ const newFormData = manipulatingFormData (
146+ duplicatedForm ,
147+ MULTITENANCY_ENABLED ,
148+ tenantKey ,
149+ formAccess ,
150+ submissionAccess
151+ ) ;
152+
153+ setDuplicateProgress ( 90 ) ;
154+ // Create the duplicated form
155+ const createResponse = await formCreate ( newFormData ) ;
156+
157+ setDuplicateProgress ( 100 ) ;
158+ const createdForm = createResponse . data ;
159+
160+ // Redirect to edit page of duplicated form
161+ dispatch ( push ( `${ redirectUrl } formflow/${ createdForm . _id } /edit` ) ) ;
162+
163+ } catch ( err ) {
164+ console . error ( "Error duplicating form:" , err ) ;
165+ // const errorMessage = err.response?.data?.message || err.message || "Failed to duplicate form";
166+ } finally {
167+ setIsDuplicating ( false ) ;
168+ }
169+ } ;
170+
171+ // Watch for application count updates
172+ React . useEffect ( ( ) => {
173+ if ( selectedRow ) {
174+ if ( isAppCountLoading ) {
175+ setDeleteMessage ( t ( "Preparing for delete..." ) ) ;
176+ setDisableDelete ( true ) ;
177+ } else if ( applicationCount > 0 ) {
178+ setDeleteMessage (
179+ t ( `This form has ${ applicationCount } submission(s). You can’t delete it.` )
180+ ) ;
181+ setDisableDelete ( true ) ;
182+ } else {
183+ setDeleteMessage ( t ( "Are you sure you want to delete this form?" ) ) ;
184+ setDisableDelete ( false ) ;
185+ }
186+ }
187+ } , [ applicationCount , selectedRow , isAppCountLoading , t ] ) ;
49188 // Prepare DataGrid columns
50189 const columns = [
51190 {
@@ -117,17 +256,35 @@ function FormTable() {
117256 flex : 1 ,
118257 sortable : false ,
119258 cellClassName : "last-column" ,
120- renderCell : params => (
121- ( createDesigns || viewDesigns ) && (
259+ renderCell : params => {
260+ const dropdownItems = [
261+ {
262+ label : t ( "Duplicate form" ) ,
263+ onClick : ( ) => handleDuplicate ( params . row ) ,
264+ } ,
265+ {
266+ label : params . row . status === "active" ? t ( "Unpublish" ) : t ( "Delete" ) ,
267+ onClick : ( ) => {
268+ if ( params . row . status === "active" ) {
269+ // dispatch(unPublishForm(params.row.mapperId));
270+ } else {
271+ deleteAction ( params . row ) ;
272+ }
273+ } ,
274+ className : params . row . status !== "active" ? "delete-dropdown-item" : "" ,
275+ } ,
276+ ] ;
277+ return ( createDesigns || viewDesigns ) && (
122278 < V8CustomDropdownButton
123279 label = { t ( "Edit" ) }
124280 variant = "secondary"
125281 menuPosition = "right"
126- dropdownItems = { [ ] }
282+ dropdownItems = { dropdownItems }
283+ disabled = { isDuplicating }
127284 onLabelClick = { ( ) => viewOrEditForm ( params . row . _id , "edit" ) }
128285 />
129- )
130- )
286+ ) ;
287+ }
131288 } ,
132289 ] ;
133290
@@ -193,6 +350,7 @@ const viewOrEditForm = (formId, path) => {
193350
194351
195352 return (
353+ < >
196354 < Paper sx = { { height : { sm : 400 , md : 510 , lg : 665 } , width : "100%" } } >
197355 < DataGrid
198356 disableColumnResize // disabed resizing
@@ -232,6 +390,19 @@ const viewOrEditForm = (formId, path) => {
232390 } }
233391 />
234392 </ Paper >
393+ < PromptModal
394+ show = { showDeleteModal }
395+ onClose = { handleCloseDelete }
396+ title = { t ( "Delete Item" ) }
397+ message = { deleteMessage }
398+ type = "warning"
399+ primaryBtnText = { t ( "Delete" ) }
400+ primaryBtnAction = { handleDelete }
401+ primaryBtnDisable = { disableDelete }
402+ secondaryBtnText = { t ( "Cancel" ) }
403+ secondaryBtnAction = { handleCloseDelete }
404+ />
405+ </ >
235406 ) ;
236407}
237408
0 commit comments