@ieeesa-npm/groups 0.15.7 → 0.15.8

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.
@@ -0,0 +1,27 @@
1
+ import { RosterImportMockScenario, RosterUploadError, RosterUploadResponse } from '../models/roster-import.model';
2
+ /**
3
+ * Mock responses for the roster import flow, used to build and exercise the UI
4
+ * before the real backend API is available. Each scenario maps to one of the
5
+ * Figma result screens.
6
+ *
7
+ * Once the API is wired up, `RosterImportComponent` will call the real service
8
+ * method instead of `RosterImportMocks.getMockResponse(...)`.
9
+ */
10
+ export declare class RosterImportMocks {
11
+ /** Row errors mirroring the Figma "All Rows are skipped" screen. */
12
+ static readonly mockRowErrors: RosterUploadError[];
13
+ /** All 30 rows added successfully (Figma: 30 / 0). */
14
+ static readonly allSuccess: RosterUploadResponse;
15
+ /** Partial import: some added, some skipped. */
16
+ static readonly partial: RosterUploadResponse;
17
+ /** Every row skipped (Figma: 0 / 30, "All Rows are skipped"). */
18
+ static readonly allSkipped: RosterUploadResponse;
19
+ /** Headers only, zero data rows (Figma: "No roster data found"). */
20
+ static readonly empty: RosterUploadResponse;
21
+ /**
22
+ * Returns the mock upload response for a given scenario.
23
+ * @param {RosterImportMockScenario} scenario - The scenario to simulate.
24
+ * @return {RosterUploadResponse}
25
+ */
26
+ static getMockResponse(scenario: RosterImportMockScenario): RosterUploadResponse;
27
+ }
@@ -0,0 +1,179 @@
1
+ import { EventEmitter, OnDestroy } from '@angular/core';
2
+ import { Subject } from 'rxjs';
3
+ import { RosterUploadError, RosterUploadResponse } from '../../models/roster-import.model';
4
+ import * as i0 from "@angular/core";
5
+ /** States of the roster import flow. */
6
+ type RosterImportState = 'upload' | 'processing' | 'result';
7
+ /**
8
+ * Roster Import dialog (MOCK data phase).
9
+ *
10
+ * Lets an admin upload a CSV/XLS/XLSX roster file, shows a processing progress
11
+ * bar, then a result summary (added / skipped) with per-row errors and an
12
+ * error-log download. Mirrors the events-and-meetings bulk-import-participants
13
+ * flow. The real backend is not wired yet — {@link onImport} resolves a mock
14
+ * response after a short delay so every Figma state can be exercised.
15
+ *
16
+ * @export
17
+ * @class RosterImportComponent
18
+ */
19
+ export declare class RosterImportComponent implements OnDestroy {
20
+ /** Carries the { groupId, groupName } payload from the opening dialog. */
21
+ componentData: any;
22
+ /** Emitted after an import that added at least one row (parent refreshes). */
23
+ submitFormResponse: EventEmitter<any>;
24
+ /** Emitted when the user cancels/closes without a successful add. */
25
+ formCancel: EventEmitter<void>;
26
+ destroy$: Subject<boolean>;
27
+ constants: any;
28
+ screenTexts: any;
29
+ /** Current state of the import flow. */
30
+ importState: RosterImportState;
31
+ /** The selected file. */
32
+ selectedFile: File | null;
33
+ /** File drag-over state. */
34
+ isDragOver: boolean;
35
+ /** Processing progress percentage (0–100) shown next to the progress bar. */
36
+ progressValue: number;
37
+ /** Terminal upload result (mock). */
38
+ uploadResult: RosterUploadResponse | null;
39
+ /** Row-level errors for the completed job. */
40
+ jobErrors: RosterUploadError[];
41
+ /** Inline validation / API error message. */
42
+ errorMessage: string | null;
43
+ /**
44
+ * Info banner shown when the file has headers but no data rows
45
+ * (Figma: "No roster data found...").
46
+ */
47
+ noDataMessage: string | null;
48
+ /** Maximum file size in bytes (5 MB). */
49
+ readonly MAX_FILE_SIZE = 5242880;
50
+ /** Accepted file extensions. */
51
+ readonly ACCEPTED_EXTENSIONS: string[];
52
+ /**
53
+ * MOCK ONLY: which scenario the next import simulates. Cycles through the
54
+ * scenarios so QA can see every result state by importing repeatedly. Remove
55
+ * when the real API is wired in.
56
+ */
57
+ private mockScenarios;
58
+ private mockScenarioIndex;
59
+ /** Required column headers for display. */
60
+ get requiredColumns(): string[];
61
+ /** Comma-separated accepted file types for the input element. */
62
+ get acceptedFileTypes(): string;
63
+ /**
64
+ * Cleans up subscriptions when the component is destroyed.
65
+ * @memberof RosterImportComponent
66
+ */
67
+ ngOnDestroy(): void;
68
+ /**
69
+ * Handles a file selected via the browse dialog.
70
+ * @param {Event} event - The change event from the hidden file input.
71
+ * @memberof RosterImportComponent
72
+ */
73
+ onFileSelected(event: Event): void;
74
+ /**
75
+ * Handles a file dropped onto the dropzone.
76
+ * @param {DragEvent} event - The drop event.
77
+ * @memberof RosterImportComponent
78
+ */
79
+ onFileDrop(event: DragEvent): void;
80
+ /**
81
+ * Handles drag-over on the dropzone.
82
+ * @param {DragEvent} event - The dragover event.
83
+ * @memberof RosterImportComponent
84
+ */
85
+ onDragOver(event: DragEvent): void;
86
+ /**
87
+ * Handles drag-leave on the dropzone.
88
+ * @param {DragEvent} event - The dragleave event.
89
+ * @memberof RosterImportComponent
90
+ */
91
+ onDragLeave(event: DragEvent): void;
92
+ /**
93
+ * Validates the file type and size, then either sets it or shows an error.
94
+ * @param {File} file - The candidate file.
95
+ * @memberof RosterImportComponent
96
+ */
97
+ processFile(file: File): void;
98
+ /**
99
+ * Shows the single-file-only validation message.
100
+ * @memberof RosterImportComponent
101
+ */
102
+ showSingleFileOnlyError(): void;
103
+ /**
104
+ * Removes the currently selected file and resets to the upload state.
105
+ * @memberof RosterImportComponent
106
+ */
107
+ onRemoveFile(): void;
108
+ /**
109
+ * MOCK: "uploads" the file by simulating a short processing delay, then
110
+ * resolves the next mock scenario. Replace the timer + mock lookup with the
111
+ * real service call once the API is available.
112
+ * @memberof RosterImportComponent
113
+ */
114
+ onImport(): void;
115
+ /**
116
+ * Stores the terminal result and moves to the result state. When the file
117
+ * had no data rows, shows the "no roster data" info banner instead of the
118
+ * summary cards.
119
+ * @param {RosterUploadResponse} body - The terminal (mock) result.
120
+ * @memberof RosterImportComponent
121
+ */
122
+ finalizeResult(body: RosterUploadResponse): void;
123
+ /**
124
+ * Whether the result state should show the summary cards (added / skipped).
125
+ * Hidden for the "no data records" case.
126
+ * @return {boolean}
127
+ * @memberof RosterImportComponent
128
+ */
129
+ get showSummaryCards(): boolean;
130
+ /**
131
+ * Whether every processed row was skipped (drives the "All Rows are skipped"
132
+ * header on the errors card).
133
+ * @return {boolean}
134
+ * @memberof RosterImportComponent
135
+ */
136
+ get allRowsSkipped(): boolean;
137
+ /**
138
+ * Builds a client-side CSV sample file from the required columns and triggers
139
+ * a browser download. (Client-side generation until the template API exists.)
140
+ * @memberof RosterImportComponent
141
+ */
142
+ downloadSampleFile(): void;
143
+ /**
144
+ * Builds a CSV from the row-level errors and triggers a browser download so
145
+ * the user can correct and re-upload.
146
+ * @memberof RosterImportComponent
147
+ */
148
+ downloadErrorLog(): void;
149
+ /**
150
+ * Escapes a value for safe inclusion in a CSV cell.
151
+ * @param {(string | number | undefined | null)} value - The raw cell value.
152
+ * @return {string} - the CSV-safe cell string.
153
+ * @memberof RosterImportComponent
154
+ */
155
+ toCsvCell(value: string | number | undefined | null): string;
156
+ /**
157
+ * Closes the dialog. Emits submitFormResponse when at least one row was added
158
+ * so the parent refreshes; otherwise emits formCancel.
159
+ * @memberof RosterImportComponent
160
+ */
161
+ onCancel(): void;
162
+ /**
163
+ * Triggers a browser download for a generated blob.
164
+ * @param {Blob} blob - The file content.
165
+ * @param {string} fileName - The suggested filename.
166
+ * @memberof RosterImportComponent
167
+ */
168
+ private triggerDownload;
169
+ /**
170
+ * MOCK ONLY: returns the next scenario in the cycle so repeated imports show
171
+ * every result state.
172
+ * @return {RosterImportMockScenario}
173
+ * @memberof RosterImportComponent
174
+ */
175
+ private nextMockScenario;
176
+ static ɵfac: i0.ɵɵFactoryDeclaration<RosterImportComponent, never>;
177
+ static ɵcmp: i0.ɵɵComponentDeclaration<RosterImportComponent, "ieeesa-roster-import", never, { "componentData": { "alias": "componentData"; "required": false; }; }, { "submitFormResponse": "submitFormResponse"; "formCancel": "formCancel"; }, never, never, true, never>;
178
+ }
179
+ export {};
@@ -1,6 +1,7 @@
1
- import { AfterViewInit, ChangeDetectorRef, OnDestroy, OnInit } from '@angular/core';
1
+ import { AfterViewInit, ChangeDetectorRef, ElementRef, OnDestroy, OnInit } from '@angular/core';
2
2
  import { Subject } from 'rxjs';
3
3
  import { Sort } from '@angular/material/sort';
4
+ import { MatMenuTrigger } from '@angular/material/menu';
4
5
  import { MatDialog } from '@angular/material/dialog';
5
6
  import { TableColumnSchema, TableActions, AlignType, ChipOption, CheckboxOption, FiltersAndOptions, ExportFormat } from '@ieeesa-npm/presentation';
6
7
  import { AlertsService, GlobalErrorHandlerService } from '@ieeesa-npm/utility';
@@ -26,6 +27,10 @@ export declare class ViewRosterComponent implements AfterViewInit, OnDestroy, On
26
27
  private globalErrorHandlerService;
27
28
  rosterExportService: RosterExportService;
28
29
  destroy$: Subject<boolean>;
30
+ /** Trigger for the "Add" menu (Add Manually / Bulk Import). */
31
+ addMenuTrigger?: MatMenuTrigger;
32
+ /** Invisible anchor element the "Add" menu attaches to. */
33
+ addMenuAnchor?: ElementRef<HTMLElement>;
29
34
  constants: any;
30
35
  viewRosterConstants: any;
31
36
  dialogRef: any;
@@ -149,10 +154,40 @@ export declare class ViewRosterComponent implements AfterViewInit, OnDestroy, On
149
154
  */
150
155
  onEditUserRoles(selectedUserInfo: GroupUserInfo[]): void;
151
156
  /**
152
- * Emits openModalNotifier event with GroupFunctionType value as ADD_USER_TO_ROSTER
157
+ * Handles a click on the data-table's add-user icon. The add icon lives inside
158
+ * the shared DataTableComponent and only emits (addRow), so we can't attach a
159
+ * menu trigger to it directly. Instead we position an invisible, fixed-position
160
+ * anchor at the add icon's on-screen location, then open the menu from that
161
+ * anchor — so the menu appears directly beneath the add icon (matching the
162
+ * Figma) rather than at the far-right export area.
153
163
  * @memberof ViewRosterComponent
154
164
  */
155
- onAddNewUser(): void;
165
+ onAddIconClick(): void;
166
+ /**
167
+ * Closes the "Add" menu on window resize/scroll. The menu is anchored to a
168
+ * point measured at open time, so a layout change (resize/scroll) would leave
169
+ * it detached from the add icon; closing it avoids showing it in a stale spot.
170
+ * The user can simply click the add icon again.
171
+ * @memberof ViewRosterComponent
172
+ */
173
+ closeAddMenuOnViewportChange(): void;
174
+ /**
175
+ * Places the add-menu anchor at the add-user icon's bottom edge. Locates the
176
+ * icon by finding the add-user sprite (icon-add-user) within the roster's
177
+ * data-table action row.
178
+ * @memberof ViewRosterComponent
179
+ */
180
+ private positionAddMenuAnchorAtAddIcon;
181
+ /**
182
+ * "Add Manually" menu option — opens the existing add-user-to-roster flow.
183
+ * @memberof ViewRosterComponent
184
+ */
185
+ onAddManually(): void;
186
+ /**
187
+ * "Bulk Import" menu option — opens the bulk roster import dialog.
188
+ * @memberof ViewRosterComponent
189
+ */
190
+ onImportRoster(): void;
156
191
  /**
157
192
  * Handles alerts for success, error, warning.
158
193
  * @param {(string | string[])} message -error or warning message
@@ -109,6 +109,14 @@ export declare class GroupsComponent implements OnInit, OnDestroy {
109
109
  * @memberof GroupsComponent
110
110
  */
111
111
  openAddUserRosterModal(selectedGroupInfo: GroupInfo): void;
112
+ /**
113
+ * Sets the dialog options for the bulk roster import modal and opens it.
114
+ * Passes the selected group's id/name to the dynamically-instantiated
115
+ * RosterImportComponent via the ModalComponent dynamic-input pattern.
116
+ * @param {GroupInfo} selectedGroupInfo
117
+ * @memberof GroupsComponent
118
+ */
119
+ openRosterImportModal(selectedGroupInfo: GroupInfo): void;
112
120
  /**
113
121
  * Sets the dialog options for users modal and calls openModal method.
114
122
  * @param {GroupInfo} group
@@ -7,5 +7,6 @@ export declare enum GroupFunctionType {
7
7
  SEARCH = "SEARCH",
8
8
  REASSIGN_PARENT_GROUP = "REASSIGN_PARENT_GROUP",
9
9
  EDIT_ROSTER_USER_ROLE = "EDIT_ROSTER_USER_ROLE",
10
- ADD_USER_TO_ROSTER = "ADD_USER_TO_ROSTER"
10
+ ADD_USER_TO_ROSTER = "ADD_USER_TO_ROSTER",
11
+ IMPORT_ROSTER = "IMPORT_ROSTER"
11
12
  }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Terminal status of a roster bulk-upload job.
3
+ */
4
+ export declare enum RosterUploadStatus {
5
+ PROCESSING = "PROCESSING",
6
+ COMPLETED = "COMPLETED",
7
+ FAILED = "FAILED"
8
+ }
9
+ /**
10
+ * A single row-level error from the roster import validation.
11
+ */
12
+ export interface RosterUploadError {
13
+ /** 1-based row number in the uploaded file. */
14
+ rowNumber: number;
15
+ /** The field/column the error relates to (e.g. "Name", "Email"). */
16
+ field?: string;
17
+ /** The offending value, when relevant (e.g. an unrecognized role). */
18
+ value?: string;
19
+ /** Human-readable error message shown in the row-errors list. */
20
+ errorMessage: string;
21
+ }
22
+ /**
23
+ * Terminal result of a roster bulk-upload job.
24
+ *
25
+ * NOTE: This is a MOCK-friendly shape used to build the UI before the real API
26
+ * is available. It will be reconciled with the backend contract once shared.
27
+ */
28
+ export interface RosterUploadResponse {
29
+ /** Job identifier (unused by the mock flow, kept for parity with the API). */
30
+ uploadJobId?: string;
31
+ /** Terminal job status. */
32
+ status: RosterUploadStatus;
33
+ /** Total data rows detected in the file (excludes the header row). */
34
+ totalRecords: number;
35
+ /** Rows successfully added/updated. */
36
+ successCount: number;
37
+ /** Rows skipped due to validation errors. */
38
+ failureCount: number;
39
+ /** Row-level errors (present when failureCount > 0). */
40
+ rowErrors: RosterUploadError[];
41
+ }
42
+ /**
43
+ * The kind of mock scenario to simulate, so the UI can be exercised against
44
+ * each Figma state without a backend.
45
+ */
46
+ export declare enum RosterImportMockScenario {
47
+ /** All rows imported successfully (Figma: 30 added / 0 skipped). */
48
+ ALL_SUCCESS = "ALL_SUCCESS",
49
+ /** Some rows imported, some skipped with errors (partial). */
50
+ PARTIAL = "PARTIAL",
51
+ /** Every row skipped with errors (Figma: 0 added / 30 skipped). */
52
+ ALL_SKIPPED = "ALL_SKIPPED",
53
+ /** File contained headers but zero data rows (Figma: "No roster data found"). */
54
+ EMPTY = "EMPTY"
55
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ieeesa-npm/groups",
3
- "version": "0.15.7",
3
+ "version": "0.15.8",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^16.1.0",
6
6
  "@angular/core": "^16.1.0"
package/public-api.d.ts CHANGED
@@ -3,6 +3,7 @@ export * from './lib/components/tree-view/tree-view.component';
3
3
  export * from './lib/components/edit-user-roles/edit-user-roles.component';
4
4
  export * from './lib/components/reciprocal-credit-section/reciprocal-credit-section.component';
5
5
  export * from './lib/components/add-working-groups/add-working-groups.component';
6
+ export * from './lib/components/roster-import/roster-import.component';
6
7
  export * from './lib/groups.component';
7
8
  export * from './lib/models/create-group.model';
8
9
  export * from './lib/models/group-action.model';
@@ -25,5 +26,6 @@ export * from './lib/models/parent-groups-info.model';
25
26
  export * from './lib/models/reciprocal-credit.model';
26
27
  export * from './lib/services/groups.service';
27
28
  export * from './lib/services/roster-export.service';
29
+ export * from './lib/models/roster-import.model';
28
30
  export * from './lib/assets/form-validations';
29
31
  export * from './lib/groups.module';