1
+ import { useLazyQuery , useMutation } from "@apollo/client"
2
+ import React , { useEffect , useState } from "react"
3
+ import { useTranslation } from "react-i18next"
4
+ import * as XLSX from "xlsx"
5
+ import { ADD_RATINGS_BY_FILE , GET_RATINGS_BY_USER_COHORT } from "../Mutations/Ratings"
6
+ import { toast } from "react-toastify"
7
+ import { GET_TEAMS_BY_USER_ROLE } from "../Mutations/teamMutation"
8
+
9
+ type BulkRatingModalProps = {
10
+ bulkRateModal : boolean ,
11
+ setBulkRateModal : React . Dispatch < React . SetStateAction < boolean > >
12
+ }
13
+
14
+ type AddRatingsByFileFormData = {
15
+ sprint : string ,
16
+ file : File | null ,
17
+ }
18
+
19
+ const BulkRatingModal = ( { bulkRateModal, setBulkRateModal } : BulkRatingModalProps ) => {
20
+ const { t } = useTranslation ( )
21
+ const [ getRatingsByUserCohort , { data : ratings , loading : loadingRatings , error : ratingsError } ] = useLazyQuery ( GET_RATINGS_BY_USER_COHORT , {
22
+ variables : {
23
+ orgToken : localStorage . getItem ( 'orgToken' )
24
+ } ,
25
+ fetchPolicy : 'network-only' ,
26
+ } )
27
+ const [ getTeamsByUserRole , { data : teams , loading : loadingTeams , error : teamsError } ] = useLazyQuery ( GET_TEAMS_BY_USER_ROLE , {
28
+ variables : {
29
+ orgToken : localStorage . getItem ( 'orgToken' )
30
+ } ,
31
+ fetchPolicy : 'network-only' ,
32
+ } )
33
+ const [ addRatingsByFile , { data : bulkRatings , loading : loadingBulkRatings , error : bulkRatingsError } ] = useMutation ( ADD_RATINGS_BY_FILE )
34
+ const [ formData , setFormData ] = useState < AddRatingsByFileFormData > ( {
35
+ sprint : '' ,
36
+ file : null
37
+ } )
38
+ const [ selectedTeam , setSelectedTeam ] = useState < string > ( '' )
39
+
40
+ const saveRatings = async ( e : React . FormEvent ) => {
41
+ try {
42
+ e . preventDefault ( )
43
+ if ( ! formData . sprint ) throw new Error ( "Please select a sprint" )
44
+ if ( ! formData . file ) throw new Error ( "Please select a file" )
45
+ await addRatingsByFile ( {
46
+ variables : {
47
+ file : formData . file ,
48
+ sprint : parseInt ( formData . sprint ) ,
49
+ orgToken : localStorage . getItem ( 'orgToken' )
50
+ } ,
51
+ } )
52
+ getRatingsByUserCohort ( )
53
+ toast . success ( "Rating completed successfully" )
54
+ } catch ( err : any ) {
55
+ toast . error ( err ?. message )
56
+ }
57
+ }
58
+
59
+ const downloadTeamFile = async ( e : any ) => {
60
+ try {
61
+ if ( selectedTeam === '' ) throw new Error ( "No Team was selected" )
62
+ const team = teams . getTeamsByUserRole . find ( ( team :any ) => team . id === selectedTeam )
63
+ const rows : any = [ ]
64
+ team . members . forEach ( ( member : any ) => {
65
+ console . log ( member )
66
+ if ( member . role === "trainee" ) {
67
+ rows . push ( {
68
+ email : member . email ,
69
+ quantity : '' ,
70
+ quality : '' ,
71
+ professional_skills : '' ,
72
+ feedBacks : ''
73
+ } )
74
+ }
75
+ } )
76
+ const workSheet = rows . length ? XLSX . utils . json_to_sheet ( rows ) : XLSX . utils . json_to_sheet ( [ {
77
+ email : '' ,
78
+ quantity : '' ,
79
+ quality : '' ,
80
+ professional_skills :'' ,
81
+ feedBacks : ''
82
+ } ] )
83
+ const workBook = XLSX . utils . book_new ( )
84
+ workSheet [ "!cols" ] = [ { wch : 20 } ]
85
+ XLSX . utils . book_append_sheet ( workBook , workSheet , "ratings" )
86
+ XLSX . writeFile ( workBook , `${ team . name . replace ( ' ' , '' ) } _Ratings.xlsx` )
87
+ } catch ( err : any ) {
88
+ toast . error ( err ?. message )
89
+ }
90
+ }
91
+
92
+ useEffect ( ( ) => {
93
+ getRatingsByUserCohort ( )
94
+ getTeamsByUserRole ( )
95
+ } , [ ] )
96
+
97
+ return (
98
+ < div className = { `${ bulkRateModal ? "block" : "hidden" } h-screen w-screen z-20 bg-black bg-opacity-30 backdrop-blur-sm fixed top-0 left-0 flex items-center justify-center px-4` } >
99
+ < div className = "w-full p-4 pb-8 bg-indigo-100 rounded-lg dark:bg-dark-bg sm:w-3/4 xl:w-4/12" >
100
+ < div className = "flex flex-wrap items-center justify-center w-full card-title" >
101
+ < h3 className = "w-11/12 text-sm font-bold text-center dark:text-white" >
102
+ { t ( 'Bulk Rating' ) }
103
+ </ h3 >
104
+ < hr className = "w-full my-3 border-b bg-primary" />
105
+ </ div >
106
+ < div >
107
+ < form data-testid = "bulk-rating-form" className = "flex flex-col gap-5" onSubmit = { saveRatings } >
108
+ < div className = "flex flex-col gap-1" >
109
+ < label > Choose a sprint</ label >
110
+ < select data-testid = "select-sprint" className = "p-2 text-black dark:text-white rounded-lg bg-white dark:bg-dark border-2 border-primary"
111
+ defaultValue = { "" }
112
+ onChange = { ( e ) => {
113
+ e . preventDefault ( )
114
+ setFormData ( { ...formData , sprint : e . target . value } )
115
+ } }
116
+ >
117
+ < option > Choose a sprint</ option >
118
+ {
119
+ ratings && ! ratings . getRatingsByUserCohort . length ?
120
+ < option data-testid = "sprint-default-option" value = { 1 } > Sprint 1</ option >
121
+ : ''
122
+ }
123
+ {
124
+ ratings && ratings . getRatingsByUserCohort . length ?
125
+ [ ...ratings . getRatingsByUserCohort ] . map ( ( rating : any ) =>
126
+ < option data-testid = { `sprint-option-${ rating . id } ` } key = { rating . id } value = { rating . sprint } > Sprint { rating . sprint } </ option >
127
+ )
128
+ : ''
129
+ }
130
+ {
131
+ ratings && ratings . getRatingsByUserCohort . length ?
132
+ < option data-testid = "sprint-new-option" value = { [ ...ratings . getRatingsByUserCohort ] . pop ( ) . sprint + 1 } > Sprint { [ ...ratings . getRatingsByUserCohort ] . pop ( ) . sprint + 1 } </ option >
133
+ : ''
134
+ }
135
+ {
136
+ loadingRatings ?
137
+ < option data-testid = "sprint-loading-option" > Loading...</ option >
138
+ : ''
139
+ }
140
+ {
141
+ ratingsError ?
142
+ < option data-testid = "sprint-error-option" > No sprints found...</ option >
143
+ : ''
144
+ }
145
+ </ select >
146
+ </ div >
147
+ < div className = "flex items-center justify-between" >
148
+ < input
149
+ data-testid = "file-input"
150
+ className = "w-1/2 h-full bg-gray-600 rounded-md"
151
+ type = "file"
152
+ onChange = { ( e ) => {
153
+ const file = e . target . files ?. [ 0 ]
154
+ setFormData ( { ...formData , file : file ? file : null } )
155
+ } }
156
+ accept = ".xlsx, .xls"
157
+ >
158
+ </ input >
159
+ < div className = "flex gap-2" >
160
+ < select data-testid = "select-team" className = "p-2 text-sm text-black dark:text-white rounded-lg bg-white dark:bg-dark border-2 border-primary" defaultValue = { "" } onChange = { ( e ) => setSelectedTeam ( e . target . value ) } >
161
+ < option data-testid = "team-default-option" > Choose a team</ option >
162
+ {
163
+ teams && teams . getTeamsByUserRole . length > 0 ?
164
+ teams . getTeamsByUserRole . map ( ( team : any ) => < option data-testid = { `team-option-${ team . id } ` } key = { team . id } value = { team . id } > { team . name } </ option > )
165
+ : ''
166
+ }
167
+ {
168
+ loadingTeams ?
169
+ < option > Loading...</ option >
170
+ : ''
171
+ }
172
+ {
173
+ teamsError ?
174
+ < option > No teams found...</ option >
175
+ : ''
176
+ }
177
+ </ select >
178
+ < button data-testid = "download-button" type = "button" onClick = { downloadTeamFile } className = "p-3 text-white rounded-lg bg-green-500 text-sm font-serif font-semibold" > Download</ button >
179
+ </ div >
180
+ </ div >
181
+
182
+ < div >
183
+ {
184
+ bulkRatings && bulkRatings . addRatingsByFile . RejectedRatings . length > 0 ?
185
+ < div className = "my-1 overflow-x-auto" >
186
+ < table className = "table-fixed min-w-full" >
187
+ < caption className = "caption-top text-left my-2" >
188
+ Rejected Ratings
189
+ </ caption >
190
+ < thead className = "border-b bg-neutral-700 border-neutral-400" >
191
+ < tr >
192
+ < th scope = "col" className = "text-left py-4 px-2" > Email</ th >
193
+ < th scope = "col" className = "text-left py-4 px-2" > Quantity</ th >
194
+ < th scope = "col" className = "text-left py-4 px-2" > Quality</ th >
195
+ < th scope = "col" className = "text-left py-4 px-2" > Professional_Skills</ th >
196
+ < th scope = "col" className = "text-left py-4 px-2" > Feedback</ th >
197
+ </ tr >
198
+ </ thead >
199
+ < tbody >
200
+ { bulkRatings . addRatingsByFile ?. RejectedRatings . map ( ( rating : any , index : number ) =>
201
+ < tr key = { index } className = "text-red-400" >
202
+ < td className = "text-left py-1 px-2" > { rating . email ? rating . email : "null" } </ td >
203
+ < td className = "text-left py-1 px-2" > { rating . quantity ? rating . quantity : "null" } </ td >
204
+ < td className = "text-left py-1 px-2" > { rating . quality ? rating . quality : "null" } </ td >
205
+ < td className = "text-left py-1 px-2" > { rating . professional_skills ? rating . professional_skills : "null" } </ td >
206
+ < td className = "text-left py-1 px-2" > { rating . feedBacks ? rating . feedBacks : "null" } </ td >
207
+ </ tr >
208
+ ) }
209
+ </ tbody >
210
+ </ table >
211
+ </ div >
212
+ : ''
213
+ }
214
+ </ div >
215
+ < div className = "flex justify-between w-full" >
216
+ < button className = "w-[40%] md:w-1/4 p-3 text-white rounded-lg bg-primary text-sm font-serif font-semibold" type = "button" onClick = { ( ) => setBulkRateModal ( false ) } >
217
+ Cancel
218
+ </ button >
219
+ < button className = "w-[40%] md:w-1/4 p-3 text-white rounded-lg bg-primary text-sm font-serif font-semibold" type = "submit" >
220
+ Save
221
+ </ button >
222
+ </ div >
223
+ </ form >
224
+ </ div >
225
+ </ div >
226
+ </ div >
227
+ )
228
+ }
229
+
230
+ export default BulkRatingModal
0 commit comments