Change Theme, Animations, and Loading Spinners
This commit is contained in:
parent
3f0a50d2bd
commit
f3ef7e326e
@ -30,7 +30,9 @@
|
||||
"styles": [
|
||||
"./node_modules/font-awesome/css/font-awesome.css",
|
||||
"./node_modules/ngx-toastr/toastr.css",
|
||||
"src/styles.scss"
|
||||
"src/styles.scss",
|
||||
"./node_modules/ngx-spinner/animations/ball-clip-rotate-multiple.css",
|
||||
"./node_modules/bootswatch/dist/cyborg/bootstrap.min.css"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
|
32
client/package-lock.json
generated
32
client/package-lock.json
generated
@ -17,8 +17,10 @@
|
||||
"@angular/platform-browser-dynamic": "~13.3.0",
|
||||
"@angular/router": "~13.3.0",
|
||||
"bootstrap": "^5.1.3",
|
||||
"bootswatch": "^5.1.3",
|
||||
"font-awesome": "^4.7.0",
|
||||
"ngx-bootstrap": "^8.0.0",
|
||||
"ngx-spinner": "^13.1.1",
|
||||
"ngx-toastr": "^14.3.0",
|
||||
"rxjs": "~7.5.0",
|
||||
"tslib": "^2.3.0",
|
||||
@ -3511,6 +3513,11 @@
|
||||
"@popperjs/core": "^2.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/bootswatch": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/bootswatch/-/bootswatch-5.1.3.tgz",
|
||||
"integrity": "sha512-NmZFN6rOCoXWQ/PkzmD8FFWDe24kocX9OXWHNVaLxVVnpqpAzEbMFsf8bAfKwVtpNXibasZCzv09B5fLieAh2g=="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@ -7976,6 +7983,18 @@
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-spinner": {
|
||||
"version": "13.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ngx-spinner/-/ngx-spinner-13.1.1.tgz",
|
||||
"integrity": "sha512-6aJz4KxIsBrlQckJxcM3CEvMcYbZoSDnQZPu0F/ZYAYunbBOTb9iydw0Gjczg9moum1VURWjX5dTVKmFo85c2Q==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^13.0.0",
|
||||
"@angular/core": "^13.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-toastr": {
|
||||
"version": "14.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-14.3.0.tgz",
|
||||
@ -14317,6 +14336,11 @@
|
||||
"integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==",
|
||||
"requires": {}
|
||||
},
|
||||
"bootswatch": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/bootswatch/-/bootswatch-5.1.3.tgz",
|
||||
"integrity": "sha512-NmZFN6rOCoXWQ/PkzmD8FFWDe24kocX9OXWHNVaLxVVnpqpAzEbMFsf8bAfKwVtpNXibasZCzv09B5fLieAh2g=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@ -17564,6 +17588,14 @@
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"ngx-spinner": {
|
||||
"version": "13.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ngx-spinner/-/ngx-spinner-13.1.1.tgz",
|
||||
"integrity": "sha512-6aJz4KxIsBrlQckJxcM3CEvMcYbZoSDnQZPu0F/ZYAYunbBOTb9iydw0Gjczg9moum1VURWjX5dTVKmFo85c2Q==",
|
||||
"requires": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"ngx-toastr": {
|
||||
"version": "14.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-14.3.0.tgz",
|
||||
|
@ -19,8 +19,10 @@
|
||||
"@angular/platform-browser-dynamic": "~13.3.0",
|
||||
"@angular/router": "~13.3.0",
|
||||
"bootstrap": "^5.1.3",
|
||||
"bootswatch": "^5.1.3",
|
||||
"font-awesome": "^4.7.0",
|
||||
"ngx-bootstrap": "^8.0.0",
|
||||
"ngx-spinner": "^13.1.1",
|
||||
"ngx-toastr": "^14.3.0",
|
||||
"rxjs": "~7.5.0",
|
||||
"tslib": "^2.3.0",
|
||||
|
@ -1,5 +1,6 @@
|
||||
<ngx-spinner>
|
||||
<h3>Loading...</h3>
|
||||
</ngx-spinner>
|
||||
<app-nav-bar></app-nav-bar>
|
||||
<app-section-header></app-section-header>
|
||||
<div class="container">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
<router-outlet></router-outlet>
|
||||
|
@ -9,6 +9,8 @@ import { CoreModule } from './core/core.module';
|
||||
import { ShopModule } from './shop/shop.module';
|
||||
import { HomeModule } from './home/home.module';
|
||||
import { ErrorInterceptor } from './core/interceptors/error.interceptor';
|
||||
import { NgxSpinnerModule } from 'ngx-spinner';
|
||||
import { LoadingInterceptor } from './core/interceptors/loading.interceptor';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -20,10 +22,12 @@ import { ErrorInterceptor } from './core/interceptors/error.interceptor';
|
||||
BrowserAnimationsModule,
|
||||
HttpClientModule,
|
||||
CoreModule,
|
||||
HomeModule
|
||||
HomeModule,
|
||||
NgxSpinnerModule
|
||||
],
|
||||
providers: [
|
||||
{provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true}
|
||||
{provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true},
|
||||
{provide: HTTP_INTERCEPTORS, useClass: LoadingInterceptor, multi: true}
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
HttpEvent,
|
||||
HttpInterceptor
|
||||
} from '@angular/common/http';
|
||||
import { catchError, Observable, throwError } from 'rxjs';
|
||||
import { catchError, delay, Observable, throwError } from 'rxjs';
|
||||
import { NavigationExtras, Router } from '@angular/router';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
|
||||
|
20
client/src/app/core/interceptors/loading.interceptor.ts
Normal file
20
client/src/app/core/interceptors/loading.interceptor.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { delay, finalize, Observable } from "rxjs";
|
||||
import { BusyService } from "../services/busy.service";
|
||||
|
||||
@Injectable()
|
||||
export class LoadingInterceptor implements HttpInterceptor {
|
||||
constructor(private busyService: BusyService) {}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
this.busyService.busy();
|
||||
return next.handle(req).pipe(
|
||||
delay(1000),
|
||||
finalize(()=> {
|
||||
this.busyService.idle();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
<div class="d-flex flex-column flex-md-row align-items-center justify-content-between p-3 px-md-4 mb-3 bg-white border-bottom shadown-sm fixed-top">
|
||||
<div class="d-flex flex-column flex-md-row align-items-center justify-content-between p-3 px-md-4 mb-3 border-bottom border-dark fixed-top" style="background-color: #0d0d0e;">
|
||||
<img class="logo" src="/assets/images/logo.png" style="max-height: 70px;" alt="logo" routerLink="/">
|
||||
<nav class="me-3 my-md-0 mr-md-3 text-uppercase" style="font-size: larger;">
|
||||
<a class="me-3 py-2" [routerLink]="['/']" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">Home</a>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<ng-container *ngIf="(breadcrumb$ | async) as breadcrumbs">
|
||||
<section *ngIf="breadcrumbs.length > 0 && breadcrumbs[breadcrumbs.length-1].label !== 'Home'" class="py-5" style="margin-top: 105px; background-color:#f5f5f5;">
|
||||
<section *ngIf="breadcrumbs.length > 0 && breadcrumbs[breadcrumbs.length-1].label !== 'Home'" class="py-5" style="margin-top: 105px;">
|
||||
<div class="container">
|
||||
<div class="row d-flex align-item-center">
|
||||
<div class="col-9">
|
||||
<h1>{{breadcrumbs.length > 0 && breadcrumbs[breadcrumbs.length-1].label | titlecase}}</h1>
|
||||
<h1 class="text-dark">{{breadcrumbs.length > 0 && breadcrumbs[breadcrumbs.length-1].label | titlecase}}</h1>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<xng-breadcrumb></xng-breadcrumb>
|
||||
|
30
client/src/app/core/services/busy.service.ts
Normal file
30
client/src/app/core/services/busy.service.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { NgxSpinnerService } from 'ngx-spinner';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class BusyService {
|
||||
busyRequestCount = 0;
|
||||
|
||||
constructor(private spinnerService: NgxSpinnerService) { }
|
||||
|
||||
busy(){
|
||||
this.busyRequestCount++;
|
||||
this.spinnerService.show(undefined, {
|
||||
type: 'ball-clip-rotate-multiple',
|
||||
size: 'large',
|
||||
bdColor: 'rgba(0, 0, 0, 0.8)',
|
||||
color: '#dddddd'
|
||||
});
|
||||
}
|
||||
|
||||
idle(){
|
||||
this.busyRequestCount--;
|
||||
if (this.busyRequestCount <= 0){
|
||||
this.busyRequestCount = 0;
|
||||
this.spinnerService.hide();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1 +1,16 @@
|
||||
<p>home works!</p>
|
||||
<carousel>
|
||||
<slide>
|
||||
<img src="assets/images/hero1.jpg" alt="first slide" style="display: block; width: 100%">
|
||||
</slide>
|
||||
<slide>
|
||||
<img src="assets/images/hero2.jpg" alt="first slide" style="display: block; width: 100%">
|
||||
</slide>
|
||||
<slide>
|
||||
<img src="assets/images/hero3.jpg" alt="first slide" style="display: block; width: 100%">
|
||||
</slide>
|
||||
</carousel>
|
||||
<section class="featured">
|
||||
<div class="d-flex justify-content-center pt-5">
|
||||
<h1>Welcome to our Shop!</h1>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -0,0 +1,9 @@
|
||||
.featured {
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
height: 600px;
|
||||
width: 100%;
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { HomeComponent } from './home.component';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
|
||||
|
||||
@ -9,7 +10,8 @@ import { HomeComponent } from './home.component';
|
||||
HomeComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule
|
||||
CommonModule,
|
||||
SharedModule
|
||||
],
|
||||
exports: [HomeComponent]
|
||||
})
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { PaginationModule } from 'ngx-bootstrap/pagination';
|
||||
import { CarouselModule } from 'ngx-bootstrap/carousel';
|
||||
import { PagingHeaderComponent } from './components/paging-header/paging-header.component';
|
||||
import { PagerComponent } from './components/pager/pager.component';
|
||||
|
||||
@ -13,12 +14,14 @@ import { PagerComponent } from './components/pager/pager.component';
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
PaginationModule.forRoot()
|
||||
PaginationModule.forRoot(),
|
||||
CarouselModule.forRoot()
|
||||
],
|
||||
exports: [
|
||||
PaginationModule,
|
||||
PagingHeaderComponent,
|
||||
PagerComponent
|
||||
PagerComponent,
|
||||
CarouselModule
|
||||
]
|
||||
})
|
||||
export class SharedModule { }
|
||||
|
@ -1,22 +1,23 @@
|
||||
<div class="row" *ngIf="product">
|
||||
<div class="col-6">
|
||||
<img src="{{product.pictureUrl}}" alt="{{product.name}}" class="img-fluid w-100">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<h3>{{product.name}}</h3>
|
||||
<p>{{product.price | currency}}</p>
|
||||
<div class="d-flex justify-content-start align-items-center">
|
||||
<i class="fa fa-minus-circle text-warning me-2" style="cursor: pointer; font-size: 2em"></i>
|
||||
<span class="font-weight-bold" style="font-size: 1.5em;">2</span>
|
||||
<i class="fa fa-plus-circle text-warning mx-2" style="cursor: pointer; font-size: 2em"></i>
|
||||
<button class="btn btn-outline-secondary btn-lg ms-4">Ad to Cart</button>
|
||||
<div class="container mt-5">
|
||||
<div class="row" *ngIf="product">
|
||||
<div class="col-6">
|
||||
<img src="{{product.pictureUrl}}" alt="{{product.name}}" class="img-fluid w-100">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<div class="col-12 ms-3">
|
||||
<h4>Description</h4>
|
||||
<p>{{product.description}}</p>
|
||||
<div class="col-6">
|
||||
<h3>{{product.name}}</h3>
|
||||
<p style="font-size: 2em;">{{product.price | currency}}</p>
|
||||
<div class="d-flex justify-content-start align-items-center">
|
||||
<i class="fa fa-minus-circle text-warning me-2" style="cursor: pointer; font-size: 2em"></i>
|
||||
<span class="font-weight-bold" style="font-size: 1.5em;">2</span>
|
||||
<i class="fa fa-plus-circle text-warning mx-2" style="cursor: pointer; font-size: 2em"></i>
|
||||
<button class="btn btn-outline-secondary btn-lg ms-4">Ad to Cart</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<div class="col-12 ms-3">
|
||||
<h4>Description</h4>
|
||||
<p>{{product.description}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -12,7 +12,9 @@ import { ShopService } from '../shop.service';
|
||||
export class ProductDetailsComponent implements OnInit {
|
||||
product: IProduct;
|
||||
|
||||
constructor(private shopService: ShopService, private activatedRoute: ActivatedRoute, private bcService: BreadcrumbService) { }
|
||||
constructor(private shopService: ShopService, private activatedRoute: ActivatedRoute, private bcService: BreadcrumbService) {
|
||||
this.bcService.set('@productDetails', ' ');
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadProduct();
|
||||
|
@ -1,13 +1,15 @@
|
||||
<div class="card h-100 shadow-sm">
|
||||
<img src="{{product.pictureUrl}}" alt="{{product.name}}" class="img-fluid bg-light">
|
||||
<div class="d-flex align-items-center justify-content-center hover-overlay">
|
||||
<button type="button" class="btn btn-sm btn-primary fa fa-shopping-cart me-2"></button>
|
||||
<button routerLink="/shop/{{product.id}}" type="button" class="btn btn-sm btn-primary">View</button>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column">
|
||||
<a routerLink="/shop/{{product.id}}">
|
||||
<h6 class="text-uppercase">{{product.name}}</h6>
|
||||
</a>
|
||||
<span class="mb-2">{{product.price | currency}}</span>
|
||||
</div>
|
||||
<div class="image position-relative" style="cursor: pointer;">
|
||||
<img src="{{product.pictureUrl}}" alt="{{product.name}}" class="img-fluid bg-light">
|
||||
<div class="d-flex align-items-center justify-content-center hover-overlay">
|
||||
<button type="button" class="btn btn-sm btn-primary fa fa-shopping-cart me-2"></button>
|
||||
<button routerLink="/shop/{{product.id}}" type="button" class="btn btn-sm btn-primary">View</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column">
|
||||
<a routerLink="/shop/{{product.id}}">
|
||||
<h6 class="text-uppercase">{{product.name}}</h6>
|
||||
</a>
|
||||
<span class="mb-2">{{product.price | currency}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,3 +2,36 @@
|
||||
width: 30%;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.image :hover {
|
||||
opacity: 1;
|
||||
& button {
|
||||
transform: none;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.hover-overlay {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgba($color: rgb(0, 0, 0), $alpha: 0.5);
|
||||
opacity: 0;
|
||||
transition: all 0.5s;
|
||||
|
||||
& button {
|
||||
z-index: 1000;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
& button:first-of-type {
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
|
||||
& button:last-of-type {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,32 +1,34 @@
|
||||
<div class="container">
|
||||
<div class="row mt-5">
|
||||
<section class="col-3">
|
||||
<h5 class="text-warning ml-3">Sort</h5>
|
||||
<select class="form-select mb-3" style="width: 100%;" (change)="onSortSelected($event.target.value)">
|
||||
<option *ngFor="let sort of sortOptions"
|
||||
[value]="sort.value"
|
||||
>
|
||||
{{sort.name}}
|
||||
</option>
|
||||
</select>
|
||||
<h5 class="text-warning ml-3">Brands</h5>
|
||||
<ul class="list-group my-3">
|
||||
<li class="list-group-item"
|
||||
*ngFor="let brand of brands"
|
||||
[class.active]="brand.id === this.shopParams.brandId"
|
||||
[value]="brand.id"
|
||||
(click)="onBrandSelected(brand.id)"
|
||||
>{{brand.name}}</li>
|
||||
</ul>
|
||||
<h5 class="text-warning ml-3">Type</h5>
|
||||
<ul class="list-group my-3">
|
||||
<li class="list-group-item"
|
||||
*ngFor="let type of productType"
|
||||
[class.active]="type.id === this.shopParams.typeId"
|
||||
[value]="type.id"
|
||||
(click)="onTypeSelected(type.id)"
|
||||
>{{type.name}}</li>
|
||||
</ul>
|
||||
<ng-container *ngIf="productType && brands">
|
||||
<h5 class="text-warning ml-3">Sort</h5>
|
||||
<select class="form-select mb-3" style="width: 100%;" (change)="onSortSelected($event.target.value)">
|
||||
<option *ngFor="let sort of sortOptions"
|
||||
[value]="sort.value"
|
||||
>
|
||||
{{sort.name}}
|
||||
</option>
|
||||
</select>
|
||||
<h5 class="text-warning ml-3">Brands</h5>
|
||||
<ul class="list-group my-3">
|
||||
<li class="list-group-item" style="cursor: pointer;"
|
||||
*ngFor="let brand of brands"
|
||||
[class.active]="brand.id === this.shopParams.brandId"
|
||||
[value]="brand.id"
|
||||
(click)="onBrandSelected(brand.id)"
|
||||
>{{brand.name}}</li>
|
||||
</ul>
|
||||
<h5 class="text-warning ml-3">Type</h5>
|
||||
<ul class="list-group my-3">
|
||||
<li class="list-group-item" style="cursor: pointer;"
|
||||
*ngFor="let type of productType"
|
||||
[class.active]="type.id === this.shopParams.typeId"
|
||||
[value]="type.id"
|
||||
(click)="onTypeSelected(type.id)"
|
||||
>{{type.name}}</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
</section>
|
||||
<section class="col-9">
|
||||
<div class="d-flex justify-content-between align-items-center pb-2">
|
||||
@ -35,7 +37,7 @@
|
||||
[pageSize]="this.shopParams.pageSize"
|
||||
[pageNumber]="this.shopParams.pageNumber"
|
||||
></app-paging-header>
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="row g-2 align-items-center" *ngIf="products">
|
||||
<div class="col-sm">
|
||||
<input (keyup.enter)="onSearch()" style="width:300px" #search placeholder="Search" type="text" class="form-control">
|
||||
</div>
|
||||
|
@ -11,7 +11,7 @@ import { ShopService } from './shop.service';
|
||||
styleUrls: ['./shop.component.scss']
|
||||
})
|
||||
export class ShopComponent implements OnInit {
|
||||
@ViewChild('search', {static: true}) searchTerm: ElementRef;
|
||||
@ViewChild('search', {static: false}) searchTerm: ElementRef;
|
||||
products: IProduct[];
|
||||
brands: IBrand[];
|
||||
productType: IType[];
|
||||
|
@ -17,6 +17,11 @@ label.xng-breadcrumb-trail {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
xng-breadcrumb-separator {
|
||||
.xng-breadcrumb-separator {
|
||||
padding: 0 0;
|
||||
color: #343a40;
|
||||
}
|
||||
|
||||
.xng-breadcrumb-link, a.xng-breadcrumb-link {
|
||||
color: #343a40;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user