@ieeesa-npm/groups 0.15.3 → 0.15.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import { AfterViewInit, ChangeDetectorRef, OnDestroy, OnInit } from '@angular/co
2
2
  import { Subject } from 'rxjs';
3
3
  import { Sort } from '@angular/material/sort';
4
4
  import { MatDialog } from '@angular/material/dialog';
5
- import { TableColumnSchema, TableActions, AlignType, ChipOption, CheckboxOption, FiltersAndOptions } from '@ieeesa-npm/presentation';
5
+ import { TableColumnSchema, TableActions, AlignType, ChipOption, CheckboxOption, FiltersAndOptions, ExportFormat } from '@ieeesa-npm/presentation';
6
6
  import { AlertsService, GlobalErrorHandlerService } from '@ieeesa-npm/utility';
7
7
  import { AlertType } from '@ieeesa-npm/models';
8
8
  import { GroupsService } from '../../services/groups.service';
@@ -10,6 +10,7 @@ import { User, LoggedInUserInfo } from '../../models/user.model';
10
10
  import { GroupUserInfo } from '../../models/group-user-info.model';
11
11
  import { GroupRosterType } from '../../models/roster-type.model';
12
12
  import { FilterType } from '../../models/filter-type.model';
13
+ import { RosterExportService } from '../../services/roster-export.service';
13
14
  import * as i0 from "@angular/core";
14
15
  /**
15
16
  * ViewRosterComponent implements the functionalities to add, edit, delete user to group roster, assign and modify roles(s) to the group users, and search user from roster.
@@ -23,6 +24,7 @@ export declare class ViewRosterComponent implements AfterViewInit, OnDestroy, On
23
24
  private alertService;
24
25
  private cdr;
25
26
  private globalErrorHandlerService;
27
+ rosterExportService: RosterExportService;
26
28
  destroy$: Subject<boolean>;
27
29
  constants: any;
28
30
  viewRosterConstants: any;
@@ -53,7 +55,7 @@ export declare class ViewRosterComponent implements AfterViewInit, OnDestroy, On
53
55
  tableColumnsForSystemUsers: any;
54
56
  emptyFieldValueReplacedBy: any;
55
57
  userInfo: LoggedInUserInfo;
56
- constructor(dialog: MatDialog, groupService: GroupsService, alertService: AlertsService, cdr: ChangeDetectorRef, globalErrorHandlerService: GlobalErrorHandlerService);
58
+ constructor(dialog: MatDialog, groupService: GroupsService, alertService: AlertsService, cdr: ChangeDetectorRef, globalErrorHandlerService: GlobalErrorHandlerService, rosterExportService: RosterExportService);
57
59
  ngOnInit(): void;
58
60
  /**
59
61
  * Show add new user success message when canShowMessage is set true
@@ -159,6 +161,18 @@ export declare class ViewRosterComponent implements AfterViewInit, OnDestroy, On
159
161
  * @memberof ViewRosterComponent
160
162
  */
161
163
  showAlertMessage(message: string | string[], alertType: AlertType): void;
164
+ /**
165
+ * Toggles the export format popover — delegates to RosterExportService.
166
+ * @memberof ViewRosterComponent
167
+ */
168
+ onExportClick(): void;
169
+ /**
170
+ * Handles export format selection — delegates to RosterExportService using the
171
+ * currently selected group's id and name.
172
+ * @param {ExportFormat} format
173
+ * @memberof ViewRosterComponent
174
+ */
175
+ onExportFormatSelected(format: ExportFormat): void;
162
176
  /**
163
177
  * NgOnDestroy performs necessary cleanup tasks, such as unsubscribing from subscription when the component is being destroyed.
164
178
  * @memberof ViewRosterComponent
@@ -46,14 +46,6 @@ export declare class GroupsService {
46
46
  "1006": string;
47
47
  "1007": string;
48
48
  "1008": string;
49
- /**
50
- * Handles custom API errors: maps BE error codes to messages and shows an
51
- * alert. Mirrors the events-and-meetings error handling so reciprocal-credit
52
- * failures (e.g. MTG-011 duplicate, MTG-012 permission) surface consistently.
53
- * @param {*} error the HttpErrorResponse from a failed call
54
- * @param {boolean} [showAlert=true] whether to display the alert
55
- * @memberof GroupsService
56
- */
57
49
  "1009": string;
58
50
  "1010": string;
59
51
  "1011": string;
@@ -86,11 +78,7 @@ export declare class GroupsService {
86
78
  "1038": string;
87
79
  "1039": string;
88
80
  "2001": string;
89
- "2002": string; /**
90
- * Sets group function type.
91
- * @param {GroupFunctionType} groupFunctionType -group function type.
92
- * @memberof GroupsService
93
- */
81
+ "2002": string;
94
82
  "2010": string;
95
83
  "2011": string;
96
84
  "20l2": string;
@@ -0,0 +1,171 @@
1
+ import { HttpClient } from '@angular/common/http';
2
+ import { MatDialog } from '@angular/material/dialog';
3
+ import { ExportFormat, ExportFormatOption } from '@ieeesa-npm/presentation';
4
+ import { AlertsService } from '@ieeesa-npm/utility';
5
+ import * as i0 from "@angular/core";
6
+ /**
7
+ * RosterExportService owns the complete group roster export flow:
8
+ * 1. Manages export popover open/close state
9
+ * 2. Opens IEEE Data Access and Use Policy confirmation dialog
10
+ * 3. Calls the backend roster export API (blob response)
11
+ * 4. Triggers file download via File System Access API / file-saver
12
+ * 5. Shows success/error dialogs and toasts
13
+ *
14
+ * The component only needs to:
15
+ * - Subscribe to `isPopoverOpen$` for popover visibility
16
+ * - Call `togglePopover()` on Export button click
17
+ * - Call `onFormatSelected(format, groupId, groupName)` when user picks a format
18
+ *
19
+ * Follows the same pattern as MeetingExportService in the events-and-meetings library.
20
+ * @export
21
+ * @class RosterExportService
22
+ */
23
+ export declare class RosterExportService {
24
+ private httpClient;
25
+ private dialog;
26
+ private alertService;
27
+ private constants;
28
+ private apiBaseUrl;
29
+ private destroy$;
30
+ /** Controls popover open/close state */
31
+ private isPopoverOpenSubject$;
32
+ isPopoverOpen$: import("rxjs").Observable<boolean>;
33
+ /** Controls in-progress state for button disable */
34
+ private isExportInProgressSubject$;
35
+ isExportInProgress$: import("rxjs").Observable<boolean>;
36
+ /** Format options for the export popover */
37
+ exportFormatOptions: ExportFormatOption[];
38
+ /** Subtitle labels for the format selection cards */
39
+ exportFormatSubtitles: Record<string, string>;
40
+ /** Popover title */
41
+ popoverTitle: any;
42
+ constructor(httpClient: HttpClient, dialog: MatDialog, alertService: AlertsService);
43
+ /**
44
+ * Sets the API base URL for roster export calls.
45
+ * @param {string} url - Base URL (e.g., 'https://saappsapidev.ieee.org/v1')
46
+ * @memberof RosterExportService
47
+ */
48
+ setApiBaseUrl(url: string): void;
49
+ /**
50
+ * Toggles the export format popover open/closed.
51
+ * Called by the component on Export button click.
52
+ * @memberof RosterExportService
53
+ */
54
+ togglePopover(): void;
55
+ /**
56
+ * Closes the export format popover.
57
+ * @memberof RosterExportService
58
+ */
59
+ closePopover(): void;
60
+ /**
61
+ * Handles format selection from the export popover.
62
+ * Closes popover, opens IEEE Policy dialog, on confirm triggers export.
63
+ * This is the main entry point called by the component after user picks a format.
64
+ * @param {ExportFormat} format - Selected format (CSV or XLS)
65
+ * @param {string} groupId - The group ID to export the roster for
66
+ * @param {string} groupName - The group name for filename generation
67
+ * @memberof RosterExportService
68
+ */
69
+ onFormatSelected(format: ExportFormat, groupId: string, groupName: string): void;
70
+ /**
71
+ * Starts the export after the user confirms the policy dialog.
72
+ *
73
+ * When the File System Access API is available, the native Save picker is opened
74
+ * HERE — synchronously within the confirm handler — while the user activation from
75
+ * the confirm click is still valid. (Opening it later, inside the async HTTP
76
+ * response callback, would exceed the activation window and the browser would
77
+ * reject it with a SecurityError.) The obtained file handle is then passed to the
78
+ * HTTP flow, which writes the downloaded bytes to it once the response arrives, so
79
+ * the success dialog is still shown only AFTER the file is saved.
80
+ *
81
+ * If the user cancels the picker we abort before making the request. If the API is
82
+ * unavailable (Firefox/Safari) we proceed without a handle and fall back to
83
+ * `saveAs()` after the response.
84
+ * @param {ExportFormat} format
85
+ * @param {string} groupId
86
+ * @param {string} groupName
87
+ * @memberof RosterExportService
88
+ */
89
+ private startExport;
90
+ /**
91
+ * Executes the roster export API call and writes the downloaded file.
92
+ * Endpoint: GET /v1/groups/roster/export/{groupId}?format=csv|xlsx
93
+ * When a pre-obtained file handle is provided (from the picker opened at confirm
94
+ * time) the bytes are written to it; otherwise it falls back to `saveAs()`.
95
+ * The success dialog is shown only after the file has been written.
96
+ * @param {ExportFormat} format - The selected export format
97
+ * @param {string} groupId - The group identifier
98
+ * @param {string} groupName - The group name for filename
99
+ * @param {FileSystemFileHandle | null} fileHandle - Handle from the Save picker, or null
100
+ * @memberof RosterExportService
101
+ */
102
+ private executeExport;
103
+ /**
104
+ * Shows the export success dialog after a successful file download.
105
+ * @param {string} filename - The downloaded filename
106
+ * @memberof RosterExportService
107
+ */
108
+ private showExportSuccessDialogAfterDownload;
109
+ /**
110
+ * Returns the API base URL for roster export.
111
+ * @return {string}
112
+ * @memberof RosterExportService
113
+ */
114
+ private getExportApiUrl;
115
+ /**
116
+ * Extracts filename from Content-Disposition header.
117
+ * Falls back to buildFilename() if header is missing.
118
+ * @param {HttpResponse<Blob>} response
119
+ * @param {ExportFormat} format
120
+ * @param {string} groupName
121
+ * @return {string}
122
+ * @memberof RosterExportService
123
+ */
124
+ private extractFilenameFromResponse;
125
+ /**
126
+ * Builds a sanitized filename for the roster export.
127
+ * Pattern: {SanitizedGroupName}_Roster.{extension}
128
+ * @param {string} groupName
129
+ * @param {ExportFormat} format
130
+ * @return {string}
131
+ * @memberof RosterExportService
132
+ */
133
+ buildFilename(groupName: string, format: ExportFormat): string;
134
+ /**
135
+ * Sanitizes the group name for use in a filename.
136
+ * Replaces non-alphanumeric characters (except hyphen/underscore) with underscore,
137
+ * collapses consecutive underscores, and truncates to 200 chars.
138
+ * @param {string} groupName
139
+ * @return {string}
140
+ * @memberof RosterExportService
141
+ */
142
+ sanitizeGroupName(groupName: string): string;
143
+ /**
144
+ * Writes the downloaded bytes to disk and resolves ONLY when the save has actually
145
+ * completed.
146
+ *
147
+ * The Save picker is opened earlier (at policy-confirm time, while user activation
148
+ * is still valid — see {@link startExport}); this method only writes to the handle
149
+ * it produced. That keeps the "success dialog after save" ordering without hitting
150
+ * the SecurityError that occurs when the picker is opened from this async callback.
151
+ *
152
+ * When no handle is provided (File System Access API unavailable, or the picker
153
+ * errored), we fall back to FileSaver's `saveAs()`, which resolves immediately.
154
+ * @param {Blob} blob - The file contents
155
+ * @param {string} filename - The suggested filename (used by the saveAs fallback)
156
+ * @param {ExportFormat} format - The selected export format (drives MIME type)
157
+ * @param {FileSystemFileHandle | null} fileHandle - Handle from the Save picker, or null
158
+ * @return {Promise<{ saved: boolean }>} Resolves with `saved: true` once written.
159
+ * @memberof RosterExportService
160
+ */
161
+ downloadFile(blob: Blob, filename: string, format: ExportFormat, fileHandle: any): Promise<{
162
+ saved: boolean;
163
+ }>;
164
+ /**
165
+ * Cleanup — call from the component's ngOnDestroy.
166
+ * @memberof RosterExportService
167
+ */
168
+ destroy(): void;
169
+ static ɵfac: i0.ɵɵFactoryDeclaration<RosterExportService, never>;
170
+ static ɵprov: i0.ɵɵInjectableDeclaration<RosterExportService>;
171
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ieeesa-npm/groups",
3
- "version": "0.15.3",
3
+ "version": "0.15.4",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^16.1.0",
6
6
  "@angular/core": "^16.1.0"
package/public-api.d.ts CHANGED
@@ -24,5 +24,6 @@ export * from './lib/models/table-column-schema-user-group.model';
24
24
  export * from './lib/models/parent-groups-info.model';
25
25
  export * from './lib/models/reciprocal-credit.model';
26
26
  export * from './lib/services/groups.service';
27
+ export * from './lib/services/roster-export.service';
27
28
  export * from './lib/assets/form-validations';
28
29
  export * from './lib/groups.module';