Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

87 frontend email notification reciever list #168

Merged
merged 25 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/frontend/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@
<cds-icon shape="bell"></cds-icon>
<span class="nav-text">Notifications</span>
</button>
<button clrDropdownItem>
<cds-icon shape="envelope"></cds-icon>
<span
routerLinkActive="active"
routerLink="email-receiver"
class="nav-text"
>Email Receiver</span
>
</button>
</clr-dropdown-menu>
</clr-dropdown>
<app-notification-settings></app-notification-settings>
Expand Down
14 changes: 13 additions & 1 deletion apps/frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ import {
tagIcon,
dataClusterIcon,
filterIcon,
envelopeIcon,
plusIcon,
lockIcon,
trashIcon,
} from '@cds/core/icon';
import { NgxEchartsModule } from 'ngx-echarts';
import { TestUploadComponent } from './test-upload/component/test-upload/test-upload.component';
Expand All @@ -31,6 +35,8 @@ import { BackupsComponent } from './backups-overview/backups/backups/backups.com
import { BASE_URL } from './shared/types/configuration';
import { AlertComponent } from './alert/component/alert.component';
import { NotificationSettingsComponent } from './management/components/settings/notification-settings/notification-settings.component';
import { EmailReceiverSettingsComponent } from './management/components/settings/email-receiver-settings/email-receiver-settings.component';
import { ConfirmDialogComponent } from './shared/components/confirm-dialog/component/confirm-dialog/confirm-dialog.component';

@NgModule({
declarations: [
Expand All @@ -40,6 +46,8 @@ import { NotificationSettingsComponent } from './management/components/settings/
BackupsComponent,
AlertComponent,
NotificationSettingsComponent,
EmailReceiverSettingsComponent,
ConfirmDialogComponent,
],
imports: [
BrowserModule,
Expand Down Expand Up @@ -69,7 +77,11 @@ export class AppModule {
angleIcon,
tagIcon,
dataClusterIcon,
filterIcon
filterIcon,
envelopeIcon,
plusIcon,
lockIcon,
trashIcon,
);
}
}
3 changes: 3 additions & 0 deletions apps/frontend/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import { TestUploadComponent } from './test-upload/component/test-upload/test-up
import { FindTestDataComponent } from './test-upload/component/find-test-data/find-test-data.component';
import { BackupsComponent } from './backups-overview/backups/backups/backups.component';
import { AlertComponent } from './alert/component/alert.component';
import { EmailReceiverSettingsComponent } from './management/components/settings/email-receiver-settings/email-receiver-settings.component';

export const appRoutes: Route[] = [
{ path: 'upload', component: TestUploadComponent },
{ path: 'findData', component: FindTestDataComponent },
{ path: 'email-receiver', component: EmailReceiverSettingsComponent},
{ path: '', component: BackupsComponent },

];
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.clr-dg-cell {
text-align: center ;
}

.error {
color: red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<h3>Email Alert Recipients</h3>
<div class="clr-row">
<div class="clr-col-lg-12 clr-col-lg-12">
<clr-datagrid
*ngIf="emailsSubject$ | async as emails"
[clrDgLoading]="isLoading"
[(clrDgSelected)]="selectedEmails"
>
<clr-dg-action-bar>
<div class="btn-group">
<button
type="button"
class="btn btn-sm btn-secondary"
(click)="showEmailModal = true"
>
<cds-icon shape="plus"></cds-icon>
New
</button>
<button
type="button"
class="btn btn-sm btn-danger"
(click)="removeEmail(selectedEmails)"
[disabled]="selectedEmails.length === 0"
>
<cds-icon shape="lock"></cds-icon>
Delete
</button>
</div>
</clr-dg-action-bar>

<clr-dg-column [clrDgField]="'mail'">
<ng-container *clrDgHideableColumn>
Email
<clr-dg-filter [clrDgFilter]="emailFilter">
<clr-input-container>
<label>Email:</label>
<input
clrInput
type="string"
[(ngModel)]="emailFilter.ranges.mail"
(ngModelChange)="emailFilter.updateRanges({ mail: $event })"
/>
</clr-input-container>
</clr-dg-filter>
chrisklg marked this conversation as resolved.
Show resolved Hide resolved
</ng-container>
</clr-dg-column>
<clr-dg-placeholder>We couldn't find any emails!</clr-dg-placeholder>
chrisklg marked this conversation as resolved.
Show resolved Hide resolved
<clr-dg-row
*ngFor="let email of emailsSubject$.getValue()"
[clrDgItem]="email"
>
<clr-dg-cell class="clr-dg-cell">{{ email.mail }}</clr-dg-cell>
</clr-dg-row>

<clr-dg-footer> </clr-dg-footer>
</clr-datagrid>
</div>
</div>

<clr-modal [(clrModalOpen)]="showEmailModal">
<h3 class="modal-title">Add Email Recipient</h3>
<div class="modal-body">
<form clrForm [formGroup]="emailForm" (ngSubmit)="saveChanges()">
<clr-spinner *ngIf="isLoading"></clr-spinner>
<clr-input-container>
<label>Email Address</label>
<input clrInput type="email" formControlName="email" />
<clr-control-error
*ngIf="
emailForm.get('email')?.touched && emailForm.get('email')?.invalid
"
>Please enter a valid email address</clr-control-error
>
</clr-input-container>
<div class="error" *ngIf="modalError">
{{ modalError }}
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline" (click)="resetForm()">
Cancel
</button>
<button
type="button"
class="btn btn-primary"
(click)="saveChanges()"
[disabled]="!emailForm.valid"
>
Add
</button>
</div>
</clr-modal>
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { EmailReceiverSettingsComponent } from './email-receiver-settings.component';
import { FormBuilder } from '@angular/forms';
import { EmailReceiverService } from '../../../services/email-receiver/email-receiver.service';
import { of, throwError } from 'rxjs';
import { ConfirmDialogService } from '../../../../shared/components/confirm-dialog/service/confirm-dialog.service';
import { EmailType } from '../../../../shared/types/email';

describe('EmailReceiverSettingsComponent', () => {
let component: EmailReceiverSettingsComponent;
let emailService: EmailReceiverService;
let confirmDialogService: ConfirmDialogService;

const mockEmails: EmailType[] = [
{ id: '1', mail: '[email protected]' },
{ id: '2', mail: '[email protected]' },
];

beforeEach(() => {
emailService = {
getAllEmailReceiver: vi.fn().mockReturnValue(of(mockEmails)),
deleteEmail: vi.fn().mockReturnValue(of({})),
updateEmailReceiver: vi.fn(),
} as any;

confirmDialogService = {
handleConfirmation: vi.fn().mockImplementation((options, onConfirm) => {
onConfirm();
return Promise.resolve();
}),
} as any;

component = new EmailReceiverSettingsComponent(
new FormBuilder(),
emailService,
confirmDialogService
);

component.ngOnInit();
});

it('should create', () => {
expect(component).toBeTruthy();
});

describe('loadEmailReceiver', () => {
it('should load emails successfully', () => {
component.loadEmailReceiver();

expect(component.isLoading).toBe(false);
expect(emailService.getAllEmailReceiver).toHaveBeenCalled();

component['emailsSubject$'].subscribe((emails) => {
expect(emails).toEqual(mockEmails);
});
});

it('should handle error when loading emails', () => {
emailService.getAllEmailReceiver = vi
.fn()
.mockReturnValue(throwError(() => new Error('Failed to load')));

component.loadEmailReceiver();

expect(component.isLoading).toBe(false);
expect(emailService.getAllEmailReceiver).toHaveBeenCalled();
});
});

describe('removeEmail', () => {
it('should not proceed if no emails to remove', async () => {
await component.removeEmail([]);

expect(confirmDialogService.handleConfirmation).not.toHaveBeenCalled();
expect(emailService.deleteEmail).not.toHaveBeenCalled();
});

it('should delete emails when confirmed', async () => {
const emailsToRemove = [mockEmails[0]];

await component.removeEmail(emailsToRemove);

expect(confirmDialogService.handleConfirmation).toHaveBeenCalledWith(
expect.objectContaining({
title: 'Delete Email Recipients',
confirmText: 'Delete',
cancelText: 'Cancel',
}),
expect.any(Function)
);
expect(emailService.deleteEmail).toHaveBeenCalledWith('1');
});

it('should handle cancel confirmation', async () => {
confirmDialogService.handleConfirmation = vi
.fn()
.mockImplementation((options, onConfirm, onCancel) => {
if (onCancel) onCancel();
return Promise.resolve();
});

await component.removeEmail([mockEmails[0]]);

expect(emailService.deleteEmail).not.toHaveBeenCalled();
});

it('should handle error during deletion', async () => {
emailService.deleteEmail = vi
.fn()
.mockReturnValue(throwError(() => new Error('Delete failed')));

await component.removeEmail([mockEmails[0]]);

expect(component.isLoading).toBe(false);
});
});

describe('saveChanges', () => {
it('should not save when form is invalid', () => {
component.emailForm.controls['email'].setValue('');

component.saveChanges();

expect(emailService.updateEmailReceiver).not.toHaveBeenCalled();
});

it('should create new email when form is valid', () => {
const newEmail = { mail: '[email protected]' };
const createdEmail = { id: '3', mail: '[email protected]' };

emailService.updateEmailReceiver = vi
.fn()
.mockReturnValue(of(createdEmail));
component.emailForm.controls['email'].setValue('[email protected]');

component.saveChanges();

expect(emailService.updateEmailReceiver).toHaveBeenCalledWith(newEmail);
expect(component.showEmailModal).toBe(false);
expect(component.isLoading).toBe(false);
});

it('should handle duplicate email error', () => {
emailService.updateEmailReceiver = vi
.fn()
.mockReturnValue(throwError(() => ({ status: 409 })));
component.emailForm.controls['email'].setValue('[email protected]');

component.saveChanges();

expect(component.modalError).toBe('This email already exists');
expect(component.isLoading).toBe(false);
});
});

describe('resetForm', () => {
it('should reset form and modal state', () => {
// Setup form with values
component.emailForm.controls['email'].setValue('[email protected]');
component.showEmailModal = true;
component.modalError = 'Some error';
component.isLoading = true;

// Reset form
component.resetForm();

// Verify reset state
expect(component.emailForm.get('email')?.value).toBeNull();
expect(component.showEmailModal).toBe(false);
expect(component.modalError).toBe('');
expect(component.isLoading).toBe(false);
});
});

describe('ngOnDestroy', () => {
it('should complete subjects', () => {
const destroySpy = vi.spyOn(component['destroy$'], 'complete');

component.ngOnDestroy();

expect(destroySpy).toHaveBeenCalled();
});
});
});
Loading
Loading