1- import React , { useCallback } from 'react' ;
1+ import React , { useCallback , useMemo , useState } from 'react' ;
22import { gql , useQuery , useLazyQuery } from '@apollo/client' ;
33import { encodeDate } from '@togglecorp/fujs' ;
44import { useParams } from 'react-router-dom' ;
@@ -20,11 +20,15 @@ import {
2020 UserGroupStatsQueryVariables ,
2121 FilteredUserGroupStatsQuery ,
2222 FilteredUserGroupStatsQueryVariables ,
23+ UserMembershipsExportQuery ,
24+ UserMembershipsExportQueryVariables ,
2325} from '#generated/types' ;
2426import { defaultPagePerItemOptions } from '#utils/common' ;
2527
2628import styles from './styles.css' ;
2729
30+ const EXPORT_LIMIT = 500 ;
31+
2832const USER_GROUP_STATS = gql `
2933 query UserGroupStats($pk: ID!, $limit: Int!, $offset: Int!) {
3034 userGroup(pk: $pk) {
@@ -100,6 +104,30 @@ const FILTERED_USER_GROUP_STATS = gql`
100104 }
101105` ;
102106
107+ const USER_MEMBERSHIPS_EXPORT = gql `
108+ query UserMembershipsExport(
109+ $pk: ID!,
110+ $limit: Int!,
111+ $offset: Int!,
112+ ) {
113+ userGroup(pk: $pk) {
114+ userMemberships(pagination: { limit: $limit, offset: $offset }) {
115+ count
116+ limit
117+ offset
118+ items {
119+ userId
120+ username
121+ isActive
122+ totalMappingProjects
123+ totalSwipeTime
124+ totalSwipes
125+ }
126+ }
127+ }
128+ }
129+ ` ;
130+
103131type UserGroupMember = NonNullable < NonNullable < NonNullable < UserGroupStatsQuery [ 'userGroup' ] > [ 'userMemberships' ] > [ 'items' ] > [ number ] ;
104132
105133function memberKeySelector ( member : UserGroupMember ) {
@@ -120,10 +148,15 @@ interface Props {
120148 className ?: string ;
121149}
122150
151+ type UserMembershipType = NonNullable < NonNullable < UserMembershipsExportQuery [ 'userGroup' ] > [ 'userMemberships' ] > [ 'items' ] ;
152+
123153function UserGroupDashboard ( props : Props ) {
124154 const { className } = props ;
125155
126156 const { userGroupId } = useParams < { userGroupId : string | undefined } > ( ) ;
157+ const [ userMembershipsData , setUserMembershipsData ] = useState < UserMembershipType > ( [ ] ) ;
158+ const [ exportPending , setExportPending ] = useState < boolean > ( false ) ;
159+
127160 const [
128161 dateRange ,
129162 setDateRange ,
@@ -146,6 +179,7 @@ function UserGroupDashboard(props: Props) {
146179
147180 const [ activePage , setActivePage ] = React . useState ( 1 ) ;
148181 const [ pagePerItem , setPagePerItem ] = React . useState ( 10 ) ;
182+ const [ offset , setOffset ] = useState < number > ( EXPORT_LIMIT ) ;
149183
150184 const {
151185 data : userGroupStats ,
@@ -162,49 +196,78 @@ function UserGroupDashboard(props: Props) {
162196 } ,
163197 ) ;
164198
165- const [
166- getUserGroupStatsDownload ,
167- {
168- loading : userGroupStatsDownloadLoading ,
169- } ,
170- ] = useLazyQuery < UserGroupStatsQuery , UserGroupStatsQueryVariables > (
171- USER_GROUP_STATS ,
172- {
173- variables : userGroupId ? {
199+ const userGroupExportVariable = useMemo (
200+ ( ) : UserMembershipsExportQueryVariables | undefined => (
201+ userGroupId ? {
174202 pk : userGroupId ,
175- limit : 500 , // NOTE this is a temporary fix we need to do recursive fetch later
203+ limit : EXPORT_LIMIT ,
176204 offset : 0 ,
177- } : undefined ,
178- onCompleted : ( data ) => {
179- const userGroupData = [
180- [ 'User' , 'Total swipes' , 'Project contributed' , 'Time spent(mins)' ] ,
181- ...( data . userGroup . userMemberships . items . map ( ( user ) => (
182- [
183- user . username ,
184- user . totalSwipes ,
185- user . totalMappingProjects ,
186- user . totalSwipeTime ,
187- ]
188- ) ) ?? [ ] ) ] ;
189- let csvContent = '' ;
190- userGroupData . forEach ( ( row ) => {
191- csvContent += `${ row . join ( ',' ) } \n` ;
192- } ) ;
193- const blob = new Blob ( [ csvContent ] , { type : 'text/csv;charset=utf-8,' } ) ;
194- const objUrl = URL . createObjectURL ( blob ) ;
195- const link = document . createElement ( 'a' ) ;
196- link . href = objUrl ;
197- link . download = `${ userGroupStats ?. userGroup ?. name ?? 'users' } .csv` ;
198- document . body . appendChild ( link ) ;
199- link . dispatchEvent (
200- new MouseEvent ( 'click' , {
201- bubbles : true ,
202- cancelable : true ,
203- view : window ,
204- } ) ,
205- ) ;
206- document . body . removeChild ( link ) ;
207- window . URL . revokeObjectURL ( objUrl ) ;
205+ } : undefined
206+ ) , [ userGroupId ] ,
207+ ) ;
208+
209+ const [
210+ exportUserMembership ,
211+ ] = useLazyQuery < UserMembershipsExportQuery , UserGroupStatsQueryVariables > (
212+ USER_MEMBERSHIPS_EXPORT ,
213+ {
214+ variables : userGroupExportVariable ,
215+ onCompleted : ( response ) => {
216+ const result = response ?. userGroup ?. userMemberships ;
217+ const userMembershipsCount = response ?. userGroup ?. userMemberships ?. count ?? 0 ;
218+
219+ setUserMembershipsData ( ( prevValue ) => [ ...prevValue , ...result ?. items ?? [ ] ] ) ;
220+ setOffset ( ( prevValue ) => prevValue + EXPORT_LIMIT ) ;
221+
222+ if ( userMembershipsData ?. length < userMembershipsCount ) {
223+ setExportPending ( true ) ;
224+ exportUserMembership ( {
225+ variables : userGroupId ? ( {
226+ pk : userGroupId ,
227+ limit : EXPORT_LIMIT ,
228+ offset,
229+ } ) : undefined ,
230+ } ) ;
231+ }
232+
233+ if ( userMembershipsData ?. length === userMembershipsCount ) {
234+ const userGroupData = [
235+ [ 'User' , 'Total swipes' , 'Project contributed' , 'Time spent(mins)' ] ,
236+ ...( userMembershipsData ?. map ( ( user ) => (
237+ [
238+ user . username ,
239+ user . totalSwipes ,
240+ user . totalMappingProjects ,
241+ user . totalSwipeTime ,
242+ ]
243+ ) ) ?? [ ] ) ,
244+ ] ;
245+ let csvContent = '' ;
246+ userGroupData . forEach ( ( row ) => {
247+ csvContent += `${ row . join ( ',' ) } \n` ;
248+ } ) ;
249+ const blob = new Blob ( [ csvContent ] , { type : 'text/csv;charset=utf-8,' } ) ;
250+ const objUrl = URL . createObjectURL ( blob ) ;
251+ const link = document . createElement ( 'a' ) ;
252+ link . href = objUrl ;
253+ link . download = `${ userGroupStats ?. userGroup ?. name ?? 'users' } .csv` ;
254+ document . body . appendChild ( link ) ;
255+ link . dispatchEvent (
256+ new MouseEvent ( 'click' , {
257+ bubbles : true ,
258+ cancelable : true ,
259+ view : window ,
260+ } ) ,
261+ ) ;
262+ document . body . removeChild ( link ) ;
263+ window . URL . revokeObjectURL ( objUrl ) ;
264+ setExportPending ( false ) ;
265+ }
266+ } ,
267+ onError : ( err ) => {
268+ // NOTE: we don't show any alert on failure and success for now
269+ // eslint-disable-next-line no-console
270+ console . log ( 'some error ocoured' , err ) ;
208271 } ,
209272 } ,
210273 ) ;
@@ -278,11 +341,11 @@ function UserGroupDashboard(props: Props) {
278341 Group Members
279342 </ Heading >
280343 < Button
281- disabled = { userGroupStatsDownloadLoading }
282- onClick = { getUserGroupStatsDownload }
344+ disabled = { exportPending }
345+ onClick = { exportUserMembership }
283346 name = { undefined }
284347 >
285- { userGroupStatsDownloadLoading ? 'Exporting' : 'Export' }
348+ { exportPending ? 'Exporting' : 'Export' }
286349 </ Button >
287350 </ div >
288351 < div className = { styles . membersContainer } >
0 commit comments