@ieeesa-npm/dashboard 0.0.0-stage → 0.0.2

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 (73) hide show
  1. package/README.md +75 -3
  2. package/esm2022/ieeesa-npm-dashboard.mjs +5 -0
  3. package/esm2022/lib/assets/constants.json +158 -0
  4. package/esm2022/lib/assets/dashboard-mock.mjs +221 -0
  5. package/esm2022/lib/assets/dashboard-route-paths.mock.mjs +14 -0
  6. package/esm2022/lib/assets/dashboard-user-permissions.mock.mjs +23 -0
  7. package/esm2022/lib/assets/date-formats.mjs +28 -0
  8. package/esm2022/lib/components/event-group-card/event-group-card.component.mjs +414 -0
  9. package/esm2022/lib/components/meeting-attendance-row/meeting-attendance-row.component.mjs +182 -0
  10. package/esm2022/lib/components/member-level-badge/member-level-badge.component.mjs +79 -0
  11. package/esm2022/lib/components/upcoming-meetings-events/upcoming-meetings-events.component.mjs +261 -0
  12. package/esm2022/lib/components/welcome-header/dashboard-welcome-header.component.mjs +151 -0
  13. package/esm2022/lib/components/your-groups/your-groups.component.mjs +243 -0
  14. package/esm2022/lib/dashboard.component.mjs +417 -0
  15. package/esm2022/lib/dashboard.module.mjs +16 -0
  16. package/esm2022/lib/dashboard.routes.mjs +7 -0
  17. package/esm2022/lib/models/attendance-action-permissions.model.mjs +2 -0
  18. package/esm2022/lib/models/attendance-state.model.mjs +7 -0
  19. package/esm2022/lib/models/dashboard-list-response.model.mjs +2 -0
  20. package/esm2022/lib/models/dashboard-route-config.model.mjs +2 -0
  21. package/esm2022/lib/models/dashboard-section-state.model.mjs +9 -0
  22. package/esm2022/lib/models/dashboard-user-info.model.mjs +2 -0
  23. package/esm2022/lib/models/event-meeting-list-item.model.mjs +2 -0
  24. package/esm2022/lib/models/event-meeting-list-payload.model.mjs +8 -0
  25. package/esm2022/lib/models/group-row.model.mjs +2 -0
  26. package/esm2022/lib/models/meeting-row.model.mjs +2 -0
  27. package/esm2022/lib/models/my-groups-view.model.mjs +2 -0
  28. package/esm2022/lib/models/page-change.model.mjs +2 -0
  29. package/esm2022/lib/models/upcoming-event.model.mjs +13 -0
  30. package/esm2022/lib/services/attendance-reducers.mjs +54 -0
  31. package/esm2022/lib/services/dashboard.service.mjs +402 -0
  32. package/esm2022/lib/services/user-permissions.service.mjs +50 -0
  33. package/esm2022/lib/utils/attendance-action.util.mjs +53 -0
  34. package/esm2022/lib/utils/date-format.util.mjs +110 -0
  35. package/esm2022/lib/utils/event-meeting-mapper.util.mjs +96 -0
  36. package/esm2022/public-api.mjs +36 -0
  37. package/fesm2022/ieeesa-npm-dashboard.mjs +2981 -0
  38. package/fesm2022/ieeesa-npm-dashboard.mjs.map +1 -0
  39. package/index.d.ts +5 -0
  40. package/lib/assets/dashboard-mock.d.ts +31 -0
  41. package/lib/assets/dashboard-route-paths.mock.d.ts +2 -0
  42. package/lib/assets/dashboard-user-permissions.mock.d.ts +2 -0
  43. package/lib/assets/date-formats.d.ts +27 -0
  44. package/lib/components/event-group-card/event-group-card.component.d.ts +268 -0
  45. package/lib/components/meeting-attendance-row/meeting-attendance-row.component.d.ts +115 -0
  46. package/lib/components/member-level-badge/member-level-badge.component.d.ts +177 -0
  47. package/lib/components/upcoming-meetings-events/upcoming-meetings-events.component.d.ts +175 -0
  48. package/lib/components/welcome-header/dashboard-welcome-header.component.d.ts +98 -0
  49. package/lib/components/your-groups/your-groups.component.d.ts +159 -0
  50. package/lib/dashboard.component.d.ts +414 -0
  51. package/lib/dashboard.module.d.ts +7 -0
  52. package/lib/dashboard.routes.d.ts +2 -0
  53. package/lib/models/attendance-action-permissions.model.d.ts +4 -0
  54. package/lib/models/attendance-state.model.d.ts +4 -0
  55. package/lib/models/dashboard-list-response.model.d.ts +5 -0
  56. package/lib/models/dashboard-route-config.model.d.ts +7 -0
  57. package/lib/models/dashboard-section-state.model.d.ts +6 -0
  58. package/lib/models/dashboard-user-info.model.d.ts +5 -0
  59. package/lib/models/event-meeting-list-item.model.d.ts +107 -0
  60. package/lib/models/event-meeting-list-payload.model.d.ts +40 -0
  61. package/lib/models/group-row.model.d.ts +33 -0
  62. package/lib/models/meeting-row.model.d.ts +28 -0
  63. package/lib/models/my-groups-view.model.d.ts +56 -0
  64. package/lib/models/page-change.model.d.ts +4 -0
  65. package/lib/models/upcoming-event.model.d.ts +51 -0
  66. package/lib/services/attendance-reducers.d.ts +43 -0
  67. package/lib/services/dashboard.service.d.ts +202 -0
  68. package/lib/services/user-permissions.service.d.ts +31 -0
  69. package/lib/utils/attendance-action.util.d.ts +38 -0
  70. package/lib/utils/date-format.util.d.ts +60 -0
  71. package/lib/utils/event-meeting-mapper.util.d.ts +50 -0
  72. package/package.json +30 -3
  73. package/public-api.d.ts +26 -0
@@ -0,0 +1,2981 @@
1
+ import * as i1 from '@angular/common';
2
+ import { CommonModule } from '@angular/common';
3
+ import * as i0 from '@angular/core';
4
+ import { EventEmitter, Component, ViewEncapsulation, Input, Output, Injectable, ElementRef, ViewChild, NgModule } from '@angular/core';
5
+ import { BehaviorSubject, map, catchError, of, Subject, takeUntil } from 'rxjs';
6
+ import { AlertType } from '@ieeesa-npm/models';
7
+ import * as i1$1 from '@ieeesa-npm/utility';
8
+ import { AlertsService } from '@ieeesa-npm/utility';
9
+ import * as i2 from '@angular/material/paginator';
10
+ import { MatPaginatorModule } from '@angular/material/paginator';
11
+ import * as i3 from '@ieeesa-npm/presentation';
12
+ import { LoaderComponent, LegendComponent } from '@ieeesa-npm/presentation';
13
+ import { DateTime } from 'luxon';
14
+
15
+ /* eslint-disable no-unused-vars */
16
+ var DashboardSectionState;
17
+ (function (DashboardSectionState) {
18
+ DashboardSectionState["LOADING"] = "LOADING";
19
+ DashboardSectionState["LOADED"] = "LOADED";
20
+ DashboardSectionState["EMPTY"] = "EMPTY";
21
+ DashboardSectionState["ERROR"] = "ERROR";
22
+ })(DashboardSectionState || (DashboardSectionState = {}));
23
+
24
+ /* eslint-disable no-unused-vars */
25
+ var AttendanceState;
26
+ (function (AttendanceState) {
27
+ AttendanceState["CURRENTLY_ATTENDING"] = "CURRENTLY_ATTENDING";
28
+ AttendanceState["NOT_ATTENDING"] = "NOT_ATTENDING";
29
+ })(AttendanceState || (AttendanceState = {}));
30
+
31
+ /* eslint-disable no-unused-vars */
32
+ /**
33
+ * Discriminates how a row in the "Upcoming Meetings & Events" list is rendered.
34
+ * EVENT rows render as an expandable event block that owns child meetings;
35
+ * STANDALONE_MEETING rows render as a single meeting card and always sort LAST
36
+ * within the list.
37
+ */
38
+ var UpcomingItemKind;
39
+ (function (UpcomingItemKind) {
40
+ UpcomingItemKind["EVENT"] = "EVENT";
41
+ UpcomingItemKind["STANDALONE_MEETING"] = "STANDALONE_MEETING";
42
+ })(UpcomingItemKind || (UpcomingItemKind = {}));
43
+
44
+ /**
45
+ * Luxon format tokens used across the dashboard library for rendering
46
+ * event/meeting dates, times, and timezones.
47
+ *
48
+ * These tokens follow the luxon `DateTime.toFormat()` convention
49
+ * (NOT moment). New code in this library must use luxon exclusively.
50
+ *
51
+ * @see https://moment.github.io/luxon/#/formatting?id=table-of-tokens
52
+ */
53
+ const DateFormats = {
54
+ /** ISO date used for request payloads, e.g. 2025-03-14. */
55
+ requestPayloadFormat: 'yyyy-MM-dd',
56
+ /** ISO date-time used for request payloads, e.g. 2025-03-14T09:30:00. */
57
+ requestPayloadDateTimeFormat: "yyyy-MM-dd'T'HH:mm:ss",
58
+ /** Human-readable date for display, e.g. 14 Mar 2025. */
59
+ displayFormat: 'dd MMM yyyy',
60
+ /** Human-readable long date for display, e.g. 14 March 2025. */
61
+ displayLongFormat: 'dd MMMM yyyy',
62
+ /** 24-hour time for display, e.g. 09:30. */
63
+ timeFormat: 'HH:mm',
64
+ /** 12-hour time with meridiem for display, e.g. 9:30 AM. */
65
+ timeMeridiemFormat: 'h:mm a',
66
+ /** Short timezone abbreviation for display, e.g. EST. */
67
+ timezoneFormat: 'ZZZZ',
68
+ /** Long timezone name for display, e.g. Eastern Standard Time. */
69
+ timezoneLongFormat: 'ZZZZZ',
70
+ };
71
+
72
+ /**
73
+ * Pure helpers for the per-meeting attendance action state machine. No Angular /
74
+ * service dependencies so they are trivially testable (property-tested as
75
+ * Property 1) and reusable by `MeetingAttendanceRowComponent`.
76
+ *
77
+ * The handler is the single source of truth for what a meeting row's action
78
+ * button should emit when activated. The displayed `AttendanceState` only
79
+ * advances on a successful resolution (handled by the service reducers), so this
80
+ * function is a pure mapping from the current inputs to the action to emit.
81
+ */
82
+ /* eslint-disable no-unused-vars */
83
+ /**
84
+ * The action a meeting row's action button should emit when activated.
85
+ * `NONE` means the activation is non-actionable (empty id or in-flight) and the
86
+ * row must emit no `logAttendance` / `leaveMeeting` / navigation event.
87
+ */
88
+ var AttendanceAction;
89
+ (function (AttendanceAction) {
90
+ AttendanceAction["LOG_ATTENDANCE"] = "LOG_ATTENDANCE";
91
+ AttendanceAction["LEAVE_MEETING"] = "LEAVE_MEETING";
92
+ AttendanceAction["NONE"] = "NONE";
93
+ })(AttendanceAction || (AttendanceAction = {}));
94
+ /* eslint-enable no-unused-vars */
95
+ /**
96
+ * Resolves the attendance action to emit for a meeting row, following the
97
+ * attendance action state machine (see design: Attendance Action State Machine).
98
+ *
99
+ * Behavior:
100
+ * - Returns `NONE` when `meetingId` is empty or undefined (non-actionable row).
101
+ * - Returns `NONE` while the row is in-flight (re-entrancy guard — no further
102
+ * event is emitted until the current action resolves).
103
+ * - Returns `LEAVE_MEETING` when the state is `CURRENTLY_ATTENDING`.
104
+ * - Returns `LOG_ATTENDANCE` when the state is `NOT_ATTENDING` or undefined.
105
+ *
106
+ * @param {AttendanceState | undefined} attendanceState The row's current attendance state.
107
+ * @param {boolean} isInFlight Whether an attendance action for this row is awaiting resolution.
108
+ * @param {string | undefined} meetingId The row's meeting identifier.
109
+ * @return {AttendanceAction} The action to emit, or `NONE` when non-actionable.
110
+ */
111
+ function resolveAttendanceAction(attendanceState, isInFlight, meetingId) {
112
+ if (!meetingId) {
113
+ return AttendanceAction.NONE;
114
+ }
115
+ if (isInFlight) {
116
+ return AttendanceAction.NONE;
117
+ }
118
+ if (attendanceState === AttendanceState.CURRENTLY_ATTENDING) {
119
+ return AttendanceAction.LEAVE_MEETING;
120
+ }
121
+ return AttendanceAction.LOG_ATTENDANCE;
122
+ }
123
+
124
+ /**
125
+ * Pure luxon date/time formatting helpers for the dashboard library.
126
+ *
127
+ * The real "events & meetings" backend returns date-times as ISO-8601 strings
128
+ * (e.g. "2026-01-01T00:00:00") together with a display timezone string
129
+ * (e.g. "(UTC-04:00) America/New_York"). These helpers parse the ISO value and
130
+ * render it in the item's own zone (never moment). A numeric epoch-milliseconds
131
+ * value is also accepted for backward compatibility with fixtures/tests. All
132
+ * helpers fall back gracefully to an empty string when the value is missing or
133
+ * unparseable.
134
+ */
135
+ /**
136
+ * Extracts a luxon-usable IANA zone from a backend timezone string such as
137
+ * "(UTC-04:00) America/New_York" or "America/New_York". Returns `undefined`
138
+ * when no usable zone can be derived so luxon falls back to the local zone.
139
+ * @param {string | undefined} timezone - The backend timezone string.
140
+ * @return {string | undefined} The IANA zone id, or undefined.
141
+ */
142
+ function extractIanaZone(timezone) {
143
+ if (!timezone) {
144
+ return undefined;
145
+ }
146
+ // Strip a leading "(UTC±HH:MM) " offset prefix when present.
147
+ const closingParen = timezone.indexOf(') ');
148
+ const zone = closingParen > -1
149
+ ? timezone.slice(closingParen + 2).trim()
150
+ : timezone.trim();
151
+ return zone.length > 0 ? zone : undefined;
152
+ }
153
+ /**
154
+ * Parses a backend date-time value — either an ISO-8601 string
155
+ * (e.g. "2026-01-01T00:00:00") or an epoch-milliseconds number — into a luxon
156
+ * {@link DateTime} in the supplied timezone. The ISO value is treated as a wall
157
+ * time in the item's zone so the rendered time matches the backend's intent.
158
+ * Returns an invalid DateTime when the value is missing/unparseable.
159
+ * @param {string | number | undefined | null} value - The ISO string or epoch ms.
160
+ * @param {string | undefined} timezone - The backend timezone string.
161
+ * @return {DateTime} The parsed luxon DateTime (may be invalid).
162
+ */
163
+ function parseBackendDateTime(value, timezone) {
164
+ const zone = extractIanaZone(timezone);
165
+ if (value === undefined || value === null || value === '') {
166
+ return DateTime.invalid('empty');
167
+ }
168
+ if (typeof value === 'number') {
169
+ return zone
170
+ ? DateTime.fromMillis(value, { zone })
171
+ : DateTime.fromMillis(value);
172
+ }
173
+ // ISO string: parse as a wall time in the item's zone when known.
174
+ const isoOptions = zone ? { zone } : {};
175
+ const parsed = DateTime.fromISO(value, isoOptions);
176
+ return parsed.isValid ? parsed : DateTime.fromISO(value);
177
+ }
178
+ /**
179
+ * Formats a backend date-time value (ISO string or epoch ms) with luxon using
180
+ * the supplied format token, honouring the supplied timezone. Returns an empty
181
+ * string when the value is missing or invalid.
182
+ * @param {string | number | undefined | null} value - The ISO string or epoch ms.
183
+ * @param {string} format - The luxon `toFormat` token.
184
+ * @param {string | undefined} timezone - The backend timezone string.
185
+ * @return {string} The formatted value, or an empty string.
186
+ */
187
+ function formatBackendDateTime(value, format, timezone) {
188
+ const parsed = parseBackendDateTime(value, timezone);
189
+ return parsed.isValid ? parsed.toFormat(format) : '';
190
+ }
191
+ /**
192
+ * Backward-compatible alias kept for existing callers. Accepts ISO strings or
193
+ * epoch ms and formats them in the item's zone.
194
+ * @param {string | number | undefined | null} value - The ISO string or epoch ms.
195
+ * @param {string} format - The luxon `toFormat` token.
196
+ * @param {string | undefined} timezone - The backend timezone string.
197
+ * @return {string} The formatted value, or an empty string.
198
+ */
199
+ function formatEpoch(value, format, timezone) {
200
+ return formatBackendDateTime(value, format, timezone);
201
+ }
202
+ /**
203
+ * Computes a human "Day X of Y" indicator for a multi-day event from its start
204
+ * and end date-times, relative to "now". Returns an empty string when the dates
205
+ * are missing/invalid or the event spans a single day.
206
+ * @param {string | number | undefined | null} start - Event start (ISO or epoch ms).
207
+ * @param {string | number | undefined | null} end - Event end (ISO or epoch ms).
208
+ * @param {string | undefined} timezone - The backend timezone string.
209
+ * @return {string} The "Day X of Y" label, or an empty string.
210
+ */
211
+ function computeDayIndicator(start, end, timezone) {
212
+ const zone = extractIanaZone(timezone);
213
+ const startDt = parseBackendDateTime(start, timezone);
214
+ const endDt = parseBackendDateTime(end, timezone);
215
+ if (!startDt.isValid || !endDt.isValid) {
216
+ return '';
217
+ }
218
+ const startDay = startDt.startOf('day');
219
+ const endDay = endDt.startOf('day');
220
+ const totalDays = Math.floor(endDay.diff(startDay, 'days').days) + 1;
221
+ // Only meaningful for short, multi-day events (a conference-week style span).
222
+ // Very long spans (e.g. a year-long series) are not shown as "Day X of Y".
223
+ const MAX_DAY_INDICATOR_SPAN = 31;
224
+ if (totalDays <= 1 || totalDays > MAX_DAY_INDICATOR_SPAN) {
225
+ return '';
226
+ }
227
+ const now = (zone ? DateTime.now().setZone(zone) : DateTime.now()).startOf('day');
228
+ const clampedNow = now < startDay ? startDay : now > endDay ? endDay : now;
229
+ const currentDay = Math.floor(clampedNow.diff(startDay, 'days').days) + 1;
230
+ return `Day ${currentDay} of ${totalDays}`;
231
+ }
232
+
233
+ var welcomeHeader = {
234
+ greeting: "Welcome back, ",
235
+ fallbackGreeting: "Welcome back",
236
+ helperText: "Here's what's happening inside the portal",
237
+ jumpToLabel: "JUMP TO",
238
+ shortcuts: {
239
+ meetings: {
240
+ label: "Upcoming Meetings & Events",
241
+ iconUrl: "/assets/images/icons/attendance-flag.svg",
242
+ ariaLabel: "Jump to upcoming meetings and events"
243
+ },
244
+ groups: {
245
+ label: "Your Groups",
246
+ iconUrl: "/assets/images/icons/icon-committees.svg",
247
+ ariaLabel: "Jump to your groups"
248
+ }
249
+ }
250
+ };
251
+ var upcomingSection = {
252
+ title: "Upcoming Meetings & Events",
253
+ viewAllLabel: "View All",
254
+ expandLabel: "Expand",
255
+ collapseLabel: "Collapse",
256
+ ariaLabel: {
257
+ viewAll: "View all upcoming meetings and events",
258
+ toggle: "Collapse or expand upcoming meetings and events"
259
+ },
260
+ emptyMessage: "You have no upcoming meetings or events.",
261
+ errorMessage: "Unable to load upcoming meetings and events. Please try again.",
262
+ paginator: {
263
+ rangeTemplate: "{start}-{end} of {total} Meetings & Events"
264
+ }
265
+ };
266
+ var eventGroupCard = {
267
+ eventBadge: "EVENT",
268
+ standaloneBadge: "STANDALONE MEETING",
269
+ viewLabel: "View",
270
+ viewEventLabel: "View Event",
271
+ eventDatesLabel: "EVENT DATES & TIMEZONE",
272
+ meetingDateLabel: "MEETING DATE",
273
+ timeTimezoneLabel: "TIME & TIMEZONE",
274
+ meetingsLabel: "Meetings",
275
+ openForAttendanceTemplate: "{count} Meetings open for attendance",
276
+ meetingListToggle: {
277
+ showLabel: "Show meetings",
278
+ showLabelPrefix: "Show Meetings (",
279
+ showLabelSuffix: " open for attendance)",
280
+ hideLabel: "Hide meetings"
281
+ },
282
+ ariaLabel: {
283
+ view: "View details",
284
+ viewEvent: "View event details",
285
+ meetingListToggle: "Show or hide the meeting list for this event"
286
+ },
287
+ paginator: {
288
+ rangeTemplate: "{start}-{end} of {total} meetings",
289
+ totalTemplate: "{total} meetings scheduled across the event"
290
+ }
291
+ };
292
+ var meetingRow = {
293
+ emptyFieldPlaceholder: "—",
294
+ timeTimezoneLabel: "TIME & TIMEZONE",
295
+ viewLabel: "View",
296
+ currentlyAttendingIndicator: "Currently Attending",
297
+ logAttendanceLabel: "Log Attendance",
298
+ leaveMeetingLabel: "Leave Meeting",
299
+ ariaLabel: {
300
+ meetingNameLink: "View meeting details",
301
+ logAttendance: "Log attendance for this meeting",
302
+ leaveMeeting: "Leave this meeting"
303
+ }
304
+ };
305
+ var groupsSection = {
306
+ title: "Your Groups",
307
+ viewAllLabel: "View All",
308
+ expandLabel: "Expand",
309
+ collapseLabel: "Collapse",
310
+ countSuffix: "Groups",
311
+ emptyValue: "-",
312
+ unassignedMemberLevel: "Unassigned",
313
+ itemsPerPageLabel: "Items per page",
314
+ ariaLabel: {
315
+ viewAll: "View all your groups",
316
+ toggle: "Collapse or expand your groups",
317
+ groupLink: "View group details"
318
+ },
319
+ columns: {
320
+ group: "Group",
321
+ type: "Type",
322
+ officerRole: "Officer Role",
323
+ affiliation: "Affiliation",
324
+ memberLevel: "Member Level"
325
+ },
326
+ paginator: {
327
+ rangeTemplate: "Showing {start}-{end} of {total}",
328
+ totalTemplate: "{total} groups"
329
+ },
330
+ memberLevelBadge: {
331
+ success: "ieeesa-your-groups__badge--success",
332
+ warning: "ieeesa-your-groups__badge--warning"
333
+ },
334
+ emptyMessage: "You are not a member of any groups.",
335
+ errorMessage: "Unable to load your groups. Please try again."
336
+ };
337
+ var memberLevel = {
338
+ colorMap: {
339
+ "Voting Member": "$ieeesa-color-success",
340
+ Observer: "$ieeesa-color-info",
341
+ "Associate Member": "$ieeesa-color-warning",
342
+ Member: "$ieeesa-color-primary",
343
+ "default": "$ieeesa-color-neutral"
344
+ }
345
+ };
346
+ var attendance = {
347
+ errorMessage: "Unable to update your attendance. Please try again."
348
+ };
349
+ var footer = {
350
+ links: [
351
+ {
352
+ label: "Dashboard",
353
+ target: "/dashboard"
354
+ },
355
+ {
356
+ label: "Contact & Support",
357
+ target: "https://supportcenter.ieee.org/app/home"
358
+ },
359
+ {
360
+ label: "Accessibility",
361
+ target: "https://www.ieee.org/accessibility-statement.html"
362
+ },
363
+ {
364
+ label: "Nondiscrimination Policy",
365
+ target: "https://www.ieee.org/about/corporate/governance/p9-26.html"
366
+ },
367
+ {
368
+ label: "IEEE Ethics Reporting",
369
+ target: "https://www.ieee.org/about/ethics/index.html"
370
+ },
371
+ {
372
+ label: "IEEE Privacy Policy",
373
+ target: "https://www.ieee.org/security-privacy.html"
374
+ },
375
+ {
376
+ label: "IEEE SA Copyright Policy",
377
+ target: "https://standards.ieee.org/about/policies/"
378
+ }
379
+ ],
380
+ copyright: "© Copyright 2024 IEEE - All rights reserved. Use of this website signifies your agreement to the IEEE Terms and Conditions."
381
+ };
382
+ var defaultRouteConfig = {
383
+ eventDetail: "/events",
384
+ meetingDetail: "/meetings",
385
+ groupDetail: "/groups",
386
+ viewAllEvents: "/events-and-meetings/view-events-meetings-list",
387
+ viewAllGroups: "/groups"
388
+ };
389
+ var constants = {
390
+ welcomeHeader: welcomeHeader,
391
+ upcomingSection: upcomingSection,
392
+ eventGroupCard: eventGroupCard,
393
+ meetingRow: meetingRow,
394
+ groupsSection: groupsSection,
395
+ memberLevel: memberLevel,
396
+ attendance: attendance,
397
+ footer: footer,
398
+ defaultRouteConfig: defaultRouteConfig
399
+ };
400
+
401
+ var constants$1 = /*#__PURE__*/Object.freeze({
402
+ __proto__: null,
403
+ attendance: attendance,
404
+ default: constants,
405
+ defaultRouteConfig: defaultRouteConfig,
406
+ eventGroupCard: eventGroupCard,
407
+ footer: footer,
408
+ groupsSection: groupsSection,
409
+ meetingRow: meetingRow,
410
+ memberLevel: memberLevel,
411
+ upcomingSection: upcomingSection,
412
+ welcomeHeader: welcomeHeader
413
+ });
414
+
415
+ /**
416
+ * Meeting_Attendance_Row renders a single meeting within an event-group card:
417
+ * the meeting-name link, group/committee, room/location, date, and
418
+ * time+timezone (all luxon-formatted), together with the attendance action
419
+ * button that drives the per-meeting attendance state machine.
420
+ *
421
+ * The component is purely presentational — it emits navigation and attendance
422
+ * intents via `@Output()` and never performs navigation or HTTP itself. The
423
+ * action button delegates to the pure `resolveAttendanceAction` handler so the
424
+ * emit/guard logic is shared with the property-tested core (Property 1), and the
425
+ * displayed state only advances when the host updates `attendanceState`.
426
+ * @export
427
+ * @class MeetingAttendanceRowComponent
428
+ */
429
+ class MeetingAttendanceRowComponent {
430
+ constructor() {
431
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
432
+ this.constants = constants$1;
433
+ /** The current attendance state controlling label/indicator and action. */
434
+ this.attendanceState = AttendanceState.NOT_ATTENDING;
435
+ /** Whether an attendance action for this meeting is awaiting resolution. */
436
+ this.isInFlight = false;
437
+ /** Host permissions gating the log-attendance / leave-meeting buttons. */
438
+ this.permissions = {
439
+ canLogAttendance: false,
440
+ canLeaveMeeting: false,
441
+ };
442
+ /**
443
+ * Renders the leaner "standalone meeting" variant used by standalone-meeting
444
+ * cards (the group/location and status live on the parent card, so the row
445
+ * only shows the TIME & TIMEZONE block and the attendance + View actions).
446
+ */
447
+ this.compact = false;
448
+ /** Emits the meeting id when the meeting-name link is activated (Req 7.4). */
449
+ this.openMeeting = new EventEmitter();
450
+ /** Emits the meeting id to request logging attendance (Req 7.7). */
451
+ this.logAttendance = new EventEmitter();
452
+ /** Emits the meeting id to request leaving the meeting (Req 7.8). */
453
+ this.leaveMeeting = new EventEmitter();
454
+ /** Expose the enum to the template for state comparisons. */
455
+ this.AttendanceState = AttendanceState;
456
+ }
457
+ /**
458
+ * Whether this row is actionable — a non-empty `meetingId` is required for the
459
+ * meeting-name link and action button to emit anything (Req 7.10).
460
+ * @return {boolean} True when the row has a usable meeting id.
461
+ * @memberof MeetingAttendanceRowComponent
462
+ */
463
+ get isActionable() {
464
+ return !!this.meeting?.meetingId;
465
+ }
466
+ /**
467
+ * Whether the row is in the currently-attending state, which shows the
468
+ * currently-attending indicator and the leave-meeting action (Req 7.5).
469
+ * @return {boolean} True when the attendance state is `CURRENTLY_ATTENDING`.
470
+ * @memberof MeetingAttendanceRowComponent
471
+ */
472
+ get isCurrentlyAttending() {
473
+ return this.attendanceState === AttendanceState.CURRENTLY_ATTENDING;
474
+ }
475
+ /**
476
+ * Whether the log-attendance button should be shown: only when the row is in
477
+ * the log state (not currently attending) and the host grants
478
+ * `canLogAttendance` (Req 7.6, 12.1).
479
+ * @return {boolean} True when the log-attendance button is visible.
480
+ * @memberof MeetingAttendanceRowComponent
481
+ */
482
+ get showLogAttendanceButton() {
483
+ return !this.isCurrentlyAttending && !!this.permissions?.canLogAttendance;
484
+ }
485
+ /**
486
+ * Whether the leave-meeting button should be shown: only when the row is in
487
+ * the currently-attending state and the host grants `canLeaveMeeting`
488
+ * (Req 7.5, 12.2).
489
+ * @return {boolean} True when the leave-meeting button is visible.
490
+ * @memberof MeetingAttendanceRowComponent
491
+ */
492
+ get showLeaveMeetingButton() {
493
+ return this.isCurrentlyAttending && !!this.permissions?.canLeaveMeeting;
494
+ }
495
+ /**
496
+ * Resolves a group-or-committee / room-or-location field value, substituting
497
+ * the empty-field placeholder from `constants.json` when the value is empty or
498
+ * undefined (Req 7.2).
499
+ * @param {string | undefined} value - The raw field value from the meeting.
500
+ * @return {string} The value to display, or the empty-field placeholder.
501
+ * @memberof MeetingAttendanceRowComponent
502
+ */
503
+ resolveFieldValue(value) {
504
+ const placeholder = this.constants?.meetingRow?.emptyFieldPlaceholder ?? '—';
505
+ return value && value.trim().length > 0 ? value : placeholder;
506
+ }
507
+ /**
508
+ * Formats the meeting date for display using the shared luxon display format
509
+ * (Req 7.3). Falls back to the raw value when it cannot be parsed.
510
+ * @return {string} The formatted meeting date.
511
+ * @memberof MeetingAttendanceRowComponent
512
+ */
513
+ get formattedDate() {
514
+ const value = this.meeting?.startDateTime ?? this.meeting?.meetingDate;
515
+ return formatEpoch(value, DateFormats.displayFormat, this.meeting?.timezone);
516
+ }
517
+ /**
518
+ * Formats the meeting time + timezone for display using luxon (Req 7.3).
519
+ * The timezone abbreviation from the meeting model is appended to the parsed
520
+ * time. Falls back to the raw value when it cannot be parsed.
521
+ * @return {string} The formatted meeting time with timezone.
522
+ * @memberof MeetingAttendanceRowComponent
523
+ */
524
+ get formattedTime() {
525
+ const timezone = this.meeting?.timezone ?? '';
526
+ const startValue = this.meeting?.startDateTime ?? this.meeting?.meetingTime;
527
+ const start = formatEpoch(startValue, DateFormats.timeMeridiemFormat, timezone);
528
+ const end = formatEpoch(this.meeting?.endDateTime, DateFormats.timeMeridiemFormat, timezone);
529
+ const range = end ? `${start} – ${end}` : start;
530
+ if (!range) {
531
+ return '';
532
+ }
533
+ return timezone ? `${range} ${timezone}` : range;
534
+ }
535
+ /**
536
+ * Handles activation of the attendance action button. Delegates to the pure
537
+ * `resolveAttendanceAction` handler and emits exactly one event based on the
538
+ * resolved action: `logAttendance` for `LOG_ATTENDANCE`, `leaveMeeting` for
539
+ * `LEAVE_MEETING`, and nothing for `NONE` (empty id or in-flight)
540
+ * (Req 7.7, 7.8, 7.9, 7.10, 13.6).
541
+ * @memberof MeetingAttendanceRowComponent
542
+ */
543
+ onAttendanceAction() {
544
+ const action = resolveAttendanceAction(this.attendanceState, this.isInFlight, this.meeting?.meetingId);
545
+ if (action === AttendanceAction.LOG_ATTENDANCE) {
546
+ this.logAttendance.emit(this.meeting.meetingId);
547
+ return;
548
+ }
549
+ if (action === AttendanceAction.LEAVE_MEETING) {
550
+ this.leaveMeeting.emit(this.meeting.meetingId);
551
+ }
552
+ }
553
+ /**
554
+ * Handles activation of the meeting-name link. Emits the meeting id only when
555
+ * the row is actionable (non-empty meeting id) (Req 7.4, 7.10).
556
+ * @memberof MeetingAttendanceRowComponent
557
+ */
558
+ onOpenMeeting() {
559
+ if (!this.isActionable) {
560
+ return;
561
+ }
562
+ this.openMeeting.emit(this.meeting.meetingId);
563
+ }
564
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MeetingAttendanceRowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
565
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: MeetingAttendanceRowComponent, isStandalone: true, selector: "ieeesa-meeting-attendance-row", inputs: { meeting: "meeting", attendanceState: "attendanceState", isInFlight: "isInFlight", permissions: "permissions", compact: "compact" }, outputs: { openMeeting: "openMeeting", logAttendance: "logAttendance", leaveMeeting: "leaveMeeting" }, ngImport: i0, template: "<div\r\n class=\"ieeesa-meeting-attendance-row\"\r\n [class.ieeesa-meeting-attendance-row--compact]=\"compact\"\r\n>\r\n <!-- Meeting name + group/location (hidden in the compact standalone variant). -->\r\n <div class=\"ieeesa-meeting-attendance-row__name-col\">\r\n <ng-container *ngIf=\"!compact\">\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-meeting-attendance-row__name-link\"\r\n [disabled]=\"!isActionable\"\r\n [attr.aria-label]=\"constants.meetingRow.ariaLabel.meetingNameLink\"\r\n (click)=\"onOpenMeeting()\"\r\n >\r\n {{ meeting.meetingName }}\r\n </button>\r\n\r\n <p class=\"ieeesa-meeting-attendance-row__subtitle\">\r\n {{ resolveFieldValue(meeting.groupOrCommittee) }}<ng-container\r\n *ngIf=\"meeting.roomOrLocation\"\r\n > &middot; {{ meeting.roomOrLocation }}</ng-container>\r\n </p>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- MEETING DATE column. -->\r\n <dl class=\"ieeesa-meeting-attendance-row__col ieeesa-meeting-attendance-row__col--date\">\r\n <dt class=\"ieeesa-meeting-attendance-row__col-label\">\r\n {{ constants.eventGroupCard.meetingDateLabel }}\r\n </dt>\r\n <dd class=\"ieeesa-meeting-attendance-row__col-value\">\r\n {{ formattedDate }}\r\n </dd>\r\n </dl>\r\n\r\n <!-- TIME & TIMEZONE column. -->\r\n <dl class=\"ieeesa-meeting-attendance-row__col ieeesa-meeting-attendance-row__col--time\">\r\n <dt class=\"ieeesa-meeting-attendance-row__col-label\">\r\n {{ constants.meetingRow.timeTimezoneLabel }}\r\n </dt>\r\n <dd class=\"ieeesa-meeting-attendance-row__col-value\">\r\n {{ formattedTime }}\r\n </dd>\r\n </dl>\r\n\r\n <!-- Attendance indicator + actions column. -->\r\n <div class=\"ieeesa-meeting-attendance-row__actions\">\r\n <!-- Currently-attending pill shown only in the attending state. -->\r\n <span\r\n *ngIf=\"isCurrentlyAttending\"\r\n class=\"ieeesa-meeting-attendance-row__attending-pill\"\r\n >\r\n <span class=\"ieeesa-meeting-attendance-row__attending-dot\" aria-hidden=\"true\"></span>\r\n {{ constants.meetingRow.currentlyAttendingIndicator }}\r\n </span>\r\n\r\n <!-- Log-attendance button: shown in the log state with the permission. -->\r\n <button\r\n *ngIf=\"showLogAttendanceButton\"\r\n type=\"button\"\r\n class=\"ieeesa-meeting-attendance-row__action-button ieeesa-meeting-attendance-row__action-button--log\"\r\n [disabled]=\"isInFlight || !isActionable\"\r\n [attr.aria-label]=\"constants.meetingRow.ariaLabel.logAttendance\"\r\n (click)=\"onAttendanceAction()\"\r\n >\r\n {{ constants.meetingRow.logAttendanceLabel }}\r\n </button>\r\n\r\n <!-- Leave-meeting button: shown in the attending state with the permission. -->\r\n <button\r\n *ngIf=\"showLeaveMeetingButton\"\r\n type=\"button\"\r\n class=\"ieeesa-meeting-attendance-row__action-button ieeesa-meeting-attendance-row__action-button--leave\"\r\n [disabled]=\"isInFlight || !isActionable\"\r\n [attr.aria-label]=\"constants.meetingRow.ariaLabel.leaveMeeting\"\r\n (click)=\"onAttendanceAction()\"\r\n >\r\n {{ constants.meetingRow.leaveMeetingLabel }}\r\n </button>\r\n </div>\r\n</div>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-meeting-attendance-row{display:grid;grid-template-columns:minmax(220px,2fr) minmax(120px,1fr) minmax(180px,1.2fr) auto;align-items:center;column-gap:24px;min-height:72px;padding:16px 20px;background-color:#fff;font-family:Calibri Regular}.ieeesa-meeting-attendance-row__name-col{display:flex;flex-direction:column;gap:4px;min-width:0}.ieeesa-meeting-attendance-row__name-link{padding:0;border:0;background:none;text-align:left;text-decoration:underline;font-family:Calibri Bold;font-size:1rem;line-height:20px;color:#00629b;cursor:pointer}.ieeesa-meeting-attendance-row__name-link:disabled{color:#939094;text-decoration:none;cursor:default}.ieeesa-meeting-attendance-row__subtitle{margin:0;font-size:.875rem;line-height:18px;color:#333436}.ieeesa-meeting-attendance-row__col{display:flex;flex-direction:column;gap:4px;margin:0;min-width:0}.ieeesa-meeting-attendance-row__col-label{font-family:Calibri Bold;font-size:.75rem;letter-spacing:.03125em;text-transform:uppercase;color:#939094}.ieeesa-meeting-attendance-row__col-value{margin:0;font-size:.875rem;line-height:18px;color:#333436}.ieeesa-meeting-attendance-row__actions{display:flex;align-items:center;justify-content:flex-end;gap:16px;flex-shrink:0}.ieeesa-meeting-attendance-row__attending-pill{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:16px;white-space:nowrap;font-size:.8125rem;color:#1a7f37;background-color:#e8f5ec}.ieeesa-meeting-attendance-row__attending-dot{width:8px;height:8px;border-radius:50%;background-color:#1a7f37}.ieeesa-meeting-attendance-row__action-button{min-width:146px;padding:10px 16px;border-radius:4px;font-family:Calibri Bold;font-size:.875rem;cursor:pointer}.ieeesa-meeting-attendance-row__action-button--log{border:1px solid #00629b;background-color:#00629b;color:#fff}.ieeesa-meeting-attendance-row__action-button--leave{border:1px solid #00629b;background-color:#fff;color:#00629b}.ieeesa-meeting-attendance-row__action-button:disabled{border-color:#939094;background-color:#939094;color:#fff;cursor:default}.ieeesa-meeting-attendance-row:not(.ieeesa-meeting-attendance-row--compact)+.ieeesa-meeting-attendance-row:not(.ieeesa-meeting-attendance-row--compact){border-top:1px solid #d7d6d6}.ieeesa-meeting-attendance-row--compact{grid-template-columns:minmax(180px,1.2fr) auto;padding:0;min-height:0}.ieeesa-meeting-attendance-row--compact .ieeesa-meeting-attendance-row__col--date{display:none}@media (max-width: 767px){.ieeesa-meeting-attendance-row{grid-template-columns:1fr;row-gap:12px}.ieeesa-meeting-attendance-row__actions{justify-content:flex-start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], encapsulation: i0.ViewEncapsulation.None }); }
566
+ }
567
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MeetingAttendanceRowComponent, decorators: [{
568
+ type: Component,
569
+ args: [{ selector: 'ieeesa-meeting-attendance-row', standalone: true, imports: [CommonModule], encapsulation: ViewEncapsulation.None, template: "<div\r\n class=\"ieeesa-meeting-attendance-row\"\r\n [class.ieeesa-meeting-attendance-row--compact]=\"compact\"\r\n>\r\n <!-- Meeting name + group/location (hidden in the compact standalone variant). -->\r\n <div class=\"ieeesa-meeting-attendance-row__name-col\">\r\n <ng-container *ngIf=\"!compact\">\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-meeting-attendance-row__name-link\"\r\n [disabled]=\"!isActionable\"\r\n [attr.aria-label]=\"constants.meetingRow.ariaLabel.meetingNameLink\"\r\n (click)=\"onOpenMeeting()\"\r\n >\r\n {{ meeting.meetingName }}\r\n </button>\r\n\r\n <p class=\"ieeesa-meeting-attendance-row__subtitle\">\r\n {{ resolveFieldValue(meeting.groupOrCommittee) }}<ng-container\r\n *ngIf=\"meeting.roomOrLocation\"\r\n > &middot; {{ meeting.roomOrLocation }}</ng-container>\r\n </p>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- MEETING DATE column. -->\r\n <dl class=\"ieeesa-meeting-attendance-row__col ieeesa-meeting-attendance-row__col--date\">\r\n <dt class=\"ieeesa-meeting-attendance-row__col-label\">\r\n {{ constants.eventGroupCard.meetingDateLabel }}\r\n </dt>\r\n <dd class=\"ieeesa-meeting-attendance-row__col-value\">\r\n {{ formattedDate }}\r\n </dd>\r\n </dl>\r\n\r\n <!-- TIME & TIMEZONE column. -->\r\n <dl class=\"ieeesa-meeting-attendance-row__col ieeesa-meeting-attendance-row__col--time\">\r\n <dt class=\"ieeesa-meeting-attendance-row__col-label\">\r\n {{ constants.meetingRow.timeTimezoneLabel }}\r\n </dt>\r\n <dd class=\"ieeesa-meeting-attendance-row__col-value\">\r\n {{ formattedTime }}\r\n </dd>\r\n </dl>\r\n\r\n <!-- Attendance indicator + actions column. -->\r\n <div class=\"ieeesa-meeting-attendance-row__actions\">\r\n <!-- Currently-attending pill shown only in the attending state. -->\r\n <span\r\n *ngIf=\"isCurrentlyAttending\"\r\n class=\"ieeesa-meeting-attendance-row__attending-pill\"\r\n >\r\n <span class=\"ieeesa-meeting-attendance-row__attending-dot\" aria-hidden=\"true\"></span>\r\n {{ constants.meetingRow.currentlyAttendingIndicator }}\r\n </span>\r\n\r\n <!-- Log-attendance button: shown in the log state with the permission. -->\r\n <button\r\n *ngIf=\"showLogAttendanceButton\"\r\n type=\"button\"\r\n class=\"ieeesa-meeting-attendance-row__action-button ieeesa-meeting-attendance-row__action-button--log\"\r\n [disabled]=\"isInFlight || !isActionable\"\r\n [attr.aria-label]=\"constants.meetingRow.ariaLabel.logAttendance\"\r\n (click)=\"onAttendanceAction()\"\r\n >\r\n {{ constants.meetingRow.logAttendanceLabel }}\r\n </button>\r\n\r\n <!-- Leave-meeting button: shown in the attending state with the permission. -->\r\n <button\r\n *ngIf=\"showLeaveMeetingButton\"\r\n type=\"button\"\r\n class=\"ieeesa-meeting-attendance-row__action-button ieeesa-meeting-attendance-row__action-button--leave\"\r\n [disabled]=\"isInFlight || !isActionable\"\r\n [attr.aria-label]=\"constants.meetingRow.ariaLabel.leaveMeeting\"\r\n (click)=\"onAttendanceAction()\"\r\n >\r\n {{ constants.meetingRow.leaveMeetingLabel }}\r\n </button>\r\n </div>\r\n</div>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-meeting-attendance-row{display:grid;grid-template-columns:minmax(220px,2fr) minmax(120px,1fr) minmax(180px,1.2fr) auto;align-items:center;column-gap:24px;min-height:72px;padding:16px 20px;background-color:#fff;font-family:Calibri Regular}.ieeesa-meeting-attendance-row__name-col{display:flex;flex-direction:column;gap:4px;min-width:0}.ieeesa-meeting-attendance-row__name-link{padding:0;border:0;background:none;text-align:left;text-decoration:underline;font-family:Calibri Bold;font-size:1rem;line-height:20px;color:#00629b;cursor:pointer}.ieeesa-meeting-attendance-row__name-link:disabled{color:#939094;text-decoration:none;cursor:default}.ieeesa-meeting-attendance-row__subtitle{margin:0;font-size:.875rem;line-height:18px;color:#333436}.ieeesa-meeting-attendance-row__col{display:flex;flex-direction:column;gap:4px;margin:0;min-width:0}.ieeesa-meeting-attendance-row__col-label{font-family:Calibri Bold;font-size:.75rem;letter-spacing:.03125em;text-transform:uppercase;color:#939094}.ieeesa-meeting-attendance-row__col-value{margin:0;font-size:.875rem;line-height:18px;color:#333436}.ieeesa-meeting-attendance-row__actions{display:flex;align-items:center;justify-content:flex-end;gap:16px;flex-shrink:0}.ieeesa-meeting-attendance-row__attending-pill{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:16px;white-space:nowrap;font-size:.8125rem;color:#1a7f37;background-color:#e8f5ec}.ieeesa-meeting-attendance-row__attending-dot{width:8px;height:8px;border-radius:50%;background-color:#1a7f37}.ieeesa-meeting-attendance-row__action-button{min-width:146px;padding:10px 16px;border-radius:4px;font-family:Calibri Bold;font-size:.875rem;cursor:pointer}.ieeesa-meeting-attendance-row__action-button--log{border:1px solid #00629b;background-color:#00629b;color:#fff}.ieeesa-meeting-attendance-row__action-button--leave{border:1px solid #00629b;background-color:#fff;color:#00629b}.ieeesa-meeting-attendance-row__action-button:disabled{border-color:#939094;background-color:#939094;color:#fff;cursor:default}.ieeesa-meeting-attendance-row:not(.ieeesa-meeting-attendance-row--compact)+.ieeesa-meeting-attendance-row:not(.ieeesa-meeting-attendance-row--compact){border-top:1px solid #d7d6d6}.ieeesa-meeting-attendance-row--compact{grid-template-columns:minmax(180px,1.2fr) auto;padding:0;min-height:0}.ieeesa-meeting-attendance-row--compact .ieeesa-meeting-attendance-row__col--date{display:none}@media (max-width: 767px){.ieeesa-meeting-attendance-row{grid-template-columns:1fr;row-gap:12px}.ieeesa-meeting-attendance-row__actions{justify-content:flex-start}}\n"] }]
570
+ }], propDecorators: { meeting: [{
571
+ type: Input
572
+ }], attendanceState: [{
573
+ type: Input
574
+ }], isInFlight: [{
575
+ type: Input
576
+ }], permissions: [{
577
+ type: Input
578
+ }], compact: [{
579
+ type: Input
580
+ }], openMeeting: [{
581
+ type: Output
582
+ }], logAttendance: [{
583
+ type: Output
584
+ }], leaveMeeting: [{
585
+ type: Output
586
+ }] } });
587
+
588
+ /**
589
+ * Event_Group_Card renders a single upcoming event as a card: the status badge,
590
+ * title, day indicator, meeting-count summary sub-line, start/end date, and
591
+ * timezone (all luxon-formatted). It exposes a "View Event" action, a
592
+ * show/hide toggle for the meeting list labeled with the open-for-attendance
593
+ * count, the per-event meeting rows (one {@link MeetingAttendanceRowComponent}
594
+ * per meeting on the current page), and a per-card paginator.
595
+ *
596
+ * The component is purely presentational — it emits navigation, attendance, and
597
+ * pagination intents via `@Output()` and never performs navigation or HTTP
598
+ * itself. Meeting-row outputs (`openMeeting`/`logAttendance`/`leaveMeeting`) are
599
+ * bubbled upward unchanged so the container can delegate to the service.
600
+ * @export
601
+ * @class EventGroupCardComponent
602
+ */
603
+ class EventGroupCardComponent {
604
+ constructor() {
605
+ /** User-facing strings sourced from `constants.json`. */
606
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
607
+ this.constants = constants$1;
608
+ /** Enum reference for EVENT vs STANDALONE_MEETING comparisons in the template. */
609
+ this.itemKinds = UpcomingItemKind;
610
+ /** Host permissions gating the per-row log-attendance / leave-meeting buttons. */
611
+ this.permissions = {
612
+ canLogAttendance: false,
613
+ canLeaveMeeting: false,
614
+ };
615
+ /** Map of meeting id to current attendance state, passed down to each row. */
616
+ this.attendanceStates = {};
617
+ /** Set of meeting ids with an attendance action awaiting resolution. */
618
+ this.inFlightMeetingIds = new Set();
619
+ /** Whether the host has determined the viewport is at/below the mobile breakpoint. */
620
+ this.isMobileView = false;
621
+ /** Emits the event id when the "View Event" button is activated (Req 6.4). */
622
+ this.viewEvent = new EventEmitter();
623
+ /** Bubbles the meeting id when a row's meeting-name link is activated (Req 6.7, 7.4). */
624
+ this.openMeeting = new EventEmitter();
625
+ /** Bubbles the meeting id when a row requests logging attendance (Req 6.7, 7.7). */
626
+ this.logAttendance = new EventEmitter();
627
+ /** Bubbles the meeting id when a row requests leaving the meeting (Req 6.7, 7.8). */
628
+ this.leaveMeeting = new EventEmitter();
629
+ /** Emits the requested page when the per-card paginator changes (Req 6.9, 3.3). */
630
+ this.pageChange = new EventEmitter();
631
+ /**
632
+ * Emits the one-based page to load when the meeting list is opened (or paged)
633
+ * so the container can lazily fetch the event's child meetings from the
634
+ * backend. Standalone-meeting cards never emit this.
635
+ */
636
+ this.loadMeetings = new EventEmitter();
637
+ /** The default page size for the per-card meeting paginator. */
638
+ this.defaultPageSize = 5;
639
+ /** The current zero-based page index of the meeting list. */
640
+ this.pageIndex = 0;
641
+ /** The current page size of the meeting list. */
642
+ this.pageSize = this.defaultPageSize;
643
+ /** Whether the meeting list is currently shown (toggled open). */
644
+ this.isMeetingListOpen = false;
645
+ }
646
+ /**
647
+ * The event status slugified into a BEM modifier suffix (lowercase, spaces to
648
+ * hyphens), e.g. "In Progress" -> "in-progress", used to colour the status
649
+ * pill per status.
650
+ * @return {string} The status modifier suffix.
651
+ * @memberof EventGroupCardComponent
652
+ */
653
+ get statusModifier() {
654
+ return (this.event?.status ?? '')
655
+ .trim()
656
+ .toLowerCase()
657
+ .replace(/\s+/g, '-');
658
+ }
659
+ /**
660
+ * Whether this card renders a STANDALONE_MEETING (leaner single-meeting card
661
+ * with no child-meeting list) rather than an expandable EVENT block.
662
+ * @return {boolean} True for standalone-meeting cards.
663
+ * @memberof EventGroupCardComponent
664
+ */
665
+ get isStandalone() {
666
+ return this.event?.kind === UpcomingItemKind.STANDALONE_MEETING;
667
+ }
668
+ /**
669
+ * The full list of meetings for the event, defaulting to an empty list when
670
+ * the event has none.
671
+ * @return {MeetingRow[]} The event's meetings.
672
+ * @memberof EventGroupCardComponent
673
+ */
674
+ get meetings() {
675
+ return this.event?.meetings ?? [];
676
+ }
677
+ /**
678
+ * The total number of meetings for the event (paginator length). Uses the
679
+ * backend `meetingCount` (the full total) rather than the length of the
680
+ * currently loaded page, so the paginator can advance through every page.
681
+ * @return {number} The total meeting count.
682
+ * @memberof EventGroupCardComponent
683
+ */
684
+ get totalMeetings() {
685
+ return this.event?.meetingCount || this.meetings.length;
686
+ }
687
+ /**
688
+ * The meetings to render for the current page. The server returns exactly one
689
+ * page of meetings into `event.meetings`, so the card renders them as-is
690
+ * without local slicing (pagination is server-side).
691
+ * @return {MeetingRow[]} The meetings on the current page.
692
+ * @memberof EventGroupCardComponent
693
+ */
694
+ get pagedMeetings() {
695
+ return this.meetings;
696
+ }
697
+ /**
698
+ * The one-based index of the first meeting shown on the current page, or 0
699
+ * when there are no meetings.
700
+ * @return {number} The start index for the range template.
701
+ * @memberof EventGroupCardComponent
702
+ */
703
+ get rangeStart() {
704
+ return this.totalMeetings === 0 ? 0 : this.pageIndex * this.pageSize + 1;
705
+ }
706
+ /**
707
+ * The one-based index of the last meeting shown on the current page, clamped
708
+ * to the total meeting count.
709
+ * @return {number} The end index for the range template.
710
+ * @memberof EventGroupCardComponent
711
+ */
712
+ get rangeEnd() {
713
+ return Math.min((this.pageIndex + 1) * this.pageSize, this.totalMeetings);
714
+ }
715
+ /**
716
+ * The paginator range label (e.g. "Showing 1-5 of 12") built from the
717
+ * `constants.json` range template with the current start/end/total values.
718
+ * @return {string} The formatted range label.
719
+ * @memberof EventGroupCardComponent
720
+ */
721
+ get rangeLabel() {
722
+ const template = this.constants?.eventGroupCard?.paginator?.rangeTemplate ??
723
+ 'Showing {start}-{end} of {total}';
724
+ return template
725
+ .replace('{start}', `${this.rangeStart}`)
726
+ .replace('{end}', `${this.rangeEnd}`)
727
+ .replace('{total}', `${this.totalMeetings}`);
728
+ }
729
+ /**
730
+ * The meeting-count summary sub-line (e.g. "12 meetings") built from the
731
+ * `constants.json` total template with the event's meeting count.
732
+ * @return {string} The formatted meeting-count summary.
733
+ * @memberof EventGroupCardComponent
734
+ */
735
+ get meetingCountSummary() {
736
+ const template = this.constants?.eventGroupCard?.paginator?.totalTemplate ??
737
+ '{total} meetings';
738
+ return template.replace('{total}', `${this.event?.meetingCount ?? 0}`);
739
+ }
740
+ /**
741
+ * The header sub-line: the group · location line for a STANDALONE meeting, or
742
+ * the "{n} meetings scheduled across the event" summary for an EVENT when its
743
+ * meeting count is known (hidden until the count is loaded so it never shows
744
+ * "0 meetings").
745
+ * @return {string} The sub-line text, or an empty string to hide it.
746
+ * @memberof EventGroupCardComponent
747
+ */
748
+ get subtitleText() {
749
+ if (this.isStandalone) {
750
+ const group = this.event?.groupOrCommittee ?? '';
751
+ const location = this.event?.roomOrLocation ?? '';
752
+ if (group && location) {
753
+ return `${group} \u00b7 ${location}`;
754
+ }
755
+ return group || location;
756
+ }
757
+ return (this.event?.meetingCount ?? 0) > 0 ? this.meetingCountSummary : '';
758
+ }
759
+ /**
760
+ * The meeting-list show/hide toggle label. When the list is hidden the label
761
+ * reads "Show Meetings ({count} open for attendance)" built from the
762
+ * `constants.json` templates and the event's open-for-attendance count; when
763
+ * the list is shown the label reads the configured hide label (Req 6.5).
764
+ * @return {string} The toggle button label.
765
+ * @memberof EventGroupCardComponent
766
+ */
767
+ get meetingListToggleLabel() {
768
+ const toggle = this.constants?.eventGroupCard?.meetingListToggle;
769
+ if (this.isMeetingListOpen) {
770
+ return toggle?.hideLabel ?? 'Hide meetings';
771
+ }
772
+ return toggle?.showLabel ?? 'Show meetings';
773
+ }
774
+ /**
775
+ * The "{count} Meetings open for attendance" summary shown in the event's
776
+ * meetings bar, built from the `constants.json` template and the event's
777
+ * open-for-attendance count (Req 6.5).
778
+ * @return {string} The formatted open-for-attendance summary.
779
+ * @memberof EventGroupCardComponent
780
+ */
781
+ get openForAttendanceSummary() {
782
+ const template = this.constants?.eventGroupCard?.openForAttendanceTemplate ??
783
+ '{count} Meetings open for attendance';
784
+ return template.replace('{count}', `${this.event?.openForAttendanceCount ?? 0}`);
785
+ }
786
+ /**
787
+ * The {@link MeetingRow} view model for a STANDALONE_MEETING card, projected
788
+ * from the event entry so the shared {@link MeetingAttendanceRowComponent}
789
+ * renders its time/timezone block and attendance action.
790
+ * @return {MeetingRow} The standalone meeting row.
791
+ * @memberof EventGroupCardComponent
792
+ */
793
+ get standaloneMeeting() {
794
+ return {
795
+ meetingId: this.event?.eventId ?? '',
796
+ meetingName: this.event?.title ?? '',
797
+ groupOrCommittee: this.event?.groupOrCommittee,
798
+ roomOrLocation: this.event?.roomOrLocation,
799
+ startDateTime: this.event?.startDateTime ?? this.event?.startDate,
800
+ endDateTime: this.event?.endDateTime ?? this.event?.endDate,
801
+ timezone: this.event?.timezone ?? '',
802
+ status: this.event?.status,
803
+ isMemberOfGroup: this.event?.isMemberOfGroup,
804
+ };
805
+ }
806
+ /**
807
+ * The formatted "start – end time (timezone)" string for a STANDALONE meeting,
808
+ * mirroring the inner meeting row's TIME & TIMEZONE value.
809
+ * @return {string} The formatted time + timezone.
810
+ * @memberof EventGroupCardComponent
811
+ */
812
+ get standaloneTime() {
813
+ const timezone = this.event?.timezone ?? '';
814
+ const start = formatEpoch(this.event?.startDateTime ?? this.event?.startDate, DateFormats.timeMeridiemFormat, timezone);
815
+ const end = formatEpoch(this.event?.endDateTime ?? this.event?.endDate, DateFormats.timeMeridiemFormat, timezone);
816
+ const range = end ? `${start} \u2013 ${end}` : start;
817
+ if (!range) {
818
+ return '';
819
+ }
820
+ return timezone ? `${range} ${timezone}` : range;
821
+ }
822
+ /**
823
+ * Whether the STANDALONE meeting is currently being attended (drives the pill
824
+ * and the leave action).
825
+ * @return {boolean} True when the standalone meeting is currently attending.
826
+ * @memberof EventGroupCardComponent
827
+ */
828
+ get isStandaloneAttending() {
829
+ return (this.resolveAttendanceState(this.standaloneMeeting) ===
830
+ AttendanceState.CURRENTLY_ATTENDING);
831
+ }
832
+ /**
833
+ * Whether the STANDALONE meeting's Log Attendance button is shown (not
834
+ * attending and the host grants the permission).
835
+ * @return {boolean} True when the log-attendance button is visible.
836
+ * @memberof EventGroupCardComponent
837
+ */
838
+ get showStandaloneLog() {
839
+ return !this.isStandaloneAttending && !!this.permissions?.canLogAttendance;
840
+ }
841
+ /**
842
+ * Whether the STANDALONE meeting's Leave Meeting button is shown (attending
843
+ * and the host grants the permission).
844
+ * @return {boolean} True when the leave-meeting button is visible.
845
+ * @memberof EventGroupCardComponent
846
+ */
847
+ get showStandaloneLeave() {
848
+ return this.isStandaloneAttending && !!this.permissions?.canLeaveMeeting;
849
+ }
850
+ /** The luxon-formatted start date of the event (ISO or epoch ms) (Req 6.2). */
851
+ get formattedStartDate() {
852
+ return this.formatDate(this.event?.startDateTime ?? this.event?.startDate);
853
+ }
854
+ /** The luxon-formatted end date of the event (ISO or epoch ms) (Req 6.2). */
855
+ get formattedEndDate() {
856
+ return this.formatDate(this.event?.endDateTime ?? this.event?.endDate);
857
+ }
858
+ /**
859
+ * `trackBy` for the meeting-row `*ngFor`, keyed by the stable meeting id so
860
+ * Angular reuses row components across pagination/state changes (Req 6.7, 14.4).
861
+ * @param {number} _index - The iteration index (unused).
862
+ * @param {MeetingRow} meeting - The meeting for this row.
863
+ * @return {string} The stable track key (meeting id).
864
+ * @memberof EventGroupCardComponent
865
+ */
866
+ trackByMeetingId(_index, meeting) {
867
+ return meeting?.meetingId ?? `${_index}`;
868
+ }
869
+ /**
870
+ * Resolves the attendance state for a meeting from the `attendanceStates` map,
871
+ * defaulting to `NOT_ATTENDING` when the map has no entry for it.
872
+ * @param {MeetingRow} meeting - The meeting to resolve state for.
873
+ * @return {AttendanceState} The attendance state passed to the row.
874
+ * @memberof EventGroupCardComponent
875
+ */
876
+ resolveAttendanceState(meeting) {
877
+ return (this.attendanceStates?.[meeting?.meetingId] ??
878
+ AttendanceState.NOT_ATTENDING);
879
+ }
880
+ /**
881
+ * Resolves whether a meeting has an attendance action in flight from the
882
+ * `inFlightMeetingIds` set.
883
+ * @param {MeetingRow} meeting - The meeting to check.
884
+ * @return {boolean} True when the meeting's action is awaiting resolution.
885
+ * @memberof EventGroupCardComponent
886
+ */
887
+ resolveIsInFlight(meeting) {
888
+ return this.inFlightMeetingIds?.has(meeting?.meetingId) ?? false;
889
+ }
890
+ /**
891
+ * Toggles the meeting-list visibility, lazily requesting the first page of the
892
+ * event's child meetings from the container the first time it is opened (Req 6.6).
893
+ * @memberof EventGroupCardComponent
894
+ */
895
+ onToggleMeetingList() {
896
+ this.isMeetingListOpen = !this.isMeetingListOpen;
897
+ if (this.isMeetingListOpen) {
898
+ this.loadMeetings.emit(this.pageIndex + 1);
899
+ }
900
+ }
901
+ /**
902
+ * Handles the "View Event" button, emitting the event id to the host (Req 6.4).
903
+ * @memberof EventGroupCardComponent
904
+ */
905
+ onViewEvent() {
906
+ this.viewEvent.emit(this.event?.eventId);
907
+ }
908
+ /**
909
+ * Bubbles a row's meeting-name link activation upward (Req 6.7, 7.4).
910
+ * @param {string} meetingId - The id emitted by the row.
911
+ * @memberof EventGroupCardComponent
912
+ */
913
+ onOpenMeeting(meetingId) {
914
+ this.openMeeting.emit(meetingId);
915
+ }
916
+ /**
917
+ * Bubbles a row's log-attendance request upward (Req 6.7, 7.7).
918
+ * @param {string} meetingId - The id emitted by the row.
919
+ * @memberof EventGroupCardComponent
920
+ */
921
+ onLogAttendance(meetingId) {
922
+ this.logAttendance.emit(meetingId);
923
+ }
924
+ /**
925
+ * Bubbles a row's leave-meeting request upward (Req 6.7, 7.8).
926
+ * @param {string} meetingId - The id emitted by the row.
927
+ * @memberof EventGroupCardComponent
928
+ */
929
+ onLeaveMeeting(meetingId) {
930
+ this.leaveMeeting.emit(meetingId);
931
+ }
932
+ /**
933
+ * Handles the per-card paginator change, updating the local page state and
934
+ * emitting the requested page as a {@link PageChange} (Req 6.9, 3.3).
935
+ * @param {PageEvent} event - The Material paginator page event.
936
+ * @memberof EventGroupCardComponent
937
+ */
938
+ onPageChange(event) {
939
+ this.pageIndex = event.pageIndex;
940
+ this.pageSize = event.pageSize;
941
+ this.pageChange.emit({
942
+ pageIndex: event.pageIndex,
943
+ pageSize: event.pageSize,
944
+ });
945
+ // Request the newly selected page's child meetings from the container.
946
+ this.loadMeetings.emit(event.pageIndex + 1);
947
+ }
948
+ /**
949
+ * Formats a date for display, preferring the epoch-milliseconds value (from
950
+ * the real API) formatted in the event's timezone and falling back to a legacy
951
+ * ISO string. Returns an empty string when neither is present.
952
+ * @param {number | undefined} epochMs - The epoch-milliseconds value.
953
+ * @param {string | undefined} isoValue - The legacy ISO fallback.
954
+ * @return {string} The formatted date or an empty string.
955
+ * @memberof EventGroupCardComponent
956
+ */
957
+ formatDate(value) {
958
+ return formatEpoch(value, DateFormats.displayFormat, this.event?.timezone);
959
+ }
960
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EventGroupCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
961
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: EventGroupCardComponent, isStandalone: true, selector: "ieeesa-event-group-card", inputs: { event: "event", permissions: "permissions", attendanceStates: "attendanceStates", inFlightMeetingIds: "inFlightMeetingIds", isMobileView: "isMobileView" }, outputs: { viewEvent: "viewEvent", openMeeting: "openMeeting", logAttendance: "logAttendance", leaveMeeting: "leaveMeeting", pageChange: "pageChange", loadMeetings: "loadMeetings" }, ngImport: i0, template: "<article\r\n class=\"ieeesa-event-group-card\"\r\n [class.ieeesa-event-group-card--mobile]=\"isMobileView\"\r\n [class.ieeesa-event-group-card--standalone]=\"isStandalone\"\r\n>\r\n <!-- ============================ EVENT ============================ -->\r\n <ng-container *ngIf=\"!isStandalone\">\r\n <header class=\"ieeesa-event-group-card__header\">\r\n <div class=\"ieeesa-event-group-card__header-main\">\r\n <!-- EVENT badge + day indicator. -->\r\n <div class=\"ieeesa-event-group-card__badge-row\">\r\n <span class=\"ieeesa-event-group-card__badge\">\r\n {{ constants.eventGroupCard.eventBadge }}\r\n </span>\r\n <span\r\n *ngIf=\"event.dayIndicator\"\r\n class=\"ieeesa-event-group-card__day-indicator\"\r\n >\r\n {{ event.dayIndicator }}\r\n </span>\r\n </div>\r\n\r\n <div class=\"ieeesa-event-group-card__title-row\">\r\n <h3 class=\"ieeesa-event-group-card__title\">{{ event.title }}</h3>\r\n <span\r\n *ngIf=\"event.status\"\r\n class=\"ieeesa-event-group-card__status-pill\"\r\n [ngClass]=\"'ieeesa-event-group-card__status-pill--' + statusModifier\"\r\n >\r\n <span class=\"ieeesa-event-group-card__status-dot\" aria-hidden=\"true\"></span>\r\n {{ event.status }}\r\n </span>\r\n </div>\r\n\r\n <p\r\n *ngIf=\"subtitleText\"\r\n class=\"ieeesa-event-group-card__subtitle\"\r\n >\r\n {{ subtitleText }}\r\n </p>\r\n </div>\r\n\r\n <div class=\"ieeesa-event-group-card__header-side\">\r\n <dl class=\"ieeesa-event-group-card__dates-block\">\r\n <dt class=\"ieeesa-event-group-card__dates-label\">\r\n {{ constants.eventGroupCard.eventDatesLabel }}\r\n </dt>\r\n <dd class=\"ieeesa-event-group-card__dates-value\">\r\n {{ formattedStartDate }}<ng-container *ngIf=\"formattedEndDate && formattedEndDate !== formattedStartDate\">\r\n &ndash; {{ formattedEndDate }}</ng-container>\r\n </dd>\r\n <dd\r\n *ngIf=\"event.timezone\"\r\n class=\"ieeesa-event-group-card__timezone-value\"\r\n >\r\n {{ event.timezone }}\r\n </dd>\r\n </dl>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-event-group-card__view\"\r\n [attr.aria-label]=\"constants.eventGroupCard.ariaLabel.viewEvent\"\r\n (click)=\"onViewEvent()\"\r\n >\r\n {{ constants.eventGroupCard.viewEventLabel }}\r\n </button>\r\n </div>\r\n </header>\r\n\r\n <!-- Meetings label row + Show/Hide toggle. -->\r\n <div class=\"ieeesa-event-group-card__meetings-bar\">\r\n <span class=\"ieeesa-event-group-card__meetings-open\">\r\n <span class=\"ieeesa-event-group-card__meetings-dot\" aria-hidden=\"true\"></span>\r\n {{ openForAttendanceSummary }}\r\n </span>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-event-group-card__meeting-toggle\"\r\n [attr.aria-expanded]=\"isMeetingListOpen\"\r\n [attr.aria-label]=\"constants.eventGroupCard.ariaLabel.meetingListToggle\"\r\n (click)=\"onToggleMeetingList()\"\r\n >\r\n <span>{{ meetingListToggleLabel }}</span>\r\n <i\r\n class=\"fa-solid\"\r\n [ngClass]=\"isMeetingListOpen ? 'fa-chevron-up' : 'fa-chevron-down'\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n </button>\r\n </div>\r\n\r\n <!-- Expanded inner meeting list + per-event paginator. -->\r\n <div\r\n *ngIf=\"isMeetingListOpen\"\r\n class=\"ieeesa-event-group-card__meetings\"\r\n >\r\n <ieeesa-meeting-attendance-row\r\n *ngFor=\"let meeting of pagedMeetings; trackBy: trackByMeetingId\"\r\n [meeting]=\"meeting\"\r\n [attendanceState]=\"resolveAttendanceState(meeting)\"\r\n [isInFlight]=\"resolveIsInFlight(meeting)\"\r\n [permissions]=\"permissions\"\r\n (openMeeting)=\"onOpenMeeting($event)\"\r\n (logAttendance)=\"onLogAttendance($event)\"\r\n (leaveMeeting)=\"onLeaveMeeting($event)\"\r\n ></ieeesa-meeting-attendance-row>\r\n\r\n <div\r\n class=\"ieeesa-event-group-card__paginator\"\r\n [attr.aria-label]=\"constants.eventGroupCard.ariaLabel.meetingListToggle\"\r\n >\r\n <span class=\"ieeesa-event-group-card__paginator-range\">\r\n {{ rangeLabel }}\r\n </span>\r\n\r\n <mat-paginator\r\n class=\"ieeesa-event-group-card__paginator-control\"\r\n [pageIndex]=\"pageIndex\"\r\n [pageSize]=\"pageSize\"\r\n [length]=\"totalMeetings\"\r\n [hidePageSize]=\"true\"\r\n (page)=\"onPageChange($event)\"\r\n ></mat-paginator>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- ======================= STANDALONE MEETING ======================= -->\r\n <!-- Single horizontal row: badge+name+subtitle | MEETING DATE | TIME & TIMEZONE | action. -->\r\n <div *ngIf=\"isStandalone\" class=\"ieeesa-event-group-card__standalone\">\r\n <div class=\"ieeesa-event-group-card__standalone-main\">\r\n <span class=\"ieeesa-event-group-card__badge ieeesa-event-group-card__badge--standalone\">\r\n {{ constants.eventGroupCard.standaloneBadge }}\r\n </span>\r\n\r\n <div class=\"ieeesa-event-group-card__standalone-title-row\">\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-event-group-card__standalone-name\"\r\n [attr.aria-label]=\"constants.meetingRow.ariaLabel.meetingNameLink\"\r\n (click)=\"onOpenMeeting(event.eventId)\"\r\n >\r\n {{ event.title }}\r\n </button>\r\n <span\r\n *ngIf=\"event.status\"\r\n class=\"ieeesa-event-group-card__status-pill\"\r\n [ngClass]=\"'ieeesa-event-group-card__status-pill--' + statusModifier\"\r\n >\r\n <span class=\"ieeesa-event-group-card__status-dot\" aria-hidden=\"true\"></span>\r\n {{ event.status }}\r\n </span>\r\n </div>\r\n\r\n <p\r\n *ngIf=\"subtitleText\"\r\n class=\"ieeesa-event-group-card__standalone-subtitle\"\r\n >\r\n {{ subtitleText }}\r\n </p>\r\n </div>\r\n\r\n <!-- MEETING DATE column. -->\r\n <dl class=\"ieeesa-event-group-card__standalone-col\">\r\n <dt class=\"ieeesa-event-group-card__standalone-col-label\">\r\n {{ constants.eventGroupCard.meetingDateLabel }}\r\n </dt>\r\n <dd class=\"ieeesa-event-group-card__standalone-col-value\">\r\n {{ formattedStartDate }}<ng-container *ngIf=\"formattedEndDate && formattedEndDate !== formattedStartDate\">\r\n &ndash; {{ formattedEndDate }}</ng-container>\r\n </dd>\r\n </dl>\r\n\r\n <!-- TIME & TIMEZONE column. -->\r\n <dl class=\"ieeesa-event-group-card__standalone-col\">\r\n <dt class=\"ieeesa-event-group-card__standalone-col-label\">\r\n {{ constants.eventGroupCard.timeTimezoneLabel }}\r\n </dt>\r\n <dd class=\"ieeesa-event-group-card__standalone-col-value\">\r\n {{ standaloneTime }}\r\n </dd>\r\n </dl>\r\n\r\n <!-- Attendance action column. -->\r\n <div class=\"ieeesa-event-group-card__standalone-actions\">\r\n <span\r\n *ngIf=\"isStandaloneAttending\"\r\n class=\"ieeesa-event-group-card__attending-pill\"\r\n >\r\n <span class=\"ieeesa-event-group-card__status-dot\" aria-hidden=\"true\"></span>\r\n {{ constants.meetingRow.currentlyAttendingIndicator }}\r\n </span>\r\n\r\n <button\r\n *ngIf=\"showStandaloneLog\"\r\n type=\"button\"\r\n class=\"ieeesa-event-group-card__standalone-action ieeesa-event-group-card__standalone-action--log\"\r\n [disabled]=\"resolveIsInFlight(standaloneMeeting)\"\r\n [attr.aria-label]=\"constants.meetingRow.ariaLabel.logAttendance\"\r\n (click)=\"onLogAttendance(event.eventId)\"\r\n >\r\n {{ constants.meetingRow.logAttendanceLabel }}\r\n </button>\r\n\r\n <button\r\n *ngIf=\"showStandaloneLeave\"\r\n type=\"button\"\r\n class=\"ieeesa-event-group-card__standalone-action ieeesa-event-group-card__standalone-action--leave\"\r\n [disabled]=\"resolveIsInFlight(standaloneMeeting)\"\r\n [attr.aria-label]=\"constants.meetingRow.ariaLabel.leaveMeeting\"\r\n (click)=\"onLeaveMeeting(event.eventId)\"\r\n >\r\n {{ constants.meetingRow.leaveMeetingLabel }}\r\n </button>\r\n </div>\r\n </div>\r\n</article>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-event-group-card{display:flex;flex-direction:column;gap:16px;padding:20px 24px;border:1px solid #d7d6d6;border-radius:8px;background-color:#fff;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026;font-family:Calibri Regular}.ieeesa-event-group-card__header{display:flex;align-items:flex-start;justify-content:space-between;gap:24px}.ieeesa-event-group-card__header-main{display:flex;flex-direction:column;gap:8px;min-width:0}.ieeesa-event-group-card__badge-row{display:flex;align-items:center;gap:12px}.ieeesa-event-group-card__badge{display:inline-flex;align-items:center;padding:4px 12px;border-radius:4px;font-family:Calibri Bold;font-size:.75rem;letter-spacing:.03125em;text-transform:uppercase;color:#00629b;background-color:#eaf3f9}.ieeesa-event-group-card__badge--standalone{color:#333436;background-color:#f0f0f0}.ieeesa-event-group-card__day-indicator{font-size:.875rem;line-height:18px;color:#333436}.ieeesa-event-group-card__title-row{display:flex;align-items:center;flex-wrap:wrap;gap:12px}.ieeesa-event-group-card__title{margin:0;font-family:Calibri Bold;font-size:1.25rem;line-height:24px;color:#333436}.ieeesa-event-group-card__status-pill{display:inline-flex;align-items:center;gap:6px;padding:2px 12px;border-radius:16px;font-size:.8125rem;color:#1a7f37;background-color:#e8f5ec}.ieeesa-event-group-card__status-pill--upcoming,.ieeesa-event-group-card__status-pill--starts-soon{color:#8a6d00;background-color:#fef7e0}.ieeesa-event-group-card__status-pill--upcoming .ieeesa-event-group-card__status-dot,.ieeesa-event-group-card__status-pill--starts-soon .ieeesa-event-group-card__status-dot{background-color:#e5a200}.ieeesa-event-group-card__status-dot{width:8px;height:8px;border-radius:50%;background-color:#1a7f37}.ieeesa-event-group-card__subtitle{margin:0;font-size:.875rem;line-height:18px;color:#333436}.ieeesa-event-group-card__header-side{display:flex;align-items:flex-start;gap:24px;flex-shrink:0}.ieeesa-event-group-card__dates-block{display:flex;flex-direction:column;gap:2px;margin:0;text-align:right}.ieeesa-event-group-card__dates-label{font-family:Calibri Bold;font-size:.75rem;letter-spacing:.03125em;text-transform:uppercase;color:#939094}.ieeesa-event-group-card__dates-value{margin:0;font-size:.875rem;line-height:18px;color:#333436}.ieeesa-event-group-card__timezone-value{margin:0;font-size:.8125rem;color:#939094}.ieeesa-event-group-card__view{flex-shrink:0;min-width:122px;padding:8px 16px;border:1px solid #00629b;border-radius:4px;background-color:#fff;font-size:.875rem;color:#00629b;cursor:pointer}.ieeesa-event-group-card__view:hover{background-color:#eaf3f9}.ieeesa-event-group-card__meetings-bar{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px;padding-top:16px;border-top:1px solid #d7d6d6}.ieeesa-event-group-card__meetings-open{display:inline-flex;align-items:center;gap:8px;font-size:.875rem;color:#333436}.ieeesa-event-group-card__meetings-dot{width:8px;height:8px;border-radius:50%;background-color:#1a7f37}.ieeesa-event-group-card__meeting-toggle{display:inline-flex;align-items:center;gap:8px;padding:8px 0;border:0;background:none;font-size:.875rem;color:#00629b;cursor:pointer}.ieeesa-event-group-card__meetings{display:flex;flex-direction:column;border:1px solid #d7d6d6;border-radius:8px;overflow:hidden}.ieeesa-event-group-card__paginator{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px;padding:12px 20px;border-top:1px solid #d7d6d6}.ieeesa-event-group-card__paginator-range{font-size:.8125rem;color:#939094}.ieeesa-event-group-card__standalone{display:grid;grid-template-columns:minmax(260px,2.4fr) minmax(130px,1fr) minmax(200px,1.3fr) auto;align-items:center;column-gap:24px}.ieeesa-event-group-card__standalone-main{display:flex;flex-direction:column;gap:6px;min-width:0}.ieeesa-event-group-card__standalone-title-row{display:flex;align-items:center;flex-wrap:wrap;gap:12px}.ieeesa-event-group-card__standalone-name{padding:0;border:0;background:none;text-align:left;text-decoration:underline;font-family:Calibri Bold;font-size:1rem;line-height:20px;color:#00629b;cursor:pointer}.ieeesa-event-group-card__standalone-subtitle{margin:0;font-size:.875rem;line-height:18px;color:#333436}.ieeesa-event-group-card__standalone-col{display:flex;flex-direction:column;gap:4px;margin:0;min-width:0}.ieeesa-event-group-card__standalone-col-label{font-family:Calibri Bold;font-size:.75rem;letter-spacing:.03125em;text-transform:uppercase;color:#939094}.ieeesa-event-group-card__standalone-col-value{margin:0;font-size:.875rem;line-height:18px;color:#333436}.ieeesa-event-group-card__standalone-actions{display:flex;align-items:center;justify-content:flex-end;gap:16px}.ieeesa-event-group-card__attending-pill{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:16px;white-space:nowrap;font-size:.8125rem;color:#1a7f37;background-color:#e8f5ec}.ieeesa-event-group-card__standalone-action{min-width:146px;padding:10px 16px;border-radius:4px;font-family:Calibri Bold;font-size:.875rem;cursor:pointer}.ieeesa-event-group-card__standalone-action--log{border:1px solid #00629b;background-color:#00629b;color:#fff}.ieeesa-event-group-card__standalone-action--leave{border:1px solid #00629b;background-color:#fff;color:#00629b}.ieeesa-event-group-card__standalone-action:disabled{border-color:#939094;background-color:#939094;color:#fff;cursor:default}.ieeesa-event-group-card--mobile{padding:16px}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__header{flex-direction:column;align-items:stretch}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__header-side{flex-direction:column;align-items:stretch;gap:12px}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__dates-block{text-align:left}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__view{align-self:stretch;width:100%}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__paginator{flex-direction:column;align-items:stretch}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__standalone{grid-template-columns:1fr;row-gap:12px}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__standalone-actions{justify-content:flex-start}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i2.MatPaginator, selector: "mat-paginator", inputs: ["disabled"], exportAs: ["matPaginator"] }, { kind: "component", type: MeetingAttendanceRowComponent, selector: "ieeesa-meeting-attendance-row", inputs: ["meeting", "attendanceState", "isInFlight", "permissions", "compact"], outputs: ["openMeeting", "logAttendance", "leaveMeeting"] }], encapsulation: i0.ViewEncapsulation.None }); }
962
+ }
963
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EventGroupCardComponent, decorators: [{
964
+ type: Component,
965
+ args: [{ selector: 'ieeesa-event-group-card', standalone: true, imports: [CommonModule, MatPaginatorModule, MeetingAttendanceRowComponent], encapsulation: ViewEncapsulation.None, template: "<article\r\n class=\"ieeesa-event-group-card\"\r\n [class.ieeesa-event-group-card--mobile]=\"isMobileView\"\r\n [class.ieeesa-event-group-card--standalone]=\"isStandalone\"\r\n>\r\n <!-- ============================ EVENT ============================ -->\r\n <ng-container *ngIf=\"!isStandalone\">\r\n <header class=\"ieeesa-event-group-card__header\">\r\n <div class=\"ieeesa-event-group-card__header-main\">\r\n <!-- EVENT badge + day indicator. -->\r\n <div class=\"ieeesa-event-group-card__badge-row\">\r\n <span class=\"ieeesa-event-group-card__badge\">\r\n {{ constants.eventGroupCard.eventBadge }}\r\n </span>\r\n <span\r\n *ngIf=\"event.dayIndicator\"\r\n class=\"ieeesa-event-group-card__day-indicator\"\r\n >\r\n {{ event.dayIndicator }}\r\n </span>\r\n </div>\r\n\r\n <div class=\"ieeesa-event-group-card__title-row\">\r\n <h3 class=\"ieeesa-event-group-card__title\">{{ event.title }}</h3>\r\n <span\r\n *ngIf=\"event.status\"\r\n class=\"ieeesa-event-group-card__status-pill\"\r\n [ngClass]=\"'ieeesa-event-group-card__status-pill--' + statusModifier\"\r\n >\r\n <span class=\"ieeesa-event-group-card__status-dot\" aria-hidden=\"true\"></span>\r\n {{ event.status }}\r\n </span>\r\n </div>\r\n\r\n <p\r\n *ngIf=\"subtitleText\"\r\n class=\"ieeesa-event-group-card__subtitle\"\r\n >\r\n {{ subtitleText }}\r\n </p>\r\n </div>\r\n\r\n <div class=\"ieeesa-event-group-card__header-side\">\r\n <dl class=\"ieeesa-event-group-card__dates-block\">\r\n <dt class=\"ieeesa-event-group-card__dates-label\">\r\n {{ constants.eventGroupCard.eventDatesLabel }}\r\n </dt>\r\n <dd class=\"ieeesa-event-group-card__dates-value\">\r\n {{ formattedStartDate }}<ng-container *ngIf=\"formattedEndDate && formattedEndDate !== formattedStartDate\">\r\n &ndash; {{ formattedEndDate }}</ng-container>\r\n </dd>\r\n <dd\r\n *ngIf=\"event.timezone\"\r\n class=\"ieeesa-event-group-card__timezone-value\"\r\n >\r\n {{ event.timezone }}\r\n </dd>\r\n </dl>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-event-group-card__view\"\r\n [attr.aria-label]=\"constants.eventGroupCard.ariaLabel.viewEvent\"\r\n (click)=\"onViewEvent()\"\r\n >\r\n {{ constants.eventGroupCard.viewEventLabel }}\r\n </button>\r\n </div>\r\n </header>\r\n\r\n <!-- Meetings label row + Show/Hide toggle. -->\r\n <div class=\"ieeesa-event-group-card__meetings-bar\">\r\n <span class=\"ieeesa-event-group-card__meetings-open\">\r\n <span class=\"ieeesa-event-group-card__meetings-dot\" aria-hidden=\"true\"></span>\r\n {{ openForAttendanceSummary }}\r\n </span>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-event-group-card__meeting-toggle\"\r\n [attr.aria-expanded]=\"isMeetingListOpen\"\r\n [attr.aria-label]=\"constants.eventGroupCard.ariaLabel.meetingListToggle\"\r\n (click)=\"onToggleMeetingList()\"\r\n >\r\n <span>{{ meetingListToggleLabel }}</span>\r\n <i\r\n class=\"fa-solid\"\r\n [ngClass]=\"isMeetingListOpen ? 'fa-chevron-up' : 'fa-chevron-down'\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n </button>\r\n </div>\r\n\r\n <!-- Expanded inner meeting list + per-event paginator. -->\r\n <div\r\n *ngIf=\"isMeetingListOpen\"\r\n class=\"ieeesa-event-group-card__meetings\"\r\n >\r\n <ieeesa-meeting-attendance-row\r\n *ngFor=\"let meeting of pagedMeetings; trackBy: trackByMeetingId\"\r\n [meeting]=\"meeting\"\r\n [attendanceState]=\"resolveAttendanceState(meeting)\"\r\n [isInFlight]=\"resolveIsInFlight(meeting)\"\r\n [permissions]=\"permissions\"\r\n (openMeeting)=\"onOpenMeeting($event)\"\r\n (logAttendance)=\"onLogAttendance($event)\"\r\n (leaveMeeting)=\"onLeaveMeeting($event)\"\r\n ></ieeesa-meeting-attendance-row>\r\n\r\n <div\r\n class=\"ieeesa-event-group-card__paginator\"\r\n [attr.aria-label]=\"constants.eventGroupCard.ariaLabel.meetingListToggle\"\r\n >\r\n <span class=\"ieeesa-event-group-card__paginator-range\">\r\n {{ rangeLabel }}\r\n </span>\r\n\r\n <mat-paginator\r\n class=\"ieeesa-event-group-card__paginator-control\"\r\n [pageIndex]=\"pageIndex\"\r\n [pageSize]=\"pageSize\"\r\n [length]=\"totalMeetings\"\r\n [hidePageSize]=\"true\"\r\n (page)=\"onPageChange($event)\"\r\n ></mat-paginator>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- ======================= STANDALONE MEETING ======================= -->\r\n <!-- Single horizontal row: badge+name+subtitle | MEETING DATE | TIME & TIMEZONE | action. -->\r\n <div *ngIf=\"isStandalone\" class=\"ieeesa-event-group-card__standalone\">\r\n <div class=\"ieeesa-event-group-card__standalone-main\">\r\n <span class=\"ieeesa-event-group-card__badge ieeesa-event-group-card__badge--standalone\">\r\n {{ constants.eventGroupCard.standaloneBadge }}\r\n </span>\r\n\r\n <div class=\"ieeesa-event-group-card__standalone-title-row\">\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-event-group-card__standalone-name\"\r\n [attr.aria-label]=\"constants.meetingRow.ariaLabel.meetingNameLink\"\r\n (click)=\"onOpenMeeting(event.eventId)\"\r\n >\r\n {{ event.title }}\r\n </button>\r\n <span\r\n *ngIf=\"event.status\"\r\n class=\"ieeesa-event-group-card__status-pill\"\r\n [ngClass]=\"'ieeesa-event-group-card__status-pill--' + statusModifier\"\r\n >\r\n <span class=\"ieeesa-event-group-card__status-dot\" aria-hidden=\"true\"></span>\r\n {{ event.status }}\r\n </span>\r\n </div>\r\n\r\n <p\r\n *ngIf=\"subtitleText\"\r\n class=\"ieeesa-event-group-card__standalone-subtitle\"\r\n >\r\n {{ subtitleText }}\r\n </p>\r\n </div>\r\n\r\n <!-- MEETING DATE column. -->\r\n <dl class=\"ieeesa-event-group-card__standalone-col\">\r\n <dt class=\"ieeesa-event-group-card__standalone-col-label\">\r\n {{ constants.eventGroupCard.meetingDateLabel }}\r\n </dt>\r\n <dd class=\"ieeesa-event-group-card__standalone-col-value\">\r\n {{ formattedStartDate }}<ng-container *ngIf=\"formattedEndDate && formattedEndDate !== formattedStartDate\">\r\n &ndash; {{ formattedEndDate }}</ng-container>\r\n </dd>\r\n </dl>\r\n\r\n <!-- TIME & TIMEZONE column. -->\r\n <dl class=\"ieeesa-event-group-card__standalone-col\">\r\n <dt class=\"ieeesa-event-group-card__standalone-col-label\">\r\n {{ constants.eventGroupCard.timeTimezoneLabel }}\r\n </dt>\r\n <dd class=\"ieeesa-event-group-card__standalone-col-value\">\r\n {{ standaloneTime }}\r\n </dd>\r\n </dl>\r\n\r\n <!-- Attendance action column. -->\r\n <div class=\"ieeesa-event-group-card__standalone-actions\">\r\n <span\r\n *ngIf=\"isStandaloneAttending\"\r\n class=\"ieeesa-event-group-card__attending-pill\"\r\n >\r\n <span class=\"ieeesa-event-group-card__status-dot\" aria-hidden=\"true\"></span>\r\n {{ constants.meetingRow.currentlyAttendingIndicator }}\r\n </span>\r\n\r\n <button\r\n *ngIf=\"showStandaloneLog\"\r\n type=\"button\"\r\n class=\"ieeesa-event-group-card__standalone-action ieeesa-event-group-card__standalone-action--log\"\r\n [disabled]=\"resolveIsInFlight(standaloneMeeting)\"\r\n [attr.aria-label]=\"constants.meetingRow.ariaLabel.logAttendance\"\r\n (click)=\"onLogAttendance(event.eventId)\"\r\n >\r\n {{ constants.meetingRow.logAttendanceLabel }}\r\n </button>\r\n\r\n <button\r\n *ngIf=\"showStandaloneLeave\"\r\n type=\"button\"\r\n class=\"ieeesa-event-group-card__standalone-action ieeesa-event-group-card__standalone-action--leave\"\r\n [disabled]=\"resolveIsInFlight(standaloneMeeting)\"\r\n [attr.aria-label]=\"constants.meetingRow.ariaLabel.leaveMeeting\"\r\n (click)=\"onLeaveMeeting(event.eventId)\"\r\n >\r\n {{ constants.meetingRow.leaveMeetingLabel }}\r\n </button>\r\n </div>\r\n </div>\r\n</article>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-event-group-card{display:flex;flex-direction:column;gap:16px;padding:20px 24px;border:1px solid #d7d6d6;border-radius:8px;background-color:#fff;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026;font-family:Calibri Regular}.ieeesa-event-group-card__header{display:flex;align-items:flex-start;justify-content:space-between;gap:24px}.ieeesa-event-group-card__header-main{display:flex;flex-direction:column;gap:8px;min-width:0}.ieeesa-event-group-card__badge-row{display:flex;align-items:center;gap:12px}.ieeesa-event-group-card__badge{display:inline-flex;align-items:center;padding:4px 12px;border-radius:4px;font-family:Calibri Bold;font-size:.75rem;letter-spacing:.03125em;text-transform:uppercase;color:#00629b;background-color:#eaf3f9}.ieeesa-event-group-card__badge--standalone{color:#333436;background-color:#f0f0f0}.ieeesa-event-group-card__day-indicator{font-size:.875rem;line-height:18px;color:#333436}.ieeesa-event-group-card__title-row{display:flex;align-items:center;flex-wrap:wrap;gap:12px}.ieeesa-event-group-card__title{margin:0;font-family:Calibri Bold;font-size:1.25rem;line-height:24px;color:#333436}.ieeesa-event-group-card__status-pill{display:inline-flex;align-items:center;gap:6px;padding:2px 12px;border-radius:16px;font-size:.8125rem;color:#1a7f37;background-color:#e8f5ec}.ieeesa-event-group-card__status-pill--upcoming,.ieeesa-event-group-card__status-pill--starts-soon{color:#8a6d00;background-color:#fef7e0}.ieeesa-event-group-card__status-pill--upcoming .ieeesa-event-group-card__status-dot,.ieeesa-event-group-card__status-pill--starts-soon .ieeesa-event-group-card__status-dot{background-color:#e5a200}.ieeesa-event-group-card__status-dot{width:8px;height:8px;border-radius:50%;background-color:#1a7f37}.ieeesa-event-group-card__subtitle{margin:0;font-size:.875rem;line-height:18px;color:#333436}.ieeesa-event-group-card__header-side{display:flex;align-items:flex-start;gap:24px;flex-shrink:0}.ieeesa-event-group-card__dates-block{display:flex;flex-direction:column;gap:2px;margin:0;text-align:right}.ieeesa-event-group-card__dates-label{font-family:Calibri Bold;font-size:.75rem;letter-spacing:.03125em;text-transform:uppercase;color:#939094}.ieeesa-event-group-card__dates-value{margin:0;font-size:.875rem;line-height:18px;color:#333436}.ieeesa-event-group-card__timezone-value{margin:0;font-size:.8125rem;color:#939094}.ieeesa-event-group-card__view{flex-shrink:0;min-width:122px;padding:8px 16px;border:1px solid #00629b;border-radius:4px;background-color:#fff;font-size:.875rem;color:#00629b;cursor:pointer}.ieeesa-event-group-card__view:hover{background-color:#eaf3f9}.ieeesa-event-group-card__meetings-bar{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px;padding-top:16px;border-top:1px solid #d7d6d6}.ieeesa-event-group-card__meetings-open{display:inline-flex;align-items:center;gap:8px;font-size:.875rem;color:#333436}.ieeesa-event-group-card__meetings-dot{width:8px;height:8px;border-radius:50%;background-color:#1a7f37}.ieeesa-event-group-card__meeting-toggle{display:inline-flex;align-items:center;gap:8px;padding:8px 0;border:0;background:none;font-size:.875rem;color:#00629b;cursor:pointer}.ieeesa-event-group-card__meetings{display:flex;flex-direction:column;border:1px solid #d7d6d6;border-radius:8px;overflow:hidden}.ieeesa-event-group-card__paginator{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px;padding:12px 20px;border-top:1px solid #d7d6d6}.ieeesa-event-group-card__paginator-range{font-size:.8125rem;color:#939094}.ieeesa-event-group-card__standalone{display:grid;grid-template-columns:minmax(260px,2.4fr) minmax(130px,1fr) minmax(200px,1.3fr) auto;align-items:center;column-gap:24px}.ieeesa-event-group-card__standalone-main{display:flex;flex-direction:column;gap:6px;min-width:0}.ieeesa-event-group-card__standalone-title-row{display:flex;align-items:center;flex-wrap:wrap;gap:12px}.ieeesa-event-group-card__standalone-name{padding:0;border:0;background:none;text-align:left;text-decoration:underline;font-family:Calibri Bold;font-size:1rem;line-height:20px;color:#00629b;cursor:pointer}.ieeesa-event-group-card__standalone-subtitle{margin:0;font-size:.875rem;line-height:18px;color:#333436}.ieeesa-event-group-card__standalone-col{display:flex;flex-direction:column;gap:4px;margin:0;min-width:0}.ieeesa-event-group-card__standalone-col-label{font-family:Calibri Bold;font-size:.75rem;letter-spacing:.03125em;text-transform:uppercase;color:#939094}.ieeesa-event-group-card__standalone-col-value{margin:0;font-size:.875rem;line-height:18px;color:#333436}.ieeesa-event-group-card__standalone-actions{display:flex;align-items:center;justify-content:flex-end;gap:16px}.ieeesa-event-group-card__attending-pill{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:16px;white-space:nowrap;font-size:.8125rem;color:#1a7f37;background-color:#e8f5ec}.ieeesa-event-group-card__standalone-action{min-width:146px;padding:10px 16px;border-radius:4px;font-family:Calibri Bold;font-size:.875rem;cursor:pointer}.ieeesa-event-group-card__standalone-action--log{border:1px solid #00629b;background-color:#00629b;color:#fff}.ieeesa-event-group-card__standalone-action--leave{border:1px solid #00629b;background-color:#fff;color:#00629b}.ieeesa-event-group-card__standalone-action:disabled{border-color:#939094;background-color:#939094;color:#fff;cursor:default}.ieeesa-event-group-card--mobile{padding:16px}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__header{flex-direction:column;align-items:stretch}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__header-side{flex-direction:column;align-items:stretch;gap:12px}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__dates-block{text-align:left}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__view{align-self:stretch;width:100%}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__paginator{flex-direction:column;align-items:stretch}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__standalone{grid-template-columns:1fr;row-gap:12px}.ieeesa-event-group-card--mobile .ieeesa-event-group-card__standalone-actions{justify-content:flex-start}\n"] }]
966
+ }], propDecorators: { event: [{
967
+ type: Input
968
+ }], permissions: [{
969
+ type: Input
970
+ }], attendanceStates: [{
971
+ type: Input
972
+ }], inFlightMeetingIds: [{
973
+ type: Input
974
+ }], isMobileView: [{
975
+ type: Input
976
+ }], viewEvent: [{
977
+ type: Output
978
+ }], openMeeting: [{
979
+ type: Output
980
+ }], logAttendance: [{
981
+ type: Output
982
+ }], leaveMeeting: [{
983
+ type: Output
984
+ }], pageChange: [{
985
+ type: Output
986
+ }], loadMeetings: [{
987
+ type: Output
988
+ }] } });
989
+
990
+ /**
991
+ * Upcoming_Meetings_Events renders the "Upcoming Meetings & Events" dashboard
992
+ * section: a section title and "View All" link, a collapse/expand toggle that
993
+ * starts expanded, and one {@link EventGroupCardComponent} per upcoming event.
994
+ *
995
+ * The section is driven by its `sectionState` input: it shows the Loader while
996
+ * `LOADING`, the event-group cards while `LOADED`, the empty-state message while
997
+ * `EMPTY`, and the error message while `ERROR` — the four regions are mutually
998
+ * exclusive (Req 13.1, 13.2, 13.3, 13.5). The component is purely
999
+ * presentational: "View All" emits `viewAll` (Req 5.1, 5.2), the collapse toggle
1000
+ * flips `isExpanded` and the card-list visibility while exposing
1001
+ * `[attr.aria-expanded]` (Req 5.3, 5.4, 14.3), and each card is rendered via
1002
+ * `*ngFor` with {@link trackByEventId} to preserve input order (Req 5.5, 5.6,
1003
+ * 14.4). Card outputs (`viewEvent`/`openMeeting`/`logAttendance`/`leaveMeeting`)
1004
+ * are bubbled upward unchanged, and each card's `pageChange` is mapped to
1005
+ * `meetingPageChange` tagged with the originating event id (Req 6.9).
1006
+ * @export
1007
+ * @class UpcomingMeetingsEventsComponent
1008
+ */
1009
+ class UpcomingMeetingsEventsComponent {
1010
+ constructor() {
1011
+ /** User-facing strings sourced from `constants.json`. */
1012
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1013
+ this.constants = constants$1;
1014
+ /** Enum reference for template comparisons against `sectionState`. */
1015
+ this.sectionStates = DashboardSectionState;
1016
+ /** The upcoming events rendered as event-group cards (Req 5.5, 5.6). */
1017
+ this.events = [];
1018
+ /** The load/empty/error status driving the section's mutually exclusive regions. */
1019
+ this.sectionState = DashboardSectionState.LOADING;
1020
+ /** Host permissions gating the per-row log-attendance / leave-meeting buttons. */
1021
+ this.permissions = {
1022
+ canLogAttendance: false,
1023
+ canLeaveMeeting: false,
1024
+ };
1025
+ /** Map of meeting id to current attendance state, passed down to each card. */
1026
+ this.attendanceStates = {};
1027
+ /** Set of meeting ids with an attendance action awaiting resolution. */
1028
+ this.inFlightMeetingIds = new Set();
1029
+ /** Whether the host has determined the viewport is at/below the mobile breakpoint. */
1030
+ this.isMobileView = false;
1031
+ /** Emits when the "View All" link is activated (Req 5.2). */
1032
+ this.viewAll = new EventEmitter();
1033
+ /** Bubbles the event id when a card's "View Event" button is activated (Req 6.4). */
1034
+ this.viewEvent = new EventEmitter();
1035
+ /** Bubbles the meeting id when a card's meeting-name link is activated (Req 7.4). */
1036
+ this.openMeeting = new EventEmitter();
1037
+ /** Bubbles the meeting id when a card requests logging attendance (Req 7.7). */
1038
+ this.logAttendance = new EventEmitter();
1039
+ /** Bubbles the meeting id when a card requests leaving the meeting (Req 7.8). */
1040
+ this.leaveMeeting = new EventEmitter();
1041
+ /** Emits the requested page tagged with its event id when a card paginates (Req 6.9). */
1042
+ this.meetingPageChange = new EventEmitter();
1043
+ /** Emits the event id + one-based page to lazily load an event's child meetings. */
1044
+ this.loadEventMeetings = new EventEmitter();
1045
+ /** Whether the section's card list is currently expanded; starts collapsed. */
1046
+ this.isExpanded = false;
1047
+ /** Section-level page size — 5 events/meetings per page per the design. */
1048
+ this.sectionPageSize = 5;
1049
+ /** The current zero-based page index of the section list. */
1050
+ this.sectionPageIndex = 0;
1051
+ /**
1052
+ * Emits the new expanded state whenever the user toggles the section, so a
1053
+ * two-way `[(expanded)]` binding keeps the host's flag in sync (otherwise a
1054
+ * host-opened section the user then collapses leaves the host flag stale,
1055
+ * making a repeat "Jump to" a no-op).
1056
+ * @memberof UpcomingMeetingsEventsComponent
1057
+ */
1058
+ this.expandedChange = new EventEmitter();
1059
+ }
1060
+ /** The total number of events/meetings in the section (paginator length). */
1061
+ get totalItems() {
1062
+ return this.events?.length ?? 0;
1063
+ }
1064
+ /**
1065
+ * The slice of events/meetings for the current section page (5 per page),
1066
+ * used by the card `*ngFor`.
1067
+ * @return {UpcomingEvent[]} The events on the current page.
1068
+ * @memberof UpcomingMeetingsEventsComponent
1069
+ */
1070
+ get pagedEvents() {
1071
+ const start = this.sectionPageIndex * this.sectionPageSize;
1072
+ return (this.events ?? []).slice(start, start + this.sectionPageSize);
1073
+ }
1074
+ /** The one-based index of the first item shown on the current page. */
1075
+ get sectionRangeStart() {
1076
+ return this.totalItems === 0
1077
+ ? 0
1078
+ : this.sectionPageIndex * this.sectionPageSize + 1;
1079
+ }
1080
+ /** The one-based index of the last item shown on the current page. */
1081
+ get sectionRangeEnd() {
1082
+ return Math.min((this.sectionPageIndex + 1) * this.sectionPageSize, this.totalItems);
1083
+ }
1084
+ /**
1085
+ * The section paginator range label (e.g. "1-5 of 20 Meetings & Events")
1086
+ * built from the `constants.json` template.
1087
+ * @return {string} The formatted range label.
1088
+ * @memberof UpcomingMeetingsEventsComponent
1089
+ */
1090
+ get sectionRangeLabel() {
1091
+ const template = this.constants?.upcomingSection?.paginator?.rangeTemplate ??
1092
+ '{start}-{end} of {total} Meetings & Events';
1093
+ return template
1094
+ .replace('{start}', `${this.sectionRangeStart}`)
1095
+ .replace('{end}', `${this.sectionRangeEnd}`)
1096
+ .replace('{total}', `${this.totalItems}`);
1097
+ }
1098
+ /**
1099
+ * Handles the section paginator change, updating the current section page.
1100
+ * @param {PageEvent} event - The Material paginator page event.
1101
+ * @return {void}
1102
+ * @memberof UpcomingMeetingsEventsComponent
1103
+ */
1104
+ onSectionPageChange(event) {
1105
+ this.sectionPageIndex = event.pageIndex;
1106
+ }
1107
+ /**
1108
+ * Allows the host/container to open or close the section (e.g. a "Jump to"
1109
+ * shortcut opens it). Writing the input mirrors the value onto the internal
1110
+ * expand state while leaving the user's own toggle free to change it after.
1111
+ * @memberof UpcomingMeetingsEventsComponent
1112
+ */
1113
+ set expanded(value) {
1114
+ this.isExpanded = !!value;
1115
+ }
1116
+ get expanded() {
1117
+ return this.isExpanded;
1118
+ }
1119
+ /**
1120
+ * `trackBy` for the event-group-card `*ngFor`, keyed by the stable event id so
1121
+ * Angular renders exactly one card per event in input order (Req 5.5, 5.6, 14.4).
1122
+ * @param {number} index - The iteration index (fallback key).
1123
+ * @param {UpcomingEvent} event - The event for this card.
1124
+ * @return {string} The stable track key (event id).
1125
+ * @memberof UpcomingMeetingsEventsComponent
1126
+ */
1127
+ trackByEventId(index, event) {
1128
+ return event?.eventId ?? `${index}`;
1129
+ }
1130
+ /**
1131
+ * Toggles the section's expanded/collapsed state, inverting the card-list
1132
+ * visibility and the exposed `aria-expanded` value (Req 5.4, 14.3).
1133
+ * @memberof UpcomingMeetingsEventsComponent
1134
+ */
1135
+ onToggleExpanded() {
1136
+ this.isExpanded = !this.isExpanded;
1137
+ this.expandedChange.emit(this.isExpanded);
1138
+ }
1139
+ /**
1140
+ * Handles the "View All" link, emitting the `viewAll` intent to the host (Req 5.2).
1141
+ * @memberof UpcomingMeetingsEventsComponent
1142
+ */
1143
+ onViewAll() {
1144
+ this.viewAll.emit();
1145
+ }
1146
+ /**
1147
+ * Bubbles a card's "View Event" activation upward (Req 6.4).
1148
+ * @param {string} eventId - The event id emitted by the card.
1149
+ * @memberof UpcomingMeetingsEventsComponent
1150
+ */
1151
+ onViewEvent(eventId) {
1152
+ this.viewEvent.emit(eventId);
1153
+ }
1154
+ /**
1155
+ * Bubbles a card's meeting-name link activation upward (Req 7.4).
1156
+ * @param {string} meetingId - The meeting id emitted by the card.
1157
+ * @memberof UpcomingMeetingsEventsComponent
1158
+ */
1159
+ onOpenMeeting(meetingId) {
1160
+ this.openMeeting.emit(meetingId);
1161
+ }
1162
+ /**
1163
+ * Bubbles a card's log-attendance request upward (Req 7.7).
1164
+ * @param {string} meetingId - The meeting id emitted by the card.
1165
+ * @memberof UpcomingMeetingsEventsComponent
1166
+ */
1167
+ onLogAttendance(meetingId) {
1168
+ this.logAttendance.emit(meetingId);
1169
+ }
1170
+ /**
1171
+ * Bubbles a card's leave-meeting request upward (Req 7.8).
1172
+ * @param {string} meetingId - The meeting id emitted by the card.
1173
+ * @memberof UpcomingMeetingsEventsComponent
1174
+ */
1175
+ onLeaveMeeting(meetingId) {
1176
+ this.leaveMeeting.emit(meetingId);
1177
+ }
1178
+ /**
1179
+ * Maps a card's `pageChange` into a `meetingPageChange` tagged with the
1180
+ * originating event id so the host can page the correct event (Req 6.9).
1181
+ * @param {string} eventId - The id of the event whose card paginated.
1182
+ * @param {PageChange} page - The requested page from the card paginator.
1183
+ * @memberof UpcomingMeetingsEventsComponent
1184
+ */
1185
+ onMeetingPageChange(eventId, page) {
1186
+ this.meetingPageChange.emit({ eventId, ...page });
1187
+ }
1188
+ /**
1189
+ * Bubbles a card's lazy child-meeting load request upward, tagged with the
1190
+ * originating event id so the container can fetch that event's meetings.
1191
+ * @param {string} eventId - The event whose meetings to load.
1192
+ * @param {number} page - The one-based page requested by the card.
1193
+ * @memberof UpcomingMeetingsEventsComponent
1194
+ */
1195
+ onLoadEventMeetings(eventId, page) {
1196
+ this.loadEventMeetings.emit({ eventId, page });
1197
+ }
1198
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: UpcomingMeetingsEventsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1199
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: UpcomingMeetingsEventsComponent, isStandalone: true, selector: "ieeesa-upcoming-meetings-events", inputs: { events: "events", sectionState: "sectionState", permissions: "permissions", attendanceStates: "attendanceStates", inFlightMeetingIds: "inFlightMeetingIds", isMobileView: "isMobileView", expanded: "expanded" }, outputs: { viewAll: "viewAll", viewEvent: "viewEvent", openMeeting: "openMeeting", logAttendance: "logAttendance", leaveMeeting: "leaveMeeting", meetingPageChange: "meetingPageChange", loadEventMeetings: "loadEventMeetings", expandedChange: "expandedChange" }, ngImport: i0, template: "<section\r\n class=\"ieeesa-upcoming-meetings-events\"\r\n [class.ieeesa-upcoming-meetings-events--mobile]=\"isMobileView\"\r\n>\r\n <header class=\"ieeesa-upcoming-meetings-events__header\">\r\n <!-- Section title (Req 5.1). -->\r\n <div class=\"ieeesa-upcoming-meetings-events__heading\">\r\n <h2 class=\"ieeesa-upcoming-meetings-events__title\">\r\n {{ constants.upcomingSection.title }}\r\n </h2>\r\n </div>\r\n\r\n <!-- View All link + Expand/Collapse toggle (Req 5.1, 5.2, 5.3, 5.4, 14.3). -->\r\n <div class=\"ieeesa-upcoming-meetings-events__actions\">\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-upcoming-meetings-events__view-all\"\r\n [attr.aria-label]=\"constants.upcomingSection.ariaLabel.viewAll\"\r\n (click)=\"onViewAll()\"\r\n >\r\n {{ constants.upcomingSection.viewAllLabel }}\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-upcoming-meetings-events__toggle\"\r\n [attr.aria-expanded]=\"isExpanded\"\r\n [attr.aria-label]=\"constants.upcomingSection.ariaLabel.toggle\"\r\n (click)=\"onToggleExpanded()\"\r\n >\r\n <span>{{ isExpanded ? constants.upcomingSection.collapseLabel : constants.upcomingSection.expandLabel }}</span>\r\n <i\r\n class=\"fa-solid\"\r\n [ngClass]=\"isExpanded ? 'fa-chevron-up' : 'fa-chevron-down'\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n </button>\r\n </div>\r\n </header>\r\n\r\n <!-- Card list, shown only while expanded (Req 5.4). -->\r\n <div *ngIf=\"isExpanded\" class=\"ieeesa-upcoming-meetings-events__body\">\r\n <!-- Loader region (Req 13.1). -->\r\n <div\r\n *ngIf=\"sectionState === sectionStates.LOADING\"\r\n class=\"ieeesa-upcoming-meetings-events__loader\"\r\n >\r\n <ieeesa-loader></ieeesa-loader>\r\n </div>\r\n\r\n <!-- Content region: one card per event preserving input order (Req 5.5, 5.6, 13.2). -->\r\n <div\r\n *ngIf=\"sectionState === sectionStates.LOADED\"\r\n class=\"ieeesa-upcoming-meetings-events__cards\"\r\n >\r\n <ieeesa-event-group-card\r\n *ngFor=\"let event of pagedEvents; trackBy: trackByEventId\"\r\n [event]=\"event\"\r\n [permissions]=\"permissions\"\r\n [attendanceStates]=\"attendanceStates\"\r\n [inFlightMeetingIds]=\"inFlightMeetingIds\"\r\n [isMobileView]=\"isMobileView\"\r\n (viewEvent)=\"onViewEvent($event)\"\r\n (openMeeting)=\"onOpenMeeting($event)\"\r\n (logAttendance)=\"onLogAttendance($event)\"\r\n (leaveMeeting)=\"onLeaveMeeting($event)\"\r\n (pageChange)=\"onMeetingPageChange(event.eventId, $event)\"\r\n (loadMeetings)=\"onLoadEventMeetings(event.eventId, $event)\"\r\n ></ieeesa-event-group-card>\r\n\r\n <!-- Section-level paginator: 5 events/meetings per page (Req 3.x, design). -->\r\n <div\r\n *ngIf=\"totalItems > sectionPageSize\"\r\n class=\"ieeesa-upcoming-meetings-events__paginator\"\r\n >\r\n <span class=\"ieeesa-upcoming-meetings-events__paginator-range\">\r\n {{ sectionRangeLabel }}\r\n </span>\r\n <mat-paginator\r\n class=\"ieeesa-upcoming-meetings-events__paginator-control\"\r\n [pageIndex]=\"sectionPageIndex\"\r\n [pageSize]=\"sectionPageSize\"\r\n [length]=\"totalItems\"\r\n [hidePageSize]=\"true\"\r\n (page)=\"onSectionPageChange($event)\"\r\n ></mat-paginator>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty-state region (Req 13.3). -->\r\n <p\r\n *ngIf=\"sectionState === sectionStates.EMPTY\"\r\n class=\"ieeesa-upcoming-meetings-events__empty\"\r\n >\r\n {{ constants.upcomingSection.emptyMessage }}\r\n </p>\r\n\r\n <!-- Error region (Req 13.5). -->\r\n <p\r\n *ngIf=\"sectionState === sectionStates.ERROR\"\r\n class=\"ieeesa-upcoming-meetings-events__error\"\r\n role=\"alert\"\r\n >\r\n {{ constants.upcomingSection.errorMessage }}\r\n </p>\r\n </div>\r\n</section>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-upcoming-meetings-events{display:flex;flex-direction:column;gap:16px;background-color:#fff;border-radius:12px;box-shadow:0 1px 2px #0003;border:1px solid #e2e8f0;padding:20px 24px}.ieeesa-upcoming-meetings-events__header{display:flex;align-items:center;justify-content:space-between;gap:16px}.ieeesa-upcoming-meetings-events__heading{display:flex;min-width:0}.ieeesa-upcoming-meetings-events__title{margin:0;font-family:Calibri Bold;font-weight:700;font-size:18px;color:#0f172a}.ieeesa-upcoming-meetings-events__actions{display:flex;align-items:center;gap:16px}.ieeesa-upcoming-meetings-events__view-all{flex-shrink:0;background:none;border:none;padding:0;cursor:pointer;font-family:Calibri Bold;font-weight:700;font-size:18px;color:#00629b}.ieeesa-upcoming-meetings-events__view-all:hover{text-decoration:underline}.ieeesa-upcoming-meetings-events__toggle{display:inline-flex;align-items:center;gap:8px;padding:6px 14px;border:1px solid #dbe2eb;border-radius:8px;background-color:#fff;cursor:pointer;font-family:Calibri Bold;font-weight:700;font-size:18px;color:#00629b}.ieeesa-upcoming-meetings-events__toggle:hover{border-color:#00629b}.ieeesa-upcoming-meetings-events__toggle i{font-size:.75rem}.ieeesa-upcoming-meetings-events__body{display:flex;flex-direction:column}.ieeesa-upcoming-meetings-events__loader{display:flex;align-items:center;justify-content:center;padding:24px}.ieeesa-upcoming-meetings-events__cards{display:flex;flex-direction:column;gap:16px}.ieeesa-upcoming-meetings-events__paginator{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:12px;padding-top:8px}.ieeesa-upcoming-meetings-events__paginator-range{font-size:.8125rem;color:#939094}.ieeesa-upcoming-meetings-events__empty,.ieeesa-upcoming-meetings-events__error{margin:0;padding:16px;font-size:.875rem;color:#939094}.ieeesa-upcoming-meetings-events__error{color:#ba0c2f}.ieeesa-upcoming-meetings-events--mobile .ieeesa-upcoming-meetings-events__header{flex-wrap:wrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i2.MatPaginator, selector: "mat-paginator", inputs: ["disabled"], exportAs: ["matPaginator"] }, { kind: "component", type: LoaderComponent, selector: "ieeesa-loader", inputs: ["diameter"] }, { kind: "component", type: EventGroupCardComponent, selector: "ieeesa-event-group-card", inputs: ["event", "permissions", "attendanceStates", "inFlightMeetingIds", "isMobileView"], outputs: ["viewEvent", "openMeeting", "logAttendance", "leaveMeeting", "pageChange", "loadMeetings"] }], encapsulation: i0.ViewEncapsulation.None }); }
1200
+ }
1201
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: UpcomingMeetingsEventsComponent, decorators: [{
1202
+ type: Component,
1203
+ args: [{ selector: 'ieeesa-upcoming-meetings-events', standalone: true, imports: [
1204
+ CommonModule,
1205
+ MatPaginatorModule,
1206
+ LoaderComponent,
1207
+ EventGroupCardComponent,
1208
+ ], encapsulation: ViewEncapsulation.None, template: "<section\r\n class=\"ieeesa-upcoming-meetings-events\"\r\n [class.ieeesa-upcoming-meetings-events--mobile]=\"isMobileView\"\r\n>\r\n <header class=\"ieeesa-upcoming-meetings-events__header\">\r\n <!-- Section title (Req 5.1). -->\r\n <div class=\"ieeesa-upcoming-meetings-events__heading\">\r\n <h2 class=\"ieeesa-upcoming-meetings-events__title\">\r\n {{ constants.upcomingSection.title }}\r\n </h2>\r\n </div>\r\n\r\n <!-- View All link + Expand/Collapse toggle (Req 5.1, 5.2, 5.3, 5.4, 14.3). -->\r\n <div class=\"ieeesa-upcoming-meetings-events__actions\">\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-upcoming-meetings-events__view-all\"\r\n [attr.aria-label]=\"constants.upcomingSection.ariaLabel.viewAll\"\r\n (click)=\"onViewAll()\"\r\n >\r\n {{ constants.upcomingSection.viewAllLabel }}\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-upcoming-meetings-events__toggle\"\r\n [attr.aria-expanded]=\"isExpanded\"\r\n [attr.aria-label]=\"constants.upcomingSection.ariaLabel.toggle\"\r\n (click)=\"onToggleExpanded()\"\r\n >\r\n <span>{{ isExpanded ? constants.upcomingSection.collapseLabel : constants.upcomingSection.expandLabel }}</span>\r\n <i\r\n class=\"fa-solid\"\r\n [ngClass]=\"isExpanded ? 'fa-chevron-up' : 'fa-chevron-down'\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n </button>\r\n </div>\r\n </header>\r\n\r\n <!-- Card list, shown only while expanded (Req 5.4). -->\r\n <div *ngIf=\"isExpanded\" class=\"ieeesa-upcoming-meetings-events__body\">\r\n <!-- Loader region (Req 13.1). -->\r\n <div\r\n *ngIf=\"sectionState === sectionStates.LOADING\"\r\n class=\"ieeesa-upcoming-meetings-events__loader\"\r\n >\r\n <ieeesa-loader></ieeesa-loader>\r\n </div>\r\n\r\n <!-- Content region: one card per event preserving input order (Req 5.5, 5.6, 13.2). -->\r\n <div\r\n *ngIf=\"sectionState === sectionStates.LOADED\"\r\n class=\"ieeesa-upcoming-meetings-events__cards\"\r\n >\r\n <ieeesa-event-group-card\r\n *ngFor=\"let event of pagedEvents; trackBy: trackByEventId\"\r\n [event]=\"event\"\r\n [permissions]=\"permissions\"\r\n [attendanceStates]=\"attendanceStates\"\r\n [inFlightMeetingIds]=\"inFlightMeetingIds\"\r\n [isMobileView]=\"isMobileView\"\r\n (viewEvent)=\"onViewEvent($event)\"\r\n (openMeeting)=\"onOpenMeeting($event)\"\r\n (logAttendance)=\"onLogAttendance($event)\"\r\n (leaveMeeting)=\"onLeaveMeeting($event)\"\r\n (pageChange)=\"onMeetingPageChange(event.eventId, $event)\"\r\n (loadMeetings)=\"onLoadEventMeetings(event.eventId, $event)\"\r\n ></ieeesa-event-group-card>\r\n\r\n <!-- Section-level paginator: 5 events/meetings per page (Req 3.x, design). -->\r\n <div\r\n *ngIf=\"totalItems > sectionPageSize\"\r\n class=\"ieeesa-upcoming-meetings-events__paginator\"\r\n >\r\n <span class=\"ieeesa-upcoming-meetings-events__paginator-range\">\r\n {{ sectionRangeLabel }}\r\n </span>\r\n <mat-paginator\r\n class=\"ieeesa-upcoming-meetings-events__paginator-control\"\r\n [pageIndex]=\"sectionPageIndex\"\r\n [pageSize]=\"sectionPageSize\"\r\n [length]=\"totalItems\"\r\n [hidePageSize]=\"true\"\r\n (page)=\"onSectionPageChange($event)\"\r\n ></mat-paginator>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty-state region (Req 13.3). -->\r\n <p\r\n *ngIf=\"sectionState === sectionStates.EMPTY\"\r\n class=\"ieeesa-upcoming-meetings-events__empty\"\r\n >\r\n {{ constants.upcomingSection.emptyMessage }}\r\n </p>\r\n\r\n <!-- Error region (Req 13.5). -->\r\n <p\r\n *ngIf=\"sectionState === sectionStates.ERROR\"\r\n class=\"ieeesa-upcoming-meetings-events__error\"\r\n role=\"alert\"\r\n >\r\n {{ constants.upcomingSection.errorMessage }}\r\n </p>\r\n </div>\r\n</section>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-upcoming-meetings-events{display:flex;flex-direction:column;gap:16px;background-color:#fff;border-radius:12px;box-shadow:0 1px 2px #0003;border:1px solid #e2e8f0;padding:20px 24px}.ieeesa-upcoming-meetings-events__header{display:flex;align-items:center;justify-content:space-between;gap:16px}.ieeesa-upcoming-meetings-events__heading{display:flex;min-width:0}.ieeesa-upcoming-meetings-events__title{margin:0;font-family:Calibri Bold;font-weight:700;font-size:18px;color:#0f172a}.ieeesa-upcoming-meetings-events__actions{display:flex;align-items:center;gap:16px}.ieeesa-upcoming-meetings-events__view-all{flex-shrink:0;background:none;border:none;padding:0;cursor:pointer;font-family:Calibri Bold;font-weight:700;font-size:18px;color:#00629b}.ieeesa-upcoming-meetings-events__view-all:hover{text-decoration:underline}.ieeesa-upcoming-meetings-events__toggle{display:inline-flex;align-items:center;gap:8px;padding:6px 14px;border:1px solid #dbe2eb;border-radius:8px;background-color:#fff;cursor:pointer;font-family:Calibri Bold;font-weight:700;font-size:18px;color:#00629b}.ieeesa-upcoming-meetings-events__toggle:hover{border-color:#00629b}.ieeesa-upcoming-meetings-events__toggle i{font-size:.75rem}.ieeesa-upcoming-meetings-events__body{display:flex;flex-direction:column}.ieeesa-upcoming-meetings-events__loader{display:flex;align-items:center;justify-content:center;padding:24px}.ieeesa-upcoming-meetings-events__cards{display:flex;flex-direction:column;gap:16px}.ieeesa-upcoming-meetings-events__paginator{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:12px;padding-top:8px}.ieeesa-upcoming-meetings-events__paginator-range{font-size:.8125rem;color:#939094}.ieeesa-upcoming-meetings-events__empty,.ieeesa-upcoming-meetings-events__error{margin:0;padding:16px;font-size:.875rem;color:#939094}.ieeesa-upcoming-meetings-events__error{color:#ba0c2f}.ieeesa-upcoming-meetings-events--mobile .ieeesa-upcoming-meetings-events__header{flex-wrap:wrap}\n"] }]
1209
+ }], propDecorators: { events: [{
1210
+ type: Input
1211
+ }], sectionState: [{
1212
+ type: Input
1213
+ }], permissions: [{
1214
+ type: Input
1215
+ }], attendanceStates: [{
1216
+ type: Input
1217
+ }], inFlightMeetingIds: [{
1218
+ type: Input
1219
+ }], isMobileView: [{
1220
+ type: Input
1221
+ }], viewAll: [{
1222
+ type: Output
1223
+ }], viewEvent: [{
1224
+ type: Output
1225
+ }], openMeeting: [{
1226
+ type: Output
1227
+ }], logAttendance: [{
1228
+ type: Output
1229
+ }], leaveMeeting: [{
1230
+ type: Output
1231
+ }], meetingPageChange: [{
1232
+ type: Output
1233
+ }], loadEventMeetings: [{
1234
+ type: Output
1235
+ }], expanded: [{
1236
+ type: Input
1237
+ }], expandedChange: [{
1238
+ type: Output
1239
+ }] } });
1240
+
1241
+ /**
1242
+ * Identifies which dashboard section a "Jump to" shortcut targets, emitted via
1243
+ * {@link DashboardWelcomeHeaderComponent.shortcutSelected} for the host/container
1244
+ * to scroll to or navigate.
1245
+ */
1246
+ // eslint-disable-next-line no-unused-vars
1247
+ var DashboardJumpTarget;
1248
+ (function (DashboardJumpTarget) {
1249
+ // eslint-disable-next-line no-unused-vars
1250
+ DashboardJumpTarget["MEETINGS"] = "MEETINGS";
1251
+ // eslint-disable-next-line no-unused-vars
1252
+ DashboardJumpTarget["GROUPS"] = "GROUPS";
1253
+ })(DashboardJumpTarget || (DashboardJumpTarget = {}));
1254
+ /**
1255
+ * Welcome_Header renders the personalized greeting and the "Jump to" shortcut
1256
+ * control at the top of the dashboard.
1257
+ *
1258
+ * The greeting is composed from the greeting label in `constants.json` and the
1259
+ * supplied `userName`, falling back to the configured fallback greeting when the
1260
+ * name is empty or undefined. The "Jump to" control shows one circular icon per
1261
+ * dashboard section (Upcoming Meetings & Events, Your Groups); hovering or
1262
+ * focusing an icon expands it into a pill with the section label and a count
1263
+ * badge (the counts are supplied by the host via `meetingsCount` /
1264
+ * `groupsCount`). Activating a shortcut emits {@link shortcutSelected} with the
1265
+ * target section for the host to act on. Purely presentational — no API work.
1266
+ * @export
1267
+ * @class DashboardWelcomeHeaderComponent
1268
+ */
1269
+ class DashboardWelcomeHeaderComponent {
1270
+ constructor() {
1271
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1272
+ this.constants = constants$1;
1273
+ /** User-facing strings sourced from `constants.json`. */
1274
+ this.texts = this.constants?.welcomeHeader;
1275
+ /** Enum reference for template comparisons. */
1276
+ this.jumpTarget = DashboardJumpTarget;
1277
+ this.userNameValue = '';
1278
+ this.meetingsCountValue = 0;
1279
+ this.groupsCountValue = 0;
1280
+ /** Emits the selected "Jump to" target for the host to act on. */
1281
+ this.shortcutSelected = new EventEmitter();
1282
+ /** The jump shortcuts rendered in the control, rebuilt when counts change. */
1283
+ this.shortcuts = this.buildShortcutList();
1284
+ }
1285
+ /**
1286
+ * The signed-in user's display name. Drives the personalized greeting; when
1287
+ * empty or undefined the greeting falls back to the configured fallback text.
1288
+ * @memberof DashboardWelcomeHeaderComponent
1289
+ */
1290
+ set userName(value) {
1291
+ this.userNameValue = value ?? '';
1292
+ }
1293
+ get userName() {
1294
+ return this.userNameValue;
1295
+ }
1296
+ /** Count shown in the Upcoming Meetings & Events shortcut badge. */
1297
+ set meetingsCount(value) {
1298
+ this.meetingsCountValue = value ?? 0;
1299
+ this.buildShortcuts();
1300
+ }
1301
+ get meetingsCount() {
1302
+ return this.meetingsCountValue;
1303
+ }
1304
+ /** Count shown in the Your Groups shortcut badge. */
1305
+ set groupsCount(value) {
1306
+ this.groupsCountValue = value ?? 0;
1307
+ this.buildShortcuts();
1308
+ }
1309
+ get groupsCount() {
1310
+ return this.groupsCountValue;
1311
+ }
1312
+ /**
1313
+ * The personalized greeting: the greeting label plus the user's name when
1314
+ * present, otherwise the configured fallback greeting.
1315
+ * @readonly
1316
+ * @memberof DashboardWelcomeHeaderComponent
1317
+ */
1318
+ get greeting() {
1319
+ const name = this.userName?.trim();
1320
+ return name
1321
+ ? `${this.texts?.greeting}${name}!`
1322
+ : `${this.texts?.fallbackGreeting}!`;
1323
+ }
1324
+ /**
1325
+ * Emits the chosen shortcut's target for the host to scroll/navigate to.
1326
+ * @param {DashboardJumpTarget} target - The selected section.
1327
+ * @memberof DashboardWelcomeHeaderComponent
1328
+ */
1329
+ onShortcutClick(target) {
1330
+ this.shortcutSelected.emit(target);
1331
+ }
1332
+ /**
1333
+ * TrackBy for the shortcut list.
1334
+ * @param {number} _index
1335
+ * @param {JumpShortcut} shortcut
1336
+ * @return {string}
1337
+ * @memberof DashboardWelcomeHeaderComponent
1338
+ */
1339
+ trackByTarget(_index, shortcut) {
1340
+ return shortcut.target;
1341
+ }
1342
+ /** Rebuilds the shortcut list so the template reflects the latest counts. */
1343
+ buildShortcuts() {
1344
+ this.shortcuts = this.buildShortcutList();
1345
+ }
1346
+ /**
1347
+ * Builds the shortcut view models from `constants.json` and the current
1348
+ * counts.
1349
+ * @return {JumpShortcut[]}
1350
+ * @memberof DashboardWelcomeHeaderComponent
1351
+ */
1352
+ buildShortcutList() {
1353
+ const shortcutTexts = this.constants?.welcomeHeader?.shortcuts;
1354
+ return [
1355
+ {
1356
+ target: DashboardJumpTarget.MEETINGS,
1357
+ label: shortcutTexts?.meetings?.label,
1358
+ iconUrl: shortcutTexts?.meetings?.iconUrl,
1359
+ ariaLabel: shortcutTexts?.meetings?.ariaLabel,
1360
+ count: this.meetingsCountValue,
1361
+ },
1362
+ {
1363
+ target: DashboardJumpTarget.GROUPS,
1364
+ label: shortcutTexts?.groups?.label,
1365
+ iconUrl: shortcutTexts?.groups?.iconUrl,
1366
+ ariaLabel: shortcutTexts?.groups?.ariaLabel,
1367
+ count: this.groupsCountValue,
1368
+ },
1369
+ ];
1370
+ }
1371
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DashboardWelcomeHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1372
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: DashboardWelcomeHeaderComponent, isStandalone: true, selector: "ieeesa-dashboard-welcome-header", inputs: { userName: "userName", meetingsCount: "meetingsCount", groupsCount: "groupsCount" }, outputs: { shortcutSelected: "shortcutSelected" }, ngImport: i0, template: "<div class=\"ieeesa-dashboard-welcome-header\">\r\n <!-- Greeting + helper line -->\r\n <div class=\"ieeesa-dashboard-welcome-header__intro\">\r\n <h1 class=\"ieeesa-dashboard-welcome-header__greeting m-0\">\r\n {{ greeting }}\r\n </h1>\r\n <p class=\"ieeesa-dashboard-welcome-header__helper-text m-0\">\r\n {{ texts?.helperText }}\r\n </p>\r\n </div>\r\n\r\n <!-- \"Jump to\" shortcuts: each icon expands on hover/focus into a pill with\r\n the section label and its count badge. -->\r\n <div class=\"ieeesa-dashboard-welcome-header__jump-to\">\r\n <span class=\"ieeesa-dashboard-welcome-header__jump-to-label\">\r\n {{ texts?.jumpToLabel }}\r\n </span>\r\n\r\n <button\r\n *ngFor=\"let shortcut of shortcuts; trackBy: trackByTarget\"\r\n type=\"button\"\r\n class=\"ieeesa-dashboard-welcome-header__shortcut\"\r\n [attr.aria-label]=\"shortcut.ariaLabel + ' (' + shortcut.count + ')'\"\r\n (click)=\"onShortcutClick(shortcut.target)\"\r\n >\r\n <img\r\n class=\"ieeesa-dashboard-welcome-header__shortcut-icon\"\r\n [src]=\"shortcut.iconUrl\"\r\n alt=\"\"\r\n aria-hidden=\"true\"\r\n />\r\n <span class=\"ieeesa-dashboard-welcome-header__shortcut-label\">\r\n {{ shortcut.label }}\r\n </span>\r\n <span class=\"ieeesa-dashboard-welcome-header__shortcut-count\">\r\n {{ shortcut.count }}\r\n </span>\r\n </button>\r\n </div>\r\n</div>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-dashboard-welcome-header{display:flex;align-items:center;justify-content:flex-start;gap:10%;flex-wrap:wrap}.ieeesa-dashboard-welcome-header__intro{display:flex;flex-direction:column;gap:4px}.ieeesa-dashboard-welcome-header__greeting{font-family:Calibri Bold;font-weight:700;font-style:normal;font-size:18px;line-height:27px;letter-spacing:0px;color:#111}.ieeesa-dashboard-welcome-header__helper-text{font-family:Calibri Regular;font-weight:400;font-style:normal;font-size:12px;line-height:16.5px;letter-spacing:0px;color:#6a7282}.ieeesa-dashboard-welcome-header__jump-to{display:inline-flex;align-items:center;gap:20px;min-width:228px;width:-moz-fit-content;width:fit-content;height:60px;padding-left:18px;padding-right:18px;background-color:#fff;border-radius:10px;box-shadow:0 1px 2px #0003}.ieeesa-dashboard-welcome-header__jump-to-label{font-family:Calibri Bold;font-weight:700;font-size:.8125rem;letter-spacing:.03125em;text-transform:uppercase;color:#333436;white-space:nowrap}.ieeesa-dashboard-welcome-header__shortcut{display:inline-flex;align-items:center;justify-content:center;gap:0;width:40px;height:40px;padding:0;border:1.5px solid #D1D5DC;border-radius:20px;background-color:#fff;color:#00629b;cursor:pointer;overflow:hidden;will-change:width,padding;transition:width .32s cubic-bezier(.22,1,.36,1),padding .32s cubic-bezier(.22,1,.36,1),gap .32s cubic-bezier(.22,1,.36,1),background-color .25s ease,border-color .25s ease,box-shadow .25s ease}.ieeesa-dashboard-welcome-header__shortcut:hover,.ieeesa-dashboard-welcome-header__shortcut:focus-visible{width:auto;gap:8px;padding:0 14px;border-color:#00629b;background-color:#eaf3f9;box-shadow:0 1px 2px #0003;outline:none}.ieeesa-dashboard-welcome-header__shortcut-icon{flex:0 0 auto;width:20px;height:20px;display:block}.ieeesa-dashboard-welcome-header__shortcut-label{max-width:0;opacity:0;overflow:hidden;white-space:nowrap;font-family:Calibri Bold;font-weight:700;font-size:.875rem;color:#333436;transition:max-width .32s cubic-bezier(.22,1,.36,1),opacity .18s ease}.ieeesa-dashboard-welcome-header__shortcut-count{display:inline-flex;align-items:center;justify-content:center;max-width:0;padding:0;opacity:0;overflow:hidden;border-radius:12px;background-color:#eaf3f9;color:#00629b;font-family:Calibri Bold;font-weight:700;font-size:.75rem;white-space:nowrap;transition:max-width .32s cubic-bezier(.22,1,.36,1),opacity .18s ease,padding .2s ease}.ieeesa-dashboard-welcome-header__shortcut:hover .ieeesa-dashboard-welcome-header__shortcut-label,.ieeesa-dashboard-welcome-header__shortcut:focus-visible .ieeesa-dashboard-welcome-header__shortcut-label{max-width:240px;opacity:1;transition:max-width .32s cubic-bezier(.22,1,.36,1),opacity .22s ease .1s}.ieeesa-dashboard-welcome-header__shortcut:hover .ieeesa-dashboard-welcome-header__shortcut-count,.ieeesa-dashboard-welcome-header__shortcut:focus-visible .ieeesa-dashboard-welcome-header__shortcut-count{max-width:48px;padding:2px 10px;opacity:1;transition:max-width .32s cubic-bezier(.22,1,.36,1),opacity .22s ease .14s,padding .2s ease}@media (max-width: 767px){.ieeesa-dashboard-welcome-header{align-items:flex-start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], encapsulation: i0.ViewEncapsulation.None }); }
1373
+ }
1374
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DashboardWelcomeHeaderComponent, decorators: [{
1375
+ type: Component,
1376
+ args: [{ selector: 'ieeesa-dashboard-welcome-header', standalone: true, imports: [CommonModule], encapsulation: ViewEncapsulation.None, template: "<div class=\"ieeesa-dashboard-welcome-header\">\r\n <!-- Greeting + helper line -->\r\n <div class=\"ieeesa-dashboard-welcome-header__intro\">\r\n <h1 class=\"ieeesa-dashboard-welcome-header__greeting m-0\">\r\n {{ greeting }}\r\n </h1>\r\n <p class=\"ieeesa-dashboard-welcome-header__helper-text m-0\">\r\n {{ texts?.helperText }}\r\n </p>\r\n </div>\r\n\r\n <!-- \"Jump to\" shortcuts: each icon expands on hover/focus into a pill with\r\n the section label and its count badge. -->\r\n <div class=\"ieeesa-dashboard-welcome-header__jump-to\">\r\n <span class=\"ieeesa-dashboard-welcome-header__jump-to-label\">\r\n {{ texts?.jumpToLabel }}\r\n </span>\r\n\r\n <button\r\n *ngFor=\"let shortcut of shortcuts; trackBy: trackByTarget\"\r\n type=\"button\"\r\n class=\"ieeesa-dashboard-welcome-header__shortcut\"\r\n [attr.aria-label]=\"shortcut.ariaLabel + ' (' + shortcut.count + ')'\"\r\n (click)=\"onShortcutClick(shortcut.target)\"\r\n >\r\n <img\r\n class=\"ieeesa-dashboard-welcome-header__shortcut-icon\"\r\n [src]=\"shortcut.iconUrl\"\r\n alt=\"\"\r\n aria-hidden=\"true\"\r\n />\r\n <span class=\"ieeesa-dashboard-welcome-header__shortcut-label\">\r\n {{ shortcut.label }}\r\n </span>\r\n <span class=\"ieeesa-dashboard-welcome-header__shortcut-count\">\r\n {{ shortcut.count }}\r\n </span>\r\n </button>\r\n </div>\r\n</div>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-dashboard-welcome-header{display:flex;align-items:center;justify-content:flex-start;gap:10%;flex-wrap:wrap}.ieeesa-dashboard-welcome-header__intro{display:flex;flex-direction:column;gap:4px}.ieeesa-dashboard-welcome-header__greeting{font-family:Calibri Bold;font-weight:700;font-style:normal;font-size:18px;line-height:27px;letter-spacing:0px;color:#111}.ieeesa-dashboard-welcome-header__helper-text{font-family:Calibri Regular;font-weight:400;font-style:normal;font-size:12px;line-height:16.5px;letter-spacing:0px;color:#6a7282}.ieeesa-dashboard-welcome-header__jump-to{display:inline-flex;align-items:center;gap:20px;min-width:228px;width:-moz-fit-content;width:fit-content;height:60px;padding-left:18px;padding-right:18px;background-color:#fff;border-radius:10px;box-shadow:0 1px 2px #0003}.ieeesa-dashboard-welcome-header__jump-to-label{font-family:Calibri Bold;font-weight:700;font-size:.8125rem;letter-spacing:.03125em;text-transform:uppercase;color:#333436;white-space:nowrap}.ieeesa-dashboard-welcome-header__shortcut{display:inline-flex;align-items:center;justify-content:center;gap:0;width:40px;height:40px;padding:0;border:1.5px solid #D1D5DC;border-radius:20px;background-color:#fff;color:#00629b;cursor:pointer;overflow:hidden;will-change:width,padding;transition:width .32s cubic-bezier(.22,1,.36,1),padding .32s cubic-bezier(.22,1,.36,1),gap .32s cubic-bezier(.22,1,.36,1),background-color .25s ease,border-color .25s ease,box-shadow .25s ease}.ieeesa-dashboard-welcome-header__shortcut:hover,.ieeesa-dashboard-welcome-header__shortcut:focus-visible{width:auto;gap:8px;padding:0 14px;border-color:#00629b;background-color:#eaf3f9;box-shadow:0 1px 2px #0003;outline:none}.ieeesa-dashboard-welcome-header__shortcut-icon{flex:0 0 auto;width:20px;height:20px;display:block}.ieeesa-dashboard-welcome-header__shortcut-label{max-width:0;opacity:0;overflow:hidden;white-space:nowrap;font-family:Calibri Bold;font-weight:700;font-size:.875rem;color:#333436;transition:max-width .32s cubic-bezier(.22,1,.36,1),opacity .18s ease}.ieeesa-dashboard-welcome-header__shortcut-count{display:inline-flex;align-items:center;justify-content:center;max-width:0;padding:0;opacity:0;overflow:hidden;border-radius:12px;background-color:#eaf3f9;color:#00629b;font-family:Calibri Bold;font-weight:700;font-size:.75rem;white-space:nowrap;transition:max-width .32s cubic-bezier(.22,1,.36,1),opacity .18s ease,padding .2s ease}.ieeesa-dashboard-welcome-header__shortcut:hover .ieeesa-dashboard-welcome-header__shortcut-label,.ieeesa-dashboard-welcome-header__shortcut:focus-visible .ieeesa-dashboard-welcome-header__shortcut-label{max-width:240px;opacity:1;transition:max-width .32s cubic-bezier(.22,1,.36,1),opacity .22s ease .1s}.ieeesa-dashboard-welcome-header__shortcut:hover .ieeesa-dashboard-welcome-header__shortcut-count,.ieeesa-dashboard-welcome-header__shortcut:focus-visible .ieeesa-dashboard-welcome-header__shortcut-count{max-width:48px;padding:2px 10px;opacity:1;transition:max-width .32s cubic-bezier(.22,1,.36,1),opacity .22s ease .14s,padding .2s ease}@media (max-width: 767px){.ieeesa-dashboard-welcome-header{align-items:flex-start}}\n"] }]
1377
+ }], propDecorators: { userName: [{
1378
+ type: Input
1379
+ }], meetingsCount: [{
1380
+ type: Input
1381
+ }], groupsCount: [{
1382
+ type: Input
1383
+ }], shortcutSelected: [{
1384
+ type: Output
1385
+ }] } });
1386
+
1387
+ /**
1388
+ * Groups_Section (`ieeesa-your-groups`) renders the signed-in user's groups in
1389
+ * a collapsible card as a custom hierarchy table styled like the My Profile
1390
+ * Find Groups tree: each group row carries a blue +/- toggle (when it has an
1391
+ * ancestor hierarchy) that expands to reveal the group's hierarchy path
1392
+ * (root→leaf), indented. Columns: Group, Type, Officer Role, Affiliation,
1393
+ * Member Level (the member level renders as a coloured pill — green for an
1394
+ * assigned level, amber for "Unassigned"). The card paginates the top-level
1395
+ * groups (5 per page).
1396
+ *
1397
+ * Purely presentational: data arrives via `@Input`, intents leave via
1398
+ * `@Output`; no API or navigation here. States (loader / empty / error / table)
1399
+ * are driven by `sectionState`.
1400
+ * @export
1401
+ * @class YourGroupsComponent
1402
+ */
1403
+ class YourGroupsComponent {
1404
+ constructor() {
1405
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1406
+ this.constants = constants$1;
1407
+ /** User-facing strings sourced from `constants.json`. */
1408
+ this.texts = this.constants?.groupsSection;
1409
+ /** Enum reference for template comparisons against `sectionState`. */
1410
+ this.sectionStates = DashboardSectionState;
1411
+ /** Per-level indentation step (px) for the expanded hierarchy rows. */
1412
+ this.indentPerLevel = 28;
1413
+ /** Selectable "Items per page" page sizes. */
1414
+ this.pageSizeOptions = [5, 10, 25];
1415
+ this.groupsValue = [];
1416
+ /** The section's load/empty/error state driving the mutually-exclusive regions. */
1417
+ this.sectionState = DashboardSectionState.LOADED;
1418
+ /** Total number of groups (for the "{n} Groups" count and paginator). */
1419
+ this.totalGroups = 0;
1420
+ this.pageIndexValue = 0;
1421
+ this.pageSizeValue = 5;
1422
+ /** Emits when the "View All" link is activated. */
1423
+ this.viewAllGroups = new EventEmitter();
1424
+ /** Emits the requested page when the paginator changes. */
1425
+ this.pageChange = new EventEmitter();
1426
+ /**
1427
+ * Emits the new expanded state whenever the user toggles the card, so a
1428
+ * two-way `[(expanded)]` binding keeps the host's flag in sync. Without this,
1429
+ * a host that opened the card (expanded=true) and then the user collapsing it
1430
+ * would leave the host flag stale, so re-opening via the same input value
1431
+ * would be a no-op.
1432
+ * @memberof YourGroupsComponent
1433
+ */
1434
+ this.expandedChange = new EventEmitter();
1435
+ /** Whether the card is expanded (table visible). Starts collapsed. */
1436
+ this.isExpanded = false;
1437
+ /** GroupIds of the top-level groups whose hierarchy is currently expanded. */
1438
+ this.expandedGroupIds = new Set();
1439
+ /** The flattened rows currently rendered (group rows + any expanded hierarchy rows). */
1440
+ this.visibleRows = [];
1441
+ }
1442
+ /** The groups rendered in the table. */
1443
+ set groups(value) {
1444
+ this.groupsValue = value ?? [];
1445
+ this.rebuildRows();
1446
+ }
1447
+ get groups() {
1448
+ return this.groupsValue;
1449
+ }
1450
+ /** Current zero-based page index. */
1451
+ set pageIndex(value) {
1452
+ this.pageIndexValue = value ?? 0;
1453
+ this.rebuildRows();
1454
+ }
1455
+ get pageIndex() {
1456
+ return this.pageIndexValue;
1457
+ }
1458
+ /** Current page size (defaults to 5 rows per page per the design). */
1459
+ set pageSize(value) {
1460
+ this.pageSizeValue = value ?? 5;
1461
+ this.rebuildRows();
1462
+ }
1463
+ get pageSize() {
1464
+ return this.pageSizeValue;
1465
+ }
1466
+ /**
1467
+ * Allows the host/container to open or close the card (e.g. a "Jump to"
1468
+ * shortcut opens it).
1469
+ * @memberof YourGroupsComponent
1470
+ */
1471
+ set expanded(value) {
1472
+ this.isExpanded = !!value;
1473
+ }
1474
+ get expanded() {
1475
+ return this.isExpanded;
1476
+ }
1477
+ /**
1478
+ * The count shown in the header subtitle — the server total when provided,
1479
+ * otherwise the number of groups.
1480
+ * @readonly
1481
+ * @memberof YourGroupsComponent
1482
+ */
1483
+ get groupCount() {
1484
+ return this.totalGroups || this.groups.length || 0;
1485
+ }
1486
+ /**
1487
+ * Whether a group row can expand: it has an ancestor hierarchy (more than the
1488
+ * group's own node). A single-node hierarchy (the group itself) is a leaf.
1489
+ * @param {GroupRow} group
1490
+ * @return {boolean}
1491
+ * @memberof YourGroupsComponent
1492
+ */
1493
+ hasHierarchy(group) {
1494
+ return (group?.hierarchy?.length ?? 0) > 1;
1495
+ }
1496
+ /**
1497
+ * Whether the given group row is currently expanded.
1498
+ * @param {GroupRow} group
1499
+ * @return {boolean}
1500
+ * @memberof YourGroupsComponent
1501
+ */
1502
+ isGroupExpanded(group) {
1503
+ return this.expandedGroupIds.has(group.groupId);
1504
+ }
1505
+ /**
1506
+ * Toggles a group's hierarchy expansion and rebuilds the visible rows.
1507
+ * @param {GroupRow} group
1508
+ * @memberof YourGroupsComponent
1509
+ */
1510
+ onToggleHierarchy(group) {
1511
+ if (!this.hasHierarchy(group)) {
1512
+ return;
1513
+ }
1514
+ if (this.expandedGroupIds.has(group.groupId)) {
1515
+ this.expandedGroupIds.delete(group.groupId);
1516
+ }
1517
+ else {
1518
+ this.expandedGroupIds.add(group.groupId);
1519
+ }
1520
+ this.rebuildRows();
1521
+ }
1522
+ /**
1523
+ * Toggles the card's expanded/collapsed state (whole table visibility) and
1524
+ * emits the new state so the host's two-way binding stays in sync.
1525
+ * @memberof YourGroupsComponent
1526
+ */
1527
+ onToggleExpand() {
1528
+ this.isExpanded = !this.isExpanded;
1529
+ this.expandedChange.emit(this.isExpanded);
1530
+ }
1531
+ /**
1532
+ * Emits the "View All" intent for the host.
1533
+ * @memberof YourGroupsComponent
1534
+ */
1535
+ onViewAll() {
1536
+ this.viewAllGroups.emit();
1537
+ }
1538
+ /**
1539
+ * Re-emits the Material paginator change (page index or "Items per page"
1540
+ * size) as a {@link PageChange} for the host.
1541
+ * @param {PageEvent} event - The Material paginator event.
1542
+ * @memberof YourGroupsComponent
1543
+ */
1544
+ onPaginatorChange(event) {
1545
+ this.pageChange.emit({
1546
+ pageIndex: event.pageIndex,
1547
+ pageSize: event.pageSize,
1548
+ });
1549
+ }
1550
+ /**
1551
+ * Resolves the member-level pill CSS class, mirroring the My Groups profile
1552
+ * tab: an assigned level renders green (success), "Unassigned" renders amber.
1553
+ * @param {string} memberLevel
1554
+ * @return {string}
1555
+ * @memberof YourGroupsComponent
1556
+ */
1557
+ resolveBadgeClass(memberLevel) {
1558
+ const badge = this.texts?.memberLevelBadge;
1559
+ return memberLevel === this.texts?.unassignedMemberLevel
1560
+ ? badge?.warning
1561
+ : badge?.success;
1562
+ }
1563
+ /**
1564
+ * TrackBy for the rendered table rows.
1565
+ * @param {number} index
1566
+ * @param {TableRow} row
1567
+ * @return {string}
1568
+ * @memberof YourGroupsComponent
1569
+ */
1570
+ trackByRow(index, row) {
1571
+ return row.kind === 'group'
1572
+ ? `g:${row.group?.groupId}`
1573
+ : `h:${row.group?.groupId}:${row.node?.groupId}:${index}`;
1574
+ }
1575
+ /**
1576
+ * Rebuilds the flat list of visible rows: the current page of top-level group
1577
+ * rows, each optionally followed by its hierarchy (ancestor) rows when the
1578
+ * group is expanded. The hierarchy path is shown root→leaf, indented by depth.
1579
+ * @memberof YourGroupsComponent
1580
+ */
1581
+ rebuildRows() {
1582
+ const start = this.pageIndex * this.pageSize;
1583
+ const pageGroups = this.groups.slice(start, start + this.pageSize);
1584
+ const rows = [];
1585
+ pageGroups.forEach((group) => {
1586
+ rows.push({ kind: 'group', group, level: 0 });
1587
+ if (this.expandedGroupIds.has(group.groupId)) {
1588
+ (group.hierarchy ?? []).forEach((node, depth) => {
1589
+ rows.push({ kind: 'hierarchy', group, node, level: depth + 1 });
1590
+ });
1591
+ }
1592
+ });
1593
+ this.visibleRows = rows;
1594
+ }
1595
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: YourGroupsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1596
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: YourGroupsComponent, isStandalone: true, selector: "ieeesa-your-groups", inputs: { groups: "groups", sectionState: "sectionState", totalGroups: "totalGroups", pageIndex: "pageIndex", pageSize: "pageSize", expanded: "expanded" }, outputs: { viewAllGroups: "viewAllGroups", pageChange: "pageChange", expandedChange: "expandedChange" }, ngImport: i0, template: "<section class=\"ieeesa-your-groups\">\r\n <!-- Header: title + count, View All link, collapse/expand toggle -->\r\n <div class=\"ieeesa-your-groups__header\">\r\n <div class=\"ieeesa-your-groups__heading\">\r\n <h2 class=\"ieeesa-your-groups__title m-0\">{{ texts?.title }}</h2>\r\n </div>\r\n\r\n <div class=\"ieeesa-your-groups__actions\">\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-your-groups__view-all\"\r\n [attr.aria-label]=\"texts?.ariaLabel?.viewAll\"\r\n (click)=\"onViewAll()\"\r\n >\r\n {{ texts?.viewAllLabel }}\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-your-groups__toggle\"\r\n [attr.aria-expanded]=\"isExpanded\"\r\n [attr.aria-label]=\"texts?.ariaLabel?.toggle\"\r\n (click)=\"onToggleExpand()\"\r\n >\r\n <span>{{ isExpanded ? texts?.collapseLabel : texts?.expandLabel }}</span>\r\n <i\r\n class=\"fa-solid\"\r\n [ngClass]=\"isExpanded ? 'fa-chevron-up' : 'fa-chevron-down'\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Body: only when the card is expanded. -->\r\n <div class=\"ieeesa-your-groups__body\" *ngIf=\"isExpanded\">\r\n <ieeesa-loader\r\n *ngIf=\"sectionState === sectionStates.LOADING\"\r\n ></ieeesa-loader>\r\n\r\n <p\r\n *ngIf=\"sectionState === sectionStates.ERROR\"\r\n class=\"ieeesa-your-groups__message ieeesa-your-groups__message--error\"\r\n role=\"alert\"\r\n >\r\n {{ texts?.errorMessage }}\r\n </p>\r\n\r\n <p\r\n *ngIf=\"sectionState === sectionStates.EMPTY\"\r\n class=\"ieeesa-your-groups__message\"\r\n role=\"status\"\r\n >\r\n {{ texts?.emptyMessage }}\r\n </p>\r\n\r\n <ng-container *ngIf=\"sectionState === sectionStates.LOADED\">\r\n <table class=\"ieeesa-your-groups__table\">\r\n <thead>\r\n <tr>\r\n <th scope=\"col\">{{ texts?.columns?.group }}</th>\r\n <th scope=\"col\">{{ texts?.columns?.type }}</th>\r\n <th scope=\"col\">{{ texts?.columns?.officerRole }}</th>\r\n <th scope=\"col\">{{ texts?.columns?.affiliation }}</th>\r\n <th scope=\"col\">{{ texts?.columns?.memberLevel }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let row of visibleRows; trackBy: trackByRow\">\r\n <!-- GROUP ROW -->\r\n <ng-container *ngIf=\"row.kind === 'group'\">\r\n <td class=\"ieeesa-your-groups__group-cell\">\r\n <!-- Parent: blue +/- toggle to reveal the hierarchy. -->\r\n <button\r\n *ngIf=\"hasHierarchy(row.group!)\"\r\n type=\"button\"\r\n class=\"ieeesa-your-groups__hierarchy-toggle\"\r\n [attr.aria-expanded]=\"isGroupExpanded(row.group!)\"\r\n [attr.aria-label]=\"\r\n (isGroupExpanded(row.group!)\r\n ? texts?.collapseLabel\r\n : texts?.expandLabel) +\r\n ' ' +\r\n row.group!.group\r\n \"\r\n (click)=\"onToggleHierarchy(row.group!)\"\r\n >\r\n <i\r\n class=\"fa-solid\"\r\n [ngClass]=\"isGroupExpanded(row.group!) ? 'fa-minus' : 'fa-plus'\"\r\n ></i>\r\n </button>\r\n <!-- Leaf: spacer keeps names aligned with parents' names. -->\r\n <span\r\n *ngIf=\"!hasHierarchy(row.group!)\"\r\n class=\"ieeesa-your-groups__toggle-spacer\"\r\n aria-hidden=\"true\"\r\n ></span>\r\n <span class=\"ieeesa-your-groups__group-name\">\r\n {{ row.group!.group }}\r\n </span>\r\n </td>\r\n <td>{{ row.group!.type }}</td>\r\n <td>{{ row.group!.officerRole }}</td>\r\n <td>{{ row.group!.affiliation }}</td>\r\n <td>\r\n <span\r\n class=\"ieeesa-your-groups__badge\"\r\n [ngClass]=\"resolveBadgeClass(row.group!.memberLevel)\"\r\n >\r\n {{ row.group!.memberLevel }}\r\n </span>\r\n </td>\r\n </ng-container>\r\n\r\n <!-- HIERARCHY (ancestor) ROW -->\r\n <ng-container *ngIf=\"row.kind === 'hierarchy'\">\r\n <td class=\"ieeesa-your-groups__group-cell ieeesa-your-groups__hierarchy-cell\">\r\n <span\r\n class=\"ieeesa-your-groups__indent\"\r\n [style.width.px]=\"row.level * indentPerLevel\"\r\n aria-hidden=\"true\"\r\n ></span>\r\n <span class=\"ieeesa-your-groups__hierarchy-connector\" aria-hidden=\"true\">\r\n <i class=\"fa-solid fa-turn-up fa-rotate-90\"></i>\r\n </span>\r\n <span class=\"ieeesa-your-groups__hierarchy-name\">\r\n {{ row.node!.groupName }}\r\n <span\r\n *ngIf=\"row.node!.groupAcronym\"\r\n class=\"ieeesa-your-groups__hierarchy-acronym\"\r\n >\r\n ({{ row.node!.groupAcronym }})\r\n </span>\r\n </span>\r\n </td>\r\n <td colspan=\"4\"></td>\r\n </ng-container>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Pagination: the same Angular Material paginator the shared data-table\r\n uses \u2014 it provides the \"Items per page\" dropdown, range label, and\r\n prev/next controls. -->\r\n <mat-paginator\r\n *ngIf=\"groupCount > 0\"\r\n class=\"ieeesa-your-groups__paginator\"\r\n [length]=\"groupCount\"\r\n [pageSize]=\"pageSize\"\r\n [pageIndex]=\"pageIndex\"\r\n [pageSizeOptions]=\"pageSizeOptions\"\r\n (page)=\"onPaginatorChange($event)\"\r\n ></mat-paginator>\r\n </ng-container>\r\n </div>\r\n</section>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-your-groups{display:block;background-color:#fff;border-radius:12px;box-shadow:0 1px 2px #0003;padding:20px 24px;border:1px solid #E2E8F0}.ieeesa-your-groups__header{display:flex;align-items:center;justify-content:space-between;gap:16px}.ieeesa-your-groups__heading{display:flex;flex-direction:column;gap:2px}.ieeesa-your-groups__title{font-family:Calibri Bold;font-weight:700;font-size:18px;color:#0f172a}.ieeesa-your-groups__count{font-family:Calibri Regular;font-size:.75rem;color:#777a7e}.ieeesa-your-groups__actions{display:flex;align-items:center;gap:16px}.ieeesa-your-groups__view-all{background:none;border:none;padding:0;cursor:pointer;font-family:Calibri Bold;font-weight:700;font-size:18px;color:#00629b}.ieeesa-your-groups__view-all:hover{text-decoration:underline}.ieeesa-your-groups__toggle{display:inline-flex;align-items:center;gap:8px;padding:6px 14px;border:1px solid #dbe2eb;border-radius:8px;background-color:#fff;cursor:pointer;font-family:Calibri Bold;font-weight:700;font-size:18px;color:#00629b}.ieeesa-your-groups__toggle:hover{border-color:#00629b}.ieeesa-your-groups__toggle i{font-size:.75rem}.ieeesa-your-groups__body{margin-top:16px}.ieeesa-your-groups__message{text-align:center;padding:32px 0;font-family:Calibri Regular;font-size:.875rem;color:#777a7e}.ieeesa-your-groups__message--error{color:#ba0c2f}.ieeesa-your-groups__table{width:100%;border-collapse:collapse}.ieeesa-your-groups__table th,.ieeesa-your-groups__table td{text-align:left;padding:14px 16px;border-bottom:1px solid #dbe2eb;vertical-align:middle}.ieeesa-your-groups__table th{background-color:#f4f5f7;color:#333436;font-family:Calibri Bold;font-weight:700;font-size:.875rem}.ieeesa-your-groups__table td{color:#333436;font-size:.875rem}.ieeesa-your-groups__table tbody tr:nth-child(2n) td{background-color:#f8fcff}.ieeesa-your-groups__group-cell{display:flex;align-items:center;gap:10px}.ieeesa-your-groups__indent{flex:0 0 auto;display:inline-block}.ieeesa-your-groups__hierarchy-toggle{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;border-radius:4px;background-color:#00629b;color:#fff;font-size:.875rem;line-height:1;cursor:pointer}.ieeesa-your-groups__hierarchy-toggle:hover{background-color:#003e65}.ieeesa-your-groups__toggle-spacer{flex:0 0 auto;display:inline-block;width:24px;height:24px}.ieeesa-your-groups__group-name{color:#00629b;font-family:Calibri Bold;font-weight:700}.ieeesa-your-groups__hierarchy-cell{gap:6px}.ieeesa-your-groups__hierarchy-connector{flex:0 0 auto;display:inline-flex;align-items:center;color:#777a7e}.ieeesa-your-groups__hierarchy-name{color:#333436}.ieeesa-your-groups__hierarchy-acronym{color:#777a7e}.ieeesa-your-groups__badge{display:inline-flex;align-items:center;padding:.125em .75em;border-radius:1em;font-size:.875rem;white-space:nowrap}.ieeesa-your-groups__badge--success{color:#2e7d32;background-color:#2e7d321f}.ieeesa-your-groups__badge--warning{color:#b35600;background-color:#b356001f}.ieeesa-your-groups__paginator{margin-top:8px}.ieeesa-your-groups__paginator ::ng-deep .mat-mdc-paginator-container{justify-content:flex-end}.ieeesa-your-groups__paginator ::ng-deep .mat-mdc-paginator{background-color:transparent}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i2.MatPaginator, selector: "mat-paginator", inputs: ["disabled"], exportAs: ["matPaginator"] }, { kind: "component", type: LoaderComponent, selector: "ieeesa-loader", inputs: ["diameter"] }], encapsulation: i0.ViewEncapsulation.None }); }
1597
+ }
1598
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: YourGroupsComponent, decorators: [{
1599
+ type: Component,
1600
+ args: [{ selector: 'ieeesa-your-groups', standalone: true, imports: [CommonModule, MatPaginatorModule, LoaderComponent], encapsulation: ViewEncapsulation.None, template: "<section class=\"ieeesa-your-groups\">\r\n <!-- Header: title + count, View All link, collapse/expand toggle -->\r\n <div class=\"ieeesa-your-groups__header\">\r\n <div class=\"ieeesa-your-groups__heading\">\r\n <h2 class=\"ieeesa-your-groups__title m-0\">{{ texts?.title }}</h2>\r\n </div>\r\n\r\n <div class=\"ieeesa-your-groups__actions\">\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-your-groups__view-all\"\r\n [attr.aria-label]=\"texts?.ariaLabel?.viewAll\"\r\n (click)=\"onViewAll()\"\r\n >\r\n {{ texts?.viewAllLabel }}\r\n </button>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"ieeesa-your-groups__toggle\"\r\n [attr.aria-expanded]=\"isExpanded\"\r\n [attr.aria-label]=\"texts?.ariaLabel?.toggle\"\r\n (click)=\"onToggleExpand()\"\r\n >\r\n <span>{{ isExpanded ? texts?.collapseLabel : texts?.expandLabel }}</span>\r\n <i\r\n class=\"fa-solid\"\r\n [ngClass]=\"isExpanded ? 'fa-chevron-up' : 'fa-chevron-down'\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Body: only when the card is expanded. -->\r\n <div class=\"ieeesa-your-groups__body\" *ngIf=\"isExpanded\">\r\n <ieeesa-loader\r\n *ngIf=\"sectionState === sectionStates.LOADING\"\r\n ></ieeesa-loader>\r\n\r\n <p\r\n *ngIf=\"sectionState === sectionStates.ERROR\"\r\n class=\"ieeesa-your-groups__message ieeesa-your-groups__message--error\"\r\n role=\"alert\"\r\n >\r\n {{ texts?.errorMessage }}\r\n </p>\r\n\r\n <p\r\n *ngIf=\"sectionState === sectionStates.EMPTY\"\r\n class=\"ieeesa-your-groups__message\"\r\n role=\"status\"\r\n >\r\n {{ texts?.emptyMessage }}\r\n </p>\r\n\r\n <ng-container *ngIf=\"sectionState === sectionStates.LOADED\">\r\n <table class=\"ieeesa-your-groups__table\">\r\n <thead>\r\n <tr>\r\n <th scope=\"col\">{{ texts?.columns?.group }}</th>\r\n <th scope=\"col\">{{ texts?.columns?.type }}</th>\r\n <th scope=\"col\">{{ texts?.columns?.officerRole }}</th>\r\n <th scope=\"col\">{{ texts?.columns?.affiliation }}</th>\r\n <th scope=\"col\">{{ texts?.columns?.memberLevel }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let row of visibleRows; trackBy: trackByRow\">\r\n <!-- GROUP ROW -->\r\n <ng-container *ngIf=\"row.kind === 'group'\">\r\n <td class=\"ieeesa-your-groups__group-cell\">\r\n <!-- Parent: blue +/- toggle to reveal the hierarchy. -->\r\n <button\r\n *ngIf=\"hasHierarchy(row.group!)\"\r\n type=\"button\"\r\n class=\"ieeesa-your-groups__hierarchy-toggle\"\r\n [attr.aria-expanded]=\"isGroupExpanded(row.group!)\"\r\n [attr.aria-label]=\"\r\n (isGroupExpanded(row.group!)\r\n ? texts?.collapseLabel\r\n : texts?.expandLabel) +\r\n ' ' +\r\n row.group!.group\r\n \"\r\n (click)=\"onToggleHierarchy(row.group!)\"\r\n >\r\n <i\r\n class=\"fa-solid\"\r\n [ngClass]=\"isGroupExpanded(row.group!) ? 'fa-minus' : 'fa-plus'\"\r\n ></i>\r\n </button>\r\n <!-- Leaf: spacer keeps names aligned with parents' names. -->\r\n <span\r\n *ngIf=\"!hasHierarchy(row.group!)\"\r\n class=\"ieeesa-your-groups__toggle-spacer\"\r\n aria-hidden=\"true\"\r\n ></span>\r\n <span class=\"ieeesa-your-groups__group-name\">\r\n {{ row.group!.group }}\r\n </span>\r\n </td>\r\n <td>{{ row.group!.type }}</td>\r\n <td>{{ row.group!.officerRole }}</td>\r\n <td>{{ row.group!.affiliation }}</td>\r\n <td>\r\n <span\r\n class=\"ieeesa-your-groups__badge\"\r\n [ngClass]=\"resolveBadgeClass(row.group!.memberLevel)\"\r\n >\r\n {{ row.group!.memberLevel }}\r\n </span>\r\n </td>\r\n </ng-container>\r\n\r\n <!-- HIERARCHY (ancestor) ROW -->\r\n <ng-container *ngIf=\"row.kind === 'hierarchy'\">\r\n <td class=\"ieeesa-your-groups__group-cell ieeesa-your-groups__hierarchy-cell\">\r\n <span\r\n class=\"ieeesa-your-groups__indent\"\r\n [style.width.px]=\"row.level * indentPerLevel\"\r\n aria-hidden=\"true\"\r\n ></span>\r\n <span class=\"ieeesa-your-groups__hierarchy-connector\" aria-hidden=\"true\">\r\n <i class=\"fa-solid fa-turn-up fa-rotate-90\"></i>\r\n </span>\r\n <span class=\"ieeesa-your-groups__hierarchy-name\">\r\n {{ row.node!.groupName }}\r\n <span\r\n *ngIf=\"row.node!.groupAcronym\"\r\n class=\"ieeesa-your-groups__hierarchy-acronym\"\r\n >\r\n ({{ row.node!.groupAcronym }})\r\n </span>\r\n </span>\r\n </td>\r\n <td colspan=\"4\"></td>\r\n </ng-container>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Pagination: the same Angular Material paginator the shared data-table\r\n uses \u2014 it provides the \"Items per page\" dropdown, range label, and\r\n prev/next controls. -->\r\n <mat-paginator\r\n *ngIf=\"groupCount > 0\"\r\n class=\"ieeesa-your-groups__paginator\"\r\n [length]=\"groupCount\"\r\n [pageSize]=\"pageSize\"\r\n [pageIndex]=\"pageIndex\"\r\n [pageSizeOptions]=\"pageSizeOptions\"\r\n (page)=\"onPaginatorChange($event)\"\r\n ></mat-paginator>\r\n </ng-container>\r\n </div>\r\n</section>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-your-groups{display:block;background-color:#fff;border-radius:12px;box-shadow:0 1px 2px #0003;padding:20px 24px;border:1px solid #E2E8F0}.ieeesa-your-groups__header{display:flex;align-items:center;justify-content:space-between;gap:16px}.ieeesa-your-groups__heading{display:flex;flex-direction:column;gap:2px}.ieeesa-your-groups__title{font-family:Calibri Bold;font-weight:700;font-size:18px;color:#0f172a}.ieeesa-your-groups__count{font-family:Calibri Regular;font-size:.75rem;color:#777a7e}.ieeesa-your-groups__actions{display:flex;align-items:center;gap:16px}.ieeesa-your-groups__view-all{background:none;border:none;padding:0;cursor:pointer;font-family:Calibri Bold;font-weight:700;font-size:18px;color:#00629b}.ieeesa-your-groups__view-all:hover{text-decoration:underline}.ieeesa-your-groups__toggle{display:inline-flex;align-items:center;gap:8px;padding:6px 14px;border:1px solid #dbe2eb;border-radius:8px;background-color:#fff;cursor:pointer;font-family:Calibri Bold;font-weight:700;font-size:18px;color:#00629b}.ieeesa-your-groups__toggle:hover{border-color:#00629b}.ieeesa-your-groups__toggle i{font-size:.75rem}.ieeesa-your-groups__body{margin-top:16px}.ieeesa-your-groups__message{text-align:center;padding:32px 0;font-family:Calibri Regular;font-size:.875rem;color:#777a7e}.ieeesa-your-groups__message--error{color:#ba0c2f}.ieeesa-your-groups__table{width:100%;border-collapse:collapse}.ieeesa-your-groups__table th,.ieeesa-your-groups__table td{text-align:left;padding:14px 16px;border-bottom:1px solid #dbe2eb;vertical-align:middle}.ieeesa-your-groups__table th{background-color:#f4f5f7;color:#333436;font-family:Calibri Bold;font-weight:700;font-size:.875rem}.ieeesa-your-groups__table td{color:#333436;font-size:.875rem}.ieeesa-your-groups__table tbody tr:nth-child(2n) td{background-color:#f8fcff}.ieeesa-your-groups__group-cell{display:flex;align-items:center;gap:10px}.ieeesa-your-groups__indent{flex:0 0 auto;display:inline-block}.ieeesa-your-groups__hierarchy-toggle{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;border-radius:4px;background-color:#00629b;color:#fff;font-size:.875rem;line-height:1;cursor:pointer}.ieeesa-your-groups__hierarchy-toggle:hover{background-color:#003e65}.ieeesa-your-groups__toggle-spacer{flex:0 0 auto;display:inline-block;width:24px;height:24px}.ieeesa-your-groups__group-name{color:#00629b;font-family:Calibri Bold;font-weight:700}.ieeesa-your-groups__hierarchy-cell{gap:6px}.ieeesa-your-groups__hierarchy-connector{flex:0 0 auto;display:inline-flex;align-items:center;color:#777a7e}.ieeesa-your-groups__hierarchy-name{color:#333436}.ieeesa-your-groups__hierarchy-acronym{color:#777a7e}.ieeesa-your-groups__badge{display:inline-flex;align-items:center;padding:.125em .75em;border-radius:1em;font-size:.875rem;white-space:nowrap}.ieeesa-your-groups__badge--success{color:#2e7d32;background-color:#2e7d321f}.ieeesa-your-groups__badge--warning{color:#b35600;background-color:#b356001f}.ieeesa-your-groups__paginator{margin-top:8px}.ieeesa-your-groups__paginator ::ng-deep .mat-mdc-paginator-container{justify-content:flex-end}.ieeesa-your-groups__paginator ::ng-deep .mat-mdc-paginator{background-color:transparent}\n"] }]
1601
+ }], propDecorators: { groups: [{
1602
+ type: Input
1603
+ }], sectionState: [{
1604
+ type: Input
1605
+ }], totalGroups: [{
1606
+ type: Input
1607
+ }], pageIndex: [{
1608
+ type: Input
1609
+ }], pageSize: [{
1610
+ type: Input
1611
+ }], viewAllGroups: [{
1612
+ type: Output
1613
+ }], pageChange: [{
1614
+ type: Output
1615
+ }], expandedChange: [{
1616
+ type: Output
1617
+ }], expanded: [{
1618
+ type: Input
1619
+ }] } });
1620
+
1621
+ /**
1622
+ * Determines whether a raw list item is an EVENT (owns child meetings) or a
1623
+ * STANDALONE_MEETING. An item is an EVENT when the backend flags it with
1624
+ * `isMeetingEvent === true` or `hasChildren === true`.
1625
+ * @param {EventMeetingListItem} item - The raw list item.
1626
+ * @return {UpcomingItemKind} The resolved render kind.
1627
+ */
1628
+ function resolveItemKind(item) {
1629
+ const isEvent = item?.isMeetingEvent === true || item?.hasChildren === true;
1630
+ return isEvent ? UpcomingItemKind.EVENT : UpcomingItemKind.STANDALONE_MEETING;
1631
+ }
1632
+ /**
1633
+ * Maps a raw backend list item to the {@link UpcomingEvent} view model. EVENT
1634
+ * rows start with an empty `meetings` array (child meetings are lazily loaded on
1635
+ * expand); STANDALONE_MEETING rows carry their own group/location fields.
1636
+ * @param {EventMeetingListItem} item - The raw list item.
1637
+ * @return {UpcomingEvent} The mapped view model.
1638
+ */
1639
+ function mapListItemToUpcomingEvent(item) {
1640
+ const kind = resolveItemKind(item);
1641
+ const timezone = item?.timeZone ?? '';
1642
+ const dayIndicator = item?.dayIndicator ??
1643
+ (kind === UpcomingItemKind.EVENT
1644
+ ? computeDayIndicator(item?.startDateTime, item?.endDateTime, timezone)
1645
+ : '');
1646
+ return {
1647
+ eventId: item?.id ?? item?.eventId ?? item?.meetingId ?? '',
1648
+ kind,
1649
+ // The list endpoint uses `name`; fall back to `meetingTitle` for meeting rows.
1650
+ title: item?.name ?? item?.meetingTitle ?? '',
1651
+ // The list screen copies status into contentUnderLink; accept either.
1652
+ status: item?.status ?? item?.contentUnderLink ?? '',
1653
+ dayIndicator,
1654
+ meetingCount: kind === UpcomingItemKind.STANDALONE_MEETING
1655
+ ? 1
1656
+ : item?.meetingCount ?? 0,
1657
+ openForAttendanceCount: item?.openForAttendanceCount ?? 0,
1658
+ startDateTime: item?.startDateTime,
1659
+ endDateTime: item?.endDateTime,
1660
+ timezone,
1661
+ groupOrCommittee: item?.groupName ?? item?.groupAcronym ?? '',
1662
+ roomOrLocation: item?.meetingLocation ?? item?.room ?? '',
1663
+ isMemberOfGroup: item?.isMemberOfGroup ?? false,
1664
+ meetings: [],
1665
+ };
1666
+ }
1667
+ /**
1668
+ * Maps a raw backend list of events & meetings into the dashboard view models,
1669
+ * enforcing that STANDALONE_MEETING rows always sort LAST while preserving the
1670
+ * backend's within-group ordering for each partition (stable sort).
1671
+ * @param {EventMeetingListItem[]} items - The raw list items.
1672
+ * @return {UpcomingEvent[]} The mapped, standalone-last ordered view models.
1673
+ */
1674
+ function mapEventMeetingList(items) {
1675
+ const mapped = (items ?? []).map((item) => mapListItemToUpcomingEvent(item));
1676
+ const events = mapped.filter((entry) => entry.kind === UpcomingItemKind.EVENT);
1677
+ const standalone = mapped.filter((entry) => entry.kind === UpcomingItemKind.STANDALONE_MEETING);
1678
+ return [...events, ...standalone];
1679
+ }
1680
+ /**
1681
+ * Maps a raw backend child-meeting detail item to the {@link MeetingRow} view
1682
+ * model used by the event block's expanded meeting list.
1683
+ * @param {EventMeetingDetailItem} item - The raw child-meeting item.
1684
+ * @return {MeetingRow} The mapped meeting row.
1685
+ */
1686
+ function mapDetailItemToMeetingRow(item) {
1687
+ const status = item?.status ?? '';
1688
+ // A meeting is "open for attendance" while it is In Progress. The
1689
+ // meetingsDetails endpoint has no dedicated flag, so derive it from status
1690
+ // (honouring an explicit backend flag when one is provided).
1691
+ const isOpenForAttendance = item?.isOpenForAttendance ?? status.trim().toLowerCase() === 'in progress';
1692
+ return {
1693
+ meetingId: item?.meetingId ?? '',
1694
+ meetingName: item?.meetingTitle ?? '',
1695
+ groupOrCommittee: item?.groupName ?? '',
1696
+ roomOrLocation: item?.meetingLocation ?? '',
1697
+ startDateTime: item?.meetingStDate,
1698
+ endDateTime: item?.meetingEdDate,
1699
+ timezone: item?.timeZone ?? '',
1700
+ status,
1701
+ isMemberOfGroup: item?.isMemberOfGroup ?? false,
1702
+ isOpenForAttendance,
1703
+ };
1704
+ }
1705
+ /**
1706
+ * Maps a raw backend list of child-meeting detail items to {@link MeetingRow}
1707
+ * view models.
1708
+ * @param {EventMeetingDetailItem[]} items - The raw child-meeting items.
1709
+ * @return {MeetingRow[]} The mapped meeting rows.
1710
+ */
1711
+ function mapEventMeetingDetails(items) {
1712
+ return (items ?? []).map((item) => mapDetailItemToMeetingRow(item));
1713
+ }
1714
+
1715
+ /**
1716
+ * Pure reducers for the dashboard attendance state. These functions never
1717
+ * mutate their inputs; each returns a new object/set so they can be exercised
1718
+ * directly by property tests and composed by the DashboardService.
1719
+ */
1720
+ /**
1721
+ * Produce a new attendance-state map with the target meeting set to
1722
+ * CURRENTLY_ATTENDING. Every other entry is copied unchanged (frame property).
1723
+ *
1724
+ * @param {Record<string, AttendanceState>} map The current attendance-state map (not mutated).
1725
+ * @param {string} meetingId The meeting whose state transitions to CURRENTLY_ATTENDING.
1726
+ * @return {Record<string, AttendanceState>} A new map reflecting the successful log-attendance action.
1727
+ */
1728
+ const applyLogAttendanceSuccess = (map, meetingId) => ({
1729
+ ...map,
1730
+ [meetingId]: AttendanceState.CURRENTLY_ATTENDING,
1731
+ });
1732
+ /**
1733
+ * Produce a new attendance-state map with the target meeting set to
1734
+ * NOT_ATTENDING. Every other entry is copied unchanged (frame property).
1735
+ *
1736
+ * @param {Record<string, AttendanceState>} map The current attendance-state map (not mutated).
1737
+ * @param {string} meetingId The meeting whose state transitions to NOT_ATTENDING.
1738
+ * @return {Record<string, AttendanceState>} A new map reflecting the successful leave-meeting action.
1739
+ */
1740
+ const applyLeaveMeetingSuccess = (map, meetingId) => ({
1741
+ ...map,
1742
+ [meetingId]: AttendanceState.NOT_ATTENDING,
1743
+ });
1744
+ /**
1745
+ * Add a meeting to the in-flight set, returning a new set. Prior membership is
1746
+ * retained so the displayed attendance state is preserved while the request
1747
+ * resolves.
1748
+ *
1749
+ * @param {ReadonlySet<string>} set The current in-flight meeting-id set (not mutated).
1750
+ * @param {string} meetingId The meeting whose attendance action has started.
1751
+ * @return {Set<string>} A new set that additionally contains the meeting id.
1752
+ */
1753
+ const beginAttendanceAction = (set, meetingId) => new Set([...set, meetingId]);
1754
+ /**
1755
+ * Remove a meeting from the in-flight set, returning a new set. All other
1756
+ * in-flight ids are retained.
1757
+ *
1758
+ * @param {ReadonlySet<string>} set The current in-flight meeting-id set (not mutated).
1759
+ * @param {string} meetingId The meeting whose attendance action has resolved.
1760
+ * @return {Set<string>} A new set that no longer contains the meeting id.
1761
+ */
1762
+ const endAttendanceAction = (set, meetingId) => {
1763
+ const next = new Set(set);
1764
+ next.delete(meetingId);
1765
+ return next;
1766
+ };
1767
+
1768
+ /** Fallback label for an empty group type / member level, matching the My Groups tab. */
1769
+ const UNASSIGNED_LABEL = 'Unassigned';
1770
+ /** Placeholder for an empty cell value. */
1771
+ const EMPTY_VALUE = '-';
1772
+ /** Statuses included in the upcoming section, matching the view-events-meetings-list screen. */
1773
+ const UPCOMING_STATUSES = ['In Progress', 'Upcoming'];
1774
+ /** Sort column used for the top-level events & meetings list. */
1775
+ const UPCOMING_SORT_COLUMN = 'startDateTime';
1776
+ /** Sort column used for an event's expanded child-meeting list (meetingsDetails). */
1777
+ const EVENT_MEETINGS_SORT_COLUMN = 'meetingStDate';
1778
+ /** Default page size for the top-level upcoming section list. */
1779
+ const UPCOMING_PAGE_SIZE = 10;
1780
+ /** Default page size for an event's expanded child-meeting list. */
1781
+ const EVENT_MEETINGS_PAGE_SIZE = 5;
1782
+ /** Large page size used by the eager count prefetch to capture the full set. */
1783
+ const EVENT_COUNT_PAGE_SIZE = 200;
1784
+ /**
1785
+ * DashboardService is the single source of truth for the dashboard landing
1786
+ * page's data and attendance state. It mirrors the `events-and-meetings`
1787
+ * service conventions: it is a `providedIn: 'root'` singleton, holds the host
1788
+ * `apiBaseUrl` / `userPermissions` set via {@link setAppConfig}, owns its state
1789
+ * via `BehaviorSubject`s exposed as observables, and reaches the backend only
1790
+ * through the shared `HttpService` from `@ieeesa-npm/utility` (never
1791
+ * `HttpClient` directly).
1792
+ *
1793
+ * The upcoming-events load calls the real `POST /meetings/eventsAndMeetings`
1794
+ * endpoint (child meetings are lazily loaded per-event via
1795
+ * `POST /meetings/meetingsDetails`); the groups load calls `GET
1796
+ * /groups/my-groups`. The attendance actions compose the pure reducers from
1797
+ * `attendance-reducers.ts` so the displayed `AttendanceState` only advances on a
1798
+ * successful resolution and the prior state is retained on failure.
1799
+ * @export
1800
+ * @class DashboardService
1801
+ */
1802
+ class DashboardService {
1803
+ constructor(
1804
+ // eslint-disable-next-line no-unused-vars
1805
+ httpService // eslint-disable-next-line no-empty-function
1806
+ ) {
1807
+ this.httpService = httpService;
1808
+ this.upcomingEvents = new BehaviorSubject([]);
1809
+ this.groups = new BehaviorSubject([]);
1810
+ this.upcomingState = new BehaviorSubject(DashboardSectionState.LOADING);
1811
+ this.groupsState = new BehaviorSubject(DashboardSectionState.LOADING);
1812
+ this.attendanceStates = new BehaviorSubject({});
1813
+ this.inFlightMeetingIds = new BehaviorSubject(new Set());
1814
+ /** Emits the list of the user's upcoming events. */
1815
+ this.upcomingEvents$ = this.upcomingEvents.asObservable();
1816
+ /** Emits the list of the user's groups. */
1817
+ this.groups$ = this.groups.asObservable();
1818
+ /** Emits the load/empty/error state of the upcoming-events section. */
1819
+ this.upcomingState$ = this.upcomingState.asObservable();
1820
+ /** Emits the load/empty/error state of the groups section. */
1821
+ this.groupsState$ = this.groupsState.asObservable();
1822
+ /** Emits the per-meeting attendance-state map. */
1823
+ this.attendanceStates$ = this.attendanceStates.asObservable();
1824
+ /** Emits the set of meeting ids with an attendance action in flight. */
1825
+ this.inFlightMeetingIds$ = this.inFlightMeetingIds.asObservable();
1826
+ }
1827
+ /**
1828
+ * Stores the host-supplied API base URL and user permissions used to build
1829
+ * request URLs and gate permission-controlled actions. Mirrors the
1830
+ * `events-and-meetings` service `setAppConfig`.
1831
+ * @param {string} apiBaseURL - The host application's API base URL.
1832
+ * @param {UserPermission[]} userPermissions - The signed-in user's permissions.
1833
+ * @return {void}
1834
+ * @memberof DashboardService
1835
+ */
1836
+ setAppConfig(apiBaseURL, userPermissions) {
1837
+ this.apiBaseUrl = apiBaseURL;
1838
+ this.userPermissions = userPermissions;
1839
+ }
1840
+ /**
1841
+ * Loads the user's upcoming events & meetings from the real backend via
1842
+ * `POST {apiBaseUrl}/meetings/eventsAndMeetings`, the same endpoint the
1843
+ * `view-events-meetings-list` screen uses. The response is mapped into the
1844
+ * dashboard view models (with STANDALONE_MEETING rows ordered last), the
1845
+ * per-meeting attendance-state map is seeded, and the section advances through
1846
+ * LOADING → LOADED/EMPTY on success, leaving the caller to set ERROR on
1847
+ * failure. Follows the shared `pipe(map, catchError(throw))` pattern.
1848
+ * @return {Observable<EventMeetingListResponse>} The upcoming events & meetings response envelope.
1849
+ * @memberof DashboardService
1850
+ */
1851
+ loadUpcomingEvents() {
1852
+ this.upcomingState.next(DashboardSectionState.LOADING);
1853
+ const payload = {
1854
+ payload: {
1855
+ eventId: null,
1856
+ sortColumn: UPCOMING_SORT_COLUMN,
1857
+ ascendingOrder: true,
1858
+ page: 1,
1859
+ size: UPCOMING_PAGE_SIZE,
1860
+ filter: {
1861
+ status: UPCOMING_STATUSES,
1862
+ isAllMeetingSelected: false,
1863
+ },
1864
+ },
1865
+ };
1866
+ return this.httpService
1867
+ .postData(`${this.apiBaseUrl}/meetings/eventsAndMeetings`, payload)
1868
+ .pipe(map((response) => {
1869
+ const events = mapEventMeetingList(response?.body?.eventMeetingList ?? []);
1870
+ this.upcomingEvents.next(events);
1871
+ this.seedAttendanceStates(events);
1872
+ this.upcomingState.next(events.length > 0
1873
+ ? DashboardSectionState.LOADED
1874
+ : DashboardSectionState.EMPTY);
1875
+ // Eagerly fetch each EVENT's meeting count so the "{n} meetings" and
1876
+ // "{n} open for attendance" summaries are correct before expanding.
1877
+ this.loadEventCounts(events);
1878
+ return response;
1879
+ }), catchError((error) => {
1880
+ this.upcomingState.next(DashboardSectionState.ERROR);
1881
+ throw error;
1882
+ }));
1883
+ }
1884
+ /**
1885
+ * Lazily loads (and paginates) the child meetings of an EVENT row via
1886
+ * `POST {apiBaseUrl}/meetings/meetingsDetails` — the per-event expand endpoint
1887
+ * used by the reference screen. On success the loaded meetings are mapped and
1888
+ * merged onto the matching event in the `upcomingEvents` list (with its
1889
+ * `meetingCount` / `openForAttendanceCount` refreshed), and the per-meeting
1890
+ * attendance-state map is extended. Each event paginates independently.
1891
+ * @param {string} eventId - The event whose meetings to load.
1892
+ * @param {number} [page=1] - The one-based page to request.
1893
+ * @param {number} [size=EVENT_MEETINGS_PAGE_SIZE] - The page size.
1894
+ * @return {Observable<EventMeetingDetailResponse>} The child-meetings response envelope.
1895
+ * @memberof DashboardService
1896
+ */
1897
+ loadEventMeetings(eventId, page = 1, size = EVENT_MEETINGS_PAGE_SIZE) {
1898
+ return this.fetchEventMeetingsPage(eventId, page, size).pipe(map((response) => {
1899
+ const meetings = mapEventMeetingDetails(response?.body?.meetings ?? []);
1900
+ const total = response?.body?.totalRecords ?? meetings.length;
1901
+ // Prefer the already-known full open count on the event; fall back to
1902
+ // the current page's open count when it hasn't been prefetched.
1903
+ const existing = this.upcomingEvents.value.find((event) => event.eventId === eventId);
1904
+ const pageOpenCount = meetings.filter((meeting) => meeting.isOpenForAttendance).length;
1905
+ const openForAttendanceCount = existing?.openForAttendanceCount || pageOpenCount;
1906
+ this.mergeEventMeetings(eventId, meetings, total, openForAttendanceCount);
1907
+ this.seedAttendanceStatesFromMeetings(meetings);
1908
+ return response;
1909
+ }), catchError((error) => {
1910
+ throw error;
1911
+ }));
1912
+ }
1913
+ /**
1914
+ * Merges a freshly loaded page of child meetings onto the matching event in
1915
+ * the `upcomingEvents` list, refreshing the event's `meetings`,
1916
+ * `meetingCount` (from the backend `totalRecords`), and
1917
+ * `openForAttendanceCount`. Leaves all other events untouched.
1918
+ * @param {string} eventId - The event to merge meetings onto.
1919
+ * @param {MeetingRow[]} meetings - The mapped child meetings for the page.
1920
+ * @param {number} totalRecords - The event's total meeting count from the backend.
1921
+ * @return {void}
1922
+ * @memberof DashboardService
1923
+ */
1924
+ mergeEventMeetings(eventId, meetings, totalRecords, openForAttendanceCount) {
1925
+ const next = this.upcomingEvents.value.map((event) => {
1926
+ if (event.eventId !== eventId) {
1927
+ return event;
1928
+ }
1929
+ return {
1930
+ ...event,
1931
+ meetings,
1932
+ meetingCount: totalRecords,
1933
+ openForAttendanceCount,
1934
+ };
1935
+ });
1936
+ this.upcomingEvents.next(next);
1937
+ }
1938
+ /**
1939
+ * Eagerly fetches each EVENT's total meeting count and open-for-attendance
1940
+ * count by requesting a large first page of its child meetings, then merging
1941
+ * only the counts (not the rows) so the collapsed card summaries are accurate
1942
+ * before the user expands. Standalone meetings are skipped. Failures per event
1943
+ * are swallowed so one failing count never breaks the list.
1944
+ * @param {UpcomingEvent[]} events - The loaded events to fetch counts for.
1945
+ * @return {void}
1946
+ * @memberof DashboardService
1947
+ */
1948
+ loadEventCounts(events) {
1949
+ events
1950
+ .filter((event) => event.kind === UpcomingItemKind.EVENT && event.eventId)
1951
+ .forEach((event) => {
1952
+ this.fetchEventMeetingsPage(event.eventId, 1, EVENT_COUNT_PAGE_SIZE)
1953
+ .pipe(catchError(() => of(null)))
1954
+ .subscribe((response) => {
1955
+ if (!response) {
1956
+ return;
1957
+ }
1958
+ const meetings = mapEventMeetingDetails(response?.body?.meetings ?? []);
1959
+ const total = response?.body?.totalRecords ?? meetings.length;
1960
+ const openCount = meetings.filter((meeting) => meeting.isOpenForAttendance).length;
1961
+ this.mergeEventCounts(event.eventId, total, openCount);
1962
+ this.seedAttendanceStatesFromMeetings(meetings);
1963
+ });
1964
+ });
1965
+ }
1966
+ /**
1967
+ * Merges only the meeting counts onto the matching event, leaving its
1968
+ * `meetings` rows untouched (used by the eager count prefetch).
1969
+ * @param {string} eventId - The event to update.
1970
+ * @param {number} totalRecords - The event's total meeting count.
1971
+ * @param {number} openForAttendanceCount - The open-for-attendance count.
1972
+ * @return {void}
1973
+ * @memberof DashboardService
1974
+ */
1975
+ mergeEventCounts(eventId, totalRecords, openForAttendanceCount) {
1976
+ const next = this.upcomingEvents.value.map((event) => event.eventId === eventId
1977
+ ? { ...event, meetingCount: totalRecords, openForAttendanceCount }
1978
+ : event);
1979
+ this.upcomingEvents.next(next);
1980
+ }
1981
+ /**
1982
+ * Builds and posts the `meetingsDetails` request for a single event page,
1983
+ * returning the raw response envelope. Shared by {@link loadEventMeetings} and
1984
+ * the eager count prefetch.
1985
+ * @param {string} eventId - The event whose meetings to fetch.
1986
+ * @param {number} page - The one-based page to request.
1987
+ * @param {number} size - The page size.
1988
+ * @return {Observable<EventMeetingDetailResponse>} The child-meetings response.
1989
+ * @memberof DashboardService
1990
+ */
1991
+ fetchEventMeetingsPage(eventId, page, size) {
1992
+ const payload = {
1993
+ payload: {
1994
+ eventId,
1995
+ sortColumn: EVENT_MEETINGS_SORT_COLUMN,
1996
+ ascendingOrder: true,
1997
+ page,
1998
+ size,
1999
+ filter: {
2000
+ status: UPCOMING_STATUSES,
2001
+ isAllMeetingSelected: false,
2002
+ },
2003
+ },
2004
+ };
2005
+ return this.httpService.postData(`${this.apiBaseUrl}/meetings/meetingsDetails`, payload);
2006
+ }
2007
+ /**
2008
+ * Loads the user's groups, driving the groups section through
2009
+ * LOADING → LOADED/EMPTY on success and leaving the caller to set ERROR on
2010
+ * failure. Follows the `pipe(map, catchError(throw))` pattern.
2011
+ *
2012
+ * TODO: Replace mock with real API call when BE is ready
2013
+ * @return {Observable<DashboardListResponse<GroupRow[]>>} The groups response envelope.
2014
+ * @memberof DashboardService
2015
+ */
2016
+ loadGroups() {
2017
+ this.groupsState.next(DashboardSectionState.LOADING);
2018
+ return this.httpService
2019
+ .getData(`${this.apiBaseUrl}/groups/my-groups`)
2020
+ .pipe(map((response) => {
2021
+ const groups = (response?.body?.groups ?? []).map((group) => this.mapGroup(group));
2022
+ this.groups.next(groups);
2023
+ this.groupsState.next(groups.length > 0
2024
+ ? DashboardSectionState.LOADED
2025
+ : DashboardSectionState.EMPTY);
2026
+ return response;
2027
+ }), catchError((error) => {
2028
+ this.groupsState.next(DashboardSectionState.ERROR);
2029
+ throw error;
2030
+ }));
2031
+ }
2032
+ /**
2033
+ * Maps a backend {@link MyGroupView} to the table-facing {@link GroupRow}.
2034
+ * Mirrors the My Groups profile tab: the type falls back to "Unassigned" when
2035
+ * `groupTypeName` is null, affiliations are comma-joined, empty officer role /
2036
+ * affiliation render as a placeholder, and a blank member level becomes
2037
+ * "Unassigned" (rendered as the warning pill by the Your Groups component).
2038
+ * @param {MyGroupView} group - The backend group view.
2039
+ * @return {GroupRow}
2040
+ * @memberof DashboardService
2041
+ */
2042
+ mapGroup(group) {
2043
+ // The backend returns officer roles as an `officer` array of
2044
+ // { officerId, officerName }. Join the names for the Officer Role column
2045
+ // (e.g. "Chair" or "Chair, Vice Chair"); show the placeholder when empty.
2046
+ const officerRole = (group.officer ?? [])
2047
+ .map((officer) => officer?.officerName?.trim())
2048
+ .filter((name) => !!name)
2049
+ .join(', ') || EMPTY_VALUE;
2050
+ return {
2051
+ groupId: group.groupId,
2052
+ group: group.groupName,
2053
+ type: group.groupTypeName || group.groupType || UNASSIGNED_LABEL,
2054
+ officerRole,
2055
+ affiliation: group.affiliations?.join(', ') || EMPTY_VALUE,
2056
+ memberLevel: group.memberLevel?.trim() || UNASSIGNED_LABEL,
2057
+ // Carry the ancestor hierarchy through for the expandable hierarchy rows.
2058
+ hierarchy: group.hierarchy ?? [],
2059
+ };
2060
+ }
2061
+ /**
2062
+ * Logs attendance for a meeting. Marks the meeting in-flight (retaining the
2063
+ * prior displayed state), posts through `HttpService`, applies the
2064
+ * log-attendance-success reducer on success, clears the in-flight flag on
2065
+ * completion, and rethrows while retaining prior state on failure.
2066
+ * @param {string} meetingId - The meeting to log attendance for.
2067
+ * @return {Observable<DashboardListResponse<unknown>>} The attendance response envelope.
2068
+ * @memberof DashboardService
2069
+ */
2070
+ logAttendance(meetingId) {
2071
+ this.inFlightMeetingIds.next(beginAttendanceAction(this.inFlightMeetingIds.value, meetingId));
2072
+ return this.httpService
2073
+ .postData(`${this.apiBaseUrl}/meetings/attendance/log`, { meetingId })
2074
+ .pipe(map((response) => {
2075
+ this.attendanceStates.next(applyLogAttendanceSuccess(this.attendanceStates.value, meetingId));
2076
+ this.inFlightMeetingIds.next(endAttendanceAction(this.inFlightMeetingIds.value, meetingId));
2077
+ return response;
2078
+ }), catchError((error) => {
2079
+ // Retain prior attendance state; only clear the in-flight flag.
2080
+ this.inFlightMeetingIds.next(endAttendanceAction(this.inFlightMeetingIds.value, meetingId));
2081
+ throw error;
2082
+ }));
2083
+ }
2084
+ /**
2085
+ * Leaves a meeting. Marks the meeting in-flight (retaining the prior
2086
+ * displayed state), posts through `HttpService`, applies the
2087
+ * leave-meeting-success reducer on success, clears the in-flight flag on
2088
+ * completion, and rethrows while retaining prior state on failure.
2089
+ * @param {string} meetingId - The meeting to leave.
2090
+ * @return {Observable<DashboardListResponse<unknown>>} The attendance response envelope.
2091
+ * @memberof DashboardService
2092
+ */
2093
+ leaveMeeting(meetingId) {
2094
+ this.inFlightMeetingIds.next(beginAttendanceAction(this.inFlightMeetingIds.value, meetingId));
2095
+ return this.httpService
2096
+ .postData(`${this.apiBaseUrl}/meetings/attendance/leave`, { meetingId })
2097
+ .pipe(map((response) => {
2098
+ this.attendanceStates.next(applyLeaveMeetingSuccess(this.attendanceStates.value, meetingId));
2099
+ this.inFlightMeetingIds.next(endAttendanceAction(this.inFlightMeetingIds.value, meetingId));
2100
+ return response;
2101
+ }), catchError((error) => {
2102
+ // Retain prior attendance state; only clear the in-flight flag.
2103
+ this.inFlightMeetingIds.next(endAttendanceAction(this.inFlightMeetingIds.value, meetingId));
2104
+ throw error;
2105
+ }));
2106
+ }
2107
+ /**
2108
+ * Returns a synchronous snapshot of the per-meeting attendance-state map.
2109
+ * @return {Record<string, AttendanceState>} The current attendance-state map.
2110
+ * @memberof DashboardService
2111
+ */
2112
+ getAttendanceStatesSnapshot() {
2113
+ return this.attendanceStates.value;
2114
+ }
2115
+ /**
2116
+ * Seeds the attendance-state map from a freshly loaded list of events, using
2117
+ * each meeting's `attendanceState` (defaulting to `NOT_ATTENDING`). Keeps the
2118
+ * displayed state in sync with the data load without overwriting ids that are
2119
+ * not present in the new list.
2120
+ * @param {UpcomingEvent[]} events - The loaded upcoming events.
2121
+ * @return {void}
2122
+ * @memberof DashboardService
2123
+ */
2124
+ seedAttendanceStates(events) {
2125
+ const meetings = events.flatMap((event) => event.meetings ?? []);
2126
+ this.seedAttendanceStatesFromMeetings(meetings);
2127
+ }
2128
+ /**
2129
+ * Seeds the attendance-state map from a list of meeting rows, using each
2130
+ * meeting's `attendanceState` (defaulting to `NOT_ATTENDING`) without
2131
+ * overwriting ids absent from the new list. Shared by the list load and the
2132
+ * per-event expand so freshly loaded child meetings pick up their state.
2133
+ * @param {MeetingRow[]} meetings - The meeting rows to seed from.
2134
+ * @return {void}
2135
+ * @memberof DashboardService
2136
+ */
2137
+ seedAttendanceStatesFromMeetings(meetings) {
2138
+ const next = {
2139
+ ...this.attendanceStates.value,
2140
+ };
2141
+ meetings.forEach((meeting) => {
2142
+ if (meeting.meetingId) {
2143
+ next[meeting.meetingId] =
2144
+ meeting.attendanceState ??
2145
+ next[meeting.meetingId] ??
2146
+ AttendanceState.NOT_ATTENDING;
2147
+ }
2148
+ });
2149
+ this.attendanceStates.next(next);
2150
+ }
2151
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DashboardService, deps: [{ token: i1$1.HttpService }], target: i0.ɵɵFactoryTarget.Injectable }); }
2152
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DashboardService, providedIn: 'root' }); }
2153
+ }
2154
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DashboardService, decorators: [{
2155
+ type: Injectable,
2156
+ args: [{
2157
+ providedIn: 'root',
2158
+ }]
2159
+ }], ctorParameters: function () { return [{ type: i1$1.HttpService }]; } });
2160
+
2161
+ /**
2162
+ * Permission identifiers (by name) that gate the dashboard attendance actions.
2163
+ * Mirrors the permission-type pattern used by the `events-and-meetings` and
2164
+ * `groups` user-permissions services.
2165
+ */
2166
+ /* eslint-disable no-unused-vars */
2167
+ var DashboardPermissionType;
2168
+ (function (DashboardPermissionType) {
2169
+ DashboardPermissionType["LOG_ATTENDANCE"] = "Log Attendance";
2170
+ DashboardPermissionType["LEAVE_MEETING"] = "Leave Meeting";
2171
+ })(DashboardPermissionType || (DashboardPermissionType = {}));
2172
+ /* eslint-enable no-unused-vars */
2173
+ class UserPermissionsService {
2174
+ /**
2175
+ * Returns access provision for the passed action/feature based on whether the signed in user has appropriate permission.
2176
+ * @param {DashboardPermissionType} permission
2177
+ * @param {UserPermission[]} userPermissions
2178
+ * @return {boolean}
2179
+ * @memberof UserPermissionsService
2180
+ */
2181
+ validateAccessPermission(permission, userPermissions) {
2182
+ const isPermitted = userPermissions?.length > 0
2183
+ ? userPermissions?.some(({ permissionName }) => permissionName === permission)
2184
+ : false;
2185
+ return isPermitted;
2186
+ }
2187
+ /**
2188
+ * Maps the host-supplied user permissions to the attendance-action permissions consumed by the dashboard rows.
2189
+ * @param {UserPermission[]} userPermissions
2190
+ * @return {AttendanceActionPermissions}
2191
+ * @memberof UserPermissionsService
2192
+ */
2193
+ mapToAttendanceActionPermissions(userPermissions) {
2194
+ return {
2195
+ canLogAttendance: this.validateAccessPermission(DashboardPermissionType.LOG_ATTENDANCE, userPermissions),
2196
+ canLeaveMeeting: this.validateAccessPermission(DashboardPermissionType.LEAVE_MEETING, userPermissions),
2197
+ };
2198
+ }
2199
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: UserPermissionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2200
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: UserPermissionsService, providedIn: 'root' }); }
2201
+ }
2202
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: UserPermissionsService, decorators: [{
2203
+ type: Injectable,
2204
+ args: [{
2205
+ providedIn: 'root',
2206
+ }]
2207
+ }] });
2208
+
2209
+ /**
2210
+ * Dashboard (`ieeesa-dashboard`) is the root container and single host entry
2211
+ * point for the "NESC Revision Cycle" dashboard landing page. It owns the
2212
+ * host-supplied configuration (`routePaths`, `apiBaseURL`, `userInfo`,
2213
+ * `userPermissions`), composes the four regions — welcome header, upcoming
2214
+ * meetings & events, your groups, and footer — and orchestrates data loading
2215
+ * and attendance actions through the {@link DashboardService}.
2216
+ *
2217
+ * The component is composition-first and host-agnostic: it subscribes to the
2218
+ * service observables (`upcomingEvents$`, `groups$`, the per-section states, the
2219
+ * attendance-state map, and the in-flight set) with `takeUntil(destroy$)` and
2220
+ * tracks `isMobileView` from the Presentation_Library
2221
+ * {@link BreakpointObserverService} (Req 13.1, 13.2, 13.5, 15.1, 15.2). Child
2222
+ * navigation `@Output()`s are composed into a single {@link dashboardNavigate}
2223
+ * stream of `{ path, id }` intents resolved from `routePaths` (falling back to
2224
+ * the default route configuration in `constants.json` when `routePaths` is
2225
+ * omitted) (Req 5.2, 6.4, 7.4, 8.2, 8.7, 11.1, 11.5). Attendance `@Output()`s
2226
+ * are delegated to the service, and on failure the attendance-action error
2227
+ * message from `constants.json` is surfaced via the shared {@link AlertsService}
2228
+ * (Req 7.7, 7.8, 13.7). Per-row {@link AttendanceActionPermissions} are computed
2229
+ * from `userPermissions` and passed down to the rows (Req 12.3).
2230
+ * @export
2231
+ * @class DashboardComponent
2232
+ */
2233
+ class DashboardComponent {
2234
+ /**
2235
+ * Total number of meetings across the loaded upcoming events, shown in the
2236
+ * welcome header's "Upcoming Meetings & Events" shortcut badge.
2237
+ * @readonly
2238
+ * @memberof DashboardComponent
2239
+ */
2240
+ get meetingsCount() {
2241
+ return this.upcomingEvents.reduce((total, event) => total + (event?.meetingCount ?? 0), 0);
2242
+ }
2243
+ /**
2244
+ * Number of loaded groups, shown in the welcome header's "Your Groups"
2245
+ * shortcut badge.
2246
+ * @readonly
2247
+ * @memberof DashboardComponent
2248
+ */
2249
+ get groupsCount() {
2250
+ return this.groups.length;
2251
+ }
2252
+ constructor(
2253
+ // eslint-disable-next-line no-unused-vars
2254
+ dashboardService,
2255
+ // eslint-disable-next-line no-unused-vars
2256
+ userPermissionsService,
2257
+ // eslint-disable-next-line no-unused-vars
2258
+ breakpointObserverService,
2259
+ // eslint-disable-next-line no-unused-vars
2260
+ alertService // eslint-disable-next-line no-empty-function
2261
+ ) {
2262
+ this.dashboardService = dashboardService;
2263
+ this.userPermissionsService = userPermissionsService;
2264
+ this.breakpointObserverService = breakpointObserverService;
2265
+ this.alertService = alertService;
2266
+ /** User-facing strings and the default route config sourced from `constants.json`. */
2267
+ this.constants = constants$1;
2268
+ /** Emits a resolved navigation intent for the host to act on (Req 5.2, 6.4, 7.4, 8.2, 8.7, 11.1). */
2269
+ this.dashboardNavigate = new EventEmitter();
2270
+ /** The user's upcoming events, bound to the upcoming section (Req 13.1, 13.2). */
2271
+ this.upcomingEvents = [];
2272
+ /** The user's groups, bound to the groups section (Req 13.1, 13.2). */
2273
+ this.groups = [];
2274
+ /** The upcoming-events section's load/empty/error state (Req 13.1, 13.2, 13.5). */
2275
+ this.upcomingState = DashboardSectionState.LOADING;
2276
+ /** The groups section's load/empty/error state (Req 13.1, 13.2, 13.5). */
2277
+ this.groupsState = DashboardSectionState.LOADING;
2278
+ /** The per-meeting attendance-state map passed down to the rows. */
2279
+ this.attendanceStates = {};
2280
+ /** The set of meeting ids with an attendance action awaiting resolution. */
2281
+ this.inFlightMeetingIds = new Set();
2282
+ /** The per-row attendance-action permissions derived from `userPermissions` (Req 12.3). */
2283
+ this.attendancePermissions = {
2284
+ canLogAttendance: false,
2285
+ canLeaveMeeting: false,
2286
+ };
2287
+ /** Whether the viewport is at/below the mobile breakpoint (Req 15.1, 15.2). */
2288
+ this.isMobileView = false;
2289
+ /** Whether the Upcoming Meetings & Events card is expanded. Starts collapsed. */
2290
+ this.meetingsExpanded = false;
2291
+ /** Whether the Your Groups card is expanded. Starts collapsed. */
2292
+ this.groupsExpanded = false;
2293
+ /** The current zero-based page index of the groups table. */
2294
+ this.groupsPageIndex = 0;
2295
+ /** The current page size of the groups table (5 rows per page per the design). */
2296
+ this.groupsPageSize = 5;
2297
+ /** Completes on destroy to tear down all subscriptions (Req 2.4, 2.5). */
2298
+ this.destroy$ = new Subject();
2299
+ }
2300
+ /**
2301
+ * Initializes the dashboard: forwards the host config to the service, derives
2302
+ * the per-row attendance permissions, kicks off the upcoming-events and groups
2303
+ * loads, and wires up subscriptions to the service state observables and the
2304
+ * breakpoint observer — all cleaned up via `takeUntil(destroy$)` (Req 11.2,
2305
+ * 11.4, 12.3, 13.1, 13.2, 13.5, 15.1, 15.2).
2306
+ * @return {void}
2307
+ * @memberof DashboardComponent
2308
+ */
2309
+ ngOnInit() {
2310
+ this.dashboardService.setAppConfig(this.apiBaseURL, this.userPermissions);
2311
+ this.attendancePermissions =
2312
+ this.userPermissionsService.mapToAttendanceActionPermissions(this.userPermissions);
2313
+ this.subscribeToServiceState();
2314
+ this.subscribeToBreakpoint();
2315
+ this.loadUpcomingEvents();
2316
+ this.loadGroups();
2317
+ }
2318
+ /**
2319
+ * Tears down all subscriptions on destroy (Req 2.4, 2.5).
2320
+ * @return {void}
2321
+ * @memberof DashboardComponent
2322
+ */
2323
+ ngOnDestroy() {
2324
+ this.destroy$.next(true);
2325
+ this.destroy$.complete();
2326
+ }
2327
+ /**
2328
+ * Handles the welcome-header "Jump to" date selection. The dashboard currently
2329
+ * has no date-scoped data load, so the selection is accepted without side
2330
+ * effects; the hook keeps the header's contract satisfied for future filtering.
2331
+ * @return {void}
2332
+ * @memberof DashboardComponent
2333
+ */
2334
+ onJumpToDateSelected() {
2335
+ // No date-scoped reload yet; retained for future date filtering (Req 4.4).
2336
+ }
2337
+ /**
2338
+ * Handles a welcome-header "Jump to" shortcut: opens the matching section's
2339
+ * card (so its content is visible) and smoothly scrolls it into view. It does
2340
+ * NOT navigate away — the shortcut is an in-page jump to the section.
2341
+ * @param {DashboardJumpTarget} target - The selected shortcut target.
2342
+ * @return {void}
2343
+ * @memberof DashboardComponent
2344
+ */
2345
+ onJumpToShortcut(target) {
2346
+ const section = target === DashboardJumpTarget.MEETINGS
2347
+ ? this.upcomingSection
2348
+ : this.groupsSection;
2349
+ if (target === DashboardJumpTarget.MEETINGS) {
2350
+ this.meetingsExpanded = true;
2351
+ }
2352
+ else {
2353
+ this.groupsExpanded = true;
2354
+ }
2355
+ // Scroll after the expand has rendered so the target's final position is used.
2356
+ setTimeout(() => {
2357
+ section?.nativeElement?.scrollIntoView({
2358
+ behavior: 'smooth',
2359
+ block: 'start',
2360
+ });
2361
+ });
2362
+ }
2363
+ /**
2364
+ * Emits the "View All" upcoming-events navigation intent, targeting the
2365
+ * `viewAllEvents` route with no id (Req 5.2, 11.1).
2366
+ * @return {void}
2367
+ * @memberof DashboardComponent
2368
+ */
2369
+ onViewAllEvents() {
2370
+ this.emitNavigation(this.activeRouteConfig.viewAllEvents);
2371
+ }
2372
+ /**
2373
+ * Emits the "View Event" navigation intent, targeting the `eventDetail` route
2374
+ * with the event id (Req 6.4, 11.1).
2375
+ * @param {string} eventId - The id of the event to view.
2376
+ * @return {void}
2377
+ * @memberof DashboardComponent
2378
+ */
2379
+ onViewEvent(eventId) {
2380
+ this.emitNavigation(this.activeRouteConfig.eventDetail, eventId);
2381
+ }
2382
+ /**
2383
+ * Emits the open-meeting navigation intent, targeting the `meetingDetail`
2384
+ * route with the meeting id (Req 7.4, 11.1).
2385
+ * @param {string} meetingId - The id of the meeting to open.
2386
+ * @return {void}
2387
+ * @memberof DashboardComponent
2388
+ */
2389
+ onOpenMeeting(meetingId) {
2390
+ this.emitNavigation(this.activeRouteConfig.meetingDetail, meetingId);
2391
+ }
2392
+ /**
2393
+ * Emits the "View All" groups navigation intent, targeting the
2394
+ * `viewAllGroups` route with no id (Req 8.2, 11.1).
2395
+ * @return {void}
2396
+ * @memberof DashboardComponent
2397
+ */
2398
+ onViewAllGroups() {
2399
+ this.emitNavigation(this.activeRouteConfig.viewAllGroups);
2400
+ }
2401
+ /**
2402
+ * Emits the group-detail navigation intent, targeting the `groupDetail` route
2403
+ * with the group id (Req 8.7, 11.1).
2404
+ * @param {string} groupId - The id of the selected group.
2405
+ * @return {void}
2406
+ * @memberof DashboardComponent
2407
+ */
2408
+ onGroupSelected(groupId) {
2409
+ this.emitNavigation(this.activeRouteConfig.groupDetail, groupId);
2410
+ }
2411
+ /**
2412
+ * Updates the groups table paging state in response to a paginator change so
2413
+ * the groups section reflects the requested page (Req 8.10).
2414
+ * @param {PageChange} page - The requested page index and size.
2415
+ * @return {void}
2416
+ * @memberof DashboardComponent
2417
+ */
2418
+ onGroupsPageChange(page) {
2419
+ this.groupsPageIndex = page.pageIndex;
2420
+ this.groupsPageSize = page.pageSize;
2421
+ }
2422
+ /**
2423
+ * Lazily loads (or paginates) the child meetings of an expanded EVENT card via
2424
+ * the service, surfacing the attendance error message on failure. Each event
2425
+ * paginates independently.
2426
+ * @param {{ eventId: string; page: number }} request - The event id and one-based page to load.
2427
+ * @return {void}
2428
+ * @memberof DashboardComponent
2429
+ */
2430
+ onLoadEventMeetings(request) {
2431
+ if (!request?.eventId) {
2432
+ return;
2433
+ }
2434
+ this.dashboardService
2435
+ .loadEventMeetings(request.eventId, request.page)
2436
+ .pipe(takeUntil(this.destroy$))
2437
+ .subscribe({
2438
+ error: () => this.showAttendanceError(),
2439
+ });
2440
+ }
2441
+ /**
2442
+ * Delegates a log-attendance request to the service, surfacing the
2443
+ * attendance-action error message via the shared {@link AlertsService} on
2444
+ * failure while the service retains the prior displayed state (Req 7.7, 13.7).
2445
+ * @param {string} meetingId - The meeting to log attendance for.
2446
+ * @return {void}
2447
+ * @memberof DashboardComponent
2448
+ */
2449
+ onLogAttendance(meetingId) {
2450
+ this.dashboardService
2451
+ .logAttendance(meetingId)
2452
+ .pipe(takeUntil(this.destroy$))
2453
+ .subscribe({
2454
+ error: () => this.showAttendanceError(),
2455
+ });
2456
+ }
2457
+ /**
2458
+ * Delegates a leave-meeting request to the service, surfacing the
2459
+ * attendance-action error message via the shared {@link AlertsService} on
2460
+ * failure while the service retains the prior displayed state (Req 7.8, 13.7).
2461
+ * @param {string} meetingId - The meeting to leave.
2462
+ * @return {void}
2463
+ * @memberof DashboardComponent
2464
+ */
2465
+ onLeaveMeeting(meetingId) {
2466
+ this.dashboardService
2467
+ .leaveMeeting(meetingId)
2468
+ .pipe(takeUntil(this.destroy$))
2469
+ .subscribe({
2470
+ error: () => this.showAttendanceError(),
2471
+ });
2472
+ }
2473
+ /**
2474
+ * The route configuration in effect: the host-supplied `routePaths` when
2475
+ * provided, otherwise the default route config from `constants.json` so
2476
+ * navigation intents always carry valid targets (Req 11.1, 11.5).
2477
+ * @return {DashboardRouteConfig} The active route configuration.
2478
+ * @memberof DashboardComponent
2479
+ */
2480
+ get activeRouteConfig() {
2481
+ return this.routePaths ?? this.constants.defaultRouteConfig;
2482
+ }
2483
+ /**
2484
+ * Subscribes to the service's state observables, mirroring each into a local
2485
+ * field for the template to bind. All subscriptions are torn down via
2486
+ * `takeUntil(destroy$)` (Req 13.1, 13.2, 13.5).
2487
+ * @return {void}
2488
+ * @memberof DashboardComponent
2489
+ */
2490
+ subscribeToServiceState() {
2491
+ this.dashboardService.upcomingEvents$
2492
+ .pipe(takeUntil(this.destroy$))
2493
+ .subscribe((events) => (this.upcomingEvents = events));
2494
+ this.dashboardService.groups$
2495
+ .pipe(takeUntil(this.destroy$))
2496
+ .subscribe((groups) => {
2497
+ this.groups = groups;
2498
+ });
2499
+ this.dashboardService.upcomingState$
2500
+ .pipe(takeUntil(this.destroy$))
2501
+ .subscribe((state) => (this.upcomingState = state));
2502
+ this.dashboardService.groupsState$
2503
+ .pipe(takeUntil(this.destroy$))
2504
+ .subscribe((state) => (this.groupsState = state));
2505
+ this.dashboardService.attendanceStates$
2506
+ .pipe(takeUntil(this.destroy$))
2507
+ .subscribe((states) => (this.attendanceStates = states));
2508
+ this.dashboardService.inFlightMeetingIds$
2509
+ .pipe(takeUntil(this.destroy$))
2510
+ .subscribe((ids) => (this.inFlightMeetingIds = ids));
2511
+ }
2512
+ /**
2513
+ * Subscribes to the Presentation_Library breakpoint observer to keep
2514
+ * `isMobileView` in sync with the viewport (Req 15.1, 15.2).
2515
+ * @return {void}
2516
+ * @memberof DashboardComponent
2517
+ */
2518
+ subscribeToBreakpoint() {
2519
+ this.breakpointObserverService
2520
+ .getBreakpoint()
2521
+ .pipe(takeUntil(this.destroy$))
2522
+ .subscribe((result) => {
2523
+ this.isMobileView = result?.matches;
2524
+ });
2525
+ }
2526
+ /**
2527
+ * Triggers the upcoming-events load, setting the section to `ERROR` on failure
2528
+ * so only the section error region renders (Req 13.1, 13.2, 13.5).
2529
+ * @return {void}
2530
+ * @memberof DashboardComponent
2531
+ */
2532
+ loadUpcomingEvents() {
2533
+ this.dashboardService
2534
+ .loadUpcomingEvents()
2535
+ .pipe(takeUntil(this.destroy$))
2536
+ .subscribe({
2537
+ error: () => (this.upcomingState = DashboardSectionState.ERROR),
2538
+ });
2539
+ }
2540
+ /**
2541
+ * Triggers the groups load, setting the section to `ERROR` on failure so only
2542
+ * the section error region renders. Isolated from the upcoming-events load so
2543
+ * one failing does not affect the other (Req 13.1, 13.2, 13.5).
2544
+ * @return {void}
2545
+ * @memberof DashboardComponent
2546
+ */
2547
+ loadGroups() {
2548
+ this.dashboardService
2549
+ .loadGroups()
2550
+ .pipe(takeUntil(this.destroy$))
2551
+ .subscribe({
2552
+ error: () => (this.groupsState = DashboardSectionState.ERROR),
2553
+ });
2554
+ }
2555
+ /**
2556
+ * Composes and emits a {@link DashboardNavigationIntent} for the host from the
2557
+ * resolved target path and an optional triggering id (Req 11.1).
2558
+ * @param {string} path - The resolved target route path.
2559
+ * @param {string} [id] - The triggering identifier, when applicable.
2560
+ * @return {void}
2561
+ * @memberof DashboardComponent
2562
+ */
2563
+ emitNavigation(path, id) {
2564
+ this.dashboardNavigate.emit(id ? { path, id } : { path });
2565
+ }
2566
+ /**
2567
+ * Surfaces the attendance-action error message from `constants.json` via the
2568
+ * shared {@link AlertsService} (Req 13.7).
2569
+ * @return {void}
2570
+ * @memberof DashboardComponent
2571
+ */
2572
+ showAttendanceError() {
2573
+ this.alertService?.showMessage({
2574
+ status: 'custom',
2575
+ message: this.constants?.attendance?.errorMessage ??
2576
+ 'Unable to update your attendance. Please try again.',
2577
+ alertType: AlertType.ERROR,
2578
+ isCloseNeeded: true,
2579
+ });
2580
+ }
2581
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DashboardComponent, deps: [{ token: DashboardService }, { token: UserPermissionsService }, { token: i3.BreakpointObserverService }, { token: i1$1.AlertsService }], target: i0.ɵɵFactoryTarget.Component }); }
2582
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: DashboardComponent, isStandalone: true, selector: "ieeesa-dashboard", inputs: { routePaths: "routePaths", apiBaseURL: "apiBaseURL", userInfo: "userInfo", userPermissions: "userPermissions" }, outputs: { dashboardNavigate: "dashboardNavigate" }, providers: [AlertsService], viewQueries: [{ propertyName: "upcomingSection", first: true, predicate: ["upcomingSection"], descendants: true, read: ElementRef }, { propertyName: "groupsSection", first: true, predicate: ["groupsSection"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div\r\n class=\"ieeesa-dashboard\"\r\n [ngClass]=\"{ 'ieeesa-dashboard--mobile': isMobileView }\"\r\n>\r\n <ieeesa-dashboard-welcome-header\r\n class=\"ieeesa-dashboard__welcome\"\r\n [userName]=\"userInfo?.name ?? ''\"\r\n [meetingsCount]=\"meetingsCount\"\r\n [groupsCount]=\"groupsCount\"\r\n (shortcutSelected)=\"onJumpToShortcut($event)\"\r\n ></ieeesa-dashboard-welcome-header>\r\n\r\n <main class=\"ieeesa-dashboard__content\">\r\n <ieeesa-upcoming-meetings-events\r\n #upcomingSection\r\n class=\"ieeesa-dashboard__upcoming\"\r\n [(expanded)]=\"meetingsExpanded\"\r\n [events]=\"upcomingEvents\"\r\n [sectionState]=\"upcomingState\"\r\n [permissions]=\"attendancePermissions\"\r\n [attendanceStates]=\"attendanceStates\"\r\n [inFlightMeetingIds]=\"inFlightMeetingIds\"\r\n [isMobileView]=\"isMobileView\"\r\n (viewAll)=\"onViewAllEvents()\"\r\n (viewEvent)=\"onViewEvent($event)\"\r\n (openMeeting)=\"onOpenMeeting($event)\"\r\n (logAttendance)=\"onLogAttendance($event)\"\r\n (leaveMeeting)=\"onLeaveMeeting($event)\"\r\n (loadEventMeetings)=\"onLoadEventMeetings($event)\"\r\n ></ieeesa-upcoming-meetings-events>\r\n\r\n <ieeesa-your-groups\r\n #groupsSection\r\n [(expanded)]=\"groupsExpanded\"\r\n [groups]=\"groups\"\r\n [sectionState]=\"groupsState\"\r\n [totalGroups]=\"groups.length\"\r\n [pageIndex]=\"groupsPageIndex\"\r\n [pageSize]=\"groupsPageSize\"\r\n (viewAllGroups)=\"onViewAllGroups()\"\r\n (pageChange)=\"onGroupsPageChange($event)\"\r\n ></ieeesa-your-groups>\r\n </main>\r\n</div>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-dashboard{display:flex;flex-direction:column;gap:24px;padding:16px;height:100vh;max-height:100vh;overflow:hidden}.ieeesa-dashboard__welcome{display:block;flex:0 0 auto}.ieeesa-dashboard__content{display:flex;flex-direction:column;gap:24px;flex:1 1 auto;overflow-y:auto;padding-right:4px}.ieeesa-dashboard__upcoming,.ieeesa-dashboard__groups,.ieeesa-dashboard__footer{display:block}.ieeesa-dashboard--mobile{padding:8px;gap:16px}.ieeesa-dashboard--mobile .ieeesa-dashboard__content{gap:16px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: UpcomingMeetingsEventsComponent, selector: "ieeesa-upcoming-meetings-events", inputs: ["events", "sectionState", "permissions", "attendanceStates", "inFlightMeetingIds", "isMobileView", "expanded"], outputs: ["viewAll", "viewEvent", "openMeeting", "logAttendance", "leaveMeeting", "meetingPageChange", "loadEventMeetings", "expandedChange"] }, { kind: "component", type: YourGroupsComponent, selector: "ieeesa-your-groups", inputs: ["groups", "sectionState", "totalGroups", "pageIndex", "pageSize", "expanded"], outputs: ["viewAllGroups", "pageChange", "expandedChange"] }, { kind: "component", type: DashboardWelcomeHeaderComponent, selector: "ieeesa-dashboard-welcome-header", inputs: ["userName", "meetingsCount", "groupsCount"], outputs: ["shortcutSelected"] }], encapsulation: i0.ViewEncapsulation.None }); }
2583
+ }
2584
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DashboardComponent, decorators: [{
2585
+ type: Component,
2586
+ args: [{ selector: 'ieeesa-dashboard', standalone: true, imports: [
2587
+ CommonModule,
2588
+ UpcomingMeetingsEventsComponent,
2589
+ YourGroupsComponent,
2590
+ DashboardWelcomeHeaderComponent
2591
+ ], encapsulation: ViewEncapsulation.None, providers: [AlertsService], template: "<div\r\n class=\"ieeesa-dashboard\"\r\n [ngClass]=\"{ 'ieeesa-dashboard--mobile': isMobileView }\"\r\n>\r\n <ieeesa-dashboard-welcome-header\r\n class=\"ieeesa-dashboard__welcome\"\r\n [userName]=\"userInfo?.name ?? ''\"\r\n [meetingsCount]=\"meetingsCount\"\r\n [groupsCount]=\"groupsCount\"\r\n (shortcutSelected)=\"onJumpToShortcut($event)\"\r\n ></ieeesa-dashboard-welcome-header>\r\n\r\n <main class=\"ieeesa-dashboard__content\">\r\n <ieeesa-upcoming-meetings-events\r\n #upcomingSection\r\n class=\"ieeesa-dashboard__upcoming\"\r\n [(expanded)]=\"meetingsExpanded\"\r\n [events]=\"upcomingEvents\"\r\n [sectionState]=\"upcomingState\"\r\n [permissions]=\"attendancePermissions\"\r\n [attendanceStates]=\"attendanceStates\"\r\n [inFlightMeetingIds]=\"inFlightMeetingIds\"\r\n [isMobileView]=\"isMobileView\"\r\n (viewAll)=\"onViewAllEvents()\"\r\n (viewEvent)=\"onViewEvent($event)\"\r\n (openMeeting)=\"onOpenMeeting($event)\"\r\n (logAttendance)=\"onLogAttendance($event)\"\r\n (leaveMeeting)=\"onLeaveMeeting($event)\"\r\n (loadEventMeetings)=\"onLoadEventMeetings($event)\"\r\n ></ieeesa-upcoming-meetings-events>\r\n\r\n <ieeesa-your-groups\r\n #groupsSection\r\n [(expanded)]=\"groupsExpanded\"\r\n [groups]=\"groups\"\r\n [sectionState]=\"groupsState\"\r\n [totalGroups]=\"groups.length\"\r\n [pageIndex]=\"groupsPageIndex\"\r\n [pageSize]=\"groupsPageSize\"\r\n (viewAllGroups)=\"onViewAllGroups()\"\r\n (pageChange)=\"onGroupsPageChange($event)\"\r\n ></ieeesa-your-groups>\r\n </main>\r\n</div>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-dashboard{display:flex;flex-direction:column;gap:24px;padding:16px;height:100vh;max-height:100vh;overflow:hidden}.ieeesa-dashboard__welcome{display:block;flex:0 0 auto}.ieeesa-dashboard__content{display:flex;flex-direction:column;gap:24px;flex:1 1 auto;overflow-y:auto;padding-right:4px}.ieeesa-dashboard__upcoming,.ieeesa-dashboard__groups,.ieeesa-dashboard__footer{display:block}.ieeesa-dashboard--mobile{padding:8px;gap:16px}.ieeesa-dashboard--mobile .ieeesa-dashboard__content{gap:16px}\n"] }]
2592
+ }], ctorParameters: function () { return [{ type: DashboardService }, { type: UserPermissionsService }, { type: i3.BreakpointObserverService }, { type: i1$1.AlertsService }]; }, propDecorators: { routePaths: [{
2593
+ type: Input
2594
+ }], apiBaseURL: [{
2595
+ type: Input
2596
+ }], userInfo: [{
2597
+ type: Input
2598
+ }], userPermissions: [{
2599
+ type: Input
2600
+ }], dashboardNavigate: [{
2601
+ type: Output
2602
+ }], upcomingSection: [{
2603
+ type: ViewChild,
2604
+ args: ['upcomingSection', { read: ElementRef }]
2605
+ }], groupsSection: [{
2606
+ type: ViewChild,
2607
+ args: ['groupsSection', { read: ElementRef }]
2608
+ }] } });
2609
+
2610
+ var dashboard_component = /*#__PURE__*/Object.freeze({
2611
+ __proto__: null,
2612
+ DashboardComponent: DashboardComponent
2613
+ });
2614
+
2615
+ /**
2616
+ * Member_Level_Badge renders a single member-level value as a colored pill.
2617
+ * It reuses the Presentation_Library Legend component for the pill primitive,
2618
+ * sourcing the level-to-color mapping from `constants.json` (`memberLevel.colorMap`)
2619
+ * with a nullish-coalescing fallback to the configured default color.
2620
+ * @export
2621
+ * @class MemberLevelBadgeComponent
2622
+ */
2623
+ class MemberLevelBadgeComponent {
2624
+ constructor() {
2625
+ this.constants = constants$1;
2626
+ this.memberLevelValue = '';
2627
+ /** The Legend input consumed by the Presentation_Library Legend component. */
2628
+ this.legends = this.buildLegends('');
2629
+ }
2630
+ set memberLevel(value) {
2631
+ this.memberLevelValue = value ?? '';
2632
+ this.legends = this.buildLegends(this.memberLevelValue);
2633
+ }
2634
+ get memberLevel() {
2635
+ return this.memberLevelValue;
2636
+ }
2637
+ /**
2638
+ * Builds the single-item Legend list for the supplied member level, resolving
2639
+ * the pill color from the `constants.json` color map and falling back to the
2640
+ * configured default color when the level has no explicit mapping.
2641
+ * @param {string} memberLevel - The member-level label to render.
2642
+ * @return {Legend[]} A single-element Legend array for the Legend component.
2643
+ * @memberof MemberLevelBadgeComponent
2644
+ */
2645
+ buildLegends(memberLevel) {
2646
+ return [
2647
+ {
2648
+ label: memberLevel,
2649
+ color: this.resolveColorClass(memberLevel),
2650
+ hierarchy: 0,
2651
+ },
2652
+ ];
2653
+ }
2654
+ /**
2655
+ * Resolves the CSS modifier class for a member level from the `constants.json`
2656
+ * color map, using the `??` fallback to the `default` entry when the level is
2657
+ * missing from the map.
2658
+ * @param {string} memberLevel - The member-level label to resolve a color for.
2659
+ * @return {string} The CSS modifier class applied to the pill.
2660
+ * @memberof MemberLevelBadgeComponent
2661
+ */
2662
+ resolveColorClass(memberLevel) {
2663
+ const colorMap = this.constants?.memberLevel?.colorMap;
2664
+ const token = colorMap?.[memberLevel] ?? colorMap?.['default'] ?? '$ieeesa-color-neutral';
2665
+ return this.toColorClass(token);
2666
+ }
2667
+ /**
2668
+ * Converts a shared-styles color token (for example `$ieeesa-color-success`)
2669
+ * into a valid BEM-style CSS modifier class defined in this component's SCSS.
2670
+ * @param {string} token - The color token from the color map.
2671
+ * @return {string} A valid CSS class name.
2672
+ * @memberof MemberLevelBadgeComponent
2673
+ */
2674
+ toColorClass(token) {
2675
+ const normalized = token.replace(/^\$ieeesa-color-/, '').replace(/^\$/, '');
2676
+ return `ieeesa-member-level-badge--${normalized || 'neutral'}`;
2677
+ }
2678
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MemberLevelBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2679
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: MemberLevelBadgeComponent, isStandalone: true, selector: "ieeesa-member-level-badge", inputs: { memberLevel: "memberLevel" }, ngImport: i0, template: "<span\r\n class=\"ieeesa-member-level-badge\"\r\n [attr.aria-label]=\"memberLevel\"\r\n>\r\n <ieeesa-legend\r\n *ngIf=\"memberLevel\"\r\n [legends]=\"legends\"\r\n ></ieeesa-legend>\r\n</span>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-member-level-badge,.ieeesa-member-level-badge .ieeesa-legend{display:inline-flex}.ieeesa-member-level-badge .ieeesa-legend__indicator{align-items:center;gap:4px;padding:4px 12px;border-radius:16px;background-color:#f0f5f9}.ieeesa-member-level-badge .ieeesa-legend__square{width:8px;min-width:8px;height:8px;margin:0;border-radius:50%}.ieeesa-member-level-badge .ieeesa-legend p{margin:0}.ieeesa-member-level-badge .ieeesa-legend__square.ieeesa-member-level-badge--success{background-color:#78d7ef}.ieeesa-member-level-badge .ieeesa-legend__square.ieeesa-member-level-badge--info{background-color:#00b5e2}.ieeesa-member-level-badge .ieeesa-legend__square.ieeesa-member-level-badge--warning{background-color:#ffd92e}.ieeesa-member-level-badge .ieeesa-legend__square.ieeesa-member-level-badge--primary{background-color:#00629b}.ieeesa-member-level-badge .ieeesa-legend__square.ieeesa-member-level-badge--neutral{background-color:#939094}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: LegendComponent, selector: "ieeesa-legend", inputs: ["legends", "isSorting"] }], encapsulation: i0.ViewEncapsulation.None }); }
2680
+ }
2681
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MemberLevelBadgeComponent, decorators: [{
2682
+ type: Component,
2683
+ args: [{ selector: 'ieeesa-member-level-badge', standalone: true, imports: [CommonModule, LegendComponent], encapsulation: ViewEncapsulation.None, template: "<span\r\n class=\"ieeesa-member-level-badge\"\r\n [attr.aria-label]=\"memberLevel\"\r\n>\r\n <ieeesa-legend\r\n *ngIf=\"memberLevel\"\r\n [legends]=\"legends\"\r\n ></ieeesa-legend>\r\n</span>\r\n", styles: [".ieeesa-subtitle{text-align:left;font-family:Calibri Regular;font-size:1.5625rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-body{text-align:left;font-family:Calibri Regular;font-size:1.25rem;letter-spacing:0;color:#000;opacity:1}.ieeesa-display-lg-57{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:3.5625rem;line-height:64px;color:#000;letter-spacing:0}.ieeesa-display-md-45{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.813rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-md-47{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.9375rem;line-height:52px;color:#000;letter-spacing:0}.ieeesa-display-sm-38{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.375rem;line-height:44px;color:#000;letter-spacing:0}.ieeesa-headline-lg-32{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-lg-34{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:2.125rem;line-height:40px;color:#000;letter-spacing:0}.ieeesa-headline-md-30{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.875rem;line-height:36px;color:#000;letter-spacing:0}.ieeesa-headline-sm-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:32px;color:#00629b;letter-spacing:0}.ieeesa-headline-sm-26{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.625rem;line-height:32px;color:#000;letter-spacing:0}.ieeesa-title-lg-bold-24{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-lg-24{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.5rem;line-height:28px;color:#000;letter-spacing:0}.ieeesa-title-md-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.009375em}.ieeesa-title-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-title-sm-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-label-lg-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.ieeesa-btn-label-lg-bold-16{font-family:Calibri Regular;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#00629b;letter-spacing:.00625em}.ieeesa-label-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-label-sm-13{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.8125rem;line-height:16px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-16{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-bold-18{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-lg-18{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1.125rem;line-height:24px;color:#000;letter-spacing:.03125em}.ieeesa-body-md-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:20px;color:#1c1b1f;letter-spacing:.015625em}.ieeesa-body-md-16{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:20px;color:#000;letter-spacing:.015625em}.ieeesa-body-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-sm-12{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.75rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-body-color-sm-14{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:.875rem;line-height:18px;color:#49454f;letter-spacing:.025em}.ieeesa-body-sm-bold-14{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:.875rem;line-height:16px;color:#000;letter-spacing:.025em}.ieeesa-title-inter-lg-bold-24{font-family:Inter Bold;font-style:normal;font-weight:700;font-size:1.5rem;line-height:29px;color:#000;letter-spacing:0}.mat-mdc-button.mat-mdc-button-base.mat-primary-button,.mat-mdc-button.mat-mdc-button-base.mat-secondary-button,.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button,.mat-mdc-button.mat-mdc-button-base.mat-text-button{min-width:103px;width:auto;min-height:40px;height:auto;border-radius:3px;text-align:center;padding:.625em 1.5em;border:none;font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#000;letter-spacing:.00625em}.mat-mdc-button.mat-mdc-button-base>.mat-icon{margin-right:11px}.mat-mdc-button.mat-mdc-button-base.mat-primary-button{background-color:#00629b;color:#fff}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:focus{background:#1f75a7}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:hover{background-color:#003e65}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:active{background-color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-primary-button:disabled{background-color:#1c1b1f1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button{color:#fff;background:#a7a8aa;box-shadow:0 1px 2px #0000004d,0 2px 6px 2px #00000026}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:hover{background:#a7a8aa}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:focus{background:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:active{background:#1d192b1f}.mat-mdc-button.mat-mdc-button-base.mat-secondary-button:disabled{background:#1c1b1f1f;color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button{background-color:#fff;border:1px solid #00629b;color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:hover{background-color:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:focus{background-color:#f0f5f9}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:active{background:#6750a41f}.mat-mdc-button.mat-mdc-button-base.mat-tertiary-button:disabled{border:1px solid rgba(28,27,31,.12);color:#1c1b1f;opacity:.38}.mat-mdc-button.mat-mdc-button-base.mat-text-button{color:#00629b}.mat-mdc-button.mat-mdc-button-base.mat-text-button:hover,.mat-mdc-button.mat-mdc-button-base.mat-text-button:active{background:#6750a414}.mat-mdc-button.mat-mdc-button-base.mat-text-button:disabled{color:#1c1b1f;opacity:.38}.ieeesa-text-button{color:#cac4d0;background:none;border:none}.ieeesa-view-more-or-less-button{border:none;background:none;text-decoration:underline;color:#00629b}.flex-align-center{display:flex;align-items:center;justify-content:center}.mat-button-toggle-checked{background-color:#a7a8aa}.mat-button-toggle-checked .mat-button-toggle-label-content:before{font-family:\"Font Awesome 5 Free\";font-weight:900;content:\"\\f00c\";padding-right:.5625em}.mat-button-toggle-group .mat-button-toggle-label-content{font-family:Calibri Bold;font-style:normal;font-weight:700;font-size:1rem;line-height:20px;color:#1c1b1f;letter-spacing:.00625em}.no-bg-color-btn{color:#00629b;border-radius:3px;border:1px solid #00629b;min-height:40px;height:auto}@media (max-width: 768px){.mat-button-toggle-group .mat-button-toggle-label-content{max-width:8ch;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;position:relative}}.cdk-overlay-pane .mat-mdc-tooltip .mdc-tooltip__surface{color:#525252;padding:6px 12px;font-family:Calibri Regular;font-size:1rem;border:1px solid #cac4d0;background:#fff;box-shadow:0 4px 8px #00000040;max-width:unset!important}.cdk-overlay-pane .mat-mdc-option .mdc-list-item__primary-text{font-family:Calibri Regular!important}.cdk-overlay-pane.ieeesa-modal-confirm-dialog,.cdk-overlay-pane.ieeesa-modal-dialog,.cdk-overlay-pane.ieeesa-alert-dialog{width:100%;min-width:312px}.mat-mdc-form-field{font-family:Calibri Regular;font-style:normal;font-weight:400;font-size:1rem;line-height:24px;color:#49454f;letter-spacing:.03125em}.ieeesa-cancel-modal-confirm-dialog{width:25vw!important}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation{padding:1.5em 1.5em 1em}.ieeesa-cancel-modal-confirm-dialog .ieeesa-confirmation .mat-mdc-dialog-content{text-align:center;padding:0 0 1em!important}.mat-mdc-button,.mat-mdc-outlined-button{--mat-mdc-button-persistent-ripple-color: unset}@media (max-width: 768px){.ieeesa-cancel-modal-confirm-dialog{width:90vw!important}}.ieeesa-wrapper-border{border:1px solid #b2b7be;border-top:none;padding:1.5em;border-radius:0 0 4px 4px}.ieeesa-member-level-badge,.ieeesa-member-level-badge .ieeesa-legend{display:inline-flex}.ieeesa-member-level-badge .ieeesa-legend__indicator{align-items:center;gap:4px;padding:4px 12px;border-radius:16px;background-color:#f0f5f9}.ieeesa-member-level-badge .ieeesa-legend__square{width:8px;min-width:8px;height:8px;margin:0;border-radius:50%}.ieeesa-member-level-badge .ieeesa-legend p{margin:0}.ieeesa-member-level-badge .ieeesa-legend__square.ieeesa-member-level-badge--success{background-color:#78d7ef}.ieeesa-member-level-badge .ieeesa-legend__square.ieeesa-member-level-badge--info{background-color:#00b5e2}.ieeesa-member-level-badge .ieeesa-legend__square.ieeesa-member-level-badge--warning{background-color:#ffd92e}.ieeesa-member-level-badge .ieeesa-legend__square.ieeesa-member-level-badge--primary{background-color:#00629b}.ieeesa-member-level-badge .ieeesa-legend__square.ieeesa-member-level-badge--neutral{background-color:#939094}\n"] }]
2684
+ }], propDecorators: { memberLevel: [{
2685
+ type: Input
2686
+ }] } });
2687
+
2688
+ class DashboardModule {
2689
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DashboardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2690
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: DashboardModule, imports: [DashboardComponent], exports: [DashboardComponent] }); }
2691
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DashboardModule, imports: [DashboardComponent] }); }
2692
+ }
2693
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DashboardModule, decorators: [{
2694
+ type: NgModule,
2695
+ args: [{
2696
+ imports: [DashboardComponent],
2697
+ exports: [DashboardComponent],
2698
+ }]
2699
+ }] });
2700
+
2701
+ const DASHBOARD_ROUTES = [
2702
+ {
2703
+ path: '',
2704
+ loadComponent: () => Promise.resolve().then(function () { return dashboard_component; }).then((m) => m.DashboardComponent),
2705
+ },
2706
+ ];
2707
+
2708
+ /**
2709
+ * Request payloads for the dashboard "events & meetings" backend calls. These
2710
+ * mirror the `events-and-meetings` library's `GetEventsAndMeetingsPayload` /
2711
+ * `GetMeetingsByEventIdPayload` so the dashboard reuses the exact contract the
2712
+ * existing `view-events-meetings-list` screen posts.
2713
+ */
2714
+
2715
+ const DashboardRoutePathsMock = {
2716
+ eventDetail: 'events-and-meetings/view-event',
2717
+ meetingDetail: 'events-and-meetings/view-meeting',
2718
+ /*
2719
+ * The demo app only registers a flat `groups` route (no group-detail screen),
2720
+ * so group-row navigation targets it rather than a `view-group` child that the
2721
+ * demo does not implement. A consuming app with a real group-detail route
2722
+ * supplies its own path via the `routePaths` input.
2723
+ */
2724
+ groupDetail: 'groups',
2725
+ viewAllEvents: 'events-and-meetings/view-events-meetings-list',
2726
+ viewAllGroups: 'groups',
2727
+ };
2728
+
2729
+ const DashboardUserPermissionsMock = [
2730
+ {
2731
+ permissionName: 'View Events',
2732
+ permissionId: 'PERM_5dd65526-5df3-428d-8acc-cee7ffb5f261',
2733
+ },
2734
+ {
2735
+ permissionName: 'View Meetings',
2736
+ permissionId: 'PERM_5dd65526-5df3-428d-8acc-cee7ffb5f261',
2737
+ },
2738
+ {
2739
+ permissionName: 'View Groups',
2740
+ permissionId: 'PERM_9a1f0c84-2b77-4e2a-9f3d-1c6b4e9a8d20',
2741
+ },
2742
+ {
2743
+ permissionName: 'Log Attendance',
2744
+ permissionId: 'PERM_1b2c3d4e-5f60-4718-8293-aabbccddeeff',
2745
+ },
2746
+ {
2747
+ permissionName: 'Leave Meeting',
2748
+ permissionId: 'PERM_6f5e4d3c-2b1a-4908-8716-ffeeddccbbaa',
2749
+ },
2750
+ ];
2751
+
2752
+ /**
2753
+ * Mock upcoming-events + groups fixtures for the dashboard library.
2754
+ *
2755
+ * These exercise the key presentation edge cases:
2756
+ * - empty/undefined `groupOrCommittee` and `roomOrLocation` (empty-field placeholder)
2757
+ * - a mix of `CURRENTLY_ATTENDING` / `NOT_ATTENDING` attendance states
2758
+ * - events with many meetings so per-card pagination is exercised
2759
+ *
2760
+ * TODO: Replace mock with real API call when BE is ready
2761
+ */
2762
+ const DashboardUpcomingEventsMock = [
2763
+ {
2764
+ eventId: 'EVT-001',
2765
+ kind: UpcomingItemKind.EVENT,
2766
+ title: 'NESC Revision Cycle Kickoff',
2767
+ status: 'In Progress',
2768
+ dayIndicator: 'Day 1 of 3',
2769
+ meetingCount: 6,
2770
+ openForAttendanceCount: 3,
2771
+ startDate: '2025-03-14T09:00:00',
2772
+ endDate: '2025-03-16T17:00:00',
2773
+ timezone: 'America/New_York',
2774
+ meetings: [
2775
+ {
2776
+ meetingId: 'MTG-001',
2777
+ meetingName: 'Opening Plenary',
2778
+ groupOrCommittee: 'NESC Main Committee',
2779
+ roomOrLocation: 'Grand Ballroom A',
2780
+ meetingDate: '2025-03-14T09:00:00',
2781
+ meetingTime: '2025-03-14T09:00:00',
2782
+ timezone: 'America/New_York',
2783
+ isOpenForAttendance: true,
2784
+ attendanceState: AttendanceState.CURRENTLY_ATTENDING,
2785
+ },
2786
+ {
2787
+ meetingId: 'MTG-002',
2788
+ meetingName: 'Subcommittee 1 Working Session',
2789
+ groupOrCommittee: 'Subcommittee 1 - Grounding',
2790
+ // RoomOrLocation intentionally omitted to exercise the empty-field placeholder.
2791
+ meetingDate: '2025-03-14T11:00:00',
2792
+ meetingTime: '2025-03-14T11:00:00',
2793
+ timezone: 'America/New_York',
2794
+ isOpenForAttendance: true,
2795
+ attendanceState: AttendanceState.NOT_ATTENDING,
2796
+ },
2797
+ {
2798
+ meetingId: 'MTG-003',
2799
+ meetingName: 'Subcommittee 2 Working Session',
2800
+ // GroupOrCommittee intentionally empty to exercise the empty-field placeholder.
2801
+ groupOrCommittee: '',
2802
+ roomOrLocation: 'Room 204',
2803
+ meetingDate: '2025-03-14T13:30:00',
2804
+ meetingTime: '2025-03-14T13:30:00',
2805
+ timezone: 'America/New_York',
2806
+ isOpenForAttendance: true,
2807
+ attendanceState: AttendanceState.NOT_ATTENDING,
2808
+ },
2809
+ {
2810
+ meetingId: 'MTG-004',
2811
+ meetingName: 'Editorial Review',
2812
+ groupOrCommittee: 'Editorial Task Force',
2813
+ roomOrLocation: 'Room 118',
2814
+ meetingDate: '2025-03-15T09:00:00',
2815
+ meetingTime: '2025-03-15T09:00:00',
2816
+ timezone: 'America/New_York',
2817
+ isOpenForAttendance: false,
2818
+ attendanceState: AttendanceState.NOT_ATTENDING,
2819
+ },
2820
+ {
2821
+ meetingId: 'MTG-005',
2822
+ meetingName: 'Interpretations Panel',
2823
+ groupOrCommittee: 'Interpretations Subcommittee',
2824
+ roomOrLocation: 'Room 310',
2825
+ meetingDate: '2025-03-15T14:00:00',
2826
+ meetingTime: '2025-03-15T14:00:00',
2827
+ timezone: 'America/New_York',
2828
+ isOpenForAttendance: true,
2829
+ attendanceState: AttendanceState.CURRENTLY_ATTENDING,
2830
+ },
2831
+ {
2832
+ meetingId: 'MTG-006',
2833
+ meetingName: 'Closing Plenary',
2834
+ groupOrCommittee: 'NESC Main Committee',
2835
+ roomOrLocation: 'Grand Ballroom A',
2836
+ meetingDate: '2025-03-16T15:00:00',
2837
+ meetingTime: '2025-03-16T15:00:00',
2838
+ timezone: 'America/New_York',
2839
+ isOpenForAttendance: false,
2840
+ attendanceState: AttendanceState.NOT_ATTENDING,
2841
+ },
2842
+ ],
2843
+ },
2844
+ {
2845
+ eventId: 'EVT-002',
2846
+ kind: UpcomingItemKind.EVENT,
2847
+ title: 'Standards Board Quarterly Meeting',
2848
+ status: 'Upcoming',
2849
+ dayIndicator: 'Day 1 of 1',
2850
+ meetingCount: 3,
2851
+ openForAttendanceCount: 1,
2852
+ startDate: '2025-04-02T08:30:00',
2853
+ endDate: '2025-04-02T16:00:00',
2854
+ timezone: 'America/Los_Angeles',
2855
+ meetings: [
2856
+ {
2857
+ meetingId: 'MTG-101',
2858
+ meetingName: 'Board Opening Remarks',
2859
+ groupOrCommittee: 'Standards Board',
2860
+ roomOrLocation: 'Pacific Hall',
2861
+ meetingDate: '2025-04-02T08:30:00',
2862
+ meetingTime: '2025-04-02T08:30:00',
2863
+ timezone: 'America/Los_Angeles',
2864
+ isOpenForAttendance: true,
2865
+ attendanceState: AttendanceState.NOT_ATTENDING,
2866
+ },
2867
+ {
2868
+ meetingId: 'MTG-102',
2869
+ meetingName: 'Ballot Review Session',
2870
+ // Both optional location/group fields omitted to exercise placeholders.
2871
+ meetingDate: '2025-04-02T10:30:00',
2872
+ meetingTime: '2025-04-02T10:30:00',
2873
+ timezone: 'America/Los_Angeles',
2874
+ isOpenForAttendance: false,
2875
+ attendanceState: AttendanceState.NOT_ATTENDING,
2876
+ },
2877
+ {
2878
+ meetingId: 'MTG-103',
2879
+ meetingName: 'Action Items & Wrap-up',
2880
+ groupOrCommittee: 'Standards Board',
2881
+ roomOrLocation: 'Pacific Hall',
2882
+ meetingDate: '2025-04-02T15:00:00',
2883
+ meetingTime: '2025-04-02T15:00:00',
2884
+ timezone: 'America/Los_Angeles',
2885
+ isOpenForAttendance: false,
2886
+ attendanceState: AttendanceState.CURRENTLY_ATTENDING,
2887
+ },
2888
+ ],
2889
+ },
2890
+ {
2891
+ eventId: 'EVT-003',
2892
+ kind: UpcomingItemKind.EVENT,
2893
+ title: 'Working Group Interim',
2894
+ status: 'Upcoming',
2895
+ // DayIndicator intentionally omitted (optional field).
2896
+ meetingCount: 0,
2897
+ openForAttendanceCount: 0,
2898
+ startDate: '2025-05-20T09:00:00',
2899
+ endDate: '2025-05-20T12:00:00',
2900
+ timezone: 'Europe/London',
2901
+ meetings: [],
2902
+ },
2903
+ ];
2904
+ /**
2905
+ * Mock a single meeting row with an empty `meetingId` to exercise the
2906
+ * non-actionable (empty/undefined id) path in the meeting row component.
2907
+ *
2908
+ * TODO: Replace mock with real API call when BE is ready
2909
+ */
2910
+ const DashboardNonActionableMeetingMock = {
2911
+ meetingId: '',
2912
+ meetingName: 'Unscheduled Placeholder Session',
2913
+ groupOrCommittee: '',
2914
+ meetingDate: '2025-06-01T09:00:00',
2915
+ meetingTime: '2025-06-01T09:00:00',
2916
+ timezone: 'America/New_York',
2917
+ isOpenForAttendance: false,
2918
+ attendanceState: AttendanceState.NOT_ATTENDING,
2919
+ };
2920
+ /**
2921
+ * Mock groups fixtures for the dashboard "Your Groups" section.
2922
+ *
2923
+ * Includes rows with missing optional fields (`type`, `officerRole`,
2924
+ * `affiliation`) and a spread of member levels so the member-level badge
2925
+ * colour map and empty-field placeholders are exercised.
2926
+ *
2927
+ * TODO: Replace mock with real API call when BE is ready
2928
+ */
2929
+ const DashboardGroupsMock = [
2930
+ {
2931
+ groupId: 'GRP-001',
2932
+ group: 'NESC Main Committee',
2933
+ type: 'Committee',
2934
+ officerRole: 'Chair',
2935
+ affiliation: 'Employer Corp',
2936
+ memberLevel: 'Senior Member',
2937
+ },
2938
+ {
2939
+ groupId: 'GRP-002',
2940
+ group: 'Subcommittee 1 - Grounding',
2941
+ type: 'Subcommittee',
2942
+ // OfficerRole intentionally omitted (member with no officer role).
2943
+ affiliation: 'Standards Member',
2944
+ memberLevel: 'Member',
2945
+ },
2946
+ {
2947
+ groupId: 'GRP-003',
2948
+ group: 'Editorial Task Force',
2949
+ // Type and affiliation intentionally omitted to exercise placeholders.
2950
+ officerRole: 'Secretary',
2951
+ memberLevel: 'Fellow',
2952
+ },
2953
+ {
2954
+ groupId: 'GRP-004',
2955
+ group: 'Interpretations Subcommittee',
2956
+ type: 'Subcommittee',
2957
+ officerRole: 'Member',
2958
+ affiliation: 'Tech Solutions Inc',
2959
+ memberLevel: 'Associate Member',
2960
+ },
2961
+ {
2962
+ groupId: 'GRP-005',
2963
+ group: 'Standards Board',
2964
+ type: 'Board',
2965
+ officerRole: 'Vice Chair',
2966
+ affiliation: 'CodeCraft Studio',
2967
+ memberLevel: 'Senior Member',
2968
+ },
2969
+ ];
2970
+
2971
+ /*
2972
+ * Public API Surface of dashboard
2973
+ */
2974
+ // Container component + navigation intent type (DashboardComponent, DashboardNavigationIntent)
2975
+
2976
+ /**
2977
+ * Generated bundle index. Do not edit.
2978
+ */
2979
+
2980
+ export { AttendanceState, DASHBOARD_ROUTES, DashboardComponent, DashboardGroupsMock, DashboardJumpTarget, DashboardModule, DashboardNonActionableMeetingMock, DashboardPermissionType, DashboardRoutePathsMock, DashboardSectionState, DashboardService, DashboardUpcomingEventsMock, DashboardUserPermissionsMock, DashboardWelcomeHeaderComponent, EventGroupCardComponent, MeetingAttendanceRowComponent, MemberLevelBadgeComponent, UpcomingItemKind, UpcomingMeetingsEventsComponent, UserPermissionsService, YourGroupsComponent };
2981
+ //# sourceMappingURL=ieeesa-npm-dashboard.mjs.map