Skip to content

Commit 519dc3a

Browse files
authored
feat(meetings): improve meeting join and registration filtering (#198)
* feat(meetings): allow authenticated users to join with alternate email LFXV2-885 - Extract guest form into reusable component - Add error handling for restricted meeting email validation - Allow authenticated users to enter alternate email when their SSO email is not registered - Prioritize form-submitted email over OIDC user email in backend 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]> * fix(meetings): filter all meetings by registration for my meetings view LFXV2-886 - Filter ALL meetings (public and private) by registration status - Writers have API access to all meetings, but my meetings shows only registered - Extract filterMeetingsByRegistration helper method for code reuse - Simplify getUserMeetings by removing writer-specific logic 🤖 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 1256a26 commit 519dc3a

File tree

7 files changed

+262
-200
lines changed

7 files changed

+262
-200
lines changed

apps/lfx-one/src/app/modules/dashboards/components/dashboard-meeting-card/dashboard-meeting-card.component.html

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -126,20 +126,6 @@
126126
icon="fa-light fa-video"
127127
styleClass="w-full bg-emerald-500 hover:bg-emerald-600 text-white h-8 text-sm font-semibold"
128128
data-testid="dashboard-meeting-card-join-button" />
129-
} @else {
130-
<!-- Fallback: redirect to join page -->
131-
<lfx-button
132-
type="button"
133-
size="small"
134-
class="w-full"
135-
label="Join Meeting"
136-
[routerLink]="meetingDetailRouterLink()"
137-
[queryParams]="meetingDetailQueryParams()"
138-
target="_blank"
139-
rel="noopener noreferrer"
140-
icon="fa-light fa-video"
141-
styleClass="w-full bg-emerald-500 hover:bg-emerald-600 text-white h-8 text-sm font-semibold"
142-
data-testid="dashboard-meeting-card-join-button" />
143129
}
144130
}
145131
</div>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<!-- Copyright The Linux Foundation and each contributor to LFX. -->
2+
<!-- SPDX-License-Identifier: MIT -->
3+
4+
<div class="flex flex-col gap-4">
5+
<p class="font-semibold text-sm text-blue-900">Enter your information</p>
6+
7+
<form [formGroup]="form()" class="flex flex-col gap-4">
8+
<!-- Name + Email Row -->
9+
<div class="flex flex-col md:flex-row gap-4">
10+
<div class="flex-1">
11+
<label class="flex items-center gap-1 text-xs text-blue-900 mb-2">
12+
<i class="fa-light fa-user text-gray-600"></i>
13+
<span>Full Name</span>
14+
<span class="text-red-500">*</span>
15+
</label>
16+
<lfx-input-text size="small" [form]="form()" control="name" placeholder="John Doe" styleClass="w-full" [attr.data-testid]="'guest-form-name'">
17+
</lfx-input-text>
18+
</div>
19+
20+
<div class="flex-1">
21+
<label class="flex items-center gap-1 text-xs text-blue-900 mb-2">
22+
<i class="fa-light fa-envelope text-gray-600"></i>
23+
<span>Email</span>
24+
<span class="text-red-500">*</span>
25+
</label>
26+
<lfx-input-text
27+
size="small"
28+
[form]="form()"
29+
control="email"
30+
type="email"
31+
placeholder="[email protected]"
32+
styleClass="w-full"
33+
[attr.data-testid]="'guest-form-email'">
34+
</lfx-input-text>
35+
</div>
36+
</div>
37+
38+
<!-- Organization Row -->
39+
<div>
40+
<label class="flex items-center gap-1 text-xs text-blue-900 mb-2">
41+
<i class="fa-light fa-building text-gray-600"></i>
42+
<span>Organization</span>
43+
</label>
44+
<lfx-input-text
45+
size="small"
46+
[form]="form()"
47+
control="organization"
48+
placeholder="Your Company"
49+
styleClass="w-full"
50+
[attr.data-testid]="'guest-form-organization'">
51+
</lfx-input-text>
52+
</div>
53+
54+
<!-- Submit Button -->
55+
@if (showSubmitButton()) {
56+
<div class="flex justify-end md:justify-center">
57+
<lfx-button
58+
class="w-full md:w-auto"
59+
styleClass="w-full md:w-auto"
60+
size="small"
61+
[label]="submitButtonLabel()"
62+
icon="fa-light fa-arrow-right-to-bracket"
63+
severity="info"
64+
[disabled]="form().invalid || !joinUrl() || isLoading()"
65+
[href]="joinUrl()"
66+
target="_blank"
67+
rel="noopener noreferrer"
68+
[attr.data-testid]="'join-meeting-button-form'">
69+
</lfx-button>
70+
</div>
71+
}
72+
</form>
73+
</div>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright The Linux Foundation and each contributor to LFX.
2+
// SPDX-License-Identifier: MIT
3+
4+
import { CommonModule } from '@angular/common';
5+
import { Component, input } from '@angular/core';
6+
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
7+
import { ButtonComponent } from '@components/button/button.component';
8+
import { InputTextComponent } from '@components/input-text/input-text.component';
9+
10+
@Component({
11+
selector: 'lfx-guest-form',
12+
standalone: true,
13+
imports: [CommonModule, ReactiveFormsModule, InputTextComponent, ButtonComponent],
14+
templateUrl: './guest-form.component.html',
15+
})
16+
export class GuestFormComponent {
17+
// Inputs
18+
public form = input.required<FormGroup>();
19+
public joinUrl = input<string | undefined>(undefined);
20+
public isLoading = input<boolean>(false);
21+
public showSubmitButton = input<boolean>(true);
22+
public submitButtonLabel = input<string>('Join Meeting');
23+
}

apps/lfx-one/src/app/modules/meetings/meeting-join/meeting-join.component.html

Lines changed: 68 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -112,21 +112,40 @@ <h1 [attr.data-testid]="'meeting-title'">
112112

113113
<!-- Right Column: Join Button (immediate meetings) or RSVP (future meetings) -->
114114
@if (canJoinMeeting() && authenticated()) {
115-
<!-- Join Button - Immediate Meeting -->
116-
<div class="w-full md:w-[296px]">
117-
<lfx-button
118-
label="Join Meeting"
119-
icon="fa-light fa-video"
120-
severity="success"
121-
size="large"
122-
[href]="fetchedJoinUrl() && !isLoadingJoinUrl() ? fetchedJoinUrl() : undefined"
123-
[disabled]="!fetchedJoinUrl() || isLoadingJoinUrl()"
124-
target="_blank"
125-
[styleClass]="!fetchedJoinUrl() || isLoadingJoinUrl() ? 'cursor-not-allowed w-full h-[40px]' : 'w-full h-[40px]'"
126-
rel="noopener noreferrer"
127-
[attr.data-testid]="'join-meeting-button-immediate'">
128-
</lfx-button>
129-
</div>
115+
@if (fetchedJoinUrl() && !showGuestForm()) {
116+
<div class="w-full md:w-[296px]">
117+
<lfx-button
118+
label="Join Meeting"
119+
icon="fa-light fa-video"
120+
severity="success"
121+
size="large"
122+
[href]="fetchedJoinUrl()"
123+
target="_blank"
124+
[styleClass]="'w-full h-[40px]'"
125+
rel="noopener noreferrer"
126+
[attr.data-testid]="'join-meeting-button-immediate'">
127+
</lfx-button>
128+
</div>
129+
} @else if (joinUrlError() !== null && !showGuestForm()) {
130+
<div class="w-full md:w-[296px] flex flex-col gap-2">
131+
<lfx-button
132+
label="Join Meeting"
133+
icon="fa-light fa-video"
134+
[disabled]="true"
135+
size="large"
136+
styleClass="w-full h-[40px] bg-gray-300 text-gray-500 cursor-not-allowed"
137+
rel="noopener noreferrer"
138+
[attr.data-testid]="'join-meeting-button-error'">
139+
</lfx-button>
140+
<div class="text-xs text-red-500 bg-red-50 border border-red-500/50 rounded px-3 py-2">
141+
<span>{{ joinUrlError() }}</span
142+
>.
143+
@if (emailError()) {
144+
<span><a class="text-primary cursor-pointer" (click)="onEmailErrorClick()">Click here</a> to join using a different email address.</span>
145+
}
146+
</div>
147+
</div>
148+
}
130149
} @else if (authenticated() && meeting().organizer && !isLegacyMeeting()) {
131150
<!-- RSVP Details - Future Meeting (Authenticated Organizers) -->
132151
<div class="w-full md:w-[296px] flex flex-col gap-3">
@@ -252,21 +271,38 @@ <h3 class="text-gray-600 text-sm font-normal">Resources</h3>
252271
<!-- Card Footer: Authentication Section -->
253272
@if (authenticated() && user(); as user) {
254273
<!-- Signed In State -->
255-
<div class="flex flex-col gap-3">
256-
<p class="text-sm text-gray-950">You are signed in as:</p>
274+
<div class="flex flex-col gap-6">
275+
<div class="flex flex-col gap-3">
276+
<p class="text-sm text-gray-950">You are signed in as:</p>
257277

258-
<div class="bg-blue-50 border border-blue-500/50 rounded px-3 py-2 flex flex-wrap items-center gap-3">
259-
<!-- Avatar -->
260-
<div class="hidden md:flex w-12 h-12 bg-blue-600 rounded-full items-center justify-center">
261-
<span class="text-white text-2xl font-medium">{{ (user.name || 'User').substring(0, 2).toUpperCase() }}</span>
262-
</div>
278+
<div class="bg-blue-50 border border-blue-500/50 rounded px-3 py-2 flex flex-wrap items-center gap-3">
279+
<!-- Avatar -->
280+
<div class="hidden md:flex w-12 h-12 bg-blue-600 rounded-full items-center justify-center">
281+
<span class="text-white text-2xl font-medium">{{ (user.name || 'User').substring(0, 2).toUpperCase() }}</span>
282+
</div>
263283

264-
<!-- User Info -->
265-
<div class="flex-1">
266-
<p class="font-semibold text-sm text-blue-900">{{ user.name }}</p>
267-
<p class="text-sm text-blue-900">{{ user.email }}</p>
284+
<!-- User Info -->
285+
<div class="flex-1">
286+
<p class="font-semibold text-sm text-blue-900">{{ user.name }}</p>
287+
<p class="text-sm text-blue-900">{{ user.email }}</p>
288+
</div>
268289
</div>
269290
</div>
291+
292+
<!-- Guest Form for authenticated users -->
293+
@if (showGuestForm()) {
294+
<!-- OR Divider -->
295+
<div class="relative h-px bg-gray-200">
296+
<span class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-white px-3 text-xs text-gray-600">OR</span>
297+
</div>
298+
299+
<lfx-guest-form [form]="joinForm" [joinUrl]="fetchedJoinUrl()" [isLoading]="isLoadingJoinUrl()"> </lfx-guest-form>
300+
@if (joinUrlError() !== null) {
301+
<div class="text-xs text-red-500 bg-red-50 border border-red-500/50 rounded px-3 py-2 text-center">
302+
<span>{{ joinUrlError() }}. Please contact the meeting organizer or support team if you believe this is an error.</span>
303+
</div>
304+
}
305+
}
270306
</div>
271307
} @else {
272308
<!-- Signed Out State -->
@@ -298,80 +334,12 @@ <h3 class="text-gray-600 text-sm font-normal">Resources</h3>
298334
</div>
299335

300336
<!-- Guest Form -->
301-
<div class="flex flex-col gap-4">
302-
<p class="font-semibold text-sm text-blue-900">Enter your information</p>
303-
304-
<form [formGroup]="joinForm" class="flex flex-col gap-4">
305-
<!-- Name + Email Row -->
306-
<div class="flex flex-col md:flex-row gap-4">
307-
<div class="flex-1">
308-
<label class="flex items-center gap-1 text-xs text-blue-900 mb-2">
309-
<i class="fa-light fa-user text-gray-600"></i>
310-
<span>Full Name</span>
311-
<span class="text-red-500">*</span>
312-
</label>
313-
<lfx-input-text
314-
size="small"
315-
[form]="joinForm"
316-
control="name"
317-
placeholder="John Doe"
318-
styleClass="w-full"
319-
[attr.data-testid]="'guest-form-name'">
320-
</lfx-input-text>
321-
</div>
322-
323-
<div class="flex-1">
324-
<label class="flex items-center gap-1 text-xs text-blue-900 mb-2">
325-
<i class="fa-light fa-envelope text-gray-600"></i>
326-
<span>Email</span>
327-
<span class="text-red-500">*</span>
328-
</label>
329-
<lfx-input-text
330-
size="small"
331-
[form]="joinForm"
332-
control="email"
333-
type="email"
334-
placeholder="[email protected]"
335-
styleClass="w-full"
336-
[attr.data-testid]="'guest-form-email'">
337-
</lfx-input-text>
338-
</div>
339-
</div>
340-
341-
<!-- Organization Row -->
342-
<div>
343-
<label class="flex items-center gap-1 text-xs text-blue-900 mb-2">
344-
<i class="fa-light fa-building text-gray-600"></i>
345-
<span>Organization</span>
346-
</label>
347-
<lfx-input-text
348-
size="small"
349-
[form]="joinForm"
350-
control="organization"
351-
placeholder="Your Company"
352-
styleClass="w-full"
353-
[attr.data-testid]="'guest-form-organization'">
354-
</lfx-input-text>
355-
</div>
356-
357-
<!-- Submit Button -->
358-
<div class="flex justify-end md:justify-center">
359-
<lfx-button
360-
class="w-full md:w-auto"
361-
styleClass="w-full md:w-auto"
362-
size="small"
363-
label="Join Meeting"
364-
icon="fa-light fa-arrow-right-to-bracket"
365-
severity="info"
366-
[disabled]="joinForm.invalid || !fetchedJoinUrl() || isLoadingJoinUrl()"
367-
[href]="fetchedJoinUrl()"
368-
target="_blank"
369-
rel="noopener noreferrer"
370-
[attr.data-testid]="'join-meeting-button-form'">
371-
</lfx-button>
372-
</div>
373-
</form>
374-
</div>
337+
<lfx-guest-form [form]="joinForm" [joinUrl]="fetchedJoinUrl()" [isLoading]="isLoadingJoinUrl()"> </lfx-guest-form>
338+
@if (joinUrlError() !== null) {
339+
<div class="text-xs text-red-500 bg-red-50 border border-red-500/50 rounded px-3 py-2 text-center">
340+
<span>{{ joinUrlError() }}. Please contact the meeting organizer or support team if you believe this is an error.</span>
341+
</div>
342+
}
375343
</div>
376344
}
377345
</lfx-card>

0 commit comments

Comments
 (0)