1- import { Component , EventEmitter , Input , OnDestroy , OnInit , Output } from '@angular/core' ;
1+ import { Component , EventEmitter , Input , Inject , OnDestroy , OnInit , Output } from '@angular/core' ;
22import { ActivatedRoute , Params } from '@angular/router' ;
33import { Store } from '@ngrx/store' ;
44import { TranslateService } from '@ngx-translate/core' ;
55import { BehaviorSubject , Observable , Subscription , take } from 'rxjs' ;
66
7+ import { APP_CONFIG , AppConfig } from '../../app.config' ;
78import { Individual } from '../../core/model/discovery' ;
89import { SidebarItemType , SidebarMenu } from '../../core/model/sidebar' ;
910import { DataAndAnalyticsView , DisplayView , ExportView } from '../../core/model/view' ;
@@ -38,14 +39,32 @@ export class ProfileSummariesExportComponent implements OnDestroy, OnInit {
3839
3940 private subscriptions : Subscription [ ] ;
4041
42+ public selectedOrganization : Observable < Individual > ;
43+
44+ public organizationsSubject : BehaviorSubject < Individual [ ] > ;
45+
46+ public organizations : Observable < Individual [ ] > ;
47+
48+ public selectedPeopleSubject : BehaviorSubject < any [ ] > ;
49+
50+ public get selectedPeople ( ) : Observable < any [ ] > {
51+ return this . selectedPeopleSubject . asObservable ( ) ;
52+ }
53+
4154 constructor (
55+ @Inject ( APP_CONFIG ) private appConfig : AppConfig ,
4256 private store : Store < AppState > ,
4357 private route : ActivatedRoute ,
4458 private translate : TranslateService ,
45- private rest : RestService ,
59+ private restService : RestService ,
4660 ) {
4761 this . labelEvent = new EventEmitter < string > ( ) ;
62+ this . selectedPeopleSubject = new BehaviorSubject < any [ ] > ( [ ] ) ;
63+ this . organizationsSubject = new BehaviorSubject < Individual [ ] > ( [ ] ) ;
64+ this . organizations = this . organizationsSubject . asObservable ( ) ;
65+
4866 this . subscriptions = [ ] ;
67+
4968 }
5069
5170 ngOnDestroy ( ) : void {
@@ -66,7 +85,6 @@ export class ProfileSummariesExportComponent implements OnDestroy, OnInit {
6685 title : this . translate . instant ( 'DATA_AND_ANALYTICS.TIME_PERIOD' ) ,
6786 items : this . displayView . exportViews . map ( ( exportView : ExportView ) => {
6887 const selected = exportView . name === queryParams . export ;
69-
7088 if ( selected ) {
7189 this . selectedExportView . next ( exportView ) ;
7290 this . labelEvent . next ( this . translate . instant ( 'DATA_AND_ANALYTICS.PROFILE_SUMMARIES' , { timePeriod : exportView . name } ) ) ;
@@ -92,29 +110,90 @@ export class ProfileSummariesExportComponent implements OnDestroy, OnInit {
92110 this . store . dispatch ( new fromSidebar . LoadSidebarAction ( { menu } ) ) ;
93111 } )
94112 ) ;
113+
95114 }
96115
97116 public getSelectedExportView ( ) : Observable < ExportView > {
98117 return this . selectedExportView . asObservable ( ) ;
99118 }
100119
101- public download ( organization : Individual , exportView : ExportView ) : void {
102- const link = exportView . name . toLowerCase ( ) . replace ( / / g, '_' ) ;
103- this . rest . get < Blob > ( organization . _links [ link ] . href , { observe : 'response' , responseType : 'blob' as 'json' } )
104- . pipe ( take ( 1 ) )
105- . subscribe ( ( response : any ) => {
106- const contentDisposition = response . headers . get ( 'Content-Disposition' ) ;
107- const filename = ! ! contentDisposition
108- ? contentDisposition . match ( / ^ .* f i l e n a m e = ( .* ) $ / ) [ 1 ]
109- : 'export.zip' ;
110-
111-
112- const url = window . URL . createObjectURL ( response . body ) ;
113- const anchor = document . createElement ( 'a' ) ;
114- anchor . download = filename ;
115- anchor . href = url ;
116- anchor . click ( ) ;
117- } ) ;
120+ public onSelectAll ( event : Event , organization : any ) : void {
121+ const checked = ( event . target as HTMLInputElement ) . checked ;
122+ const people = organization . people || [ ] ;
123+ if ( checked ) {
124+ this . selectedPeopleSubject . next ( [ ...people ] ) ;
125+ } else {
126+ this . selectedPeopleSubject . next ( [ ] ) ;
127+ }
128+ const checkboxes = document . querySelectorAll < HTMLInputElement > ( '.selected-profile-checkbox' ) ;
129+ checkboxes . forEach ( cb => cb . checked = checked ) ;
130+ }
131+
132+ public onSelectPerson ( event : Event , person : Individual ) : void {
133+ const checked = ( event . target as HTMLInputElement ) . checked ;
134+ if ( checked ) {
135+ const current = this . selectedPeopleSubject . value ;
136+ if ( ! current . find ( p => p . id === person . id ) ) {
137+ this . selectedPeopleSubject . next ( [ ...current , person ] ) ;
138+ }
139+ } else {
140+ const current = this . selectedPeopleSubject . value . filter ( p => p . id !== person . id ) ;
141+ this . selectedPeopleSubject . next ( current ) ;
142+ }
143+ }
144+
145+ private extractFilename ( response : any , defaultFileName : string ) : string {
146+ const contentDisposition = response . headers ?. get ( 'Content-Disposition' ) ;
147+ return contentDisposition ?. match ( / ^ .* f i l e n a m e = ( .* ) $ / ) [ 1 ] ?. trim ( ) || defaultFileName ;
148+ }
149+
150+ public downloadSelectedPeople ( organization : any , selected : any ) : void {
151+ this . route . queryParams . pipe ( take ( 1 ) ) . subscribe ( ( params ) => {
152+ const orgId = params ?. selectedOrganization ? params . selectedOrganization : organization . id ;
153+ const selectedIds = this . selectedPeopleSubject . value . map ( p => p . id ) ;
154+
155+ if ( ! orgId ) {
156+ console . error ( 'Download failure: Missing Organization id.' ) ;
157+ return ;
158+ }
159+
160+ if ( ! selectedIds . length || selectedIds . length === ( organization . people ?. length ?? 0 ) ) {
161+ const link = params ?. export . toLowerCase ( ) . replace ( / / g, '_' ) ;
162+ this . restService . get < Blob > (
163+ organization . _links [ link ] . href ,
164+ { observe : 'response' , responseType : 'blob' as 'json' } )
165+ . pipe ( take ( 1 ) )
166+ . subscribe ( ( response : any ) => {
167+ const filename = this . extractFilename ( response , 'export.zip' ) ;
168+ this . download ( response , filename ) ;
169+ } , ) ;
170+ } else {
171+ const exportName = ( selected ?. name ? selected . name : params ?. export ? params . export : '' )
172+ . trim ( ) . replace ( / \s + / g, ' ' ) ;
173+ const updatedHref = `${ this . appConfig . serviceUrl } /individual/${ orgId } /export?type=zip&name=${ encodeURIComponent ( exportName ) } ` ;
174+ this . restService . post (
175+ updatedHref ,
176+ selectedIds ,
177+ { observe : 'response' , responseType : 'blob' as 'json' , headers : { 'Content-Type' : 'application/json' } }
178+ ) . subscribe ( {
179+ next : ( response : any ) => {
180+ const filename = this . extractFilename ( response , 'selected_profile.zip' ) ;
181+ this . download ( response , filename ) ;
182+ } ,
183+ error : ( err ) => console . error ( 'Failed to download selected profiles.' , err ) ,
184+ } ) ;
185+ }
186+ } ) ;
187+ }
188+
189+ private download ( response : any , filename : any ) : void {
190+ const blob = response . body || response ;
191+ const url = window . URL . createObjectURL ( blob ) ;
192+ const anchor = document . createElement ( 'a' ) ;
193+ anchor . download = filename ;
194+ anchor . href = url ;
195+ anchor . click ( ) ;
196+ window . URL . revokeObjectURL ( url ) ;
118197 }
119198
120199}
0 commit comments