Skip to content

Commit

Permalink
Merge pull request #319 from PermanentOrg/PER-9373-enter-multiple-ema…
Browse files Browse the repository at this point in the history
…ils-gifting-screen

Per 9373 enter multiple emails gifting screen
  • Loading branch information
crisnicandrei authored Nov 15, 2023
2 parents bde2255 + 6b52598 commit 73da1c9
Show file tree
Hide file tree
Showing 11 changed files with 519 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@
</div>
<div class="content">
<p class="popup-text">
<b
>Are you sure you would like to gift {{ amount }} GB of storage to
{{ email }}?</b
>
<b> Are you sure you’d like to gift storage?</b>
</p>
<p class="popup-text">
Once confirmed, the gift storage cannot be reclaimed.
Once you send gift storage to someone else, it can’t be reclaimed.
</p>
<div class="buttons">
<button class="btn btn-sm btn-secondary" (click)="onDoneClick()">
Cancel
</button>

<button class="btn btn-sm btn-primary" (click)="onConfirmClick()">
Confirm
Yes, gift storage
</button>
<button class="btn btn-sm" (click)="onDoneClick()">Go back</button>
</p>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* @format */
@import 'variables';

:host {
Expand All @@ -11,7 +12,7 @@

.content {
@include tabbedDialogContent;
flex-direction: column
flex-direction: column;
}

.tabs {
Expand Down Expand Up @@ -44,3 +45,26 @@ pr-archive-small {
text-align: center;
padding: 20px;
}

.buttons {
display: flex;
flex-direction: row;
justify-content: center;
& .btn {
padding: 10px;
border-radius: 4px;
margin: 0 10px;
width: 15%;

@include beforeDesktop {
width: 20%;
}
}

& .btn-primary {
width: 30%;
@include beforeDesktop {
width: 35%;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as Testing from '@root/test/testbedConfig';
import { cloneDeep } from 'lodash';
import { BehaviorSubject, Observable } from 'rxjs';
import { ApiService } from '@shared/services/api/api.service';
import { GiftingResponse } from '@shared/services/api/billing.repo';
import { MessageService } from '@shared/services/message/message.service';
import { ConfirmGiftDialogComponent } from './confirm-gift-dialog.component';

Expand Down Expand Up @@ -46,7 +47,10 @@ describe('ConfirmGiftDialogComponent', () => {
},
};

const mockGiftResult = new BehaviorSubject<boolean>(false);
const mockGiftResult = new BehaviorSubject<{
isSuccessful: boolean;
response: GiftingResponse;
}>({ isSuccessful: false, response: null });

beforeEach(async () => {
const config: TestModuleMetadata = cloneDeep(Testing.BASE_TEST_CONFIG);
Expand All @@ -65,7 +69,7 @@ describe('ConfirmGiftDialogComponent', () => {
config.providers.push({
provide: DIALOG_DATA,
useValue: {
email: '[email protected]',
emails: ['[email protected]', '[email protected]'],
amount: 10,
message: 'test message',
giftResult: mockGiftResult,
Expand Down Expand Up @@ -100,7 +104,7 @@ describe('ConfirmGiftDialogComponent', () => {
});

it('should take the email from the dialog data', () => {
expect(component.email).toEqual('[email protected]');
expect(component.emails).toEqual(['[email protected]', '[email protected]']);
});

it('should take the amount from the dialog data', () => {
Expand All @@ -122,14 +126,27 @@ describe('ConfirmGiftDialogComponent', () => {
it('should close the dialog and send the data back when confirm method is called', fakeAsync(() => {
const dialogRefSpy = spyOn(dialogRef, 'close');
const giftResultSpy = spyOn(mockGiftResult, 'next');
mockApiService.billing.giftStorage.and.returnValue(Promise.resolve());
const giftingResponse = new GiftingResponse({
storageGifted: 10,
alreadyInvited: [],
invitationSent: [],
giftDelivered: [],
});

const response = mockApiService.billing.giftStorage.and.returnValue(
Promise.resolve(giftingResponse)
);

component.onConfirmClick();

tick();

expect(dialogRefSpy).toHaveBeenCalled();
expect(giftResultSpy).toHaveBeenCalledWith(true);

expect(giftResultSpy).toHaveBeenCalledWith({
isSuccessful: true,
response: giftingResponse,
});
}));

it('should handle failure in onConfirmClick', fakeAsync(() => {
Expand All @@ -149,7 +166,10 @@ describe('ConfirmGiftDialogComponent', () => {
expect(showErrorSpy).toHaveBeenCalledWith(
'Something went wrong! Please try again.'
);
expect(giftResultSpy).toHaveBeenCalledWith(false);
expect(giftResultSpy).toHaveBeenCalledWith({
isSuccessful: false,
response: null,
});

expect(dialogRefSpy).toHaveBeenCalled();
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,29 @@ import { DialogRef, DIALOG_DATA } from '@root/app/dialog/dialog.service';
import { ApiService } from '@shared/services/api/api.service';
import { MessageService } from '@shared/services/message/message.service';
import { BehaviorSubject } from 'rxjs';
import { GiftingResponse } from '@shared/services/api/billing.repo';

@Component({
selector: 'pr-confirm-gift-dialog',
templateUrl: './confirm-gift-dialog.component.html',
styleUrls: ['./confirm-gift-dialog.component.scss'],
})
export class ConfirmGiftDialogComponent {
email: string;
emails: string[];
amount: number;
message: string;
giftResult: BehaviorSubject<boolean>;
giftResult: BehaviorSubject<{
isSuccessful: boolean;
response: GiftingResponse | null;
}>;

constructor(
private dialogRef: DialogRef,
private api: ApiService,
@Inject(DIALOG_DATA) public data: any,
private msg: MessageService
) {
this.email = this.data.email;
this.emails = this.data.emails;
this.amount = this.data.amount;
this.message = this.data.message;
this.giftResult = this.data.giftResult;
Expand All @@ -34,11 +38,19 @@ export class ConfirmGiftDialogComponent {

public async onConfirmClick() {
try {
await this.api.billing.giftStorage(this.email, Number(this.amount));
this.giftResult.next(true);
const res = await this.api.billing.giftStorage(
this.emails,
Number(this.amount),
this.message
);
const response = {
isSuccessful: true,
response: res,
};
this.giftResult.next(response);
} catch (e) {
this.msg.showError('Something went wrong! Please try again.');
this.giftResult.next(false);
this.giftResult.next({ isSuccessful: false, response: null });
}
this.dialogRef?.close();
}
Expand Down
85 changes: 68 additions & 17 deletions src/app/core/components/gift-storage/gift-storage.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,18 @@
class="dialog-form"
(submit)="submitStorageGiftForm(giftForm.value)"
>
<div class="dialog-form-field">
<div
class="dialog-form-field dialog-form-field-emails"
[ngClass]="{
'dialog-form-field-with-error':
duplicateEmails.length || emailValidationErrors.length
}"
>
<div class="text">
<label class="label" for="email">Recipient Email</label>
<label class="label" for="email">Recipient Email(s)</label>
<span class="label-info"
>Separate multiple email addresses with commas</span
>
</div>
<input
type="text"
Expand All @@ -26,10 +35,20 @@
placeholder="[email protected]"
/>
</div>
<p class="input-error" *ngIf="giftForm.get('email').errors?.email">
The email you have entered is not valid.
</p>
<div class="dialog-form-field dialog-form-field-amount">
<div
class="error-message"
*ngFor="let invalidEmail of emailValidationErrors"
>
<img src="assets/svg/alert.svg" />
<span class="input-error"
>{{ invalidEmail }} is not a valid email address.</span
>
</div>
<div class="error-message" *ngFor="let duplicate of duplicateEmails">
<img src="assets/svg/alert.svg" />
<span class="input-error">{{ duplicate }} has already been added.</span>
</div>
<div class="dialog-form-field dialog-form-field-amount" [ngClass]="">
<div class="text">
<label class="label" for="amount">Storage Gift Amount</label>
<span class="label-info">{{ this.availableSpace }} GB available</span>
Expand All @@ -45,18 +64,34 @@
'form-control-error': giftForm.get('amount').errors?.notInteger
}"
/>
<span class="gigabytes">gigabytes</span>
<span class="gigabytes">gigabytes per recipient</span>
</div>
<p class="input-error" *ngIf="giftForm.get('amount').errors?.notInteger">
Storage can only be gifted in 1GB increments.
</p>
<p class="input-error" *ngIf="giftForm.get('amount').errors?.max">
Amount must be less than or equal to {{ availableSpace }} GB.
<div
*ngIf="giftForm.get('amount').errors?.notInteger"
class="error-message not-integer"
>
<img src="assets/svg/alert.svg" />
<p class="input-error">Storage can only be gifted in 1GB increments.</p>
</div>
<p
*ngIf="successMessage && !giftForm.get('amount').errors?.notInteger"
class="error-message success-message"
>
{{ successMessage }}
</p>
<div
class="error-message"
*ngIf="giftForm.get('amount').errors?.isGreaterThanAvailableSpace"
>
<img src="assets/svg/alert.svg" />
<span class="input-error">{{ getAmountErrorMessage() }}</span>
</div>
<div class="dialog-form-field">
<div class="text">
<label class="label" for="amount">Note to recipient</label>
<span class="label-info">Optional</span>
<span class="label-info optional"
>Optional; sent to all recipients</span
>
</div>
<textarea
class="form-control form-control-textarea"
Expand Down Expand Up @@ -100,11 +135,27 @@
</defs>
</svg>

<p class="panel-title">Storage successfully gifted</p>
<p>
Success! You sent {{ giftForm.value.amount }} GB of Permanent storage to
{{ giftForm.value.email }}.
<p *ngIf="emailsSentTo.length" class="panel-title">
Storage successfully gifted
</p>
<p *ngIf="emailsSentTo.length">
Success! You sent {{ giftForm.value.amount }} GB of Permanent storage to:
</p>
<ul>
<li class="email" *ngFor="let email of emailsSentTo">
{{ email }}
</li>
</ul>
<p *ngIf="alreadyInvited.length">
The following emails have already been invited and were not sent storage.
They must accept their invitation before they can be sent additional
storage.
</p>
<ul>
<li class="email" *ngFor="let email of alreadyInvited">
{{ email }}
</li>
</ul>
<p>
You have
{{ (+availableSpace).toFixed(2) }} GB of storage available.
Expand Down
Loading

0 comments on commit 73da1c9

Please sign in to comment.