Skip to content

Commit 92ffbd9

Browse files
authored
feat: enhance meeting cards with participant response breakdown and improved link visibility (#27)
* feat: implement committee management for meetings - Add committee association functionality to meetings with modal interface - Display committee members with deduplication and enhanced table styling - Prevent editing of committee members with informational messages - Support multi-select committee dropdown with dynamic helper text - Cache committee member data to optimize API calls - Show voting columns only when committees have voting enabled 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]> * refactor: optimize committee modal table and member display - Fix incorrect colspan calculation using computed property - Replace forEach with flatMap for better performance in member collection - Implement single-pass deduplication instead of array mutation - Add duplicate committee name prevention for members 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]> * feat: enhance committee modal with testing support and memory leak prevention - Add data-testid attributes to multi-select and action buttons for reliable E2E testing - Implement takeUntilDestroyed for automatic subscription cleanup - Follow project testing guidelines with hierarchical testid naming convention - Prevent memory leaks in valueChanges subscription 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]> * feat: enhance meeting cards with participant response breakdown and improved link visibility - Add participants_accepted_count, participants_declined_count, and participants_pending_count fields to Meeting interface - Update meeting card component to display response breakdown (e.g., "5 Guests (3 Attending, 1 Not Attending, 1 Pending Response)") - Move important links outside expandable text for better visibility and immediate access - Only show response breakdown when there are individual participants with response data - Improve meeting organizer visibility into participation status and key meeting resources 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]> --------- Signed-off-by: Asitha de Silva <[email protected]>
1 parent d82de57 commit 92ffbd9

File tree

3 files changed

+55
-25
lines changed

3 files changed

+55
-25
lines changed

apps/lfx-pcc/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.html

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -105,31 +105,31 @@ <h3 class="text-xl font-display font-semibold text-gray-900 mb-3">
105105

106106
@if (meeting().agenda) {
107107
<div class="pb-3">
108+
<!-- Important Links -->
109+
@if (importantLinks().length > 0) {
110+
<div class="mb-4">
111+
<h4 class="text-sm font-semibold text-gray-700 mb-2 flex items-center gap-2">
112+
<i class="fa-light fa-link text-blue-500"></i>
113+
Important Links
114+
</h4>
115+
<div class="flex flex-wrap gap-2">
116+
@for (link of importantLinks(); track link.url) {
117+
<a
118+
[href]="link.url"
119+
target="_blank"
120+
rel="noopener noreferrer"
121+
class="cursor-pointer inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-blue-600 bg-blue-50 hover:bg-blue-100 rounded-md transition-colors"
122+
[title]="link.url">
123+
<i class="fa-light fa-external-link"></i>
124+
{{ link.domain }}
125+
</a>
126+
}
127+
</div>
128+
</div>
129+
}
108130
<lfx-expandable-text [maxHeight]="100">
109131
<div class="flex flex-col gap-4">
110132
<div [innerHTML]="meeting().agenda | linkify"></div>
111-
<!-- Important Links -->
112-
@if (importantLinks().length > 0) {
113-
<div class="mb-4">
114-
<h4 class="text-sm font-semibold text-gray-700 mb-2 flex items-center gap-2">
115-
<i class="fa-light fa-link text-blue-500"></i>
116-
Important Links
117-
</h4>
118-
<div class="flex flex-wrap gap-2">
119-
@for (link of importantLinks(); track link.url) {
120-
<a
121-
[href]="link.url"
122-
target="_blank"
123-
rel="noopener noreferrer"
124-
class="cursor-pointer inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-blue-600 bg-blue-50 hover:bg-blue-100 rounded-md transition-colors"
125-
[title]="link.url">
126-
<i class="fa-light fa-external-link"></i>
127-
{{ link.domain }}
128-
</a>
129-
}
130-
</div>
131-
</div>
132-
}
133133
</div>
134134
</lfx-expandable-text>
135135
</div>

apps/lfx-pcc/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export class MeetingCardComponent {
6262
public readonly pastMeeting = input<boolean>(false);
6363
public readonly loading = input<boolean>(false);
6464
public readonly meetingParticipantCount: Signal<number> = this.initMeetingParticipantCount();
65+
public readonly participantResponseBreakdown: Signal<string> = this.initParticipantResponseBreakdown();
6566
public showParticipants: WritableSignal<boolean> = signal(false);
6667
public meeting: WritableSignal<Meeting> = signal({} as Meeting);
6768
public participantsLoading: WritableSignal<boolean> = signal(true);
@@ -188,11 +189,37 @@ export class MeetingCardComponent {
188189
return 'Add Guests';
189190
}
190191

191-
if (this.meetingParticipantCount() === 1) {
192-
return '1 Guest';
192+
const totalGuests = this.meetingParticipantCount();
193+
const breakdown = this.participantResponseBreakdown();
194+
195+
if (totalGuests === 1) {
196+
return breakdown ? `1 Guest (${breakdown})` : '1 Guest';
197+
}
198+
199+
return breakdown ? `${totalGuests} Guests (${breakdown})` : `${totalGuests} Guests`;
200+
});
201+
}
202+
203+
private initParticipantResponseBreakdown(): Signal<string> {
204+
return computed(() => {
205+
const meeting = this.meeting();
206+
if (!meeting) return '';
207+
208+
const accepted = meeting.participants_accepted_count || 0;
209+
const declined = meeting.participants_declined_count || 0;
210+
const pending = meeting.participants_pending_count || 0;
211+
212+
// Only show breakdown if there are individual participants with responses
213+
if (accepted === 0 && declined === 0 && pending === 0) {
214+
return '';
193215
}
194216

195-
return this.meetingParticipantCount() + ' Guests';
217+
const parts: string[] = [];
218+
if (accepted > 0) parts.push(`${accepted} Attending`);
219+
if (declined > 0) parts.push(`${declined} Not Attending`);
220+
if (pending > 0) parts.push(`${pending} Pending Response`);
221+
222+
return parts.join(', ');
196223
});
197224
}
198225

packages/shared/src/interfaces/meeting.interface.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ export interface Meeting {
6161
meeting_committees: MeetingCommittee[] | null;
6262
individual_participants_count: number;
6363
committee_members_count: number;
64+
participants_accepted_count: number;
65+
participants_declined_count: number;
66+
participants_pending_count: number;
6467
committees: string[];
6568
}
6669

0 commit comments

Comments
 (0)