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