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