1+ /* eslint-disable import/no-extraneous-dependencies */
12import * as yup from 'yup'
23import { FC , useCallback , useEffect , useMemo , useState } from 'react'
34import { FormProvider , useForm } from 'react-hook-form'
45import { useNavigate , useParams } from 'react-router-dom'
56import { toast } from 'react-toastify'
67
7- import { yupResolver } from '@hookform/resolvers/yup'
88import { Button , LinkButton } from '~/libs/ui'
9+ import { DragDropContext , DropResult } from '@hello-pangea/dnd'
10+ import { yupResolver } from '@hookform/resolvers/yup'
911
12+ import { PageWrapper } from '../../../lib'
1013import { useFetchScorecard } from '../../../lib/hooks/useFetchScorecard'
1114import { saveScorecard } from '../../../lib/services'
1215import { rootRoute } from '../../../config/routes.config'
13- import { PageWrapper } from '../../../lib'
16+ import type { ScorecardQuestion , ScorecardSection } from '../../../lib/models '
1417
1518import { getEmptyScorecard } from './utils'
1619import { EditScorecardPageContextProvider } from './EditScorecardPage.context'
@@ -50,7 +53,7 @@ const EditScorecardPage: FC = () => {
5053 }
5154 } , [ scorecardQuery . scorecard , scorecardQuery . isValidating ] )
5255
53- const handleSubmit = useCallback ( async ( value : any ) => {
56+ const handleSubmit = useCallback ( async ( value : any ) : Promise < void > => {
5457 setSaving ( true )
5558 try {
5659 const response = await saveScorecard ( value )
@@ -60,12 +63,141 @@ const EditScorecardPage: FC = () => {
6063 }
6164 } catch ( e : any ) {
6265 toast . error ( `Couldn't save scorecard! ${ e . message } ` )
63- console . error ( ' Couldn\ 't save scorecard!' , e )
66+ console . error ( " Couldn't save scorecard!" , e )
6467 } finally {
6568 setSaving ( false )
6669 }
6770 } , [ params . scorecardId , navigate ] )
6871
72+ const reorder = ( list : any [ ] , startIndex : number , endIndex : number ) : any [ ] => {
73+ const result = Array . from ( list )
74+ const [ removed ] = result . splice ( startIndex , 1 )
75+ result . splice ( endIndex , 0 , removed )
76+ return result
77+ }
78+
79+ const move = (
80+ source : any [ ] ,
81+ destination : any [ ] ,
82+ sourceIndex : number ,
83+ destinationIndex : number ,
84+ ) : { source : any [ ] ; destination : any [ ] } => {
85+ const sourceClone = Array . from ( source )
86+ const destClone = Array . from ( destination )
87+ const [ removed ] = sourceClone . splice ( sourceIndex , 1 )
88+ destClone . splice ( destinationIndex , 0 , removed )
89+ return {
90+ destination : destClone ,
91+ source : sourceClone ,
92+ }
93+ }
94+
95+ function onDragEnd ( result : DropResult ) : void {
96+ if ( ! result . destination ) return
97+
98+ const { source, destination, type } : DropResult < string > = result
99+
100+ if ( type === 'group' ) {
101+ const newGroups = reorder ( editForm . getValues ( 'scorecardGroups' ) , source . index , destination . index )
102+ editForm . setValue ( 'scorecardGroups' , newGroups , { shouldDirty : true , shouldValidate : true } )
103+ } else if ( type === 'section' ) {
104+ const groups = editForm . getValues ( 'scorecardGroups' )
105+ const sourceGroupIndex = Number ( source . droppableId . split ( '.' ) [ 1 ] )
106+ const destGroupIndex = Number ( destination . droppableId . split ( '.' ) [ 1 ] )
107+
108+ if ( sourceGroupIndex === destGroupIndex ) {
109+ const newSections = reorder ( groups [ sourceGroupIndex ] . sections , source . index , destination . index )
110+ newSections . forEach ( ( section , index ) => {
111+ section . sortOrder = index
112+ } )
113+ groups [ sourceGroupIndex ] . sections = newSections
114+ } else {
115+ const {
116+ source : newSourceSections ,
117+ destination : newDestSections ,
118+ } : { source : ScorecardSection [ ] , destination : ScorecardSection [ ] } = move (
119+ groups [ sourceGroupIndex ] . sections ,
120+ groups [ destGroupIndex ] . sections ,
121+ source . index ,
122+ destination . index ,
123+ )
124+
125+ const movedSection = newDestSections [ destination . index ]
126+ if ( movedSection ) {
127+ delete movedSection . id
128+
129+ movedSection . questions . forEach ( ( question : any ) => {
130+ delete question . id
131+ } )
132+ }
133+
134+ newSourceSections . forEach ( ( section , index ) => {
135+ section . sortOrder = index
136+ } )
137+
138+ newDestSections . forEach ( ( section , index ) => {
139+ section . sortOrder = index
140+ } )
141+
142+ groups [ sourceGroupIndex ] . sections = newSourceSections
143+ groups [ destGroupIndex ] . sections = newDestSections
144+ }
145+
146+ editForm . setValue ( 'scorecardGroups' , groups , { shouldDirty : true , shouldValidate : true } )
147+ } else if ( type === 'question' ) {
148+ const groups = editForm . getValues ( 'scorecardGroups' )
149+ const parseDroppableId = ( id : string ) : { groupIndex : number ; sectionIndex : number } => {
150+ const parts = id . split ( '.' )
151+ return {
152+ groupIndex : Number ( parts [ 1 ] ) ,
153+ sectionIndex : Number ( parts [ 3 ] ) ,
154+ }
155+ }
156+
157+ const sourceIds = parseDroppableId ( source . droppableId )
158+ const destIds = parseDroppableId ( destination . droppableId )
159+
160+ if ( sourceIds . groupIndex === destIds . groupIndex && sourceIds . sectionIndex === destIds . sectionIndex ) {
161+ const questions = groups [ sourceIds . groupIndex ] . sections [ sourceIds . sectionIndex ] . questions
162+ const newQuestions = reorder ( questions , source . index , destination . index )
163+ newQuestions . forEach ( ( question , index ) => {
164+ question . sortOrder = index
165+ } )
166+ groups [ sourceIds . groupIndex ] . sections [ sourceIds . sectionIndex ] . questions = newQuestions
167+ } else {
168+ const sourceQuestions = groups [ sourceIds . groupIndex ] . sections [ sourceIds . sectionIndex ] . questions
169+ const destQuestions = groups [ destIds . groupIndex ] . sections [ destIds . sectionIndex ] . questions
170+ const {
171+ source : newSourceQuestions ,
172+ destination : newDestQuestions ,
173+ } : { source : ScorecardQuestion [ ] , destination : ScorecardQuestion [ ] } = move (
174+ sourceQuestions ,
175+ destQuestions ,
176+ source . index ,
177+ destination . index ,
178+ )
179+
180+ const movedQuestion = newDestQuestions [ destination . index ]
181+ if ( movedQuestion ) {
182+ delete movedQuestion . id
183+ }
184+
185+ newSourceQuestions . forEach ( ( question , index ) => {
186+ question . sortOrder = index
187+ } )
188+
189+ newDestQuestions . forEach ( ( question , index ) => {
190+ question . sortOrder = index
191+ } )
192+
193+ groups [ sourceIds . groupIndex ] . sections [ sourceIds . sectionIndex ] . questions = newSourceQuestions
194+ groups [ destIds . groupIndex ] . sections [ destIds . sectionIndex ] . questions = newDestQuestions
195+ }
196+
197+ editForm . setValue ( 'scorecardGroups' , groups , { shouldDirty : true , shouldValidate : true } )
198+ }
199+ }
200+
69201 if ( scorecardQuery . isValidating ) {
70202 return < > </ >
71203 }
@@ -78,12 +210,13 @@ const EditScorecardPage: FC = () => {
78210 >
79211 < form className = { styles . pageContentWrap } onSubmit = { editForm . handleSubmit ( handleSubmit ) } >
80212 < FormProvider { ...editForm } >
213+ < DragDropContext onDragEnd = { onDragEnd } >
214+ < h3 className = { styles . sectionTitle } > 1. Scorecard Information</ h3 >
215+ < ScorecardInfoForm />
81216
82- < h3 className = { styles . sectionTitle } > 1. Scorecard Information</ h3 >
83- < ScorecardInfoForm />
84-
85- < h3 className = { styles . sectionTitle } > 2. Evaluation Structure</ h3 >
86- < ScorecardGroupForm />
217+ < h3 className = { styles . sectionTitle } > 2. Evaluation Structure</ h3 >
218+ < ScorecardGroupForm />
219+ </ DragDropContext >
87220
88221 < div className = { styles . bottomContainer } >
89222 < hr />
0 commit comments