@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.
- package/README.md +75 -3
- package/esm2022/ieeesa-npm-dashboard.mjs +5 -0
- package/esm2022/lib/assets/constants.json +158 -0
- package/esm2022/lib/assets/dashboard-mock.mjs +221 -0
- package/esm2022/lib/assets/dashboard-route-paths.mock.mjs +14 -0
- package/esm2022/lib/assets/dashboard-user-permissions.mock.mjs +23 -0
- package/esm2022/lib/assets/date-formats.mjs +28 -0
- package/esm2022/lib/components/event-group-card/event-group-card.component.mjs +414 -0
- package/esm2022/lib/components/meeting-attendance-row/meeting-attendance-row.component.mjs +182 -0
- package/esm2022/lib/components/member-level-badge/member-level-badge.component.mjs +79 -0
- package/esm2022/lib/components/upcoming-meetings-events/upcoming-meetings-events.component.mjs +261 -0
- package/esm2022/lib/components/welcome-header/dashboard-welcome-header.component.mjs +151 -0
- package/esm2022/lib/components/your-groups/your-groups.component.mjs +243 -0
- package/esm2022/lib/dashboard.component.mjs +417 -0
- package/esm2022/lib/dashboard.module.mjs +16 -0
- package/esm2022/lib/dashboard.routes.mjs +7 -0
- package/esm2022/lib/models/attendance-action-permissions.model.mjs +2 -0
- package/esm2022/lib/models/attendance-state.model.mjs +7 -0
- package/esm2022/lib/models/dashboard-list-response.model.mjs +2 -0
- package/esm2022/lib/models/dashboard-route-config.model.mjs +2 -0
- package/esm2022/lib/models/dashboard-section-state.model.mjs +9 -0
- package/esm2022/lib/models/dashboard-user-info.model.mjs +2 -0
- package/esm2022/lib/models/event-meeting-list-item.model.mjs +2 -0
- package/esm2022/lib/models/event-meeting-list-payload.model.mjs +8 -0
- package/esm2022/lib/models/group-row.model.mjs +2 -0
- package/esm2022/lib/models/meeting-row.model.mjs +2 -0
- package/esm2022/lib/models/my-groups-view.model.mjs +2 -0
- package/esm2022/lib/models/page-change.model.mjs +2 -0
- package/esm2022/lib/models/upcoming-event.model.mjs +13 -0
- package/esm2022/lib/services/attendance-reducers.mjs +54 -0
- package/esm2022/lib/services/dashboard.service.mjs +402 -0
- package/esm2022/lib/services/user-permissions.service.mjs +50 -0
- package/esm2022/lib/utils/attendance-action.util.mjs +53 -0
- package/esm2022/lib/utils/date-format.util.mjs +110 -0
- package/esm2022/lib/utils/event-meeting-mapper.util.mjs +96 -0
- package/esm2022/public-api.mjs +36 -0
- package/fesm2022/ieeesa-npm-dashboard.mjs +2981 -0
- package/fesm2022/ieeesa-npm-dashboard.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/assets/dashboard-mock.d.ts +31 -0
- package/lib/assets/dashboard-route-paths.mock.d.ts +2 -0
- package/lib/assets/dashboard-user-permissions.mock.d.ts +2 -0
- package/lib/assets/date-formats.d.ts +27 -0
- package/lib/components/event-group-card/event-group-card.component.d.ts +268 -0
- package/lib/components/meeting-attendance-row/meeting-attendance-row.component.d.ts +115 -0
- package/lib/components/member-level-badge/member-level-badge.component.d.ts +177 -0
- package/lib/components/upcoming-meetings-events/upcoming-meetings-events.component.d.ts +175 -0
- package/lib/components/welcome-header/dashboard-welcome-header.component.d.ts +98 -0
- package/lib/components/your-groups/your-groups.component.d.ts +159 -0
- package/lib/dashboard.component.d.ts +414 -0
- package/lib/dashboard.module.d.ts +7 -0
- package/lib/dashboard.routes.d.ts +2 -0
- package/lib/models/attendance-action-permissions.model.d.ts +4 -0
- package/lib/models/attendance-state.model.d.ts +4 -0
- package/lib/models/dashboard-list-response.model.d.ts +5 -0
- package/lib/models/dashboard-route-config.model.d.ts +7 -0
- package/lib/models/dashboard-section-state.model.d.ts +6 -0
- package/lib/models/dashboard-user-info.model.d.ts +5 -0
- package/lib/models/event-meeting-list-item.model.d.ts +107 -0
- package/lib/models/event-meeting-list-payload.model.d.ts +40 -0
- package/lib/models/group-row.model.d.ts +33 -0
- package/lib/models/meeting-row.model.d.ts +28 -0
- package/lib/models/my-groups-view.model.d.ts +56 -0
- package/lib/models/page-change.model.d.ts +4 -0
- package/lib/models/upcoming-event.model.d.ts +51 -0
- package/lib/services/attendance-reducers.d.ts +43 -0
- package/lib/services/dashboard.service.d.ts +202 -0
- package/lib/services/user-permissions.service.d.ts +31 -0
- package/lib/utils/attendance-action.util.d.ts +38 -0
- package/lib/utils/date-format.util.d.ts +60 -0
- package/lib/utils/event-meeting-mapper.util.d.ts +50 -0
- package/package.json +30 -3
- package/public-api.d.ts +26 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only aggregate view returned by GET /v1/groups/my-groups, mirroring the
|
|
3
|
+
* groups-api `MyGroupsView` DTO. The dashboard "Your Groups" section consumes
|
|
4
|
+
* the `groups` collection; `interests` is not shown on the dashboard.
|
|
5
|
+
*/
|
|
6
|
+
export interface MyGroupsView {
|
|
7
|
+
groups: MyGroupView[];
|
|
8
|
+
interests?: MyInterestView[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A single officer role the user holds in a group, as returned in the
|
|
12
|
+
* `officer` array of GET /v1/groups/my-groups.
|
|
13
|
+
*/
|
|
14
|
+
export interface GroupOfficer {
|
|
15
|
+
officerId: string;
|
|
16
|
+
officerName: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A single group the user is a roster member of. Carries the group's name,
|
|
20
|
+
* type, affiliations, the user's member level, and the user's officer role(s)
|
|
21
|
+
* for the group (the `officer` array — empty when the user holds no officer
|
|
22
|
+
* role).
|
|
23
|
+
*/
|
|
24
|
+
export interface MyGroupView {
|
|
25
|
+
groupId: string;
|
|
26
|
+
groupName: string;
|
|
27
|
+
groupType?: string;
|
|
28
|
+
groupTypeName?: string | null;
|
|
29
|
+
/** Ancestor hierarchy (root→leaf) of the group, for the expandable rows. */
|
|
30
|
+
hierarchy?: MyGroupHierarchyItem[];
|
|
31
|
+
affiliations?: string[];
|
|
32
|
+
memberLevelId?: string | null;
|
|
33
|
+
memberLevel?: string | null;
|
|
34
|
+
/** Officer roles the user holds in this group (e.g. "Chair"); empty if none. */
|
|
35
|
+
officer?: GroupOfficer[];
|
|
36
|
+
}
|
|
37
|
+
/** A single node in a group's ancestor hierarchy (root→leaf). */
|
|
38
|
+
export interface MyGroupHierarchyItem {
|
|
39
|
+
groupId: string;
|
|
40
|
+
groupName: string;
|
|
41
|
+
groupShortName?: string;
|
|
42
|
+
groupAcronym?: string;
|
|
43
|
+
groupLevel?: number;
|
|
44
|
+
groupParentId?: string | null;
|
|
45
|
+
groupType?: string;
|
|
46
|
+
groupTypeName?: string | null;
|
|
47
|
+
groupDesc?: string | null;
|
|
48
|
+
hasChildren?: boolean | null;
|
|
49
|
+
}
|
|
50
|
+
/** A single interest the user has expressed (not shown on the dashboard). */
|
|
51
|
+
export interface MyInterestView {
|
|
52
|
+
interestId: string;
|
|
53
|
+
groupId: string;
|
|
54
|
+
groupName: string;
|
|
55
|
+
status: string;
|
|
56
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { MeetingRow } from './meeting-row.model';
|
|
2
|
+
/**
|
|
3
|
+
* Discriminates how a row in the "Upcoming Meetings & Events" list is rendered.
|
|
4
|
+
* EVENT rows render as an expandable event block that owns child meetings;
|
|
5
|
+
* STANDALONE_MEETING rows render as a single meeting card and always sort LAST
|
|
6
|
+
* within the list.
|
|
7
|
+
*/
|
|
8
|
+
export declare enum UpcomingItemKind {
|
|
9
|
+
EVENT = "EVENT",
|
|
10
|
+
STANDALONE_MEETING = "STANDALONE_MEETING"
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* View model for a single entry in the "Upcoming Meetings & Events" section.
|
|
14
|
+
*
|
|
15
|
+
* When {@link kind} is `EVENT` the entry renders as the expandable event block
|
|
16
|
+
* (header badge, title, "Day X of Y", meeting-count sub-line, dates/timezone,
|
|
17
|
+
* View button, and the show/hide child-meeting list). When `kind` is
|
|
18
|
+
* `STANDALONE_MEETING` the entry renders as a single standalone meeting card
|
|
19
|
+
* (rendered after all events).
|
|
20
|
+
*
|
|
21
|
+
* `startEpochMs` / `endEpochMs` carry the raw epoch-milliseconds values from the
|
|
22
|
+
* backend and are formatted with luxon `DateTime.fromMillis`. The legacy ISO
|
|
23
|
+
* `startDate` / `endDate` fields are retained for existing fixtures/tests.
|
|
24
|
+
*/
|
|
25
|
+
export interface UpcomingEvent {
|
|
26
|
+
eventId: string;
|
|
27
|
+
/** EVENT vs. STANDALONE_MEETING render discriminator. */
|
|
28
|
+
kind: UpcomingItemKind;
|
|
29
|
+
title: string;
|
|
30
|
+
status: string;
|
|
31
|
+
dayIndicator?: string;
|
|
32
|
+
meetingCount: number;
|
|
33
|
+
openForAttendanceCount: number;
|
|
34
|
+
/** Start date-time as an ISO-8601 string (or epoch ms for fixtures). */
|
|
35
|
+
startDateTime?: string | number;
|
|
36
|
+
/** End date-time as an ISO-8601 string (or epoch ms for fixtures). */
|
|
37
|
+
endDateTime?: string | number;
|
|
38
|
+
/** Legacy ISO start date, retained for fixtures/tests. */
|
|
39
|
+
startDate?: string;
|
|
40
|
+
/** Legacy ISO end date, retained for fixtures/tests. */
|
|
41
|
+
endDate?: string;
|
|
42
|
+
timezone: string;
|
|
43
|
+
/** Owning group label, shown on standalone-meeting cards. */
|
|
44
|
+
groupOrCommittee?: string;
|
|
45
|
+
/** Room / location, shown on standalone-meeting cards. */
|
|
46
|
+
roomOrLocation?: string;
|
|
47
|
+
/** True when the user is a member of the owning group. */
|
|
48
|
+
isMemberOfGroup?: boolean;
|
|
49
|
+
/** The event's child meetings (empty / lazily loaded for EVENT rows). */
|
|
50
|
+
meetings: MeetingRow[];
|
|
51
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { AttendanceState } from '../models/attendance-state.model';
|
|
2
|
+
/**
|
|
3
|
+
* Pure reducers for the dashboard attendance state. These functions never
|
|
4
|
+
* mutate their inputs; each returns a new object/set so they can be exercised
|
|
5
|
+
* directly by property tests and composed by the DashboardService.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Produce a new attendance-state map with the target meeting set to
|
|
9
|
+
* CURRENTLY_ATTENDING. Every other entry is copied unchanged (frame property).
|
|
10
|
+
*
|
|
11
|
+
* @param {Record<string, AttendanceState>} map The current attendance-state map (not mutated).
|
|
12
|
+
* @param {string} meetingId The meeting whose state transitions to CURRENTLY_ATTENDING.
|
|
13
|
+
* @return {Record<string, AttendanceState>} A new map reflecting the successful log-attendance action.
|
|
14
|
+
*/
|
|
15
|
+
export declare const applyLogAttendanceSuccess: (map: Record<string, AttendanceState>, meetingId: string) => Record<string, AttendanceState>;
|
|
16
|
+
/**
|
|
17
|
+
* Produce a new attendance-state map with the target meeting set to
|
|
18
|
+
* NOT_ATTENDING. Every other entry is copied unchanged (frame property).
|
|
19
|
+
*
|
|
20
|
+
* @param {Record<string, AttendanceState>} map The current attendance-state map (not mutated).
|
|
21
|
+
* @param {string} meetingId The meeting whose state transitions to NOT_ATTENDING.
|
|
22
|
+
* @return {Record<string, AttendanceState>} A new map reflecting the successful leave-meeting action.
|
|
23
|
+
*/
|
|
24
|
+
export declare const applyLeaveMeetingSuccess: (map: Record<string, AttendanceState>, meetingId: string) => Record<string, AttendanceState>;
|
|
25
|
+
/**
|
|
26
|
+
* Add a meeting to the in-flight set, returning a new set. Prior membership is
|
|
27
|
+
* retained so the displayed attendance state is preserved while the request
|
|
28
|
+
* resolves.
|
|
29
|
+
*
|
|
30
|
+
* @param {ReadonlySet<string>} set The current in-flight meeting-id set (not mutated).
|
|
31
|
+
* @param {string} meetingId The meeting whose attendance action has started.
|
|
32
|
+
* @return {Set<string>} A new set that additionally contains the meeting id.
|
|
33
|
+
*/
|
|
34
|
+
export declare const beginAttendanceAction: (set: ReadonlySet<string>, meetingId: string) => Set<string>;
|
|
35
|
+
/**
|
|
36
|
+
* Remove a meeting from the in-flight set, returning a new set. All other
|
|
37
|
+
* in-flight ids are retained.
|
|
38
|
+
*
|
|
39
|
+
* @param {ReadonlySet<string>} set The current in-flight meeting-id set (not mutated).
|
|
40
|
+
* @param {string} meetingId The meeting whose attendance action has resolved.
|
|
41
|
+
* @return {Set<string>} A new set that no longer contains the meeting id.
|
|
42
|
+
*/
|
|
43
|
+
export declare const endAttendanceAction: (set: ReadonlySet<string>, meetingId: string) => Set<string>;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { HttpService } from '@ieeesa-npm/utility';
|
|
3
|
+
import { UserPermission } from '@ieeesa-npm/models';
|
|
4
|
+
import { AttendanceState } from '../models/attendance-state.model';
|
|
5
|
+
import { DashboardListResponse } from '../models/dashboard-list-response.model';
|
|
6
|
+
import { DashboardSectionState } from '../models/dashboard-section-state.model';
|
|
7
|
+
import { GroupRow } from '../models/group-row.model';
|
|
8
|
+
import { MyGroupsView } from '../models/my-groups-view.model';
|
|
9
|
+
import { UpcomingEvent } from '../models/upcoming-event.model';
|
|
10
|
+
import { EventMeetingDetailResponse, EventMeetingListResponse } from '../models/event-meeting-list-item.model';
|
|
11
|
+
import * as i0 from "@angular/core";
|
|
12
|
+
/**
|
|
13
|
+
* DashboardService is the single source of truth for the dashboard landing
|
|
14
|
+
* page's data and attendance state. It mirrors the `events-and-meetings`
|
|
15
|
+
* service conventions: it is a `providedIn: 'root'` singleton, holds the host
|
|
16
|
+
* `apiBaseUrl` / `userPermissions` set via {@link setAppConfig}, owns its state
|
|
17
|
+
* via `BehaviorSubject`s exposed as observables, and reaches the backend only
|
|
18
|
+
* through the shared `HttpService` from `@ieeesa-npm/utility` (never
|
|
19
|
+
* `HttpClient` directly).
|
|
20
|
+
*
|
|
21
|
+
* The upcoming-events load calls the real `POST /meetings/eventsAndMeetings`
|
|
22
|
+
* endpoint (child meetings are lazily loaded per-event via
|
|
23
|
+
* `POST /meetings/meetingsDetails`); the groups load calls `GET
|
|
24
|
+
* /groups/my-groups`. The attendance actions compose the pure reducers from
|
|
25
|
+
* `attendance-reducers.ts` so the displayed `AttendanceState` only advances on a
|
|
26
|
+
* successful resolution and the prior state is retained on failure.
|
|
27
|
+
* @export
|
|
28
|
+
* @class DashboardService
|
|
29
|
+
*/
|
|
30
|
+
export declare class DashboardService {
|
|
31
|
+
private httpService;
|
|
32
|
+
apiBaseUrl: string;
|
|
33
|
+
userPermissions: UserPermission[];
|
|
34
|
+
private readonly upcomingEvents;
|
|
35
|
+
private readonly groups;
|
|
36
|
+
private readonly upcomingState;
|
|
37
|
+
private readonly groupsState;
|
|
38
|
+
private readonly attendanceStates;
|
|
39
|
+
private readonly inFlightMeetingIds;
|
|
40
|
+
/** Emits the list of the user's upcoming events. */
|
|
41
|
+
upcomingEvents$: Observable<UpcomingEvent[]>;
|
|
42
|
+
/** Emits the list of the user's groups. */
|
|
43
|
+
groups$: Observable<GroupRow[]>;
|
|
44
|
+
/** Emits the load/empty/error state of the upcoming-events section. */
|
|
45
|
+
upcomingState$: Observable<DashboardSectionState>;
|
|
46
|
+
/** Emits the load/empty/error state of the groups section. */
|
|
47
|
+
groupsState$: Observable<DashboardSectionState>;
|
|
48
|
+
/** Emits the per-meeting attendance-state map. */
|
|
49
|
+
attendanceStates$: Observable<Record<string, AttendanceState>>;
|
|
50
|
+
/** Emits the set of meeting ids with an attendance action in flight. */
|
|
51
|
+
inFlightMeetingIds$: Observable<ReadonlySet<string>>;
|
|
52
|
+
constructor(httpService: HttpService);
|
|
53
|
+
/**
|
|
54
|
+
* Stores the host-supplied API base URL and user permissions used to build
|
|
55
|
+
* request URLs and gate permission-controlled actions. Mirrors the
|
|
56
|
+
* `events-and-meetings` service `setAppConfig`.
|
|
57
|
+
* @param {string} apiBaseURL - The host application's API base URL.
|
|
58
|
+
* @param {UserPermission[]} userPermissions - The signed-in user's permissions.
|
|
59
|
+
* @return {void}
|
|
60
|
+
* @memberof DashboardService
|
|
61
|
+
*/
|
|
62
|
+
setAppConfig(apiBaseURL: string, userPermissions: UserPermission[]): void;
|
|
63
|
+
/**
|
|
64
|
+
* Loads the user's upcoming events & meetings from the real backend via
|
|
65
|
+
* `POST {apiBaseUrl}/meetings/eventsAndMeetings`, the same endpoint the
|
|
66
|
+
* `view-events-meetings-list` screen uses. The response is mapped into the
|
|
67
|
+
* dashboard view models (with STANDALONE_MEETING rows ordered last), the
|
|
68
|
+
* per-meeting attendance-state map is seeded, and the section advances through
|
|
69
|
+
* LOADING → LOADED/EMPTY on success, leaving the caller to set ERROR on
|
|
70
|
+
* failure. Follows the shared `pipe(map, catchError(throw))` pattern.
|
|
71
|
+
* @return {Observable<EventMeetingListResponse>} The upcoming events & meetings response envelope.
|
|
72
|
+
* @memberof DashboardService
|
|
73
|
+
*/
|
|
74
|
+
loadUpcomingEvents(): Observable<EventMeetingListResponse>;
|
|
75
|
+
/**
|
|
76
|
+
* Lazily loads (and paginates) the child meetings of an EVENT row via
|
|
77
|
+
* `POST {apiBaseUrl}/meetings/meetingsDetails` — the per-event expand endpoint
|
|
78
|
+
* used by the reference screen. On success the loaded meetings are mapped and
|
|
79
|
+
* merged onto the matching event in the `upcomingEvents` list (with its
|
|
80
|
+
* `meetingCount` / `openForAttendanceCount` refreshed), and the per-meeting
|
|
81
|
+
* attendance-state map is extended. Each event paginates independently.
|
|
82
|
+
* @param {string} eventId - The event whose meetings to load.
|
|
83
|
+
* @param {number} [page=1] - The one-based page to request.
|
|
84
|
+
* @param {number} [size=EVENT_MEETINGS_PAGE_SIZE] - The page size.
|
|
85
|
+
* @return {Observable<EventMeetingDetailResponse>} The child-meetings response envelope.
|
|
86
|
+
* @memberof DashboardService
|
|
87
|
+
*/
|
|
88
|
+
loadEventMeetings(eventId: string, page?: number, size?: number): Observable<EventMeetingDetailResponse>;
|
|
89
|
+
/**
|
|
90
|
+
* Merges a freshly loaded page of child meetings onto the matching event in
|
|
91
|
+
* the `upcomingEvents` list, refreshing the event's `meetings`,
|
|
92
|
+
* `meetingCount` (from the backend `totalRecords`), and
|
|
93
|
+
* `openForAttendanceCount`. Leaves all other events untouched.
|
|
94
|
+
* @param {string} eventId - The event to merge meetings onto.
|
|
95
|
+
* @param {MeetingRow[]} meetings - The mapped child meetings for the page.
|
|
96
|
+
* @param {number} totalRecords - The event's total meeting count from the backend.
|
|
97
|
+
* @return {void}
|
|
98
|
+
* @memberof DashboardService
|
|
99
|
+
*/
|
|
100
|
+
private mergeEventMeetings;
|
|
101
|
+
/**
|
|
102
|
+
* Eagerly fetches each EVENT's total meeting count and open-for-attendance
|
|
103
|
+
* count by requesting a large first page of its child meetings, then merging
|
|
104
|
+
* only the counts (not the rows) so the collapsed card summaries are accurate
|
|
105
|
+
* before the user expands. Standalone meetings are skipped. Failures per event
|
|
106
|
+
* are swallowed so one failing count never breaks the list.
|
|
107
|
+
* @param {UpcomingEvent[]} events - The loaded events to fetch counts for.
|
|
108
|
+
* @return {void}
|
|
109
|
+
* @memberof DashboardService
|
|
110
|
+
*/
|
|
111
|
+
private loadEventCounts;
|
|
112
|
+
/**
|
|
113
|
+
* Merges only the meeting counts onto the matching event, leaving its
|
|
114
|
+
* `meetings` rows untouched (used by the eager count prefetch).
|
|
115
|
+
* @param {string} eventId - The event to update.
|
|
116
|
+
* @param {number} totalRecords - The event's total meeting count.
|
|
117
|
+
* @param {number} openForAttendanceCount - The open-for-attendance count.
|
|
118
|
+
* @return {void}
|
|
119
|
+
* @memberof DashboardService
|
|
120
|
+
*/
|
|
121
|
+
private mergeEventCounts;
|
|
122
|
+
/**
|
|
123
|
+
* Builds and posts the `meetingsDetails` request for a single event page,
|
|
124
|
+
* returning the raw response envelope. Shared by {@link loadEventMeetings} and
|
|
125
|
+
* the eager count prefetch.
|
|
126
|
+
* @param {string} eventId - The event whose meetings to fetch.
|
|
127
|
+
* @param {number} page - The one-based page to request.
|
|
128
|
+
* @param {number} size - The page size.
|
|
129
|
+
* @return {Observable<EventMeetingDetailResponse>} The child-meetings response.
|
|
130
|
+
* @memberof DashboardService
|
|
131
|
+
*/
|
|
132
|
+
private fetchEventMeetingsPage;
|
|
133
|
+
/**
|
|
134
|
+
* Loads the user's groups, driving the groups section through
|
|
135
|
+
* LOADING → LOADED/EMPTY on success and leaving the caller to set ERROR on
|
|
136
|
+
* failure. Follows the `pipe(map, catchError(throw))` pattern.
|
|
137
|
+
*
|
|
138
|
+
* TODO: Replace mock with real API call when BE is ready
|
|
139
|
+
* @return {Observable<DashboardListResponse<GroupRow[]>>} The groups response envelope.
|
|
140
|
+
* @memberof DashboardService
|
|
141
|
+
*/
|
|
142
|
+
loadGroups(): Observable<DashboardListResponse<MyGroupsView>>;
|
|
143
|
+
/**
|
|
144
|
+
* Maps a backend {@link MyGroupView} to the table-facing {@link GroupRow}.
|
|
145
|
+
* Mirrors the My Groups profile tab: the type falls back to "Unassigned" when
|
|
146
|
+
* `groupTypeName` is null, affiliations are comma-joined, empty officer role /
|
|
147
|
+
* affiliation render as a placeholder, and a blank member level becomes
|
|
148
|
+
* "Unassigned" (rendered as the warning pill by the Your Groups component).
|
|
149
|
+
* @param {MyGroupView} group - The backend group view.
|
|
150
|
+
* @return {GroupRow}
|
|
151
|
+
* @memberof DashboardService
|
|
152
|
+
*/
|
|
153
|
+
private mapGroup;
|
|
154
|
+
/**
|
|
155
|
+
* Logs attendance for a meeting. Marks the meeting in-flight (retaining the
|
|
156
|
+
* prior displayed state), posts through `HttpService`, applies the
|
|
157
|
+
* log-attendance-success reducer on success, clears the in-flight flag on
|
|
158
|
+
* completion, and rethrows while retaining prior state on failure.
|
|
159
|
+
* @param {string} meetingId - The meeting to log attendance for.
|
|
160
|
+
* @return {Observable<DashboardListResponse<unknown>>} The attendance response envelope.
|
|
161
|
+
* @memberof DashboardService
|
|
162
|
+
*/
|
|
163
|
+
logAttendance(meetingId: string): Observable<DashboardListResponse<unknown>>;
|
|
164
|
+
/**
|
|
165
|
+
* Leaves a meeting. Marks the meeting in-flight (retaining the prior
|
|
166
|
+
* displayed state), posts through `HttpService`, applies the
|
|
167
|
+
* leave-meeting-success reducer on success, clears the in-flight flag on
|
|
168
|
+
* completion, and rethrows while retaining prior state on failure.
|
|
169
|
+
* @param {string} meetingId - The meeting to leave.
|
|
170
|
+
* @return {Observable<DashboardListResponse<unknown>>} The attendance response envelope.
|
|
171
|
+
* @memberof DashboardService
|
|
172
|
+
*/
|
|
173
|
+
leaveMeeting(meetingId: string): Observable<DashboardListResponse<unknown>>;
|
|
174
|
+
/**
|
|
175
|
+
* Returns a synchronous snapshot of the per-meeting attendance-state map.
|
|
176
|
+
* @return {Record<string, AttendanceState>} The current attendance-state map.
|
|
177
|
+
* @memberof DashboardService
|
|
178
|
+
*/
|
|
179
|
+
getAttendanceStatesSnapshot(): Record<string, AttendanceState>;
|
|
180
|
+
/**
|
|
181
|
+
* Seeds the attendance-state map from a freshly loaded list of events, using
|
|
182
|
+
* each meeting's `attendanceState` (defaulting to `NOT_ATTENDING`). Keeps the
|
|
183
|
+
* displayed state in sync with the data load without overwriting ids that are
|
|
184
|
+
* not present in the new list.
|
|
185
|
+
* @param {UpcomingEvent[]} events - The loaded upcoming events.
|
|
186
|
+
* @return {void}
|
|
187
|
+
* @memberof DashboardService
|
|
188
|
+
*/
|
|
189
|
+
private seedAttendanceStates;
|
|
190
|
+
/**
|
|
191
|
+
* Seeds the attendance-state map from a list of meeting rows, using each
|
|
192
|
+
* meeting's `attendanceState` (defaulting to `NOT_ATTENDING`) without
|
|
193
|
+
* overwriting ids absent from the new list. Shared by the list load and the
|
|
194
|
+
* per-event expand so freshly loaded child meetings pick up their state.
|
|
195
|
+
* @param {MeetingRow[]} meetings - The meeting rows to seed from.
|
|
196
|
+
* @return {void}
|
|
197
|
+
* @memberof DashboardService
|
|
198
|
+
*/
|
|
199
|
+
private seedAttendanceStatesFromMeetings;
|
|
200
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<DashboardService, never>;
|
|
201
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<DashboardService>;
|
|
202
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { UserPermission } from '@ieeesa-npm/models';
|
|
2
|
+
import { AttendanceActionPermissions } from '../models/attendance-action-permissions.model';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* Permission identifiers (by name) that gate the dashboard attendance actions.
|
|
6
|
+
* Mirrors the permission-type pattern used by the `events-and-meetings` and
|
|
7
|
+
* `groups` user-permissions services.
|
|
8
|
+
*/
|
|
9
|
+
export declare enum DashboardPermissionType {
|
|
10
|
+
LOG_ATTENDANCE = "Log Attendance",
|
|
11
|
+
LEAVE_MEETING = "Leave Meeting"
|
|
12
|
+
}
|
|
13
|
+
export declare class UserPermissionsService {
|
|
14
|
+
/**
|
|
15
|
+
* Returns access provision for the passed action/feature based on whether the signed in user has appropriate permission.
|
|
16
|
+
* @param {DashboardPermissionType} permission
|
|
17
|
+
* @param {UserPermission[]} userPermissions
|
|
18
|
+
* @return {boolean}
|
|
19
|
+
* @memberof UserPermissionsService
|
|
20
|
+
*/
|
|
21
|
+
validateAccessPermission(permission: DashboardPermissionType, userPermissions: UserPermission[]): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Maps the host-supplied user permissions to the attendance-action permissions consumed by the dashboard rows.
|
|
24
|
+
* @param {UserPermission[]} userPermissions
|
|
25
|
+
* @return {AttendanceActionPermissions}
|
|
26
|
+
* @memberof UserPermissionsService
|
|
27
|
+
*/
|
|
28
|
+
mapToAttendanceActionPermissions(userPermissions: UserPermission[]): AttendanceActionPermissions;
|
|
29
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<UserPermissionsService, never>;
|
|
30
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<UserPermissionsService>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for the per-meeting attendance action state machine. No Angular /
|
|
3
|
+
* service dependencies so they are trivially testable (property-tested as
|
|
4
|
+
* Property 1) and reusable by `MeetingAttendanceRowComponent`.
|
|
5
|
+
*
|
|
6
|
+
* The handler is the single source of truth for what a meeting row's action
|
|
7
|
+
* button should emit when activated. The displayed `AttendanceState` only
|
|
8
|
+
* advances on a successful resolution (handled by the service reducers), so this
|
|
9
|
+
* function is a pure mapping from the current inputs to the action to emit.
|
|
10
|
+
*/
|
|
11
|
+
import { AttendanceState } from '../models/attendance-state.model';
|
|
12
|
+
/**
|
|
13
|
+
* The action a meeting row's action button should emit when activated.
|
|
14
|
+
* `NONE` means the activation is non-actionable (empty id or in-flight) and the
|
|
15
|
+
* row must emit no `logAttendance` / `leaveMeeting` / navigation event.
|
|
16
|
+
*/
|
|
17
|
+
export declare enum AttendanceAction {
|
|
18
|
+
LOG_ATTENDANCE = "LOG_ATTENDANCE",
|
|
19
|
+
LEAVE_MEETING = "LEAVE_MEETING",
|
|
20
|
+
NONE = "NONE"
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Resolves the attendance action to emit for a meeting row, following the
|
|
24
|
+
* attendance action state machine (see design: Attendance Action State Machine).
|
|
25
|
+
*
|
|
26
|
+
* Behavior:
|
|
27
|
+
* - Returns `NONE` when `meetingId` is empty or undefined (non-actionable row).
|
|
28
|
+
* - Returns `NONE` while the row is in-flight (re-entrancy guard — no further
|
|
29
|
+
* event is emitted until the current action resolves).
|
|
30
|
+
* - Returns `LEAVE_MEETING` when the state is `CURRENTLY_ATTENDING`.
|
|
31
|
+
* - Returns `LOG_ATTENDANCE` when the state is `NOT_ATTENDING` or undefined.
|
|
32
|
+
*
|
|
33
|
+
* @param {AttendanceState | undefined} attendanceState The row's current attendance state.
|
|
34
|
+
* @param {boolean} isInFlight Whether an attendance action for this row is awaiting resolution.
|
|
35
|
+
* @param {string | undefined} meetingId The row's meeting identifier.
|
|
36
|
+
* @return {AttendanceAction} The action to emit, or `NONE` when non-actionable.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveAttendanceAction(attendanceState: AttendanceState | undefined, isInFlight: boolean, meetingId: string | undefined): AttendanceAction;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure luxon date/time formatting helpers for the dashboard library.
|
|
3
|
+
*
|
|
4
|
+
* The real "events & meetings" backend returns date-times as ISO-8601 strings
|
|
5
|
+
* (e.g. "2026-01-01T00:00:00") together with a display timezone string
|
|
6
|
+
* (e.g. "(UTC-04:00) America/New_York"). These helpers parse the ISO value and
|
|
7
|
+
* render it in the item's own zone (never moment). A numeric epoch-milliseconds
|
|
8
|
+
* value is also accepted for backward compatibility with fixtures/tests. All
|
|
9
|
+
* helpers fall back gracefully to an empty string when the value is missing or
|
|
10
|
+
* unparseable.
|
|
11
|
+
*/
|
|
12
|
+
import { DateTime } from 'luxon';
|
|
13
|
+
/**
|
|
14
|
+
* Extracts a luxon-usable IANA zone from a backend timezone string such as
|
|
15
|
+
* "(UTC-04:00) America/New_York" or "America/New_York". Returns `undefined`
|
|
16
|
+
* when no usable zone can be derived so luxon falls back to the local zone.
|
|
17
|
+
* @param {string | undefined} timezone - The backend timezone string.
|
|
18
|
+
* @return {string | undefined} The IANA zone id, or undefined.
|
|
19
|
+
*/
|
|
20
|
+
export declare function extractIanaZone(timezone: string | undefined): string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Parses a backend date-time value — either an ISO-8601 string
|
|
23
|
+
* (e.g. "2026-01-01T00:00:00") or an epoch-milliseconds number — into a luxon
|
|
24
|
+
* {@link DateTime} in the supplied timezone. The ISO value is treated as a wall
|
|
25
|
+
* time in the item's zone so the rendered time matches the backend's intent.
|
|
26
|
+
* Returns an invalid DateTime when the value is missing/unparseable.
|
|
27
|
+
* @param {string | number | undefined | null} value - The ISO string or epoch ms.
|
|
28
|
+
* @param {string | undefined} timezone - The backend timezone string.
|
|
29
|
+
* @return {DateTime} The parsed luxon DateTime (may be invalid).
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseBackendDateTime(value: string | number | undefined | null, timezone?: string): DateTime;
|
|
32
|
+
/**
|
|
33
|
+
* Formats a backend date-time value (ISO string or epoch ms) with luxon using
|
|
34
|
+
* the supplied format token, honouring the supplied timezone. Returns an empty
|
|
35
|
+
* string when the value is missing or invalid.
|
|
36
|
+
* @param {string | number | undefined | null} value - The ISO string or epoch ms.
|
|
37
|
+
* @param {string} format - The luxon `toFormat` token.
|
|
38
|
+
* @param {string | undefined} timezone - The backend timezone string.
|
|
39
|
+
* @return {string} The formatted value, or an empty string.
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatBackendDateTime(value: string | number | undefined | null, format: string, timezone?: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Backward-compatible alias kept for existing callers. Accepts ISO strings or
|
|
44
|
+
* epoch ms and formats them in the item's zone.
|
|
45
|
+
* @param {string | number | undefined | null} value - The ISO string or epoch ms.
|
|
46
|
+
* @param {string} format - The luxon `toFormat` token.
|
|
47
|
+
* @param {string | undefined} timezone - The backend timezone string.
|
|
48
|
+
* @return {string} The formatted value, or an empty string.
|
|
49
|
+
*/
|
|
50
|
+
export declare function formatEpoch(value: string | number | undefined | null, format: string, timezone?: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Computes a human "Day X of Y" indicator for a multi-day event from its start
|
|
53
|
+
* and end date-times, relative to "now". Returns an empty string when the dates
|
|
54
|
+
* are missing/invalid or the event spans a single day.
|
|
55
|
+
* @param {string | number | undefined | null} start - Event start (ISO or epoch ms).
|
|
56
|
+
* @param {string | number | undefined | null} end - Event end (ISO or epoch ms).
|
|
57
|
+
* @param {string | undefined} timezone - The backend timezone string.
|
|
58
|
+
* @return {string} The "Day X of Y" label, or an empty string.
|
|
59
|
+
*/
|
|
60
|
+
export declare function computeDayIndicator(start: string | number | undefined | null, end: string | number | undefined | null, timezone?: string): string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure mappers converting the raw "events & meetings" backend list/detail items
|
|
3
|
+
* into the dashboard view models. No Angular / service dependencies so they are
|
|
4
|
+
* trivially unit-testable and shared by {@link DashboardService}.
|
|
5
|
+
*
|
|
6
|
+
* Backend dates are epoch milliseconds; callers format them with luxon
|
|
7
|
+
* `DateTime.fromMillis` at render time. The list mapper enforces the dashboard
|
|
8
|
+
* rule that STANDALONE_MEETING rows always sort LAST within the section.
|
|
9
|
+
*/
|
|
10
|
+
import { EventMeetingDetailItem, EventMeetingListItem } from '../models/event-meeting-list-item.model';
|
|
11
|
+
import { MeetingRow } from '../models/meeting-row.model';
|
|
12
|
+
import { UpcomingEvent, UpcomingItemKind } from '../models/upcoming-event.model';
|
|
13
|
+
/**
|
|
14
|
+
* Determines whether a raw list item is an EVENT (owns child meetings) or a
|
|
15
|
+
* STANDALONE_MEETING. An item is an EVENT when the backend flags it with
|
|
16
|
+
* `isMeetingEvent === true` or `hasChildren === true`.
|
|
17
|
+
* @param {EventMeetingListItem} item - The raw list item.
|
|
18
|
+
* @return {UpcomingItemKind} The resolved render kind.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveItemKind(item: EventMeetingListItem): UpcomingItemKind;
|
|
21
|
+
/**
|
|
22
|
+
* Maps a raw backend list item to the {@link UpcomingEvent} view model. EVENT
|
|
23
|
+
* rows start with an empty `meetings` array (child meetings are lazily loaded on
|
|
24
|
+
* expand); STANDALONE_MEETING rows carry their own group/location fields.
|
|
25
|
+
* @param {EventMeetingListItem} item - The raw list item.
|
|
26
|
+
* @return {UpcomingEvent} The mapped view model.
|
|
27
|
+
*/
|
|
28
|
+
export declare function mapListItemToUpcomingEvent(item: EventMeetingListItem): UpcomingEvent;
|
|
29
|
+
/**
|
|
30
|
+
* Maps a raw backend list of events & meetings into the dashboard view models,
|
|
31
|
+
* enforcing that STANDALONE_MEETING rows always sort LAST while preserving the
|
|
32
|
+
* backend's within-group ordering for each partition (stable sort).
|
|
33
|
+
* @param {EventMeetingListItem[]} items - The raw list items.
|
|
34
|
+
* @return {UpcomingEvent[]} The mapped, standalone-last ordered view models.
|
|
35
|
+
*/
|
|
36
|
+
export declare function mapEventMeetingList(items: EventMeetingListItem[]): UpcomingEvent[];
|
|
37
|
+
/**
|
|
38
|
+
* Maps a raw backend child-meeting detail item to the {@link MeetingRow} view
|
|
39
|
+
* model used by the event block's expanded meeting list.
|
|
40
|
+
* @param {EventMeetingDetailItem} item - The raw child-meeting item.
|
|
41
|
+
* @return {MeetingRow} The mapped meeting row.
|
|
42
|
+
*/
|
|
43
|
+
export declare function mapDetailItemToMeetingRow(item: EventMeetingDetailItem): MeetingRow;
|
|
44
|
+
/**
|
|
45
|
+
* Maps a raw backend list of child-meeting detail items to {@link MeetingRow}
|
|
46
|
+
* view models.
|
|
47
|
+
* @param {EventMeetingDetailItem[]} items - The raw child-meeting items.
|
|
48
|
+
* @return {MeetingRow[]} The mapped meeting rows.
|
|
49
|
+
*/
|
|
50
|
+
export declare function mapEventMeetingDetails(items: EventMeetingDetailItem[]): MeetingRow[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ieeesa-npm/dashboard",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"peerDependencies": {
|
|
5
|
+
"@angular/common": "^16.1.0",
|
|
6
|
+
"@angular/core": "^16.1.0",
|
|
7
|
+
"@angular/material": "^16.1.3",
|
|
8
|
+
"@angular/router": "^16.1.0",
|
|
9
|
+
"@ieeesa-npm/models": ">=0.1.4",
|
|
10
|
+
"@ieeesa-npm/presentation": ">=0.33.33",
|
|
11
|
+
"@ieeesa-npm/shared-styles": ">=0.7.5",
|
|
12
|
+
"@ieeesa-npm/utility": ">=0.12.17",
|
|
13
|
+
"luxon": "^3.7.2",
|
|
14
|
+
"rxjs": "~7.8.0"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"tslib": "^2.3.0"
|
|
18
|
+
},
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"module": "fesm2022/ieeesa-npm-dashboard.mjs",
|
|
21
|
+
"typings": "index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
"./package.json": {
|
|
24
|
+
"default": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./index.d.ts",
|
|
28
|
+
"esm2022": "./esm2022/ieeesa-npm-dashboard.mjs",
|
|
29
|
+
"esm": "./esm2022/ieeesa-npm-dashboard.mjs",
|
|
30
|
+
"default": "./fesm2022/ieeesa-npm-dashboard.mjs"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
6
33
|
}
|
package/public-api.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export * from './lib/dashboard.component';
|
|
2
|
+
export * from './lib/components/welcome-header/dashboard-welcome-header.component';
|
|
3
|
+
export * from './lib/components/upcoming-meetings-events/upcoming-meetings-events.component';
|
|
4
|
+
export * from './lib/components/event-group-card/event-group-card.component';
|
|
5
|
+
export * from './lib/components/meeting-attendance-row/meeting-attendance-row.component';
|
|
6
|
+
export * from './lib/components/your-groups/your-groups.component';
|
|
7
|
+
export * from './lib/components/member-level-badge/member-level-badge.component';
|
|
8
|
+
export * from './lib/dashboard.module';
|
|
9
|
+
export * from './lib/dashboard.routes';
|
|
10
|
+
export * from './lib/models/dashboard-route-config.model';
|
|
11
|
+
export * from './lib/models/attendance-state.model';
|
|
12
|
+
export * from './lib/models/dashboard-section-state.model';
|
|
13
|
+
export * from './lib/models/page-change.model';
|
|
14
|
+
export * from './lib/models/dashboard-user-info.model';
|
|
15
|
+
export * from './lib/models/meeting-row.model';
|
|
16
|
+
export * from './lib/models/upcoming-event.model';
|
|
17
|
+
export * from './lib/models/event-meeting-list-item.model';
|
|
18
|
+
export * from './lib/models/event-meeting-list-payload.model';
|
|
19
|
+
export * from './lib/models/group-row.model';
|
|
20
|
+
export * from './lib/models/attendance-action-permissions.model';
|
|
21
|
+
export * from './lib/models/dashboard-list-response.model';
|
|
22
|
+
export * from './lib/services/dashboard.service';
|
|
23
|
+
export * from './lib/services/user-permissions.service';
|
|
24
|
+
export * from './lib/assets/dashboard-route-paths.mock';
|
|
25
|
+
export * from './lib/assets/dashboard-user-permissions.mock';
|
|
26
|
+
export * from './lib/assets/dashboard-mock';
|