@ieeesa-npm/groups 0.15.9 → 0.15.10

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.
Files changed (35) hide show
  1. package/esm2022/lib/assets/constants.json +75 -4
  2. package/esm2022/lib/components/interest-form/interest-form.component.mjs +104 -0
  3. package/esm2022/lib/components/interest-form-management/interest-form-management.component.mjs +63 -0
  4. package/esm2022/lib/components/interested-rosters/interested-rosters.component.mjs +28 -0
  5. package/esm2022/lib/components/roster-import/roster-import.component.mjs +149 -82
  6. package/esm2022/lib/components/roster-import-history/roster-import-history.component.mjs +232 -0
  7. package/esm2022/lib/components/tree-view/tree-view.component.mjs +26 -5
  8. package/esm2022/lib/components/view-roster/view-roster.component.mjs +10 -3
  9. package/esm2022/lib/groups.component.mjs +60 -6
  10. package/esm2022/lib/models/group-action.model.mjs +2 -1
  11. package/esm2022/lib/models/group-function-type.model.mjs +2 -1
  12. package/esm2022/lib/models/permission-type.model.mjs +2 -1
  13. package/esm2022/lib/models/roster-import.model.mjs +34 -17
  14. package/esm2022/lib/services/roster-import-api.service.mjs +174 -0
  15. package/esm2022/public-api.mjs +6 -1
  16. package/fesm2022/ieeesa-npm-groups.mjs +930 -211
  17. package/fesm2022/ieeesa-npm-groups.mjs.map +1 -1
  18. package/lib/components/interest-form/interest-form.component.d.ts +75 -0
  19. package/lib/components/interest-form-management/interest-form-management.component.d.ts +31 -0
  20. package/lib/components/interested-rosters/interested-rosters.component.d.ts +17 -0
  21. package/lib/components/roster-import/roster-import.component.d.ts +77 -38
  22. package/lib/components/roster-import-history/roster-import-history.component.d.ts +102 -0
  23. package/lib/components/tree-view/tree-view.component.d.ts +6 -3
  24. package/lib/components/view-roster/view-roster.component.d.ts +5 -0
  25. package/lib/groups.component.d.ts +17 -0
  26. package/lib/models/group-action.model.d.ts +2 -1
  27. package/lib/models/group-function-type.model.d.ts +2 -1
  28. package/lib/models/permission-type.model.d.ts +2 -1
  29. package/lib/models/roster-import.model.d.ts +104 -21
  30. package/lib/services/groups.service.d.ts +4 -0
  31. package/lib/services/roster-import-api.service.d.ts +108 -0
  32. package/package.json +1 -1
  33. package/public-api.d.ts +5 -0
  34. package/esm2022/lib/assets/roster-import-mocks.mjs +0 -98
  35. package/lib/assets/roster-import-mocks.d.ts +0 -27
@@ -0,0 +1,75 @@
1
+ import { OnInit } from '@angular/core';
2
+ import { MatDialogRef } from '@angular/material/dialog';
3
+ import { RadioOption, SlideToggleOption } from '@ieeesa-npm/presentation';
4
+ import * as i0 from "@angular/core";
5
+ /**
6
+ * Standard (locked) question row shown in the Interest Form tab.
7
+ * @export
8
+ * @interface StandardQuestionRow
9
+ */
10
+ export interface StandardQuestionRow {
11
+ id: number;
12
+ label: string;
13
+ tag: string;
14
+ }
15
+ /**
16
+ * "Interest Form" tab content of the Roster Interests dialog (SP3M-317). The
17
+ * group admin configures the interest form here: enable/disable the form, view
18
+ * the locked standard questions, choose the default membership level and manage
19
+ * custom questions. Interactive controls reuse the shared presentation
20
+ * components (slide toggle, radio). Persistence via a mocked service and the
21
+ * custom-question builder will be added in subsequent increments.
22
+ * @export
23
+ * @class InterestFormComponent
24
+ */
25
+ export declare class InterestFormComponent implements OnInit {
26
+ private dialogRef;
27
+ constants: any;
28
+ interestFormTexts: any;
29
+ changesNotice: any;
30
+ sectionTitle: any;
31
+ sectionDescription: any;
32
+ standardQuestionsTitle: any;
33
+ standardQuestionsHint: any;
34
+ defaultMembershipTitle: any;
35
+ defaultMembershipDescription: any;
36
+ customQuestionsTitle: any;
37
+ addQuestionLabel: any;
38
+ standardTag: any;
39
+ cancelLabel: any;
40
+ previewLabel: any;
41
+ enableFormToggle: SlideToggleOption;
42
+ standardQuestions: StandardQuestionRow[];
43
+ membershipLevels: RadioOption[];
44
+ isFormEnabled: boolean;
45
+ constructor(dialogRef: MatDialogRef<unknown>);
46
+ /**
47
+ * Initializes the toggle, locked standard questions and default membership
48
+ * level options from constants.
49
+ * @memberof InterestFormComponent
50
+ */
51
+ ngOnInit(): void;
52
+ /**
53
+ * Updates the form-enabled state when the toggle changes so the dependent
54
+ * sections show or hide accordingly.
55
+ * @param {SlideToggleOption} toggle
56
+ * @memberof InterestFormComponent
57
+ */
58
+ onEnableFormToggle(toggle: SlideToggleOption): void;
59
+ /**
60
+ * Closes the Roster Interests dialog (Cancel action).
61
+ * @memberof InterestFormComponent
62
+ */
63
+ onCancel(): void;
64
+ /**
65
+ * Returns the unique identifier for a standard question row for ngFor
66
+ * tracking.
67
+ * @param {number} index
68
+ * @param {StandardQuestionRow} question
69
+ * @return {number}
70
+ * @memberof InterestFormComponent
71
+ */
72
+ trackByFn(index: number, question: StandardQuestionRow): number;
73
+ static ɵfac: i0.ɵɵFactoryDeclaration<InterestFormComponent, [{ optional: true; }]>;
74
+ static ɵcmp: i0.ɵɵComponentDeclaration<InterestFormComponent, "ieeesa-interest-form", never, {}, {}, never, never, true, never>;
75
+ }
@@ -0,0 +1,31 @@
1
+ import { MatDialogRef } from '@angular/material/dialog';
2
+ import { Tab } from '@ieeesa-npm/presentation';
3
+ import { GroupInfo } from '../../models/group-info.model';
4
+ import * as i0 from "@angular/core";
5
+ /**
6
+ * Roster Interests dialog host (SP3M-317). Opened from the "Manage Interest
7
+ * Form" action on a group row. Renders a two-tab layout using the shared
8
+ * TabsComponent: "Interested Rosters" (submissions list) and "Interest Form"
9
+ * (admin configuration). The group being managed is provided via the dialog
10
+ * data. The tab content components are rendered dynamically by TabsComponent
11
+ * through each tab's componentRef.
12
+ * @export
13
+ * @class InterestFormManagementComponent
14
+ */
15
+ export declare class InterestFormManagementComponent {
16
+ dialogRef: MatDialogRef<InterestFormManagementComponent>;
17
+ group: GroupInfo;
18
+ constants: any;
19
+ rosterInterestsTexts: any;
20
+ heading: any;
21
+ closeAriaLabel: any;
22
+ tabs: Tab[];
23
+ constructor(dialogRef: MatDialogRef<InterestFormManagementComponent>, group: GroupInfo);
24
+ /**
25
+ * Closes the Roster Interests dialog.
26
+ * @memberof InterestFormManagementComponent
27
+ */
28
+ onClose(): void;
29
+ static ɵfac: i0.ɵɵFactoryDeclaration<InterestFormManagementComponent, never>;
30
+ static ɵcmp: i0.ɵɵComponentDeclaration<InterestFormManagementComponent, "ieeesa-interest-form-management", never, {}, {}, never, never, true, never>;
31
+ }
@@ -0,0 +1,17 @@
1
+ import * as i0 from "@angular/core";
2
+ /**
3
+ * "Interested Rosters" tab content of the Roster Interests dialog (SP3M-317).
4
+ * Displays the list of users who expressed interest in the group. This is a
5
+ * placeholder shell for now; the submissions table will be implemented in a
6
+ * later increment once the backend is available (data will be mocked in the
7
+ * service layer meanwhile).
8
+ * @export
9
+ * @class InterestedRostersComponent
10
+ */
11
+ export declare class InterestedRostersComponent {
12
+ constants: any;
13
+ interestedRostersTexts: any;
14
+ placeholderMessage: any;
15
+ static ɵfac: i0.ɵɵFactoryDeclaration<InterestedRostersComponent, never>;
16
+ static ɵcmp: i0.ɵɵComponentDeclaration<InterestedRostersComponent, "ieeesa-interested-rosters", never, {}, {}, never, never, true, never>;
17
+ }
@@ -1,22 +1,46 @@
1
- import { EventEmitter, OnDestroy } from '@angular/core';
1
+ import { EventEmitter, OnDestroy, OnInit } from '@angular/core';
2
2
  import { Subject } from 'rxjs';
3
- import { RosterUploadError, RosterUploadResponse } from '../../models/roster-import.model';
3
+ import { RosterImportJob } from '../../models/roster-import.model';
4
+ import { RosterImportApiService } from '../../services/roster-import-api.service';
5
+ import { GroupsService } from '../../services/groups.service';
4
6
  import * as i0 from "@angular/core";
5
7
  /** States of the roster import flow. */
6
8
  type RosterImportState = 'upload' | 'processing' | 'result';
7
9
  /**
8
- * Roster Import dialog (MOCK data phase).
10
+ * View-model for the result screen. Populated from the confirmed
11
+ * {@link RosterImportJob} returned by the backend.
12
+ */
13
+ interface RosterImportResult {
14
+ totalRecords: number;
15
+ successCount: number;
16
+ failureCount: number;
17
+ }
18
+ /** Row error shown on the result screen (mapped from the BE error report). */
19
+ interface RosterImportRowError {
20
+ rowNumber: number;
21
+ field?: string;
22
+ errorMessage: string;
23
+ }
24
+ /**
25
+ * Roster Import dialog.
9
26
  *
10
- * Lets an admin upload a CSV/XLS/XLSX roster file, shows a processing progress
27
+ * Lets an admin upload a CSV/XLSX roster file, shows a processing progress
11
28
  * 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.
29
+ * error-log download.
30
+ *
31
+ * Wired to the real backend roster-import state machine
32
+ * (sa-saapps-groups-api `RosterImportController`) via
33
+ * {@link RosterImportApiService}: on Import it runs
34
+ * upload → validate → confirm and renders the confirmed job's terminal counts
35
+ * and per-row errors. Server-side validation errors (1072 unsupported format,
36
+ * 1073 too large, 1074 row limit, 1075 invalid status) are surfaced inline.
15
37
  *
16
38
  * @export
17
39
  * @class RosterImportComponent
18
40
  */
19
- export declare class RosterImportComponent implements OnDestroy {
41
+ export declare class RosterImportComponent implements OnInit, OnDestroy {
42
+ private rosterImportApiService;
43
+ private groupService;
20
44
  /** Carries the { groupId, groupName } payload from the opening dialog. */
21
45
  componentData: any;
22
46
  /** Emitted after an import that added at least one row (parent refreshes). */
@@ -32,12 +56,10 @@ export declare class RosterImportComponent implements OnDestroy {
32
56
  selectedFile: File | null;
33
57
  /** File drag-over state. */
34
58
  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;
59
+ /** Terminal upload result. */
60
+ uploadResult: RosterImportResult | null;
39
61
  /** Row-level errors for the completed job. */
40
- jobErrors: RosterUploadError[];
62
+ jobErrors: RosterImportRowError[];
41
63
  /** Inline validation / API error message. */
42
64
  errorMessage: string | null;
43
65
  /**
@@ -45,21 +67,24 @@ export declare class RosterImportComponent implements OnDestroy {
45
67
  * (Figma: "No roster data found...").
46
68
  */
47
69
  noDataMessage: string | null;
48
- /** Maximum file size in bytes (5 MB). */
49
- readonly MAX_FILE_SIZE = 5242880;
50
- /** Accepted file extensions. */
70
+ /** The import job id returned by upload (kept so cancel can clean it up). */
71
+ private importId;
72
+ /** Maximum file size in bytes (10 MB) — matches the BE 10MB limit. */
73
+ readonly MAX_FILE_SIZE = 10485760;
74
+ /** Accepted file extensions (must match the BE contract: only .csv / .xlsx). */
51
75
  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;
76
+ constructor(rosterImportApiService: RosterImportApiService, groupService: GroupsService);
59
77
  /** Required column headers for display. */
60
78
  get requiredColumns(): string[];
61
79
  /** Comma-separated accepted file types for the input element. */
62
80
  get acceptedFileTypes(): string;
81
+ /** The target group id from the dialog payload (falls back to selection). */
82
+ private get groupId();
83
+ /**
84
+ * Sets the API base URL on the import service.
85
+ * @memberof RosterImportComponent
86
+ */
87
+ ngOnInit(): void;
63
88
  /**
64
89
  * Cleans up subscriptions when the component is destroyed.
65
90
  * @memberof RosterImportComponent
@@ -106,20 +131,41 @@ export declare class RosterImportComponent implements OnDestroy {
106
131
  */
107
132
  onRemoveFile(): void;
108
133
  /**
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.
134
+ * Runs the real import: upload → validate → confirm. The progress bar is
135
+ * shown in indeterminate mode while the chain is in flight, then the
136
+ * confirmed job's terminal counts + per-row errors drive the result screen.
112
137
  * @memberof RosterImportComponent
113
138
  */
114
139
  onImport(): void;
115
140
  /**
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.
141
+ * Maps a failed import call to an inline error message and returns the UI to
142
+ * the upload state so the user can retry. Known BE error codes (1072–1075)
143
+ * are mapped via constants; anything else uses the generic message.
144
+ * @param {*} error - The HttpErrorResponse.
120
145
  * @memberof RosterImportComponent
121
146
  */
122
- finalizeResult(body: RosterUploadResponse): void;
147
+ private handleImportError;
148
+ /**
149
+ * Stores the terminal result and moves to the result state. When the job had
150
+ * no data rows, shows the "no roster data" info banner instead of the summary
151
+ * cards.
152
+ * @param {RosterImportJob} job - The confirmed import job.
153
+ * @memberof RosterImportComponent
154
+ */
155
+ finalizeResult(job: RosterImportJob): void;
156
+ /**
157
+ * Parses the BE `errorReport` raw JSON string into row-error view models.
158
+ *
159
+ * The BE stores the report as a JSONB document wrapping the per-row entries:
160
+ * `{ "rows": [ { "row": 2, "field": "Email", "message": "..." } ] }`
161
+ * (serialized by its `ErrorReportWrapper`) and returns it verbatim via
162
+ * `@JsonRawValue`. So we read `parsed.rows`. A bare top-level array is also
163
+ * accepted as a fallback in case the contract is ever flattened.
164
+ * @param {(string | undefined)} errorReport - Raw JSON error report string.
165
+ * @return {RosterImportRowError[]}
166
+ * @memberof RosterImportComponent
167
+ */
168
+ private parseErrorReport;
123
169
  /**
124
170
  * Whether the result state should show the summary cards (added / skipped).
125
171
  * Hidden for the "no data records" case.
@@ -166,13 +212,6 @@ export declare class RosterImportComponent implements OnDestroy {
166
212
  * @memberof RosterImportComponent
167
213
  */
168
214
  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
215
  static ɵfac: i0.ɵɵFactoryDeclaration<RosterImportComponent, never>;
177
216
  static ɵcmp: i0.ɵɵComponentDeclaration<RosterImportComponent, "ieeesa-roster-import", never, { "componentData": { "alias": "componentData"; "required": false; }; }, { "submitFormResponse": "submitFormResponse"; "formCancel": "formCancel"; }, never, never, true, never>;
178
217
  }
@@ -0,0 +1,102 @@
1
+ import { ChangeDetectorRef, OnDestroy, OnInit } from '@angular/core';
2
+ import { TableActions, TableColumnSchema } from '@ieeesa-npm/presentation';
3
+ import { GlobalErrorHandlerService } from '@ieeesa-npm/utility';
4
+ import { RosterImportApiService } from '../../services/roster-import-api.service';
5
+ import { GroupsService } from '../../services/groups.service';
6
+ import * as i0 from "@angular/core";
7
+ /** A single row shown in the "Imported Files" table. */
8
+ interface RosterImportHistoryRow {
9
+ timestamp: string;
10
+ personImported: string;
11
+ fileName: string;
12
+ addedRows: number | string;
13
+ errors: number | string;
14
+ targetGroup: string;
15
+ }
16
+ /**
17
+ * Roster import-history ("View History") dialog body.
18
+ *
19
+ * Rendered inside the shared ModalComponent (which supplies the
20
+ * "← Back to Rosters / View History" breadcrumb title row + X close). Shows the
21
+ * selected group's name and an "Imported Files" table listing prior roster
22
+ * imports.
23
+ *
24
+ * Data comes from the real BE endpoint via {@link RosterImportApiService}:
25
+ * `GET /v1/groups/roster/import/history/{groupId}`. The response is a list of
26
+ * `RosterImportJob` whose JSON view exposes `fileName`, `successRows`,
27
+ * `failedRows`, `status` and ids — but NOT `createdOn` / `createdBy` (both are
28
+ * `@JsonIgnore` on the BE entity). So the "Timestamp" and "Person Imported"
29
+ * columns render a placeholder until the BE exposes those fields. "Target
30
+ * Group" uses the current group's name (history is scoped to one group).
31
+ *
32
+ * @export
33
+ * @class RosterImportHistoryComponent
34
+ */
35
+ export declare class RosterImportHistoryComponent implements OnInit, OnDestroy {
36
+ private rosterImportApiService;
37
+ private groupService;
38
+ private cdr;
39
+ private globalErrorHandlerService;
40
+ /** Carries the { groupId, groupName } payload from the opening dialog. */
41
+ componentData: any;
42
+ constants: any;
43
+ screenTexts: any;
44
+ private destroy$;
45
+ /** Table column definitions for the "Imported Files" table. */
46
+ columnSchema: TableColumnSchema[];
47
+ /** Import-history rows mapped for the table. */
48
+ tableData: RosterImportHistoryRow[];
49
+ /** True while the history request is in flight. */
50
+ isLoading: boolean;
51
+ /** Inline error message when the history request fails. */
52
+ errorMessage: string | null;
53
+ /** Read-only table — no search / filter / add / edit / delete actions. */
54
+ tableActions: TableActions;
55
+ /** Placeholder shown for fields the BE does not return (timestamp / person). */
56
+ private readonly placeholder;
57
+ constructor(rosterImportApiService: RosterImportApiService, groupService: GroupsService, cdr: ChangeDetectorRef, globalErrorHandlerService: GlobalErrorHandlerService);
58
+ /** The group name shown as the heading above the Imported Files table. */
59
+ get groupName(): string;
60
+ /** The group id whose history is fetched. */
61
+ private get groupId();
62
+ /**
63
+ * Builds the table schema and loads the import history from the BE.
64
+ * @memberof RosterImportHistoryComponent
65
+ */
66
+ ngOnInit(): void;
67
+ /**
68
+ * Cleans up subscriptions when the component is destroyed.
69
+ * @memberof RosterImportHistoryComponent
70
+ */
71
+ ngOnDestroy(): void;
72
+ /**
73
+ * Fetches the import history for the current group and maps it to table rows.
74
+ * @memberof RosterImportHistoryComponent
75
+ */
76
+ loadImportHistory(): void;
77
+ /**
78
+ * Maps a BE import job to a table row. Timestamp and Person Imported are not
79
+ * present in the response (BE `@JsonIgnore`), so they render a placeholder.
80
+ * Target Group uses the current group's name (history is per-group).
81
+ * @param {RosterImportJob} job - The BE import job.
82
+ * @return {RosterImportHistoryRow}
83
+ * @memberof RosterImportHistoryComponent
84
+ */
85
+ private mapJobToRow;
86
+ /**
87
+ * Maps a failed history request to a user-facing inline error message.
88
+ * A 403 gets the permission-specific message; other errors get the generic
89
+ * one and are forwarded to the global handler for logging/interceptors.
90
+ * @param {*} error - The HttpErrorResponse.
91
+ * @memberof RosterImportHistoryComponent
92
+ */
93
+ private handleError;
94
+ /**
95
+ * Defines the "Imported Files" table columns.
96
+ * @memberof RosterImportHistoryComponent
97
+ */
98
+ private buildColumnSchema;
99
+ static ɵfac: i0.ɵɵFactoryDeclaration<RosterImportHistoryComponent, never>;
100
+ static ɵcmp: i0.ɵɵComponentDeclaration<RosterImportHistoryComponent, "ieeesa-roster-import-history", never, { "componentData": { "alias": "componentData"; "required": false; }; }, {}, never, never, true, never>;
101
+ }
102
+ export {};
@@ -155,6 +155,7 @@ export declare class TreeViewComponent implements OnInit, AfterViewInit, OnDestr
155
155
  editGroupClick: EventEmitter<EditGroupAndParentInfo>;
156
156
  deleteGroupClick: EventEmitter<GroupInfo>;
157
157
  viewRosterClick: EventEmitter<GroupInfo>;
158
+ manageInterestFormClick: EventEmitter<GroupInfo>;
158
159
  isMobileView: boolean;
159
160
  groupFunctionType: typeof GroupFunctionType;
160
161
  constructor(database: Groups, elementRef: ElementRef, groupsService: GroupsService, breakpointObserverService: BreakpointObserverService);
@@ -263,8 +264,10 @@ export declare class TreeViewComponent implements OnInit, AfterViewInit, OnDestr
263
264
  */
264
265
  getToggleButtonInfo(group: GroupNode): IconButton;
265
266
  /**
266
- * Handles the emitted group actions event
267
- * @param {GroupInfo} group
267
+ * Handles the emitted group actions event. Emits the corresponding output
268
+ * for the selected action: view roster, create sub group, edit group,
269
+ * delete group, or manage interest form.
270
+ * @param {GroupNode} group
268
271
  * @memberof TreeViewComponent
269
272
  */
270
273
  handleGroupActions(group: GroupNode): void;
@@ -289,5 +292,5 @@ export declare class TreeViewComponent implements OnInit, AfterViewInit, OnDestr
289
292
  */
290
293
  ngOnDestroy(): void;
291
294
  static ɵfac: i0.ɵɵFactoryDeclaration<TreeViewComponent, never>;
292
- static ɵcmp: i0.ɵɵComponentDeclaration<TreeViewComponent, "ieeesa-tree", never, { "parentGroupsInfo": { "alias": "parentGroupsInfo"; "required": false; }; "childGroupsInfo": { "alias": "childGroupsInfo"; "required": false; }; "isGroupSearchOrFilterActive": { "alias": "isGroupSearchOrFilterActive"; "required": false; }; "deletedGroupIdInfo": { "alias": "deletedGroupIdInfo"; "required": false; }; "userGroupPermissions": { "alias": "userGroupPermissions"; "required": false; }; }, { "toggleGroupClick": "toggleGroupClick"; "createGroupClick": "createGroupClick"; "editGroupClick": "editGroupClick"; "deleteGroupClick": "deleteGroupClick"; "viewRosterClick": "viewRosterClick"; }, never, never, true, never>;
295
+ static ɵcmp: i0.ɵɵComponentDeclaration<TreeViewComponent, "ieeesa-tree", never, { "parentGroupsInfo": { "alias": "parentGroupsInfo"; "required": false; }; "childGroupsInfo": { "alias": "childGroupsInfo"; "required": false; }; "isGroupSearchOrFilterActive": { "alias": "isGroupSearchOrFilterActive"; "required": false; }; "deletedGroupIdInfo": { "alias": "deletedGroupIdInfo"; "required": false; }; "userGroupPermissions": { "alias": "userGroupPermissions"; "required": false; }; }, { "toggleGroupClick": "toggleGroupClick"; "createGroupClick": "createGroupClick"; "editGroupClick": "editGroupClick"; "deleteGroupClick": "deleteGroupClick"; "viewRosterClick": "viewRosterClick"; "manageInterestFormClick": "manageInterestFormClick"; }, never, never, true, never>;
293
296
  }
@@ -188,6 +188,11 @@ export declare class ViewRosterComponent implements AfterViewInit, OnDestroy, On
188
188
  * @memberof ViewRosterComponent
189
189
  */
190
190
  onImportRoster(): void;
191
+ /**
192
+ * "View History" action — opens the roster import-history popup.
193
+ * @memberof ViewRosterComponent
194
+ */
195
+ onViewHistory(): void;
191
196
  /**
192
197
  * Handles alerts for success, error, warning.
193
198
  * @param {(string | string[])} message -error or warning message
@@ -117,12 +117,29 @@ export declare class GroupsComponent implements OnInit, OnDestroy {
117
117
  * @memberof GroupsComponent
118
118
  */
119
119
  openRosterImportModal(selectedGroupInfo: GroupInfo): void;
120
+ /**
121
+ * Sets the dialog options for the roster import-history ("View History")
122
+ * modal and opens it. Passes the selected group's id/name to the
123
+ * dynamically-instantiated RosterImportHistoryComponent via the
124
+ * ModalComponent dynamic-input pattern.
125
+ * @param {GroupInfo} selectedGroupInfo
126
+ * @memberof GroupsComponent
127
+ */
128
+ openRosterImportHistoryModal(selectedGroupInfo: GroupInfo): void;
120
129
  /**
121
130
  * Sets the dialog options for users modal and calls openModal method.
122
131
  * @param {GroupInfo} group
123
132
  * @memberof GroupsComponent
124
133
  */
125
134
  viewRoster(group: GroupInfo): void;
135
+ /**
136
+ * Opens the Roster Interests dialog for the selected group (SP3M-317). The
137
+ * dialog hosts the "Interested Rosters" and "Interest Form" tabs and receives
138
+ * the group via the dialog data.
139
+ * @param {GroupInfo} group
140
+ * @memberof GroupsComponent
141
+ */
142
+ openRosterInterests(group: GroupInfo): void;
126
143
  /**
127
144
  * Opens modal component with for received params and handles form submission success and errors.
128
145
  * @param {*} dialogOptions
@@ -4,5 +4,6 @@ export declare enum GroupAction {
4
4
  EDIT_GROUP = 3,
5
5
  DELETE_GROUP = 4,
6
6
  EXPAND_GROUP_VIEW = 5,
7
- COLLAPSE_GROUP_VIEW = 6
7
+ COLLAPSE_GROUP_VIEW = 6,
8
+ MANAGE_INTEREST_FORM = 7
8
9
  }
@@ -8,5 +8,6 @@ export declare enum GroupFunctionType {
8
8
  REASSIGN_PARENT_GROUP = "REASSIGN_PARENT_GROUP",
9
9
  EDIT_ROSTER_USER_ROLE = "EDIT_ROSTER_USER_ROLE",
10
10
  ADD_USER_TO_ROSTER = "ADD_USER_TO_ROSTER",
11
- IMPORT_ROSTER = "IMPORT_ROSTER"
11
+ IMPORT_ROSTER = "IMPORT_ROSTER",
12
+ VIEW_IMPORT_HISTORY = "VIEW_IMPORT_HISTORY"
12
13
  }
@@ -12,5 +12,6 @@ export declare enum PermissionType {
12
12
  VIEW_ALL_GROUPS = "VIEW_ALL_GROUPS",
13
13
  MANAGE_ALL_GROUPS = "MANAGE_ALL_GROUPS",
14
14
  VIEW_ALL_ROSTERS = "VIEW_ALL_ROSTERS",
15
- MANAGE_ALL_ROSTERS = "MANAGE_ALL_ROSTERS"
15
+ MANAGE_ALL_ROSTERS = "MANAGE_ALL_ROSTERS",
16
+ MANAGE_CUSTOM_INTEREST_QUESTIONS = "MANAGE_CUSTOM_INTEREST_QUESTIONS"
16
17
  }
@@ -1,5 +1,101 @@
1
1
  /**
2
- * Terminal status of a roster bulk-upload job.
2
+ * Roster-import status state machine. Mirrors the BE `ImportStatus` enum
3
+ * (sa-saapps-groups-api: org.ieee.standards.groups.model.ImportStatus) and the
4
+ * `status` CHECK constraint on `roster_import_job_tb`.
5
+ *
6
+ * - PENDING upload accepted, awaiting validation (default)
7
+ * - VALIDATING rows are being validated
8
+ * - COMPLETED all rows imported successfully (terminal)
9
+ * - FAILED no rows imported successfully (terminal)
10
+ * - PARTIAL some rows imported, some failed (terminal)
11
+ */
12
+ export declare enum RosterImportStatus {
13
+ PENDING = "PENDING",
14
+ VALIDATING = "VALIDATING",
15
+ COMPLETED = "COMPLETED",
16
+ FAILED = "FAILED",
17
+ PARTIAL = "PARTIAL"
18
+ }
19
+ /**
20
+ * A single per-row validation error stored in the import job's `error_report`
21
+ * JSONB column. Mirrors the BE `ErrorReport` POJO ({@code {row, field, message}}).
22
+ */
23
+ export interface RosterImportErrorReport {
24
+ /** 1-based data-row number in the uploaded file. */
25
+ row: number;
26
+ /** The field/column the error relates to (e.g. "Name", "Email"). */
27
+ field?: string;
28
+ /** Human-readable error message. */
29
+ message: string;
30
+ }
31
+ /**
32
+ * Roster import job as returned by the BE (upload / confirm / history).
33
+ * Mirrors the JSON-serialized view of the BE `RosterImportJob` entity.
34
+ *
35
+ * IMPORTANT: The BE marks `createdOn`, `createdBy`, `updatedOn`, `updatedBy` as
36
+ * `@JsonIgnore`, so the response carries NO timestamp and NO importer identity.
37
+ * If the "View History" screen needs those (Timestamp / Person Imported
38
+ * columns), the BE must expose them first — this is a BE change, tracked
39
+ * separately. `errorReport` arrives as a raw JSON string (JSONB passthrough);
40
+ * parse it into {@link RosterImportErrorReport}[] on the client when needed.
41
+ */
42
+ export interface RosterImportJob {
43
+ /** Import job identifier. */
44
+ importId: string;
45
+ /** The target group the roster was imported into. */
46
+ groupId: string;
47
+ /** Uploaded file name (e.g. "07_tenant_custom_fields.csv"). */
48
+ fileName?: string;
49
+ /** Current status (see {@link RosterImportStatus}). */
50
+ status: string;
51
+ /** Total data rows detected (`totalRows = successRows + failedRows`). */
52
+ totalRows?: number;
53
+ /** Rows successfully added/updated. */
54
+ successRows?: number;
55
+ /** Rows skipped due to validation errors. */
56
+ failedRows?: number;
57
+ /**
58
+ * Row-level errors as a RAW JSON string (BE `error_report` JSONB passthrough).
59
+ * Parse with `JSON.parse(...)` into {@link RosterImportErrorReport}[].
60
+ */
61
+ errorReport?: string;
62
+ }
63
+ /**
64
+ * Per-row validation outcome in a validate/preview response. Mirrors the BE
65
+ * `RosterImportRowOutcome` POJO.
66
+ */
67
+ export interface RosterImportRowOutcome {
68
+ /** 1-based data-row number in the uploaded file. */
69
+ row: number;
70
+ /** Whether this row passed validation. */
71
+ valid: boolean;
72
+ /** The field/column the error relates to, for an invalid row. */
73
+ field?: string;
74
+ /** Validation error message, for an invalid row. */
75
+ error?: string;
76
+ }
77
+ /**
78
+ * Validation preview returned by validate / preview. Mirrors the BE
79
+ * `RosterImportPreview` POJO. Building a preview commits no roster changes.
80
+ * `rows` is never null (BE guarantees an empty list at minimum).
81
+ */
82
+ export interface RosterImportPreview {
83
+ /** Import job identifier. */
84
+ importId: string;
85
+ /** Current status (see {@link RosterImportStatus}). */
86
+ status: string;
87
+ /** Total data rows (`totalRows = successRows + failedRows`). */
88
+ totalRows: number;
89
+ /** Rows that passed validation. */
90
+ successRows: number;
91
+ /** Rows that failed validation. */
92
+ failedRows: number;
93
+ /** Per-row outcomes (never null). */
94
+ rows: RosterImportRowOutcome[];
95
+ }
96
+ /**
97
+ * @deprecated Use {@link RosterImportStatus} instead. Retained for backward
98
+ * compatibility with the pre-BE-integration public API.
3
99
  */
4
100
  export declare enum RosterUploadStatus {
5
101
  PROCESSING = "PROCESSING",
@@ -7,7 +103,9 @@ export declare enum RosterUploadStatus {
7
103
  FAILED = "FAILED"
8
104
  }
9
105
  /**
10
- * A single row-level error from the roster import validation.
106
+ * @deprecated Use {@link RosterImportErrorReport} (`{ row, field, message }`)
107
+ * or {@link RosterImportRowOutcome} instead. Retained for backward
108
+ * compatibility with the pre-BE-integration public API.
11
109
  */
12
110
  export interface RosterUploadError {
13
111
  /** 1-based row number in the uploaded file. */
@@ -20,13 +118,12 @@ export interface RosterUploadError {
20
118
  errorMessage: string;
21
119
  }
22
120
  /**
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.
121
+ * @deprecated Use {@link RosterImportJob} (confirm/history) or
122
+ * {@link RosterImportPreview} (validate) instead. Retained for backward
123
+ * compatibility with the pre-BE-integration public API.
27
124
  */
28
125
  export interface RosterUploadResponse {
29
- /** Job identifier (unused by the mock flow, kept for parity with the API). */
126
+ /** Job identifier. */
30
127
  uploadJobId?: string;
31
128
  /** Terminal job status. */
32
129
  status: RosterUploadStatus;
@@ -39,17 +136,3 @@ export interface RosterUploadResponse {
39
136
  /** Row-level errors (present when failureCount > 0). */
40
137
  rowErrors: RosterUploadError[];
41
138
  }
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
- }
@@ -77,6 +77,10 @@ export declare class GroupsService {
77
77
  "1037": string;
78
78
  "1038": string;
79
79
  "1039": string;
80
+ "1072": string;
81
+ "1073": string;
82
+ "1074": string;
83
+ "1075": string;
80
84
  "2001": string;
81
85
  "2002": string;
82
86
  "2010": string;