import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms'; import { DialogsModule } from '@progress/kendo-angular-dialog'; import { InputsModule } from '@progress/kendo-angular-inputs'; import { LabelModule } from '@progress/kendo-angular-label'; import { ButtonsModule } from '@progress/kendo-angular-buttons'; import { IndicatorsModule } from '@progress/kendo-angular-indicators'; import { MemberListItemDto, memberDisplayName } from '../../models/member.model'; import { InvitationApiService } from '../../services/invitation-api.service'; import { ToastService } from '../../../../core/services/toast.service'; type Step = 'needEmail' | 'generating' | 'ready'; @Component({ selector: 'app-invitation-dialog', standalone: true, imports: [ CommonModule, ReactiveFormsModule, DialogsModule, InputsModule, LabelModule, ButtonsModule, IndicatorsModule, ], templateUrl: './invitation-dialog.component.html', }) export class InvitationDialogComponent implements OnInit { @Input({ required: true }) member!: MemberListItemDto; @Output() cancelled = new EventEmitter(); step: Step = 'generating'; emailForm!: FormGroup; link = ''; expiresAt: string | null = null; copied = false; isSending = false; errorMessage = ''; get memberName(): string { return memberDisplayName(this.member); } constructor( private fb: FormBuilder, private invitationApi: InvitationApiService, private toast: ToastService, ) {} ngOnInit(): void { this.emailForm = this.fb.group({ email: [this.member.email ?? '', [Validators.required, Validators.email]], }); // Auto-generate when the member already has an email; otherwise ask for one first. if (this.member.email) { this.generate(); } else { this.step = 'needEmail'; } } /** Generate (or re-issue) the link. Uses the form email when the member has none on file. */ generate(): void { if (this.step === 'needEmail') { if (this.emailForm.invalid) { this.emailForm.markAllAsTouched(); return; } } const email = this.member.email ? undefined : this.emailForm.value.email; this.step = 'generating'; this.errorMessage = ''; this.invitationApi.create(this.member.id, email).subscribe({ next: (result) => { this.link = `${window.location.origin}/accept-invitation?token=${result.token}`; this.expiresAt = result.expiresAt; this.step = 'ready'; }, error: (err) => { this.errorMessage = err.error?.message ?? 'Failed to create the invitation link.'; // Fall back to the email step so the admin can supply/correct an address. this.step = 'needEmail'; }, }); } copyLink(): void { navigator.clipboard.writeText(this.link).then(() => { this.copied = true; setTimeout(() => (this.copied = false), 2000); }); } sendEmail(): void { this.isSending = true; this.invitationApi.sendEmail(this.member.id, this.link).subscribe({ next: () => { this.toast.success(`Invitation emailed to ${this.memberName}.`); this.isSending = false; }, error: (err) => { this.toast.error(err.error?.message ?? 'Failed to send the email.'); this.isSending = false; }, }); } onClose(): void { this.cancelled.emit(); } }