Merge remote-tracking branch 'origin/dev' into test

pull/94/head
Chris Veilleux 2022-09-16 13:45:57 -05:00
commit e5f48b1e23
26 changed files with 294 additions and 324 deletions

View File

@ -50,7 +50,7 @@ export class DeviceDisplayComponent implements OnInit {
) { }
ngOnInit() {
if (!this.device.pantacorUpdateId) {
if (!this.device.pantacorConfig.deploymentId) {
this.softwareUpdateText = 'NO UPDATES AVAILABLE';
this.softwareUpdateDisabled = true;
} else {
@ -64,6 +64,17 @@ export class DeviceDisplayComponent implements OnInit {
return knownPlatform ? knownPlatform.displayName : device.platform;
}
applySoftwareUpdate() {
this.deviceService.applySoftwareUpdate(this.device.pantacorConfig.deploymentId).subscribe({
next: () => {
this.openSuccessSnackbar();
this.softwareUpdateText = 'NO UPDATES AVAILABLE';
this.softwareUpdateDisabled = true;
},
error: () => { this.openErrorSnackbar(); }
});
}
openErrorSnackbar() {
const config = new MatSnackBarConfig();
config.data = {type: 'error', message: 'An error occurred, device will not be updated.'};
@ -77,16 +88,4 @@ export class DeviceDisplayComponent implements OnInit {
this.snackbar.openFromComponent(SnackbarComponent, config);
}
applySoftwareUpdate() {
this.deviceService.applySoftwareUpdate(this.device.pantacorUpdateId).subscribe(
() => {
this.openSuccessSnackbar();
this.softwareUpdateText = 'NO UPDATES AVAILABLE';
this.softwareUpdateDisabled = true;
},
() => { this.openErrorSnackbar(); }
);
}
}

View File

@ -18,6 +18,7 @@ and limitations under the License.
import { Component, Input, OnInit } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { OptionButtonsConfig } from '@account/models/option-buttons-config.model';
import { environment } from '@account/environments/environment';
@Component({
selector: 'account-software-release-card',
@ -29,8 +30,14 @@ export class SoftwareReleaseCardComponent implements OnInit {
public releaseChannelConfig: OptionButtonsConfig;
constructor() {
let channels: string[];
if (environment.production) {
channels = ['Beta', 'Stable', 'LTS'];
} else {
channels = ['Development', 'Beta QA', 'Stable QA', 'LTS QA'];
}
this.releaseChannelConfig = {
options: ['Stable', 'Latest', 'QA'],
options: channels,
buttonWidth: '130px'
};
}

View File

@ -14,10 +14,10 @@
<div fxLayout="row wrap" fxLayoutAlign="center" fxLayoutGap.gt-xs="16px">
<mat-card *ngFor="let device of devices; let i = index">
<mat-card-title fxLayout="row" fxLayoutAlign="start center">
<img [src]="getDeviceIcon(device)"/>
{{device.name}}
</mat-card-title>
<mat-card-header>
<img mat-card-avatar [src]="getDeviceIcon(device)"/>
<mat-card-title class="mat-h1">{{device.name}}</mat-card-title>
</mat-card-header>
<ng-container [ngSwitch]="device.status">
<mat-card-subtitle
class="status-active"

View File

@ -18,19 +18,15 @@
@use "@angular/material" as mat;
@use 'components/buttons' as buttons;
@use 'components/cards' as cards;
@use 'mycroft-theme' as theme;
@mixin panel-defaults {
border-radius: 12px;
width: 330px;
margin-bottom: 16px;
}
#add-device-button {
@include buttons.action-button-primary;
@include panel-defaults;
border-radius: 12px;
cursor: pointer;
height: 50px;
margin-bottom: 16px;
max-width: 362px;
min-width: 350px;
@ -51,37 +47,28 @@
}
#edit-button {
@include buttons.action-button-primary
}
#remove-button {
@include buttons.action-button-warn
}
mat-card {
@include panel-defaults;
button {
margin-bottom: 8px;
margin-left: 8px;
margin-right: 8px;
}
mat-card-title {
color: mat.get-color-from-palette(theme.$mycroft-primary, 500);
font-weight: bold;
@include cards.selene-card;
width: 330px;
mat-card-header {
img {
height: 60px;
margin-right: 16px;
width: 60px;
}
.mat-h1 {
color: mat.get-color-from-palette(theme.$mycroft-primary, 500);
font-size: 24px;
font-weight: bold;
}
}
mat-card-subtitle {
border-radius: 4px;
padding: 4px;
font-weight: bold;
margin-top: 4px;
padding: 4px;
text-align: center;
}
@ -110,3 +97,12 @@ mat-card {
}
}
}
#edit-button {
@include buttons.action-button-primary
}
#remove-button {
@include buttons.action-button-warn
}

View File

@ -5,16 +5,16 @@
</mat-card-header>
<mat-card-content>
<p class="mat-body">
You have chosen not to subscribe to Mycroft's membership program.
You are not subscribed to Mycroft's membership program.
<p class="mat-body">
Mycroft supporters
ensure we can keep the services open and available. Your credit card information is
sent to the payment service and not stored on Mycroft's servers.
If would like to become a member, select one of the below options.
Supporting Mycroft with a membership helps keep Mycroft's services open and available.
</p>
</mat-card-content>
<mat-card-actions>
<a mat-button>$1.99 USD MONTHLY</a>
<a mat-button>$19.99 USD YEARLY</a>
<button mat-button *ngFor="let membershipType of membershipTypes" (click)="openPaymentDialog(membershipType)">
{{membershipType.rate + '/' + membershipType.ratePeriod.toUpperCase() + ' (USD)'}}
</button>
</mat-card-actions>
</mat-card>
@ -28,10 +28,14 @@
Proudly supporting Mycroft for {{accountMembership.duration}}!
</p>
<p *ngIf="!accountMembership.duration" class="mat-body">
Thank you for supporting Mycroft!
Thank you for supporting Mycroft with your membership!
</p>
<p class="mat-body">
You can cancel your membership at any time. You can also update your credit card
information.
</p>
</mat-card-content>
<mat-card-actions>
<a mat-button color="warn">CANCEL MEMBERSHIP</a>
<a mat-button id="cancel-button" (click)="openCancelConfirmDialog()">CANCEL MEMBERSHIP</a>
</mat-card-actions>
</mat-card>

View File

@ -16,11 +16,19 @@
// and limitations under the License.
// *****************************************************************************
@use "@angular/material" as mat;
@use "components/buttons" as buttons;
@use "components/cards" as cards;
@use "mycroft-theme" as theme;
mat-card {
@include cards.selene-card;
margin-left: auto;
margin-right: auto;
max-width: 800px;
#cancel-button {
@include buttons.action-button-warn
}
}

View File

@ -16,9 +16,10 @@ See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
import { Component, Input, OnDestroy } from '@angular/core';
import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
import { MediaChange, MediaObserver } from '@angular/flex-layout';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar';
import { Subscription } from 'rxjs';
import { faCreditCard, IconDefinition } from '@fortawesome/free-solid-svg-icons';
@ -27,8 +28,9 @@ import { AccountMembership } from '@account/models/account-membership.model';
import { MembershipType } from '@account/models/membership.model';
import { MembershipUpdate } from '@account/models/membership-update.model';
import { ProfileService } from '@account/http/profile.service';
const twoSeconds = 2000;
import { MembershipCancelConfirmComponent } from '@account/app/modules/profile/components/modals/membership-cancel-confirm/membership-cancel-confirm.component';
import { PaymentComponent } from '@account/app/modules/profile/components/views/payment/payment.component';
import { SnackbarComponent } from 'shared';
@Component({
@ -39,12 +41,15 @@ const twoSeconds = 2000;
export class MembershipComponent implements OnDestroy {
@Input() accountMembership: AccountMembership;
@Input() membershipTypes: MembershipType[];
@Output() membershipUpdated = new EventEmitter();
public alignVertical: boolean;
private mediaWatcher: Subscription;
public membershipIcon: IconDefinition = faCreditCard;
constructor(
public cancelConfirmDialog: MatDialog,
public mediaObserver: MediaObserver,
public paymentDialog: MatDialog,
private profileService: ProfileService,
private snackbar: MatSnackBar
) {
@ -61,17 +66,72 @@ export class MembershipComponent implements OnDestroy {
this.mediaWatcher.unsubscribe();
}
updateAccount(membershipUpdate: MembershipUpdate) {
const accountUpdate = {membership: membershipUpdate};
this.profileService.updateAccount(accountUpdate).subscribe(
() => {
this.snackbar.open(
'Membership updated',
null,
{panelClass: 'mycroft-no-action-snackbar', duration: twoSeconds}
);
openPaymentDialog(membershipType: MembershipType) {
const dialogConfig = new MatDialogConfig();
dialogConfig.data = {newAccount: false, membershipType: membershipType};
dialogConfig.disableClose = true;
dialogConfig.maxWidth = 520;
dialogConfig.restoreFocus = true;
const dialogRef = this.paymentDialog.open(PaymentComponent, dialogConfig);
dialogRef.afterClosed().subscribe(
(stripeToken) => {
const membershipUpdate: MembershipUpdate = {
action: 'add',
membershipType: membershipType.type,
paymentMethod: 'Stripe',
paymentToken: stripeToken
};
if (this.accountMembership) {
membershipUpdate.action = 'update';
}
this.updateAccount(membershipUpdate);
}
);
}
openCancelConfirmDialog() {
const dialogConfig = new MatDialogConfig();
dialogConfig.disableClose = true;
dialogConfig.maxWidth = 520;
dialogConfig.restoreFocus = true;
const dialogRef = this.cancelConfirmDialog.open(MembershipCancelConfirmComponent, dialogConfig);
dialogRef.afterClosed().subscribe(
(cancelled) => {
if (cancelled) {
const membershipUpdate: MembershipUpdate = { action: 'cancel' };
this.updateAccount(membershipUpdate);
}
}
);
}
updateAccount(membershipUpdate: MembershipUpdate) {
const accountUpdate = {membership: membershipUpdate};
const config = new MatSnackBarConfig();
let successMessage: string;
let errorMessage: string;
if (membershipUpdate.action === 'add') {
successMessage = 'Membership added. Thank you!';
errorMessage = 'Failed to add membership - contact support';
} else if (membershipUpdate.action === 'cancel') {
successMessage = 'Membership cancelled';
errorMessage = 'Failed to cancel membership - contact support';
} else {
successMessage = 'Payment information updated';
errorMessage = 'Failed to update payment information - contact support';
}
this.profileService.updateAccount(accountUpdate).subscribe({
next: () => {
this.membershipUpdated.emit();
config.data = {type: 'info', message: successMessage};
this.snackbar.openFromComponent(SnackbarComponent, config);
},
error: () => {
config.data = {type: 'error', message: errorMessage};
this.snackbar.openFromComponent(SnackbarComponent, config);
}}
);
}
}

View File

@ -1,17 +0,0 @@
<mat-button-toggle-group
class="options-button-group"
[vertical]="alignVertical"
[(ngModel)]="selectedMembershipType"
(change)="onMembershipSelect($event)"
>
<mat-button-toggle
*ngFor="let membership of membershipTypes"
[value]="membership.type"
>
<h3 class="mat-h3">{{membership.type}}</h3>
<p class="mat-body" *ngIf="membership.type != 'Maybe Later'">${{membership.rate}} USD</p>
</mat-button-toggle>
<mat-button-toggle [value]="'Maybe Later'">
<h3 id="non-supporter" class="mat-h3">Maybe Later</h3>
</mat-button-toggle>
</mat-button-toggle-group>

View File

@ -1,127 +0,0 @@
/*! *****************************************************************************
SPDX-License-Identifier: Apache-2.0
Copyright (c) Mycroft AI Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { MediaChange, MediaObserver } from '@angular/flex-layout';
import { MatButtonToggleChange } from '@angular/material/button-toggle';
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Subscription } from 'rxjs';
import { AccountMembership } from '@account/models/account-membership.model';
import { MembershipType } from '@account/models/membership.model';
import { ProfileService } from '@account/http/profile.service';
import { PaymentComponent } from '@account/app/modules/profile/components/views/payment/payment.component';
import { MembershipUpdate } from '@account/models/membership-update.model';
@Component({
selector: 'account-membership-options',
templateUrl: './membership-options.component.html',
styleUrls: ['./membership-options.component.scss']
})
export class MembershipOptionsComponent implements OnInit, OnDestroy {
@Input() accountMembership: AccountMembership;
@Input() membershipTypes: MembershipType[];
@Output() membershipChange = new EventEmitter<MembershipUpdate>();
public alignVertical: boolean;
public mediaWatcher: Subscription;
public selectedMembershipType: string;
constructor(
public mediaObserver: MediaObserver,
public paymentDialog: MatDialog,
) {
this.mediaWatcher = mediaObserver.asObservable().subscribe(
(change: MediaChange[]) => {
change.forEach((item) => {
this.alignVertical = ['xs', 'sm'].includes(item.mqAlias);
});
}
);
}
ngOnInit(): void {
this.setSelectedMembershipType();
}
ngOnDestroy(): void {
this.mediaWatcher.unsubscribe();
}
setSelectedMembershipType() {
let selectedMembership: MembershipType;
if (this.accountMembership) {
selectedMembership = this.membershipTypes.find(
(membershipType) => membershipType.type === this.accountMembership.type
);
this.selectedMembershipType = selectedMembership.type;
} else {
this.selectedMembershipType = 'Maybe Later';
}
}
onMembershipSelect(membershipType: MatButtonToggleChange) {
const selectedMembership = this.membershipTypes.find(
(membership) => membership.type === membershipType.value
);
let membershipUpdate;
if (selectedMembership) {
if (this.accountMembership) {
// We have the user's credit card info but they decide to change plans
membershipUpdate = {
paymentMethod: 'Stripe',
newMembership: false,
membershipType: membershipType
};
this.membershipChange.emit(membershipUpdate);
} else {
// No credit card info. Go to payment dialog to collect
this.openPaymentDialog(membershipType.value);
}
} else {
// Membership termination
membershipUpdate = {newMembership: false, membershipType: null};
this.membershipChange.emit(membershipUpdate);
}
}
openPaymentDialog(membershipType: string) {
const dialogConfig = new MatDialogConfig();
dialogConfig.data = {newAccount: false, membershipType: membershipType};
dialogConfig.disableClose = true;
dialogConfig.restoreFocus = true;
const dialogRef = this.paymentDialog.open(PaymentComponent, dialogConfig);
dialogRef.afterClosed().subscribe(
(stripeToken) => {
if (stripeToken) {
const membershipUpdate: MembershipUpdate = {
newMembership: true,
membershipType: membershipType,
paymentMethod: 'Stripe',
paymentToken: stripeToken
};
this.membershipChange.emit(membershipUpdate);
} else {
this.setSelectedMembershipType();
}
}
);
}
}

View File

@ -0,0 +1,21 @@
<h1 mat-dialog-title>Are you sure?</h1>
<mat-dialog-content>
<p class="mat-body">
You know how this goes. You click a red button, we make sure you meant to do it.
</p>
<p class="mat-body">
Your membership supports continued development of an open, privacy focused voice assistant.
We hope you are seeing this as a result of an unintended mouse click (those buttons can be
touchy!). If so, click the "cancel" button below.
</p>
<p class="mat-body">
If you really do want to cancel your membership, that's a bummer. You can re-activate your
membership at any time. Click the "confirm" button below and we will cancel your
subscription immediately.
</p>
</mat-dialog-content>
<mat-dialog-actions>
<button id="confirm-button" mat-button (click)="onConfirm()">CONFIRM</button>
<button id="cancel-button" mat-button (click)="onCancel()">CANCEL</button>
</mat-dialog-actions>

View File

@ -15,32 +15,15 @@
// See the Apache Version 2.0 License for specific language governing permissions
// and limitations under the License.
// *****************************************************************************
.options-button-group {
border: none;
padding: 16px;
.mat-button-toggle {
background-color: #e6e8ea;
border: none;
border-radius: 4px;
height: 80px;
margin: 8px;
padding: 8px;
width: 200px;
@use "@angular/material" as mat;
@use "components/buttons" as buttons;
@use "mycroft-theme" as theme;
.mat-h3 {
font-weight: bold;
margin-top: 8px;
margin-bottom: 8px;
}
#non-supporter {
margin-top: 24px;
}
}
.mat-button-toggle-checked {
background-color: #22a7f0;
color: white;
}
h1 {
color: mat.get-color-from-palette(theme.$mycroft-warn, 400);
}
#confirm-button {
@include buttons.action-button-warn;
}

View File

@ -0,0 +1,26 @@
import { Component, OnInit } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog';
@Component({
selector: 'account-membership-cancel-confirm',
templateUrl: './membership-cancel-confirm.component.html',
styleUrls: ['./membership-cancel-confirm.component.scss']
})
export class MembershipCancelConfirmComponent implements OnInit {
constructor(
public confirmDialogRef: MatDialogRef<MembershipCancelConfirmComponent>,
) {
}
ngOnInit() {
}
onCancel(): void {
this.confirmDialogRef.close(false);
}
onConfirm(): void {
this.confirmDialogRef.close(true);
}
}

View File

@ -5,11 +5,13 @@
<p *ngFor="let paragraph of membershipDescription" class="mat-body">
{{paragraph}}
</p>
<account-membership-options
[membershipTypes]="membershipTypes"
(membershipChange)="updateNewAccountForm($event)"
<button
mat-button
type="button"
*ngFor="let membershipType of membershipTypes" (click)="openPaymentDialog(membershipType)"
>
</account-membership-options>
{{membershipType.rate + '/' + membershipType.ratePeriod.toUpperCase() + ' (USD)'}}
</button>
</mat-card-content>
</mat-card>

View File

@ -32,8 +32,10 @@ mat-card {
color: mat.get-color-from-palette(theme.$mycroft-primary, 500);
font-weight: bold;
}
}
mat-button-toggle-group {
@include buttons.options-button-group
button {
@include buttons.action-button-primary;
margin-bottom: 32px;
margin-right: 16px;
margin-top: 32px;
}
}

View File

@ -20,6 +20,8 @@ import { Component, Input, OnInit } from '@angular/core';
import { MembershipType } from '@account/models/membership.model';
import { MembershipUpdate } from '@account/models/membership-update.model';
import { UntypedFormGroup } from '@angular/forms';
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { PaymentComponent } from '@account/app/modules/profile/components/views/payment/payment.component';
@Component({
selector: 'account-membership-step',
@ -31,7 +33,7 @@ export class MembershipStepComponent implements OnInit {
@Input() newAcctForm: UntypedFormGroup;
public membershipDescription: string[];
constructor() {
constructor(public paymentDialog: MatDialog) {
this.membershipDescription = [
'Mycroft\'s voice assistant software is open source, which means it is free to use and ' +
'the underlying source code is available to the public. Our entire platform is free ' +
@ -49,11 +51,31 @@ export class MembershipStepComponent implements OnInit {
ngOnInit() {
}
openPaymentDialog(membershipType: MembershipType) {
const dialogConfig = new MatDialogConfig();
dialogConfig.data = {newAccount: false, membershipType: membershipType};
dialogConfig.disableClose = true;
dialogConfig.maxWidth = 520;
dialogConfig.restoreFocus = true;
const dialogRef = this.paymentDialog.open(PaymentComponent, dialogConfig);
dialogRef.afterClosed().subscribe(
(stripeToken) => {
const membershipUpdate: MembershipUpdate = {
action: 'add',
membershipType: membershipType.type,
paymentMethod: 'Stripe',
paymentToken: stripeToken
};
this.updateNewAccountForm(membershipUpdate);
}
);
}
updateNewAccountForm(membershipUpdate: MembershipUpdate): void {
this.newAcctForm.patchValue(
{
membership: {
newMembership: membershipUpdate.newMembership,
action: membershipUpdate.action,
membershipType: membershipUpdate.membershipType,
paymentMethod: membershipUpdate.paymentMethod,
paymentToken: membershipUpdate.paymentToken

View File

@ -1,14 +1,17 @@
<mat-card class="mat-elevation-z0">
<mat-card-title>Thank you for supporting Mycroft!</mat-card-title>
<mat-card-content>
Your membership directly helps to improve Mycroft technology and user experience.
<p class="mat-body">Please enter your payment information below</p>
<mat-card class="mat-elevation-z0">
<ngx-stripe-card [options]="cardOptions"></ngx-stripe-card>
</mat-card>
</mat-card-content>
<mat-card-actions [align]="'end'">
<button mat-button (click)="onCancel()">CANCEL</button>
<button mat-button (click)="submitPaymentInfo()" class="submit-button">SUBMIT</button>
</mat-card-actions>
</mat-card>
<h1 mat-dialog-title>Thank you for supporting Mycroft!</h1>
<mat-dialog-content>
<p class="mat-body">
Your membership directly helps to improve Mycroft's technology and user experience.
</p>
<p class="mat-body">
Please enter your payment information below. Your credit card information is
sent to the payment service and not stored on Mycroft's servers.
</p>
<div class="stripe-card">
<ngx-stripe-card [options]="cardOptions"></ngx-stripe-card>
</div>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button (click)="submitPaymentInfo()" class="submit-button">SUBMIT</button>
<button mat-button (click)="onCancel()">CANCEL</button>
</mat-dialog-actions>

View File

@ -20,43 +20,22 @@
@use "components/buttons" as buttons;
@use "mycroft-theme" as theme;
mat-card {
margin-right: auto;
margin-left: auto;
max-width: 500px;
padding: 0;
mat-card-title {
color: mat.get-color-from-palette(theme.$mycroft-primary, 700);
font-weight: bold;
}
mat-card-content {
p {
margin-top: 32px;
margin-left: auto;
margin-right: auto;
width: 320px;
}
mat-card {
border-radius: 10px;
border-style: solid;
border-width: 1px;
border-color: mat.get-color-from-palette(theme.$mycroft-accent, 200);
padding: 16px;
margin-bottom: 40px;
width: 320px;
}
}
.mat-card-actions.mat-card-actions {
padding: 0;
margin: 0;
.submit-button {
@include buttons.action-button-primary;
}
}
h1 {
color: mat.get-color-from-palette(theme.$mycroft-primary, 700);
font-weight: bold;
}
.stripe-card {
border-style: solid;
border-color: #6c7a89;
border-radius: 8px;
border-width: thin;
margin-left: auto;
margin-right: auto;
padding: 8px;
width: 400px;
}
.submit-button {
@include buttons.action-button-primary;
}

View File

@ -18,12 +18,13 @@ and limitations under the License.
import { Component, Inject, OnInit, ViewChild } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar';
import { StripeCardComponent, StripeService } from 'ngx-stripe';
import { StripeCardElementOptions} from '@stripe/stripe-js';
import { ProfileService } from '@account/http/profile.service';
import { SnackbarComponent } from 'shared';
const twoSeconds = 2000;
@ -36,13 +37,14 @@ const twoSeconds = 2000;
export class PaymentComponent implements OnInit {
@ViewChild(StripeCardComponent) card: StripeCardComponent;
public cardOptions: StripeCardElementOptions = {
iconStyle: 'solid',
style: {
base: {
iconColor: '#22a7f0',
color: '#2c3e50',
'::placeholder': {
color: '#969fa8'
}
color: '#6c7a89'
},
}
}
};
@ -61,26 +63,21 @@ export class PaymentComponent implements OnInit {
}
submitPaymentInfo() {
this.stripeService.createToken(this.card.element, {}).subscribe(
result => {
this.stripeService.createToken(this.card.element, {}).subscribe({
next: (result) => {
if (result.token) {
this.dialogRef.close(result.token.id);
} else if (result.error) {
this.showStripeError(result.error.message);
}
},
(result) => { this.showStripeError(result.toString()); }
);
}
});
}
showStripeError(errorMessage: string) {
this.dialogRef.close();
this.snackbar.open(
errorMessage,
null,
{panelClass: 'mycroft-no-action-snackbar', duration: twoSeconds}
);
const config = new MatSnackBarConfig();
config.data = {type: 'error', message: errorMessage};
this.snackbar.openFromComponent(SnackbarComponent, config);
}
onCancel() {

View File

@ -4,6 +4,7 @@
<account-membership-edit
[accountMembership]="account.membership"
[membershipTypes]="membershipTypes"
(membershipUpdated)="refreshAccount()"
>
</account-membership-edit>
<account-agreements-edit [account]="account"></account-agreements-edit>

View File

@ -46,4 +46,10 @@ export class EditComponent implements OnInit {
updateOpenDataset(optIn: boolean) {
this.service.updateAccount({openDataset: optIn}).subscribe();
}
refreshAccount() {
this.service.getAccount().subscribe({
next: (account) => { this.account = account; }
});
}
}

View File

@ -119,7 +119,7 @@ export class NewComponent implements OnInit {
private buildForm() {
const membershipGroup = this.formBuilder.group(
{
newMembership: [null],
action: [null],
membershipType: [null],
paymentMethod: [null],
paymentToken: [null]
@ -140,9 +140,6 @@ export class NewComponent implements OnInit {
onFormSubmit() {
const newValues = this.newAcctForm.value;
if (!newValues.membership.newMembership) {
delete newValues.membership;
}
this.profileService.updateAccount(newValues).subscribe(
() => { this.router.navigate(['/']); }
);

View File

@ -44,7 +44,7 @@ import { EditComponent } from './pages/edit/edit.component';
import { environment} from '../../../environments/environment';
import { LoginComponent } from './components/cards/login/login.component';
import { MembershipComponent } from './components/cards/membership/membership.component';
import { MembershipOptionsComponent } from './components/controls/membership-options/membership-options.component';
import { MembershipCancelConfirmComponent } from './components/modals/membership-cancel-confirm/membership-cancel-confirm.component';
import { MembershipStepComponent } from './components/views/membership-step/membership-step.component';
import { NewComponent } from './pages/new/new.component';
import { PaymentComponent } from './components/views/payment/payment.component';
@ -66,12 +66,12 @@ import { VerifyEmailComponent } from './pages/verify-email/verify-email.componen
EditComponent,
LoginComponent,
MembershipComponent,
MembershipCancelConfirmComponent,
// Profile add (i.e. new account)
MembershipStepComponent,
NewComponent,
UsernameStepComponent,
// Stuff used in both edit and add components
MembershipOptionsComponent,
PaymentComponent,
VerifyCardDialogComponent,
VerifyEmailComponent

View File

@ -18,7 +18,7 @@ and limitations under the License.
import { City } from '@account/models/city.model';
import { Country } from '@account/models/country.model';
import { PantacorConfig } from '@account/models/pantacorConfig.model';
import { PantacorConfig } from '@account/models/pantacor-config.model';
import { Region } from '@account/models/region.model';
import { Timezone } from '@account/models/timezone.model';
import { WakeWord } from '@account/models/wake-word.model';
@ -40,5 +40,4 @@ export interface Device {
voice: Voice;
wakeWord: WakeWord;
pantacorConfig: PantacorConfig;
pantacorUpdateId: string;
}

View File

@ -17,8 +17,8 @@ and limitations under the License.
***************************************************************************** */
export interface MembershipUpdate {
action: string;
membershipType?: string;
newMembership: boolean;
paymentMethod?: string;
paymentToken?: string;
}

View File

@ -20,6 +20,7 @@ export interface PantacorConfig {
autoUpdate: boolean;
ipAddress: string;
pantacorId: string;
deploymentId: string;
sshPublicKey: string;
releaseChannel: string;
}

View File

@ -140,3 +140,4 @@ $mycroft-theme: mat.define-light-theme((
@include mat.stepper-theme($mycroft-theme);
@include mat.tabs-theme($mycroft-theme);
@include mat.toolbar-theme($mycroft-theme);
@include mat.tooltip-theme($mycroft-theme);