Add cart view
This commit is contained in:
@@ -12,6 +12,7 @@ import { SocketIoConfig, SocketIoModule } from 'ngx-socket-io';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { AppRoutingModule } from 'src/routes';
|
||||
import { NotFoundComponent } from './not-found/not-found.component';
|
||||
import { CartComponent } from './cart/cart.component';
|
||||
|
||||
const config: SocketIoConfig = { url: 'http://localhost:2009', options: {} };
|
||||
|
||||
@@ -22,7 +23,8 @@ const config: SocketIoConfig = { url: 'http://localhost:2009', options: {} };
|
||||
PaymentComponent,
|
||||
PayComponent,
|
||||
HelloComponent,
|
||||
NotFoundComponent
|
||||
NotFoundComponent,
|
||||
CartComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getCurrencySymbol } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Socket } from 'ngx-socket-io';
|
||||
@@ -26,8 +27,9 @@ export interface ICart {
|
||||
}
|
||||
|
||||
export interface IPaymentMethod {
|
||||
method: any;
|
||||
method: CryptoUnits;
|
||||
amount: number;
|
||||
exRate: number;
|
||||
}
|
||||
|
||||
export enum PaymentStatus {
|
||||
@@ -43,19 +45,20 @@ export enum PaymentStatus {
|
||||
export interface IInvoice {
|
||||
selector: string;
|
||||
paymentMethods: IPaymentMethod[];
|
||||
receiveAddress: string;
|
||||
paidWith?: CryptoUnits;
|
||||
paid?: number;
|
||||
paymentMethod?: CryptoUnits;
|
||||
receiveAddress?: string;
|
||||
transcationHash?: string;
|
||||
confirmation?: number;
|
||||
cart?: ICart[];
|
||||
totalPrice?: number;
|
||||
currency: string;
|
||||
dueBy: number;
|
||||
dueBy: Date;
|
||||
status?: PaymentStatus;
|
||||
email?: string;
|
||||
successUrl: string;
|
||||
cancelUrl: string;
|
||||
createdAt?: number;
|
||||
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
@@ -70,9 +73,8 @@ export class BackendService {
|
||||
selector: '',
|
||||
paymentMethods: [],
|
||||
receiveAddress: '',
|
||||
paid: 0,
|
||||
currency: 'USD',
|
||||
dueBy: Date.now(),
|
||||
dueBy: new Date(),
|
||||
successUrl: '',
|
||||
cancelUrl: ''
|
||||
};
|
||||
@@ -102,6 +104,9 @@ export class BackendService {
|
||||
return this.socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to the real-time status of the selected invoice.
|
||||
*/
|
||||
subscribeTo(selector: string): void {
|
||||
this.socket.on('subscribe', (status: boolean) => {
|
||||
if (status) {
|
||||
@@ -118,12 +123,18 @@ export class BackendService {
|
||||
this.socket.emit('subscribe', { selector });
|
||||
}
|
||||
|
||||
/**
|
||||
* This will update the current invoice
|
||||
*/
|
||||
updateInvoice(): void {
|
||||
if (this.invoice !== undefined || this.invoice !== null) {
|
||||
this.setInvoice(this.invoice.selector);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will set the current selected invoice by the `selector`.
|
||||
*/
|
||||
setInvoice(selector: string): Promise<IInvoice> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (selector === undefined || selector === 'undefined' || selector === '') {
|
||||
@@ -144,6 +155,9 @@ export class BackendService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This will notify the backend that the user just cancelled the payment.
|
||||
*/
|
||||
cancelInvoice(): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (this.invoice.selector === '') {
|
||||
@@ -162,6 +176,9 @@ export class BackendService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This will set the payment method of the selected invoice.
|
||||
*/
|
||||
setPaymentMethod(method: CryptoUnits): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (this.invoice === null) { reject('Invoice is not set!'); return; }
|
||||
@@ -170,12 +187,20 @@ export class BackendService {
|
||||
responseType: 'json'
|
||||
}).toPromise().then(() => {
|
||||
this.setInvoice(this.invoice.selector);
|
||||
resolve();
|
||||
}).catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
currencyPrefix(): string {
|
||||
return getCurrencySymbol(this.invoice.currency, 'narrow');
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used if the socket connection is broken or as initial call.
|
||||
*/
|
||||
getConfirmation(): Promise<number> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (this.invoice === null || this.invoice.status !== PaymentStatus.UNCONFIRMED) {
|
||||
@@ -197,7 +222,7 @@ export class BackendService {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Path to icon
|
||||
* @returns Path to icon in assets folder
|
||||
*/
|
||||
getIcon(unit: CryptoUnits): string {
|
||||
switch (unit) {
|
||||
@@ -226,12 +251,30 @@ export class BackendService {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The price to pay by cryptocurrency;
|
||||
*/
|
||||
getAmount(): string | undefined {
|
||||
return this.invoice?.paymentMethods.find(item => {
|
||||
return item.method === CryptoUnits.BITCOIN;
|
||||
return item.method === this.invoice.paymentMethod;
|
||||
})?.amount.toFixed(8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the price in crypto of a specifc product.
|
||||
* @param prodcut Index of product in cart
|
||||
*/
|
||||
calculateCryptoPrice(productNr: number): number {
|
||||
if (this.invoice.cart === undefined) return 0;
|
||||
if (this.invoice.paymentMethod === undefined) return 0;
|
||||
|
||||
const product = this.invoice.cart[productNr];
|
||||
const exRate = this.invoice.paymentMethods.find(method => { return method.method === this.invoice.paymentMethod })?.exRate;
|
||||
if (exRate === undefined) return 0;
|
||||
|
||||
return product.quantity * product.price / exRate;
|
||||
}
|
||||
|
||||
getStatus(): string {
|
||||
switch (this.invoice?.status) {
|
||||
case PaymentStatus.PENDING:
|
||||
|
||||
50
src/app/cart/cart.component.css
Normal file
50
src/app/cart/cart.component.css
Normal file
@@ -0,0 +1,50 @@
|
||||
.cart {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
background-color: #1c1c1c;
|
||||
border-radius: 8px;
|
||||
transform: translateY(-8px);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.cart ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
height: 359px;
|
||||
}
|
||||
|
||||
.cart ul li {
|
||||
display: grid;
|
||||
padding: 1rem;
|
||||
grid-template-columns: 64px 1fr auto;
|
||||
grid-column-gap: 1rem;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.cart ul li:nth-child(even) {
|
||||
background-color: #292929;
|
||||
}
|
||||
|
||||
.image {
|
||||
height: 64px;
|
||||
width: 64px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 11pt;
|
||||
width: 80%;
|
||||
margin: auto 0;
|
||||
line-break: loose;
|
||||
}
|
||||
|
||||
.price {
|
||||
text-align: right;
|
||||
margin: auto;
|
||||
}
|
||||
10
src/app/cart/cart.component.html
Normal file
10
src/app/cart/cart.component.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<div class="cart" xyz="stagger-0.5 fade-100% down-1 ease-ease" >
|
||||
<ul>
|
||||
<li *ngFor="let item of this.backend.invoice.cart;let indexOfelement=index;" [ngClass]="{'xyz-in': this.state.showCart.value, 'xyz-out': !this.state.showCart.value}">
|
||||
<img [src]="item.image" class="image">
|
||||
<h5 class="name">{{ item.name }}</h5>
|
||||
<span class="price"><b>{{ item.price.toFixed(2) }} {{ this.backend.currencyPrefix() }}</b><br>
|
||||
{{ this.backend.calculateCryptoPrice(indexOfelement).toFixed(8) }} {{ this.backend.invoice.paymentMethod }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
25
src/app/cart/cart.component.spec.ts
Normal file
25
src/app/cart/cart.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CartComponent } from './cart.component';
|
||||
|
||||
describe('CartComponent', () => {
|
||||
let component: CartComponent;
|
||||
let fixture: ComponentFixture<CartComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ CartComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CartComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
20
src/app/cart/cart.component.ts
Normal file
20
src/app/cart/cart.component.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { BackendService } from '../backend.service';
|
||||
import { StateService } from '../state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-cart',
|
||||
templateUrl: './cart.component.html',
|
||||
styleUrls: ['./cart.component.css']
|
||||
})
|
||||
export class CartComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
public backend: BackendService,
|
||||
public state: StateService
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
.header {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-template-columns: auto 1fr auto 1fr auto;
|
||||
grid-template-rows: 1fr;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #fff;
|
||||
@@ -12,23 +13,43 @@
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.header img {
|
||||
height: 3rem;
|
||||
.header .logo {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
grid-column: 1;
|
||||
grid-column: 3;
|
||||
}
|
||||
|
||||
.header a {
|
||||
.header .logo img {
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
/* Cancel button */
|
||||
.cancel {
|
||||
color: red;
|
||||
border: 1px solid red;
|
||||
border-block-end-width: 1px;
|
||||
border-radius: 5px;
|
||||
padding: 0.8rem;
|
||||
margin: auto 0;
|
||||
height: fit-content;
|
||||
float: right;
|
||||
margin: auto;
|
||||
cursor: pointer;
|
||||
grid-column: 2;
|
||||
width: fit-content !important;
|
||||
grid-column: 5;
|
||||
transition: .2s ease;
|
||||
}
|
||||
|
||||
#cart {
|
||||
display: flex;
|
||||
grid-column: 1;
|
||||
vertical-align: middle;
|
||||
margin: auto auto;
|
||||
padding-left: 1rem;
|
||||
cursor: pointer;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #000;
|
||||
padding: 0.8rem;
|
||||
}
|
||||
#cart span {
|
||||
margin: auto auto;
|
||||
padding-left: .5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -1,4 +1,24 @@
|
||||
<div class="header">
|
||||
<div id="cart" *ngIf="this.backend!.invoice!.status! > 0" (click)="this.state.toggleCart()">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg" *ngIf="!this.state.showCart.value" class="icon icon-tabler icon-tabler-shopping-cart"
|
||||
width="32" height="32" viewBox="0 0 24 24" stroke-width="1.5" stroke="#2c3e50" title="Shopping cart"
|
||||
fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<circle cx="9" cy="19" r="2" />
|
||||
<circle cx="17" cy="19" r="2" />
|
||||
<path d="M3 3h2l2 12a3 3 0 0 0 3 2h7a3 3 0 0 0 3 -2l1 -7h-15.2" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" *ngIf="this.state.showCart.value" class="icon icon-tabler icon-tabler-x"
|
||||
width="32" height="32" viewBox="0 0 24 24" stroke-width="1.5" stroke="#2c3e50" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<line x1="18" y1="6" x2="6" y2="18" />
|
||||
<line x1="6" y1="6" x2="18" y2="18" />
|
||||
</svg>
|
||||
<span>Cart</span>
|
||||
</div>
|
||||
<a href="https://librepay.me" target="_blank" class="logo">
|
||||
<img src="assets/logo.svg">
|
||||
<a *ngIf="this.backend.isInvoicePending()" (click)="cancel()" [style]="cancelProgressStyle">{{ startCancelling ? 'Are you sure?' : 'Cancel payment'}}</a>
|
||||
</a>
|
||||
<a class="cancel" *ngIf="this.backend.isInvoicePending()" (click)="cancel()" [style]="cancelProgressStyle">{{ startCancelling ? 'Are you sure?' : 'Cancel payment'}}</a>
|
||||
</div>
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { BackendService } from '../backend.service';
|
||||
import { StateService } from '../state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
@@ -12,7 +13,10 @@ export class HeaderComponent implements OnInit {
|
||||
cancelProgress = 0;
|
||||
cancelProgressStyle = ""; // This is the style of the cancel button
|
||||
|
||||
constructor(public backend: BackendService) { }
|
||||
constructor(
|
||||
public backend: BackendService,
|
||||
public state: StateService
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
@@ -21,9 +25,10 @@ export class HeaderComponent implements OnInit {
|
||||
if (!this.startCancelling) {
|
||||
this.startCancelling = true;
|
||||
const animation = setInterval(() => {
|
||||
this.cancelProgress += 0.3;
|
||||
this.cancelProgress += 0.5;
|
||||
this.cancelProgressStyle = `
|
||||
background-image: linear-gradient(90deg, rgba(255,120,120,0.3) ${this.cancelProgress.toFixed(1)}%, rgba(255,255,255,0) ${this.cancelProgress.toFixed(1)}%);
|
||||
margin-left: 16px;
|
||||
transform: scale(1.1);
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { BackendService } from '../backend.service';
|
||||
import { StateService } from '../state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pay',
|
||||
@@ -8,7 +9,10 @@ import { BackendService } from '../backend.service';
|
||||
})
|
||||
export class PayComponent implements OnInit {
|
||||
|
||||
constructor(public backend: BackendService) { }
|
||||
constructor(
|
||||
public backend: BackendService,
|
||||
public state: StateService
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
-->
|
||||
<div class="payment request" xyz="stagger-2 fade-100% down-1" *ngIf="this.backend.isInvoiceRequested()">
|
||||
<h3 id="title">Choose your<br>payment method</h3>
|
||||
<p id="price">{{ this.backend.invoice.totalPrice!.toFixed(2) }} {{ currencyPrefix() }}</p>
|
||||
<p id="price">{{ this.backend.invoice.totalPrice!.toFixed(2) }} {{ this.backend.currencyPrefix() }}</p>
|
||||
|
||||
<ul id="list">
|
||||
<li class="xyz-in" *ngFor="let coin of this.backend.invoice!!.paymentMethods" (click)="chooseMethod(coin.method)">
|
||||
@@ -19,10 +19,11 @@
|
||||
<!--
|
||||
Main view
|
||||
-->
|
||||
<div class="payment main" xyz="stagger-2 fade-100% down-1 ease-ease" *ngIf="!this.backend.isInvoiceRequested() && ready"
|
||||
<div class="payment main" xyz="stagger-0.5 fade-100% down-1 ease-ease"
|
||||
*ngIf="!this.backend.isInvoiceRequested() && ready && !hideMain"
|
||||
[ngClass]="{invalid: status === 'Expired' || status === 'Paid too little' || status === 'Cancelled by user'}">
|
||||
|
||||
<div class="qrWrapper xyz-in">
|
||||
<div class="qrWrapper" [ngClass]="{'xyz-in': !this.state.showCart.value, 'xyz-out': this.state.showCart.value}">
|
||||
<div class="qr">
|
||||
<img src="assets/Bitcoin.svg">
|
||||
<qrcode
|
||||
@@ -37,13 +38,13 @@
|
||||
|
||||
<div class="data">
|
||||
<!-- Payment data -->
|
||||
<span id="target" class="xyz-in">Send to
|
||||
<span id="target" [ngClass]="{'xyz-in': !this.state.showCart.value, 'xyz-out': this.state.showCart.value}">Send to
|
||||
<h3>{{ this.backend.invoice?.receiveAddress }}</h3>
|
||||
</span>
|
||||
<span id="amount" class="xyz-in">Amount
|
||||
<h3>{{ this.backend.getAmount() }} BTC <span class="price"> | {{ this.backend.invoice.totalPrice!.toFixed(2) }} {{ currencyPrefix() }}</span></h3>
|
||||
<span id="amount" [ngClass]="{'xyz-in': !this.state.showCart.value, 'xyz-out': this.state.showCart.value}">Amount
|
||||
<h3>{{ this.backend.getAmount() }} BTC <span class="price"> | {{ this.backend.invoice.totalPrice!.toFixed(2) }} {{ this.backend.currencyPrefix() }}</span></h3>
|
||||
</span>
|
||||
<span id="status" class="xyz-in">Status
|
||||
<span id="status" [ngClass]="{'xyz-in': !this.state.showCart.value, 'xyz-out': this.state.showCart.value}">Status
|
||||
<h3>
|
||||
{{ status }}
|
||||
<div class="loader" *ngIf="this.status === 'Unconfirmed'">
|
||||
@@ -66,6 +67,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Cart view
|
||||
-->
|
||||
<app-cart id="cart" [ngStyle]="{'display': !hideMain ? 'none' : 'block'}"></app-cart>
|
||||
|
||||
<!--
|
||||
Alerts
|
||||
-->
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { getCurrencySymbol } from '@angular/common';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { BackendService, CryptoUnits } from '../backend.service';
|
||||
import { StateService } from '../state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-payment',
|
||||
@@ -13,15 +13,21 @@ export class PaymentComponent implements OnInit {
|
||||
|
||||
paymentSelector = '';
|
||||
confirmations = 0;
|
||||
choosenPaymentMethod = CryptoUnits.BITCOIN;
|
||||
status: string;
|
||||
ready = false;
|
||||
|
||||
// XYZ class (will be xyz-out if cart is shown for example)
|
||||
xyzClass: string;
|
||||
hideMain: boolean;
|
||||
|
||||
constructor(
|
||||
public backend: BackendService,
|
||||
public state: StateService,
|
||||
private route: ActivatedRoute
|
||||
) {
|
||||
this.status = this.backend.getStatus();
|
||||
this.hideMain = false;
|
||||
this.xyzClass = 'xyz-in';
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -31,6 +37,20 @@ export class PaymentComponent implements OnInit {
|
||||
this.get();
|
||||
});
|
||||
|
||||
this.state.showCart.subscribe(cartStatus => {
|
||||
if (cartStatus) {
|
||||
this.xyzClass = 'xyz-out';
|
||||
setTimeout(() => {
|
||||
this.hideMain = true;
|
||||
}, 700);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.hideMain = false;
|
||||
this.xyzClass = 'xyz-in';
|
||||
}, 600);
|
||||
}
|
||||
})
|
||||
|
||||
this.backend.invoiceUpdate.subscribe(newInvoice => {
|
||||
this.status = this.backend.getStatus();
|
||||
});
|
||||
@@ -40,10 +60,6 @@ export class PaymentComponent implements OnInit {
|
||||
this.backend.setPaymentMethod(coin);
|
||||
}
|
||||
|
||||
currencyPrefix(): string {
|
||||
return getCurrencySymbol(this.backend.invoice.currency, 'narrow');
|
||||
}
|
||||
|
||||
async get(): Promise<void> {
|
||||
await this.backend.setInvoice(this.paymentSelector);
|
||||
this.backend.getConfirmation().catch();
|
||||
|
||||
16
src/app/state.service.spec.ts
Normal file
16
src/app/state.service.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { StateService } from './state.service';
|
||||
|
||||
describe('StateService', () => {
|
||||
let service: StateService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(StateService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
26
src/app/state.service.ts
Normal file
26
src/app/state.service.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { BackendService, PaymentStatus } from './backend.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
/**
|
||||
* The state service is responsible to exchange data between components.
|
||||
*/
|
||||
export class StateService {
|
||||
|
||||
showCart: BehaviorSubject<boolean>;
|
||||
|
||||
constructor(private backend: BackendService) {
|
||||
this.showCart = new BehaviorSubject<boolean>(false);
|
||||
|
||||
this.backend.invoiceUpdate.subscribe(invoice => {
|
||||
this.showCart.next(false); // Hide cart if status changes
|
||||
});
|
||||
}
|
||||
|
||||
toggleCart() {
|
||||
this.showCart.next(!this.showCart.value);
|
||||
}
|
||||
}
|
||||
@@ -12,3 +12,13 @@ body, html {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
background-color: transparent;
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(255, 255, 255);
|
||||
border-radius: 8px;
|
||||
}
|
||||
Reference in New Issue
Block a user