98 changed files with 3081 additions and 1456 deletions
@ -1,6 +1,6 @@ |
|||||
<!-- <button mat-icon-button> --> |
<!-- <button mat-icon-button> --> |
||||
<div style="display: flex;justify-content:space-around;" class="goldDiv"> |
<div style="display: flex;justify-content:space-around;" class="goldDiv"> |
||||
<img class="w-10" src="assets/images/gold1.png" alt="Logo image"> |
<img class="w-10" src="assets/images/gold1.png" alt="Logo image"> |
||||
<span style="padding-top: 9px;font-size: medium;font-weight: 700;color: #64738b;">+{{gold}}</span> |
<span style="padding-top: 9px;font-size: medium;font-weight: 700;color: #64738b;">{{gold}}</span> |
||||
</div> |
</div> |
||||
<!-- </button> --> |
<!-- </button> --> |
@ -0,0 +1,25 @@ |
|||||
|
export interface Finance { |
||||
|
businessID: string; |
||||
|
gold: number; |
||||
|
} |
||||
|
export interface TesoBusinessDetail { |
||||
|
businessId?: string, |
||||
|
Handle?: string, |
||||
|
businessName?: string, |
||||
|
businessEmail?: string, |
||||
|
businessTin?: string, |
||||
|
businessDescription?: string, |
||||
|
businessCategory?: string, |
||||
|
businessAddress?: string, |
||||
|
businessContact?: string, |
||||
|
businessLogo?: string, |
||||
|
dateOfEst?: Date, |
||||
|
businessDigitalAddress?: string, |
||||
|
businessLatitude?: string, |
||||
|
businessLongitude?: string, |
||||
|
} |
||||
|
|
||||
|
export interface BusinessCategory { |
||||
|
categoryCode: string, |
||||
|
categoryName: string, |
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
import { TesoBusinessDetail } from "./businessModel" |
||||
|
|
||||
|
export interface CouponsModel { |
||||
|
couponID: string, |
||||
|
businessID: string, |
||||
|
targetProduct: string, |
||||
|
type: string, |
||||
|
quantity: number, |
||||
|
lower: number, |
||||
|
condition: string, |
||||
|
upper: number, |
||||
|
numberClaimed: number, |
||||
|
status: string, |
||||
|
expiration: Date, |
||||
|
target?: TesoBusinessDetail, |
||||
|
} |
||||
|
|
||||
|
export interface CouponsType { |
||||
|
typeCode: string; |
||||
|
typeName: string; |
||||
|
} |
@ -0,0 +1,40 @@ |
|||||
|
export interface ProductsModel { |
||||
|
productName: string; |
||||
|
businessID: string; |
||||
|
productDesc: string; |
||||
|
productID: string; |
||||
|
categoryID: string; |
||||
|
unitPrice: number; |
||||
|
productImage: string; |
||||
|
images: ProductImages[]; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
export interface ProductImages { |
||||
|
id: string; |
||||
|
productID: string; |
||||
|
path: string; |
||||
|
} |
||||
|
|
||||
|
export interface ProductCategory { |
||||
|
catCode: string; |
||||
|
catName: string; |
||||
|
} |
||||
|
|
||||
|
export interface DesiredProduct{ |
||||
|
productID:string, |
||||
|
productName:string, |
||||
|
productImage:string, |
||||
|
cost:number, |
||||
|
category:string, |
||||
|
enlisted:boolean, |
||||
|
desires:number |
||||
|
} |
||||
|
|
||||
|
export interface DesiredList{ |
||||
|
mostWishedProduct?: DesiredProduct[], |
||||
|
mostWishedCategory?: DesiredProduct[], |
||||
|
availableProducts?: DesiredProduct[], |
||||
|
availableCategory?: DesiredProduct[], |
||||
|
|
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
export interface TesoUserDetails { |
||||
|
userGuid?: string; |
||||
|
username?: string; |
||||
|
firstname?: string; |
||||
|
surname?: string; |
||||
|
description?: string; |
||||
|
address?: string; |
||||
|
email?: string; |
||||
|
thumbnailDp?: string; |
||||
|
phonenumber?: string; |
||||
|
country?: string; |
||||
|
gender?: string; |
||||
|
dateOfBirth: Date; |
||||
|
} |
@ -1,22 +1,63 @@ |
|||||
import { Component, Inject, OnInit } from '@angular/core'; |
import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; |
||||
import { MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; |
import { MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; |
||||
|
import { tesoMediaWatcherService } from '@teso/services/media-watcher'; |
||||
|
import { TesoBusinessDetail } from 'app/models/businessModel'; |
||||
|
import { CouponsType } from 'app/models/couponsModel'; |
||||
|
import { ProductsModel } from 'app/models/productsModel'; |
||||
|
import { Subject } from 'rxjs'; |
||||
|
import { takeUntil } from 'rxjs/operators'; |
||||
import { BusinessLookUpComponent } from '../../Products/BusinessLookUp/business-look-up.component'; |
import { BusinessLookUpComponent } from '../../Products/BusinessLookUp/business-look-up.component'; |
||||
|
import { ProductsService } from '../../Products/products.service'; |
||||
|
import { CouponsService } from '../coupons.service'; |
||||
|
|
||||
@Component({ |
@Component({ |
||||
selector: 'app-admin-new-coupons', |
selector: 'app-admin-new-coupons', |
||||
templateUrl: './admin-new-coupons.component.html', |
templateUrl: './admin-new-coupons.component.html', |
||||
styleUrls: ['./admin-new-coupons.component.scss'] |
styleUrls: ['./admin-new-coupons.component.scss'] |
||||
}) |
}) |
||||
export class AdminNewCouponsComponent implements OnInit { |
export class AdminNewCouponsComponent implements OnInit, OnDestroy { |
||||
constructor(@Inject(MAT_DIALOG_DATA) public data: { product: any }, public dialog: MatDialog,) { } |
businessTargetted: TesoBusinessDetail = {}; |
||||
|
products: ProductsModel[] = []; |
||||
|
fromWorth:number; |
||||
|
toWorth:number; |
||||
|
selectedProduct: any = {}; |
||||
|
selectedType: any = {}; |
||||
|
isLoading: boolean = false; |
||||
|
isScreenSmall: boolean; |
||||
|
couponTypes: CouponsType[] = []; |
||||
|
private _unsubscribeAll: Subject<any> = new Subject<any>(); |
||||
|
constructor(@Inject(MAT_DIALOG_DATA) public data: { product: any }, |
||||
|
public dialog: MatDialog, private _productService: ProductsService, |
||||
|
private _couponService: CouponsService, private _tesoMediaWatcherService: tesoMediaWatcherService,) { } |
||||
|
|
||||
ngOnInit(): void { |
ngOnInit(): void { |
||||
|
this._couponService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { |
||||
|
this.couponTypes = d; |
||||
|
}); |
||||
|
this._productService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { |
||||
|
this.products = d; |
||||
|
}); |
||||
|
this._tesoMediaWatcherService.onMediaChange$ |
||||
|
.pipe(takeUntil(this._unsubscribeAll)) |
||||
|
.subscribe(({ matchingAliases }) => { |
||||
|
|
||||
|
// Check if the screen is small
|
||||
|
this.isScreenSmall = !matchingAliases.includes('md'); |
||||
|
}); |
||||
} |
} |
||||
lookupBusiness() { |
lookupBusiness() { |
||||
const dialogRef = this.dialog.open(BusinessLookUpComponent, { |
const dialogRef = this.dialog.open(BusinessLookUpComponent, { |
||||
disableClose: true, |
disableClose: true, |
||||
hasBackdrop: true, |
hasBackdrop: true, |
||||
|
}); |
||||
|
|
||||
|
dialogRef.afterClosed().subscribe((result) => { |
||||
|
this.businessTargetted = result; |
||||
}); |
}); |
||||
} |
} |
||||
|
ngOnDestroy(): void { |
||||
|
// Unsubscribe from all subscriptions
|
||||
|
this._unsubscribeAll.next(); |
||||
|
this._unsubscribeAll.complete(); |
||||
|
} |
||||
} |
} |
||||
|
@ -1,15 +1,50 @@ |
|||||
import { Component, OnInit } from '@angular/core'; |
import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; |
||||
|
import { MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; |
||||
|
import { tesoMediaWatcherService } from '@teso/services/media-watcher'; |
||||
|
import { CouponsType } from 'app/models/couponsModel'; |
||||
|
import { ProductsModel } from 'app/models/productsModel'; |
||||
|
import { Subject } from 'rxjs'; |
||||
|
import { takeUntil } from 'rxjs/operators'; |
||||
|
import { ProductsService } from '../../Products/products.service'; |
||||
|
import { CouponsService } from '../coupons.service'; |
||||
|
|
||||
@Component({ |
@Component({ |
||||
selector: 'app-new-coupons', |
selector: 'app-new-coupons', |
||||
templateUrl: './new-coupons.component.html', |
templateUrl: './new-coupons.component.html', |
||||
styleUrls: ['./new-coupons.component.scss'] |
styleUrls: ['./new-coupons.component.scss'] |
||||
}) |
}) |
||||
export class NewCouponsComponent implements OnInit { |
export class NewCouponsComponent implements OnInit,OnDestroy { |
||||
|
products: ProductsModel[] = []; |
||||
constructor() { } |
selectedProduct:any={}; |
||||
|
selectedType:any={}; |
||||
|
isLoading: boolean = false; |
||||
|
couponTypes:CouponsType[]=[]; |
||||
|
fromWorth:number; |
||||
|
toWorth:number; |
||||
|
isScreenSmall:boolean; |
||||
|
private _unsubscribeAll: Subject<any> = new Subject<any>(); |
||||
|
constructor(@Inject(MAT_DIALOG_DATA) public data: { product: any }, |
||||
|
public dialog: MatDialog, private _productService: ProductsService, |
||||
|
private _couponService: CouponsService, private _tesoMediaWatcherService: tesoMediaWatcherService,) { } |
||||
|
|
||||
ngOnInit(): void { |
ngOnInit(): void { |
||||
} |
this._couponService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { |
||||
|
this.couponTypes = d; |
||||
|
}); |
||||
|
this._productService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { |
||||
|
this.products = d; |
||||
|
}); |
||||
|
this._tesoMediaWatcherService.onMediaChange$ |
||||
|
.pipe(takeUntil(this._unsubscribeAll)) |
||||
|
.subscribe(({ matchingAliases }) => { |
||||
|
|
||||
|
// Check if the screen is small
|
||||
|
this.isScreenSmall = !matchingAliases.includes('md'); |
||||
|
}); |
||||
|
} |
||||
|
ngOnDestroy(): void { |
||||
|
// Unsubscribe from all subscriptions
|
||||
|
this._unsubscribeAll.next(); |
||||
|
this._unsubscribeAll.complete(); |
||||
|
} |
||||
} |
} |
||||
|
@ -0,0 +1,59 @@ |
|||||
|
import { Injectable } from '@angular/core'; |
||||
|
import { HttpClient } from '@angular/common/http'; |
||||
|
import { BehaviorSubject, Observable } from 'rxjs'; |
||||
|
import { tap } from 'rxjs/operators'; |
||||
|
import { analytics as analyticsData } from 'app/mock-api/dashboards/analytics/data'; |
||||
|
import { environment } from 'environments/environment'; |
||||
|
import { CouponsModel, CouponsType } from 'app/models/couponsModel'; |
||||
|
|
||||
|
@Injectable({ |
||||
|
providedIn: 'root' |
||||
|
}) |
||||
|
export class CouponsService { |
||||
|
private _data: BehaviorSubject<any> = new BehaviorSubject(null); |
||||
|
private _couponCategory: BehaviorSubject<CouponsType[]> = new BehaviorSubject(null); |
||||
|
|
||||
|
/** |
||||
|
* Constructor |
||||
|
*/ |
||||
|
constructor(private _httpClient: HttpClient) { |
||||
|
} |
||||
|
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
// @ Accessors
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
|
||||
|
/** |
||||
|
* Getter for data |
||||
|
*/ |
||||
|
get data$(): Observable<any> { |
||||
|
return this._data.asObservable(); |
||||
|
} |
||||
|
|
||||
|
get categories$(): Observable<CouponsType[]> { |
||||
|
return this._couponCategory.asObservable(); |
||||
|
} |
||||
|
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
// @ Public methods
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
|
||||
|
/** |
||||
|
* Get data |
||||
|
*/ |
||||
|
getData(): Observable<any> { |
||||
|
return this._httpClient.get(environment.apiURL + `allcoupons`).pipe( |
||||
|
tap((response: any) => { |
||||
|
this._data.next(response); |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
getCategory(): Observable<CouponsType[]> { |
||||
|
return this._httpClient.get(environment.apiURL + `api/CouponTypes`).pipe( |
||||
|
tap((response: CouponsType[]) => { |
||||
|
this._couponCategory.next(response); |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
} |
@ -0,0 +1,139 @@ |
|||||
|
<div class="row" style="display: flex;justify-content:space-between"> |
||||
|
<div class="row" style="display: flex;justify-content:center;width: 100%;"> |
||||
|
<h2 mat-dialog-title style="text-align: center;">Send a coupon to {{data.subscriber.firstname}} |
||||
|
{{data.subscriber.surname}}</h2> |
||||
|
</div> |
||||
|
<!-- <button mat-button mat-dialog-close>Cancel</button> --> |
||||
|
<button mat-button [mat-dialog-close]="true" cdkFocusInitial>x</button> |
||||
|
</div> |
||||
|
<mat-dialog-content class="mat-typography"> |
||||
|
<div class=" py-2 pl-3 pr-3 sm:py-4 md:pl-4 md:pr-6"> |
||||
|
<!-- <div class="flex flex-col flex-auto w-full p-8 text-center"> |
||||
|
<div class="w-32 h-32 mx-auto rounded-full overflow-hidden"> |
||||
|
<img class="w-full h-full object-cover" [src]="imageLoader(data.subscriber.thumbnailDp)"> |
||||
|
</div> |
||||
|
</div> --> |
||||
|
<div class="columns" style="display: flex;align-items: center" *ngIf="tesoGhana"> |
||||
|
<div class="column" style="display: flex;align-items: baseline"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Target Business </strong> |
||||
|
</mat-label> |
||||
|
</div> |
||||
|
<div class="column"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
{{businessTargetted.businessName}} |
||||
|
</mat-label> |
||||
|
<button style="background-color: blue;color:white;margin-right:20px;" mat-button |
||||
|
(click)="lookupBusiness()">Search</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<hr *ngIf="tesoGhana" /> |
||||
|
<div class="columns" style="display: flex;align-items: center"> |
||||
|
<div class="column" style="display: flex;align-items: baseline"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Product Name </strong> |
||||
|
</mat-label> |
||||
|
</div> |
||||
|
<div class="column"> |
||||
|
<mat-form-field appearance="fill" style="min-width: 200px;"> |
||||
|
<mat-select [(ngModel)]="selectedProduct"> |
||||
|
<mat-option *ngFor="let product of products" [value]="product"> |
||||
|
{{product.productName}} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
|
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="columns" style="display: flex;align-items: center;margin-top: 15px;"> |
||||
|
<div class="column" style="display: flex;align-items: baseline"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Original Price </strong> |
||||
|
</mat-label> |
||||
|
</div> |
||||
|
<div class="column"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
{{selectedProduct.unitPrice | currency:"GHS "}} |
||||
|
</mat-label> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="columns" style="display: flex;align-items: center;margin-top: 15px;"> |
||||
|
<div class="column" style="display: flex;align-items: baseline"> |
||||
|
<mat-label style="margin-right: 20px;"> |
||||
|
<strong class="tileHead">Coupon Type</strong> |
||||
|
</mat-label> |
||||
|
</div> |
||||
|
<div class="column"> |
||||
|
<mat-form-field appearance="fill" style="min-width: 200px;"> |
||||
|
<mat-select [(ngModel)]="selectedType"> |
||||
|
<mat-option value="TESCP005">DISCOUNT </mat-option> |
||||
|
<mat-option value="TESCP006">FREEBIE </mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="columns" style="display: flex;align-items: center"> |
||||
|
<div class="column"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Coupon Condition</strong> |
||||
|
</mat-label> |
||||
|
</div> |
||||
|
<div class="column"> |
||||
|
<textarea name="" id="" cols="30" rows="5" style="border: 1px solid #cbd5e1;"></textarea> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="columns" style="display: flex;align-items: center"> |
||||
|
<div class="column" style="display: flex;align-items: baseline"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Number Of Coupons</strong> |
||||
|
</mat-label> |
||||
|
</div> |
||||
|
<div class="column"> |
||||
|
<mat-form-field appearance="fill" style="min-width: 200px;"> |
||||
|
<input matInput style="margin-left:10px;" type="number"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
<div style="display: flex;align-items: baseline"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Percentage Off </strong> |
||||
|
</mat-label> |
||||
|
<mat-form-field appearance="fill" style="margin-right:10px;"> |
||||
|
<span matPrefix>From </span> |
||||
|
<input matInput type="number"> |
||||
|
<span matSuffix>% </span> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field appearance="fill" style="margin-left:10px;"> |
||||
|
<span matPrefix>To </span> |
||||
|
<input matInput type="number"> |
||||
|
<span matSuffix>% </span> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div style="display: flex;align-items: baseline"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Coupon Worth </strong> |
||||
|
</mat-label> |
||||
|
<mat-label> |
||||
|
From GH¢200 To GH¢800 |
||||
|
</mat-label> |
||||
|
</div> |
||||
|
<div style="display: flex;align-items: baseline;margin-top: 20px;"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Date Of Expiration </strong> |
||||
|
</mat-label> |
||||
|
<mat-form-field appearance="outline"> |
||||
|
<input matInput style="margin-left:10px;" type="date"> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field appearance="fill"> |
||||
|
<span matPrefix>Time </span> |
||||
|
<input matInput type="time"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div class="row" style="display: flex;justify-content:space-around;width:100%;"> |
||||
|
<button style="background-color: blue;color:white;margin-right:20px;" mat-button>Generate Coupons</button> |
||||
|
|
||||
|
</div> |
||||
|
</div> |
||||
|
</mat-dialog-content> |
@ -0,0 +1,11 @@ |
|||||
|
.columns { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
flex-wrap: wrap; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.column { |
||||
|
flex: 50%; |
||||
|
font-size: medium; |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'; |
||||
|
|
||||
|
import { PersonalizedCouponsComponent } from './personalized-coupons.component'; |
||||
|
|
||||
|
describe('PersonalizedCouponsComponent', () => { |
||||
|
let component: PersonalizedCouponsComponent; |
||||
|
let fixture: ComponentFixture<PersonalizedCouponsComponent>; |
||||
|
|
||||
|
beforeEach(async () => { |
||||
|
await TestBed.configureTestingModule({ |
||||
|
declarations: [ PersonalizedCouponsComponent ] |
||||
|
}) |
||||
|
.compileComponents(); |
||||
|
}); |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
fixture = TestBed.createComponent(PersonalizedCouponsComponent); |
||||
|
component = fixture.componentInstance; |
||||
|
fixture.detectChanges(); |
||||
|
}); |
||||
|
|
||||
|
it('should create', () => { |
||||
|
expect(component).toBeTruthy(); |
||||
|
}); |
||||
|
}); |
@ -0,0 +1,50 @@ |
|||||
|
import { Component, Inject, OnInit } from '@angular/core'; |
||||
|
import { MatDialog, MAT_DIALOG_DATA } from '@angular/material/dialog'; |
||||
|
import { TesoBusinessDetail } from 'app/models/businessModel'; |
||||
|
import { CouponsType } from 'app/models/couponsModel'; |
||||
|
import { ProductsModel } from 'app/models/productsModel'; |
||||
|
import { TesoUserDetails } from 'app/models/userModel'; |
||||
|
import { environment } from 'environments/environment'; |
||||
|
import { Subject } from 'rxjs'; |
||||
|
import { takeUntil } from 'rxjs/operators'; |
||||
|
import { CouponsService } from '../../Coupons/coupons.service'; |
||||
|
import { BusinessLookUpComponent } from '../../Products/BusinessLookUp/business-look-up.component'; |
||||
|
import { ProductsService } from '../../Products/products.service'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-personalized-coupons', |
||||
|
templateUrl: './personalized-coupons.component.html', |
||||
|
styleUrls: ['./personalized-coupons.component.scss'] |
||||
|
}) |
||||
|
export class PersonalizedCouponsComponent implements OnInit { |
||||
|
tesoGhana:boolean = false; |
||||
|
businessTargetted: TesoBusinessDetail = {}; |
||||
|
private _unsubscribeAll: Subject<any> = new Subject<any>(); |
||||
|
isLoading: boolean = false; |
||||
|
products: ProductsModel[] = []; |
||||
|
selectedProduct: any = {}; |
||||
|
selectedType: string; |
||||
|
couponTypes: CouponsType[] = []; |
||||
|
constructor(private _productService: ProductsService, |
||||
|
@Inject(MAT_DIALOG_DATA) public data: { subscriber: TesoUserDetails }, |
||||
|
public dialog: MatDialog,) { } |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this._productService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { |
||||
|
this.products = d; |
||||
|
}); |
||||
|
} |
||||
|
imageLoader(path: string): string { |
||||
|
return environment.apiURL + `followeruserdp/${path}`; |
||||
|
} |
||||
|
lookupBusiness() { |
||||
|
const dialogRef = this.dialog.open(BusinessLookUpComponent, { |
||||
|
disableClose: true, |
||||
|
hasBackdrop: true, |
||||
|
}); |
||||
|
|
||||
|
dialogRef.afterClosed().subscribe((result) => { |
||||
|
this.businessTargetted = result; |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,32 @@ |
|||||
|
import { Injectable } from '@angular/core'; |
||||
|
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
import { DesiresService } from './desires.service'; |
||||
|
|
||||
|
@Injectable({ |
||||
|
providedIn: 'root' |
||||
|
}) |
||||
|
export class DesiresResolver implements Resolve<any> |
||||
|
{ |
||||
|
/** |
||||
|
* Constructor |
||||
|
*/ |
||||
|
constructor(private _desiresService: DesiresService) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
// @ Public methods
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
|
||||
|
/** |
||||
|
* Resolver |
||||
|
* |
||||
|
* @param route |
||||
|
* @param state |
||||
|
*/ |
||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> |
||||
|
{ |
||||
|
return this._desiresService.getData(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,49 @@ |
|||||
|
import { Injectable } from '@angular/core'; |
||||
|
import { HttpClient } from '@angular/common/http'; |
||||
|
import { BehaviorSubject, Observable } from 'rxjs'; |
||||
|
import { tap } from 'rxjs/operators'; |
||||
|
import { analytics as analyticsData } from 'app/mock-api/dashboards/analytics/data'; |
||||
|
import { environment } from 'environments/environment'; |
||||
|
import { DesiredList, DesiredProduct, ProductCategory, ProductsModel } from 'app/models/productsModel'; |
||||
|
|
||||
|
@Injectable({ |
||||
|
providedIn: 'root' |
||||
|
}) |
||||
|
export class DesiresService { |
||||
|
private _data: BehaviorSubject<any> = new BehaviorSubject(null); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* Constructor |
||||
|
*/ |
||||
|
constructor(private _httpClient: HttpClient) { |
||||
|
} |
||||
|
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
// @ Accessors
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
|
||||
|
/** |
||||
|
* Getter for data |
||||
|
*/ |
||||
|
get data$(): Observable<DesiredList> { |
||||
|
return this._data.asObservable(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
// @ Public methods
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
|
||||
|
/** |
||||
|
* Get data |
||||
|
*/ |
||||
|
getData(): Observable<DesiredList> { |
||||
|
return this._httpClient.get(environment.apiURL + `desires/pull-monthly`).pipe( |
||||
|
tap((response: DesiredList) => { |
||||
|
this._data.next(response); |
||||
|
|
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
} |
@ -1,16 +1,47 @@ |
|||||
import { Component, Inject, OnInit } from '@angular/core'; |
import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; |
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; |
import { MatDialog, MAT_DIALOG_DATA } from '@angular/material/dialog'; |
||||
|
import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; |
||||
|
import { tesoMediaWatcherService } from '@teso/services/media-watcher'; |
||||
|
import { CouponsType } from 'app/models/couponsModel'; |
||||
|
import { ProductsModel } from 'app/models/productsModel'; |
||||
|
import { Subject } from 'rxjs'; |
||||
|
import { takeUntil } from 'rxjs/operators'; |
||||
|
import { CouponsService } from '../../Coupons/coupons.service'; |
||||
|
import { ProductsService } from '../products.service'; |
||||
|
|
||||
@Component({ |
@Component({ |
||||
selector: 'app-coupon-dialog', |
selector: 'app-coupon-dialog', |
||||
templateUrl: './coupon-dialog.component.html', |
templateUrl: './coupon-dialog.component.html', |
||||
styleUrls: ['./coupon-dialog.component.scss'] |
styleUrls: ['./coupon-dialog.component.scss'] |
||||
}) |
}) |
||||
export class CouponDialogComponent implements OnInit { |
export class CouponDialogComponent implements OnInit,OnDestroy { |
||||
|
private _unsubscribeAll: Subject<any> = new Subject<any>(); |
||||
constructor(@Inject(MAT_DIALOG_DATA) public data: { product: any },) { } |
isLoading:boolean=false; |
||||
|
selectedProduct:any={}; |
||||
|
selectedType:any={}; |
||||
|
couponTypes:CouponsType[]=[]; |
||||
|
isScreenSmall: boolean; |
||||
|
constructor( |
||||
|
private _couponService: CouponsService,@Inject(MAT_DIALOG_DATA) public data: { product: ProductsModel }, |
||||
|
public dialog: MatDialog,private _productService: ProductsService,private _tesoMediaWatcherService: tesoMediaWatcherService, |
||||
|
private confirmBoxEvokeService: ConfirmBoxEvokeService,) { } |
||||
|
|
||||
ngOnInit(): void { |
ngOnInit(): void { |
||||
} |
this._couponService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { |
||||
|
this.couponTypes = d; |
||||
|
}); |
||||
|
this._tesoMediaWatcherService.onMediaChange$ |
||||
|
.pipe(takeUntil(this._unsubscribeAll)) |
||||
|
.subscribe(({ matchingAliases }) => { |
||||
|
|
||||
|
// Check if the screen is small
|
||||
|
this.isScreenSmall = !matchingAliases.includes('md'); |
||||
|
}); |
||||
|
|
||||
} |
} |
||||
|
ngOnDestroy(): void { |
||||
|
// Unsubscribe from all subscriptions
|
||||
|
this._unsubscribeAll.next(); |
||||
|
this._unsubscribeAll.complete(); |
||||
|
} |
||||
|
} |
||||
|
@ -0,0 +1,201 @@ |
|||||
|
<div class="absolute inset-0 flex flex-col min-w-0 " *ngIf="!isScreenSmall"> |
||||
|
|
||||
|
<mat-drawer-container class="flex-auto h-full"> |
||||
|
<!-- Drawer content --> |
||||
|
<mat-drawer-content class="flex flex-col"> |
||||
|
<!-- Header --> |
||||
|
<div |
||||
|
class="flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent"> |
||||
|
<!-- Title & Actions --> |
||||
|
<button mat-icon-button [routerLink]="['../../']"> |
||||
|
<mat-icon [svgIcon]="'heroicons_outline:arrow-sm-left'"></mat-icon> |
||||
|
</button> |
||||
|
<h2 class="ml-2.5 text-md sm:text-xl font-medium tracking-tight truncate"> |
||||
|
Product # {{product.productID}} |
||||
|
</h2> |
||||
|
</div> |
||||
|
<div |
||||
|
class=" flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent"> |
||||
|
<div class="flex-auto" cdkScrollable> |
||||
|
<div class="min-h-180 border-2 border-dashed border-gray-300 rounded-2xl py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 mx-auto" |
||||
|
style="max-width: 780px;"> |
||||
|
<mat-tab-group mat-align-tabs="center"> |
||||
|
<mat-tab label="Product Information"> |
||||
|
<div class=" py-2 pl-3 pr-3 sm:py-4 md:pl-4 md:pr-6" |
||||
|
style="overflow-y: hidden !important;"> |
||||
|
<div style="display: flex;"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Product Name </strong> |
||||
|
</mat-label> |
||||
|
<mat-form-field appearance="fill" style="width: 350px;"> |
||||
|
<input disabled matInput style="margin-left:10px;" type="text" |
||||
|
value="{{product.productName}}"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div style="display:flex;"> |
||||
|
<mat-label style="margin-right: 20px;"> |
||||
|
<strong class="tileHead">Product Category</strong> |
||||
|
</mat-label> |
||||
|
<mat-label style="width: 350px;"> |
||||
|
<span class="tileHead">{{product.categoryID}}</span> |
||||
|
</mat-label> |
||||
|
</div> |
||||
|
<div style="display: flex;"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Original Price </strong> |
||||
|
</mat-label> |
||||
|
<mat-form-field appearance="fill" style="width: 350px;"> |
||||
|
<span matPrefix>GH¢ </span> |
||||
|
<input disabled matInput style="margin-left:10px;" type="number" |
||||
|
value={{product.unitPrice}}> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
|
||||
|
<div class="columns" style="display: flex;justify-content:space-around;"> |
||||
|
<div class="column"> |
||||
|
<div> |
||||
|
<strong class="tileHead">Product Description :</strong> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="column"> |
||||
|
<quill-editor [modules]="quillConfig" [(ngModel)]=product.productDesc |
||||
|
placeholder="Further details"> |
||||
|
|
||||
|
</quill-editor> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
</mat-tab> |
||||
|
<mat-tab label="Images"> |
||||
|
<div class="py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8" |
||||
|
style="min-height: 550px !important;"> |
||||
|
<div class="flex justify-between"> |
||||
|
<div> |
||||
|
<div class="font-medium tracking-tight text-secondary">Add up to 10 high |
||||
|
quality images of the product |
||||
|
</div> |
||||
|
<div class="font-medium tracking-tight text-secondary"> |
||||
|
{{productImages.length}} out of |
||||
|
<strong>10</strong> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
<div class="imageList"> |
||||
|
<div class="imageHolder" *ngFor="let images of product.images"> |
||||
|
<img src="{{images.imageSRC}}" class="productImage"> |
||||
|
<button class="deleteelement" style="border:none;" |
||||
|
(click)="removeImage(images)"> |
||||
|
<mat-icon [svgIcon]="'heroicons_outline:trash'" style="color: red;"> |
||||
|
</mat-icon> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</mat-tab> |
||||
|
</mat-tab-group> |
||||
|
|
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</mat-drawer-content> |
||||
|
</mat-drawer-container> |
||||
|
</div> |
||||
|
<div class="absolute inset-0 flex flex-col min-w-0 " *ngIf="isScreenSmall"> |
||||
|
|
||||
|
<mat-drawer-container class="flex-auto h-full"> |
||||
|
<!-- Drawer content --> |
||||
|
<mat-drawer-content class="flex flex-col"> |
||||
|
<!-- Header --> |
||||
|
<div |
||||
|
class="flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent"> |
||||
|
<!-- Title & Actions --> |
||||
|
<button mat-icon-button [routerLink]="['../../']"> |
||||
|
<mat-icon [svgIcon]="'heroicons_outline:arrow-sm-left'"></mat-icon> |
||||
|
</button> |
||||
|
<h2 class="ml-2.5 text-md sm:text-xl font-medium tracking-tight truncate"> |
||||
|
Product # {{product.productID}} |
||||
|
</h2> |
||||
|
</div> |
||||
|
<div |
||||
|
class=" flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent"> |
||||
|
<div class="flex-auto overflow-y-auto" cdkScrollable> |
||||
|
<div class="min-h-180 border-2 border-dashed border-gray-300 rounded-2xl py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 mx-auto" |
||||
|
style="max-width: 780px;"> |
||||
|
<mat-tab-group mat-align-tabs="center"> |
||||
|
<mat-tab label="Product Information"> |
||||
|
<div class=" py-2 pl-3 pr-3 sm:py-4 md:pl-4 md:pr-6" |
||||
|
style="overflow-y: hidden !important;"> |
||||
|
<div style="display: flex;"> |
||||
|
<mat-label style="margin-right: 20px;"> |
||||
|
<strong>Product Name </strong> |
||||
|
</mat-label> |
||||
|
<mat-label>{{product.productName}}</mat-label> |
||||
|
</div> |
||||
|
<hr/> |
||||
|
<div style="display:flex;"> |
||||
|
<mat-label style="margin-right: 20px;"> |
||||
|
<strong>Product Category</strong> |
||||
|
</mat-label> |
||||
|
<mat-label style="margin-right: 20px;"> |
||||
|
<span style="font-size: 13px;">{{product.categoryID}}</span> |
||||
|
</mat-label> |
||||
|
</div> |
||||
|
<hr/> |
||||
|
<div style="display: flex;"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong>Original Price </strong> |
||||
|
</mat-label> |
||||
|
<mat-label> |
||||
|
{{product.unitPrice | currency:"GHS "}} |
||||
|
</mat-label> |
||||
|
</div> |
||||
|
<hr/> |
||||
|
<div class="columns" style="display: flex;justify-content:space-around;"> |
||||
|
<div class="column"> |
||||
|
<div> |
||||
|
<strong>Product Description :</strong> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="column"> |
||||
|
<quill-editor [modules]="quillConfig" [(ngModel)]="product.productDesc" |
||||
|
placeholder="Further details"> |
||||
|
|
||||
|
</quill-editor> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
</mat-tab> |
||||
|
<mat-tab label="Images"> |
||||
|
<div class="py-2 pl-2 pr-2 sm:py-4 md:pl-6 md:pr-8"> |
||||
|
<div class="flex justify-between"> |
||||
|
<div> |
||||
|
<div class="font-medium tracking-tight text-secondary">Add up to 10 high |
||||
|
quality images of the product |
||||
|
</div> |
||||
|
<div class="font-medium tracking-tight text-secondary">0 out of |
||||
|
<strong>10</strong> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="imageList"> |
||||
|
<div class="imageHolder" *ngFor="let images of productImages"> |
||||
|
<img src="{{images.imageSRC}}" class="productImage"> |
||||
|
<button class="deleteelement" style="border:none;" |
||||
|
(click)="removeImage(images)"> |
||||
|
<mat-icon [svgIcon]="'heroicons_outline:trash'" style="color: red;"> |
||||
|
</mat-icon> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</mat-tab> |
||||
|
</mat-tab-group> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</mat-drawer-content> |
||||
|
</mat-drawer-container> |
||||
|
</div> |
@ -0,0 +1,78 @@ |
|||||
|
.columns { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
flex-wrap: wrap; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.column { |
||||
|
flex: 50%; |
||||
|
font-size: medium; |
||||
|
} |
||||
|
|
||||
|
.tileHead { |
||||
|
font-size: 17px; |
||||
|
color: #003445; |
||||
|
font-family: Roboto, "Helvetica Neue", sans-serif; |
||||
|
line-height: 3rem; |
||||
|
} |
||||
|
|
||||
|
.ticketLine > a:hover { |
||||
|
color: cornflowerblue; |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
|
||||
|
.ticketLine > a { |
||||
|
color: cornflowerblue; |
||||
|
font-style: italic; |
||||
|
} |
||||
|
|
||||
|
::ng-deep { |
||||
|
@media screen and (min-width: 992px) { |
||||
|
.ql-container .ql-editor { |
||||
|
min-height: 160px; |
||||
|
max-height: 270px; |
||||
|
height: 270px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.imageList { |
||||
|
display: flex; |
||||
|
flex-wrap: wrap; |
||||
|
} |
||||
|
|
||||
|
.imageHolder { |
||||
|
flex-grow: 1; |
||||
|
width: 22%; |
||||
|
max-width: 180px; |
||||
|
height: 180px; |
||||
|
margin: 10px; |
||||
|
border: 3px solid #003445; |
||||
|
padding: 5px; |
||||
|
position: relative; |
||||
|
} |
||||
|
.productImage{ |
||||
|
max-width: 130px; |
||||
|
margin: 10px; |
||||
|
} |
||||
|
.deleteelement { |
||||
|
position: absolute; |
||||
|
top: 5px; |
||||
|
right: 5px; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@media screen and (max-width: 992px) { |
||||
|
.imageHolder { |
||||
|
flex-grow: 1; |
||||
|
width: 22%; |
||||
|
max-width: 180px; |
||||
|
min-width: 180px; |
||||
|
height: 180px; |
||||
|
margin: 10px; |
||||
|
border: 3px solid #003445; |
||||
|
padding: 5px; |
||||
|
position: relative; |
||||
|
} |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'; |
||||
|
|
||||
|
import { DetailsProductComponent } from './details-product.component'; |
||||
|
|
||||
|
describe('DetailsProductComponent', () => { |
||||
|
let component: DetailsProductComponent; |
||||
|
let fixture: ComponentFixture<DetailsProductComponent>; |
||||
|
|
||||
|
beforeEach(async () => { |
||||
|
await TestBed.configureTestingModule({ |
||||
|
declarations: [ DetailsProductComponent ] |
||||
|
}) |
||||
|
.compileComponents(); |
||||
|
}); |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
fixture = TestBed.createComponent(DetailsProductComponent); |
||||
|
component = fixture.componentInstance; |
||||
|
fixture.detectChanges(); |
||||
|
}); |
||||
|
|
||||
|
it('should create', () => { |
||||
|
expect(component).toBeTruthy(); |
||||
|
}); |
||||
|
}); |
@ -0,0 +1,151 @@ |
|||||
|
import { Component, OnInit } from '@angular/core'; |
||||
|
import { MatDialog } from '@angular/material/dialog'; |
||||
|
import { Router, ActivatedRoute } from '@angular/router'; |
||||
|
import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; |
||||
|
import { tesoMediaWatcherService } from '@teso/services/media-watcher'; |
||||
|
import { ProductUpload } from 'app/models/generalModel'; |
||||
|
import { ProductsModel } from 'app/models/productsModel'; |
||||
|
import { Subject } from 'rxjs'; |
||||
|
import { takeUntil } from 'rxjs/operators'; |
||||
|
import { ProductsService } from '../products.service'; |
||||
|
const modules = { |
||||
|
toolbar: [ |
||||
|
[], |
||||
|
[], |
||||
|
|
||||
|
[], |
||||
|
[], |
||||
|
[], |
||||
|
[], |
||||
|
[], |
||||
|
|
||||
|
[], |
||||
|
[], |
||||
|
|
||||
|
[], |
||||
|
[], |
||||
|
[], |
||||
|
|
||||
|
[], |
||||
|
|
||||
|
|
||||
|
] |
||||
|
}; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-details-product', |
||||
|
templateUrl: './details-product.component.html', |
||||
|
styleUrls: ['./details-product.component.scss'] |
||||
|
}) |
||||
|
export class DetailsProductComponent implements OnInit { |
||||
|
productID: string; |
||||
|
title: string; |
||||
|
status: string = "Status"; |
||||
|
htmlRequest: string; |
||||
|
transferType: string; |
||||
|
placeHolder: string = ""; |
||||
|
selected: any; |
||||
|
isScreenSmall: boolean; |
||||
|
productImages: ProductUpload[] = []; |
||||
|
product: ProductsModel; |
||||
|
private _unsubscribeAll: Subject<any> = new Subject<any>(); |
||||
|
constructor(private _tesoMediaWatcherService: tesoMediaWatcherService, |
||||
|
private confirmBoxEvokeService: ConfirmBoxEvokeService, |
||||
|
private router: Router, private route: ActivatedRoute, private productServie: ProductsService) { |
||||
|
var snapshot = route.snapshot; |
||||
|
this.productID = snapshot.paramMap.get('id'); |
||||
|
this.product = productServie.getProduct(this.productID); |
||||
|
if(this.product == null){ |
||||
|
this.router.navigate(['products']); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this._tesoMediaWatcherService.onMediaChange$ |
||||
|
.pipe(takeUntil(this._unsubscribeAll)) |
||||
|
.subscribe(({ matchingAliases }) => { |
||||
|
|
||||
|
// Check if the screen is small
|
||||
|
this.isScreenSmall = !matchingAliases.includes('md'); |
||||
|
}); |
||||
|
} |
||||
|
quillConfig = modules; |
||||
|
items = [ |
||||
|
{ id: 1, name: 'Python' }, |
||||
|
{ id: 2, name: 'Node Js' }, |
||||
|
{ id: 3, name: 'Java' }, |
||||
|
{ id: 4, name: 'PHP', disabled: true }, |
||||
|
{ id: 5, name: 'Django' }, |
||||
|
{ id: 6, name: 'Angular' }, |
||||
|
{ id: 7, name: 'Vue' }, |
||||
|
{ id: 8, name: 'ReactJs' }, |
||||
|
]; |
||||
|
ngOnDestroy(): void { |
||||
|
// Unsubscribe from all subscriptions
|
||||
|
this._unsubscribeAll.next(); |
||||
|
this._unsubscribeAll.complete(); |
||||
|
} |
||||
|
|
||||
|
// readURL(event: Event): void {
|
||||
|
// if (event.target.files && event.target.files[0]) {
|
||||
|
// const file = event.target.files[0];
|
||||
|
|
||||
|
// const reader = new FileReader();
|
||||
|
// reader.onload = e => this.imageSrc = reader.result;
|
||||
|
|
||||
|
// reader.readAsDataURL(file);
|
||||
|
// }
|
||||
|
// }
|
||||
|
onFileSelected(event) { |
||||
|
|
||||
|
const file: File = event.target.files[0]; |
||||
|
if (file.type.includes("image")) { |
||||
|
console.log(file) |
||||
|
if (this.productImages.length == 0) { |
||||
|
var productImage: ProductUpload = { |
||||
|
file: file, |
||||
|
highlight: true, |
||||
|
imageSRC: "", |
||||
|
}; |
||||
|
let reader = new FileReader(); |
||||
|
reader.onload = (event: any) => { |
||||
|
productImage.imageSRC = event.target.result; |
||||
|
} |
||||
|
reader.readAsDataURL(file); |
||||
|
|
||||
|
this.productImages.push(productImage); |
||||
|
|
||||
|
} else { |
||||
|
var productImage: ProductUpload = { |
||||
|
file: file, |
||||
|
highlight: false, |
||||
|
imageSRC: "", |
||||
|
}; |
||||
|
|
||||
|
let reader = new FileReader(); |
||||
|
reader.onload = (event: any) => { |
||||
|
productImage.imageSRC = event.target.result; |
||||
|
} |
||||
|
reader.readAsDataURL(file); |
||||
|
|
||||
|
this.productImages.push(productImage); |
||||
|
} |
||||
|
} else { |
||||
|
this.confirmBoxEvokeService.danger("Incompatible File", "Only images can be added", "OK").subscribe(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
removeImage(item: ProductUpload) { |
||||
|
this.productImages = this.productImages.filter((e) => e != item); |
||||
|
} |
||||
|
|
||||
|
submit() { |
||||
|
if (this.productImages.length == 0) { |
||||
|
this.confirmBoxEvokeService.warning("Add Product Images", "To add a new product, you must add at least one image to support it", "OK").subscribe(); |
||||
|
} else { |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
@ -0,0 +1,73 @@ |
|||||
|
import { Injectable } from '@angular/core'; |
||||
|
import { HttpClient } from '@angular/common/http'; |
||||
|
import { BehaviorSubject, Observable } from 'rxjs'; |
||||
|
import { tap } from 'rxjs/operators'; |
||||
|
import { analytics as analyticsData } from 'app/mock-api/dashboards/analytics/data'; |
||||
|
import { environment } from 'environments/environment'; |
||||
|
import { ProductsModule } from './products.module'; |
||||
|
import { ProductCategory, ProductsModel } from 'app/models/productsModel'; |
||||
|
|
||||
|
@Injectable({ |
||||
|
providedIn: 'root' |
||||
|
}) |
||||
|
export class ProductsService { |
||||
|
private _data: BehaviorSubject<any> = new BehaviorSubject(null); |
||||
|
private _dataFilterable: BehaviorSubject<any> = new BehaviorSubject(null); |
||||
|
private products: ProductsModel[] = []; |
||||
|
private _productsCategory: BehaviorSubject<ProductCategory[]> = new BehaviorSubject(null); |
||||
|
|
||||
|
/** |
||||
|
* Constructor |
||||
|
*/ |
||||
|
constructor(private _httpClient: HttpClient) { |
||||
|
} |
||||
|
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
// @ Accessors
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
|
||||
|
/** |
||||
|
* Getter for data |
||||
|
*/ |
||||
|
get data$(): Observable<ProductsModel[]> { |
||||
|
return this._dataFilterable.asObservable(); |
||||
|
} |
||||
|
get dataProductsPageOnly$(): Observable<ProductsModel[]> { |
||||
|
return this._data.asObservable(); |
||||
|
} |
||||
|
get categories$(): Observable<ProductCategory[]> { |
||||
|
return this._productsCategory.asObservable(); |
||||
|
} |
||||
|
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
// @ Public methods
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
|
||||
|
/** |
||||
|
* Get data |
||||
|
*/ |
||||
|
getData(): Observable<ProductsModel[]> { |
||||
|
return this._httpClient.get(environment.apiURL + `products/allproducts`).pipe( |
||||
|
tap((response: ProductsModel[]) => { |
||||
|
this._data.next(response); |
||||
|
this._dataFilterable.next(response); |
||||
|
this.products = response; |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
filterProducts(productName: string): ProductsModel[] { |
||||
|
return this._dataFilterable.getValue().filter(p => p.productName.toLowerCase().includes(productName)); |
||||
|
// this.products = response;
|
||||
|
} |
||||
|
getProduct(id: string): ProductsModel { |
||||
|
const found = this.products.find(item => item.productID === id); |
||||
|
return found; |
||||
|
} |
||||
|
|
||||
|
getCategories(): Observable<ProductCategory[]> { |
||||
|
return this._httpClient.get(environment.apiURL + `productcategories`).pipe( |
||||
|
tap((response: ProductCategory[]) => { |
||||
|
this._productsCategory.next(response); |
||||
|
})); |
||||
|
} |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
<div class="row" style="display: flex;justify-content:space-between"> |
||||
|
<div class="row" style="display: flex;justify-content:center;width: 100%;"> |
||||
|
<h2 mat-dialog-title style="text-align: center;">Shop Description</h2> |
||||
|
</div> |
||||
|
<button mat-button [mat-dialog-close]="data.details" cdkFocusInitial>x</button> |
||||
|
</div> |
||||
|
<mat-dialog-content class="mat-typography"> |
||||
|
<quill-editor [modules]="quillConfig" [(ngModel)]="data.details" |
||||
|
placeholder="Further details"> |
||||
|
|
||||
|
</quill-editor> |
||||
|
</mat-dialog-content> |
@ -0,0 +1,25 @@ |
|||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'; |
||||
|
|
||||
|
import { DescriptionDialogComponent } from './description-dialog.component'; |
||||
|
|
||||
|
describe('DescriptionDialogComponent', () => { |
||||
|
let component: DescriptionDialogComponent; |
||||
|
let fixture: ComponentFixture<DescriptionDialogComponent>; |
||||
|
|
||||
|
beforeEach(async () => { |
||||
|
await TestBed.configureTestingModule({ |
||||
|
declarations: [ DescriptionDialogComponent ] |
||||
|
}) |
||||
|
.compileComponents(); |
||||
|
}); |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
fixture = TestBed.createComponent(DescriptionDialogComponent); |
||||
|
component = fixture.componentInstance; |
||||
|
fixture.detectChanges(); |
||||
|
}); |
||||
|
|
||||
|
it('should create', () => { |
||||
|
expect(component).toBeTruthy(); |
||||
|
}); |
||||
|
}); |
@ -0,0 +1,44 @@ |
|||||
|
import { Component, Inject, OnInit } from '@angular/core'; |
||||
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; |
||||
|
import { TesoBusinessDetail } from 'app/models/businessModel'; |
||||
|
const modules = { |
||||
|
toolbar: [ |
||||
|
['bold', 'italic', 'underline', 'strike'], |
||||
|
['blockquote', 'code-block'], |
||||
|
|
||||
|
[{ 'header': 1 }, { 'header': 2 }], |
||||
|
[{ 'list': 'ordered' }, { 'list': 'bullet' }], |
||||
|
[{ 'script': 'sub' }, { 'script': 'super' }], |
||||
|
[{ 'indent': '-1' }, { 'indent': '+1' }], |
||||
|
[{ 'direction': 'rtl' }], |
||||
|
|
||||
|
[{ 'size': ['small', false, 'large', 'huge'] }], |
||||
|
[{ 'header': [1, 2, 3, 4, 5, 6, false] }], |
||||
|
|
||||
|
[{ 'color': [] }, { 'background': [] }], |
||||
|
[{ 'font': [] }], |
||||
|
[{ 'align': [] }], |
||||
|
|
||||
|
['clean'], |
||||
|
['link'] |
||||
|
|
||||
|
] |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-description-dialog', |
||||
|
templateUrl: './description-dialog.component.html', |
||||
|
styleUrls: ['./description-dialog.component.scss'] |
||||
|
}) |
||||
|
export class DescriptionDialogComponent implements OnInit { |
||||
|
constructor(@Inject(MAT_DIALOG_DATA) public data: { details: string }, |
||||
|
public dialogRef: MatDialogRef<DescriptionDialogComponent>,) { } |
||||
|
quillConfig = modules; |
||||
|
ngOnInit(): void { |
||||
|
} |
||||
|
|
||||
|
closeDialog() { |
||||
|
this.dialogRef.close(this.data.details); |
||||
|
} |
||||
|
} |
@ -0,0 +1,83 @@ |
|||||
|
<div class="row" style="display: flex;justify-content:space-between;"> |
||||
|
<div class="row" style="display: flex;justify-content:center;width: 100%;"> |
||||
|
<h3 mat-dialog-title style="text-align: center;">Business Information</h3> |
||||
|
</div> |
||||
|
<button mat-button [mat-dialog-close]="data.details" cdkFocusInitial>x</button> |
||||
|
</div> |
||||
|
<mat-dialog-content class="mat-typography"> |
||||
|
<div class=" py-2 pl-3 pr-3 sm:py-4 md:pl-4 md:pr-6" style="overflow-y: hidden !important;"> |
||||
|
<div style="display: flex;justify-content:space-between;"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Shop Name </strong> |
||||
|
</mat-label> |
||||
|
<mat-form-field appearance="fill" style="width: 350px;"> |
||||
|
<input matInput style="margin-left:10px;" type="text" [(ngModel)]="data.details.businessName"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div style="display: flex;justify-content:space-between;"> |
||||
|
<mat-label style="margin-right: 20px;"> |
||||
|
<strong class="tileHead">Shop Category</strong> |
||||
|
</mat-label> |
||||
|
<mat-form-field appearance="fill" style="width: 350px;"> |
||||
|
<mat-select [(ngModel)]="selectedCategory"> |
||||
|
<mat-option *ngFor="let category of businessCategories" [value]="category"> |
||||
|
{{category.categoryName}} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div style="display: flex;justify-content:space-between;"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Physical Address </strong> |
||||
|
</mat-label> |
||||
|
<mat-form-field appearance="fill" style="width: 350px;"> |
||||
|
<input matInput style="margin-left:10px;" type="text" [(ngModel)]="data.details.businessAddress"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
|
||||
|
<div style="display: flex;justify-content:space-between;"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Digital Address </strong> |
||||
|
</mat-label> |
||||
|
<mat-form-field appearance="fill" style="width: 350px;"> |
||||
|
<span matPrefix> |
||||
|
<a href="https://ghanapostgps.com/map/" target="blank"> |
||||
|
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_solid:information-circle'" |
||||
|
style="color: #0152cc;" matTooltip="visit https://ghanapostgps.com/map/"> |
||||
|
</mat-icon> |
||||
|
</a> |
||||
|
</span> |
||||
|
<input matInput style="margin-left:10px;" type="text" [(ngModel)]="data.details.businessDigitalAddress"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div style="display: flex;justify-content:space-between;"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Date Of Establishment </strong> |
||||
|
</mat-label> |
||||
|
<mat-form-field appearance="fill" style="width: 350px;"> |
||||
|
<input matInput style="margin-left:10px;" type="date" [(ngModel)]="data.details.dateOfEst"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div style="display: flex;justify-content:space-between;"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Telephone </strong> |
||||
|
</mat-label> |
||||
|
<mat-form-field appearance="fill" style="width: 350px;"> |
||||
|
<input matInput style="margin-left:10px;" type="text" [(ngModel)]="data.details.businessContact" |
||||
|
disabled> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div style="display: flex;justify-content:space-between;"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Email Address </strong> |
||||
|
</mat-label> |
||||
|
<mat-form-field appearance="fill" style="width: 350px;"> |
||||
|
<input matInput style="margin-left:10px;" type="text" [(ngModel)]="data.details.businessEmail" disabled> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
|
||||
|
<div style="display: flex;justify-content:center;width: 100%;"> |
||||
|
<button mat-button style="background-color: green;color: white;">Confirm Changes</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</mat-dialog-content> |
@ -0,0 +1,28 @@ |
|||||
|
.columns { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
flex-wrap: wrap; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.column { |
||||
|
flex: 50%; |
||||
|
font-size: medium; |
||||
|
} |
||||
|
|
||||
|
.tileHead { |
||||
|
font-size: 17px; |
||||
|
color: #003445; |
||||
|
font-family: Roboto, "Helvetica Neue", sans-serif; |
||||
|
line-height: 3rem; |
||||
|
} |
||||
|
|
||||
|
.ticketLine > a:hover { |
||||
|
color: cornflowerblue; |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
|
||||
|
.ticketLine > a { |
||||
|
color: cornflowerblue; |
||||
|
font-style: italic; |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'; |
||||
|
|
||||
|
import { InformationDialogComponent } from './information-dialog.component'; |
||||
|
|
||||
|
describe('InformationDialogComponent', () => { |
||||
|
let component: InformationDialogComponent; |
||||
|
let fixture: ComponentFixture<InformationDialogComponent>; |
||||
|
|
||||
|
beforeEach(async () => { |
||||
|
await TestBed.configureTestingModule({ |
||||
|
declarations: [ InformationDialogComponent ] |
||||
|
}) |
||||
|
.compileComponents(); |
||||
|
}); |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
fixture = TestBed.createComponent(InformationDialogComponent); |
||||
|
component = fixture.componentInstance; |
||||
|
fixture.detectChanges(); |
||||
|
}); |
||||
|
|
||||
|
it('should create', () => { |
||||
|
expect(component).toBeTruthy(); |
||||
|
}); |
||||
|
}); |
@ -0,0 +1,35 @@ |
|||||
|
import { Component, Inject, OnInit } from '@angular/core'; |
||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; |
||||
|
import { UserService } from 'app/core/user/user.service'; |
||||
|
import { BusinessCategory, TesoBusinessDetail } from 'app/models/businessModel'; |
||||
|
import { Subject } from 'rxjs'; |
||||
|
import { takeUntil } from 'rxjs/operators'; |
||||
|
import { ProfileService } from '../profile.service'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-information-dialog', |
||||
|
templateUrl: './information-dialog.component.html', |
||||
|
styleUrls: ['./information-dialog.component.scss'] |
||||
|
}) |
||||
|
export class InformationDialogComponent implements OnInit { |
||||
|
businessCategories: BusinessCategory[] = []; |
||||
|
selectedCategory: BusinessCategory; |
||||
|
private _unsubscribeAll: Subject<any> = new Subject<any>(); |
||||
|
constructor(@Inject(MAT_DIALOG_DATA) public data: { details: TesoBusinessDetail }, |
||||
|
public dialogRef: MatDialogRef<InformationDialogComponent>, private _profileService: ProfileService, |
||||
|
private _userService:UserService) { } |
||||
|
ngOnInit(): void { |
||||
|
this._profileService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { |
||||
|
this.businessCategories = d; |
||||
|
this.selectedCategory = this.businessCategories.find((c)=> c.categoryCode.toLowerCase() == this.data.details.businessCategory.toLowerCase()); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
closeDialog() { |
||||
|
this.data.details.businessCategory = this.selectedCategory.categoryCode; |
||||
|
|
||||
|
this._userService.update(this.data.details); |
||||
|
this.dialogRef.close(true); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,53 @@ |
|||||
|
<div class="InfoHold"> |
||||
|
<div style="display: flex;justify-content: space-between;max-height: 46px;align-items: center;"> |
||||
|
<div> |
||||
|
<h4> |
||||
|
<mat-icon class="icon-size-5" [svgIcon]="'heroicons_solid:information-circle'"></mat-icon> Business |
||||
|
Information |
||||
|
</h4> |
||||
|
</div> |
||||
|
<div> |
||||
|
<button mat-button style="background-color: #0152cc;color: white;" (click)="editShopInformation()">Edit Information</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<mat-divider class="my-2"></mat-divider> |
||||
|
<table style="width: 100%;" > |
||||
|
<tbody> |
||||
|
<tr class="tableau"> |
||||
|
<th scope="row" class="text-dark">Shop Name: </th> |
||||
|
<td id="business-name-id">{{details.businessName}}</td> |
||||
|
</tr> |
||||
|
<tr class="tableau"> |
||||
|
<th scope="row" class="text-dark">Shop Category: </th> |
||||
|
<td id="shop-category1">{{currentCategory}}</td> |
||||
|
</tr> |
||||
|
<tr class="tableau"> |
||||
|
<th scope="row" class="text-dark">Physical Address: </th> |
||||
|
<td id="physicaladdress1">{{details.businessAddress}}</td> |
||||
|
</tr> |
||||
|
<tr class="tableau"> |
||||
|
<th scope="row" class="text-dark">Digital Address: </th> |
||||
|
<td id="digitaladdress1">{{details.businessDigitalAddress}}</td> |
||||
|
</tr> |
||||
|
|
||||
|
<tr class="tableau"> |
||||
|
<th scope="row" class="text-dark">Date of Establishment: </th> |
||||
|
<td id="dateest1"> |
||||
|
{{details.dateOfEst | date}}</td> |
||||
|
</tr> |
||||
|
<tr class="tableau"> |
||||
|
<th scope="row" class="text-dark">Phone Number: </th> |
||||
|
<td id="tel-id">{{details.businessContact}}</td> |
||||
|
</tr> |
||||
|
<tr class="tableau"> |
||||
|
<th scope="row" class="text-dark">Email Address: </th> |
||||
|
<td id="email-id">{{details.businessEmail}}</td> |
||||
|
</tr> |
||||
|
|
||||
|
|
||||
|
</tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
<div class="text-center" style="margin-top:10px;"> |
||||
|
<!-- <RadzenButton Text="Save Changes" Click="editProduct" Style="background-color:forestgreen !important;" /> --> |
||||
|
</div> |
@ -0,0 +1,12 @@ |
|||||
|
.InfoHold{ |
||||
|
padding-left: 5%; |
||||
|
padding-right: 5%; |
||||
|
} |
||||
|
.tableau{ |
||||
|
margin: 20px; |
||||
|
height: 45px; |
||||
|
padding: 20px; |
||||
|
border-bottom: 1px solid gray; |
||||
|
text-align: start; |
||||
|
align-items: flex-start; |
||||
|
} |
@ -1,20 +1,20 @@ |
|||||
import { ComponentFixture, TestBed } from '@angular/core/testing'; |
import { ComponentFixture, TestBed } from '@angular/core/testing'; |
||||
|
|
||||
import { EditProductComponent } from './edit-product.component'; |
import { InformationComponent } from './information.component'; |
||||
|
|
||||
describe('EditProductComponent', () => { |
describe('InformationComponent', () => { |
||||
let component: EditProductComponent; |
let component: InformationComponent; |
||||
let fixture: ComponentFixture<EditProductComponent>; |
let fixture: ComponentFixture<InformationComponent>; |
||||
|
|
||||
beforeEach(async () => { |
beforeEach(async () => { |
||||
await TestBed.configureTestingModule({ |
await TestBed.configureTestingModule({ |
||||
declarations: [ EditProductComponent ] |
declarations: [ InformationComponent ] |
||||
}) |
}) |
||||
.compileComponents(); |
.compileComponents(); |
||||
}); |
}); |
||||
|
|
||||
beforeEach(() => { |
beforeEach(() => { |
||||
fixture = TestBed.createComponent(EditProductComponent); |
fixture = TestBed.createComponent(InformationComponent); |
||||
component = fixture.componentInstance; |
component = fixture.componentInstance; |
||||
fixture.detectChanges(); |
fixture.detectChanges(); |
||||
}); |
}); |
@ -0,0 +1,51 @@ |
|||||
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; |
||||
|
import { MatDialog } from '@angular/material/dialog'; |
||||
|
import { MatTableDataSource } from '@angular/material/table'; |
||||
|
import { Router } from '@angular/router'; |
||||
|
import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; |
||||
|
import { tesoMediaWatcherService } from '@teso/services/media-watcher'; |
||||
|
import { UserService } from 'app/core/user/user.service'; |
||||
|
import { BusinessCategory, TesoBusinessDetail } from 'app/models/businessModel'; |
||||
|
import { Subject } from 'rxjs'; |
||||
|
import { takeUntil } from 'rxjs/operators'; |
||||
|
import { FollowersService } from '../../Followers/followers.service'; |
||||
|
import { DescriptionDialogComponent } from '../DescriptionDialog/description-dialog.component'; |
||||
|
import { InformationDialogComponent } from '../InfoDialog/information-dialog.component'; |
||||
|
import { ProfileService } from '../profile.service'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'business-information', |
||||
|
templateUrl: './information.component.html', |
||||
|
styleUrls: ['./information.component.scss'], |
||||
|
changeDetection: ChangeDetectionStrategy.OnPush, |
||||
|
exportAs: 'business-information' |
||||
|
}) |
||||
|
export class InformationComponent implements OnInit { |
||||
|
@Input() details: TesoBusinessDetail; |
||||
|
currentCategory: string; |
||||
|
categories: BusinessCategory[] = []; |
||||
|
private _unsubscribeAll: Subject<any> = new Subject<any>(); |
||||
|
constructor(private router: Router, private _tesoMediaWatcherService: tesoMediaWatcherService, |
||||
|
private _profileService: ProfileService, private _changeRef: ChangeDetectorRef, |
||||
|
public dialog: MatDialog, |
||||
|
private confirmBoxEvokeService: ConfirmBoxEvokeService, private _userService: UserService) { } |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this._profileService.categories$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { |
||||
|
this.categories = d; |
||||
|
this.currentCategory = d.find(c => c.categoryCode == this.details.businessCategory).categoryName; |
||||
|
}); |
||||
|
} |
||||
|
editShopInformation() { |
||||
|
const dialogReference = this.dialog.open(InformationDialogComponent, { |
||||
|
disableClose: true, |
||||
|
hasBackdrop: true, |
||||
|
data: { details: this.details }, |
||||
|
}); |
||||
|
|
||||
|
dialogReference.afterClosed().subscribe((d) => { |
||||
|
if (d) |
||||
|
this._userService.get(); |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
<mat-dialog-content class="mat-typography"> |
||||
|
<div class=" py-2 pl-3 pr-3 sm:py-4 md:pl-4 md:pr-6" style="overflow-y: hidden !important;"> |
||||
|
<div style="display: flex;justify-content:space-between;"> |
||||
|
<mat-form-field appearance="outline" style="width: 350px;"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Old Password </strong> |
||||
|
</mat-label> |
||||
|
<input matInput style="margin-left:10px;" type="password" > |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div style="display: flex;justify-content:space-between;"> |
||||
|
<mat-form-field appearance="outline" style="width: 350px;"> |
||||
|
<mat-label style="margin-right: 20px;"> |
||||
|
<strong class="tileHead">New Password</strong> |
||||
|
</mat-label> |
||||
|
<input matInput style="margin-left:10px;" type="password" > |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div style="display: flex;justify-content:space-between;"> |
||||
|
<mat-form-field appearance="outline" style="width: 350px;"> |
||||
|
<mat-label style="margin-right: 48px;"> |
||||
|
<strong class="tileHead">Confirm Password </strong> |
||||
|
</mat-label> |
||||
|
<input matInput style="margin-left:10px;" type="password"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
|
||||
|
<div style="display: flex;justify-content:space-between;width: 100%;"> |
||||
|
<button mat-button style="background-color: green;color: white;">Confirm</button> |
||||
|
<button mat-button style="background-color: maroon;color: white;" [mat-dialog-close]="true" cdkFocusInitial>Cancel</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</mat-dialog-content> |
@ -0,0 +1,17 @@ |
|||||
|
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'settings', |
||||
|
templateUrl: './settings.component.html', |
||||
|
styleUrls: ['./settings.component.scss'], |
||||
|
changeDetection: ChangeDetectionStrategy.OnPush, |
||||
|
exportAs: 'settings' |
||||
|
}) |
||||
|
export class SettingsComponent implements OnInit { |
||||
|
|
||||
|
constructor() { } |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
} |
||||
|
|
||||
|
} |
@ -1 +1,66 @@ |
|||||
<p>profile works!</p> |
<!-- <div class="flex flex-col flex-auto w-full"> |
||||
|
<div class="flex flex-wrap w-full max-w-screen-xl mx-auto p-6 md:p-8"> --> |
||||
|
<div class="columns"> |
||||
|
<div class="col-md-3"> |
||||
|
<div class="card card-primary card-outline"> |
||||
|
<div class="card-body box-profile"> |
||||
|
<form> |
||||
|
<input style="display: none" type="file" (change)="onFileSelected($event)" |
||||
|
#fileInput accept="image/*"> |
||||
|
<div class="text-center"> |
||||
|
<img id="logo" class="profile-user-img img-fluid img-circle" [src]="imageLoader(profile.businessLogo)" |
||||
|
style="height:150px;width:150px;" *ngIf="newProfilePicture === undefined"> |
||||
|
<img id="logo" class="profile-user-img img-fluid img-circle" src="{{newProfilePicture.imageSRC}}" |
||||
|
style="height:150px;width:150px;" *ngIf="newProfilePicture != undefined"> |
||||
|
</div> |
||||
|
<div class="text-center" style="margin-top:10px;"> |
||||
|
<button mat-button style="background-color: #0152cc;color: white;" |
||||
|
(click)="fileInput.click()" *ngIf="newProfilePicture === undefined">Select</button> |
||||
|
<button mat-button style="background-color: rgb(163, 35, 35);color: white;" |
||||
|
(click)="clear()" *ngIf="newProfilePicture != undefined">Clear</button> |
||||
|
</div> |
||||
|
|
||||
|
</form> |
||||
|
|
||||
|
<h3 class="profile-username text-center">{{profile.businessName}}</h3> |
||||
|
<ul class="list-group list-group-unbordered mb-3"> |
||||
|
<li class="list-group-item"> |
||||
|
<b style="margin-right: 10px;">Followers : </b> |
||||
|
<a href="followers" class="float-right" style="text-decoration: none;color: #0152cc;"> {{subscribers.length}}</a> |
||||
|
</li> |
||||
|
<li class="list-group-item"> |
||||
|
<button class=" sm:inline-flex" mat-flat-button style="background-color: #0152cc;margin-right:10px;"> |
||||
|
<a href="javascript:void(0)" style="color: #fff;text-decoration: none;margin-right:10px;" >Generate QR-Code</a> |
||||
|
</button> |
||||
|
<!-- <a class="btn btn-secondary btn-lg" onclick="generateCode"></a> --> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="card card-primary"> |
||||
|
<div class="card-header" style="display: flex;justify-content: space-between;"> |
||||
|
<h3 class="card-title">Shop Description</h3> |
||||
|
<!-- <div class="card-tools" (click)="openEdit()"> --> |
||||
|
<button mat-button style="background: transparent;" (click)="editShopDescription()"> |
||||
|
<mat-icon class="icon-size-3" [svgIcon]="'heroicons_solid:pencil-alt'" style="color: #fff;"></mat-icon> |
||||
|
</button> |
||||
|
<!-- </div> --> |
||||
|
</div> |
||||
|
<div class="card-body"> |
||||
|
<p class="text-muted"> |
||||
|
<strong><i class="fas fa-book mr-1"></i> </strong> |
||||
|
<span id="shop-desc">{{profile.businessDescription}}</span> |
||||
|
</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-9"> |
||||
|
<div class="card"> |
||||
|
<business-information [details]="profile"></business-information> |
||||
|
|
||||
|
|
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- </div> |
||||
|
</div> --> |
@ -0,0 +1,173 @@ |
|||||
|
.card-primary.card-outline { |
||||
|
border-top: 3px solid #0152cc; |
||||
|
} |
||||
|
.card-primary:not(.card-outline) > .card-header, .card-primary:not(.card-outline) > .card-header a { |
||||
|
color: #fff; |
||||
|
} |
||||
|
.card-primary:not(.card-outline) > .card-header { |
||||
|
background-color: #0152cc; |
||||
|
} |
||||
|
|
||||
|
.card { |
||||
|
box-shadow: 0 0 1px rgb(0 0 0 / 13%), 0 1px 3px rgb(0 0 0 / 20%); |
||||
|
margin-bottom: 1rem; |
||||
|
} |
||||
|
.card-header { |
||||
|
background-color: transparent; |
||||
|
border-bottom: 1px solid rgba(0,0,0,.125); |
||||
|
padding: .75rem 1.25rem; |
||||
|
position: relative; |
||||
|
border-top-left-radius: .25rem; |
||||
|
border-top-right-radius: .25rem; |
||||
|
} |
||||
|
|
||||
|
.list-group-unbordered > .list-group-item { |
||||
|
border-left: 0; |
||||
|
border-radius: 0; |
||||
|
border-right: 0; |
||||
|
padding-left: 0; |
||||
|
padding-right: 0; |
||||
|
} |
||||
|
.list-group-item { |
||||
|
position: relative; |
||||
|
display: block; |
||||
|
padding: .75rem 1.25rem; |
||||
|
background-color: #fff; |
||||
|
border: 1px solid rgba(0,0,0,.125); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
.card-body { |
||||
|
-ms-flex: 1 1 auto; |
||||
|
flex: 1 1 auto; |
||||
|
min-height: 1px; |
||||
|
padding: 1.25rem; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.card-body { |
||||
|
-ms-flex: 1 1 auto; |
||||
|
flex: 1 1 auto; |
||||
|
min-height: 1px; |
||||
|
padding: 1.25rem; |
||||
|
} |
||||
|
form { |
||||
|
display: block; |
||||
|
margin-top: 0em; |
||||
|
} |
||||
|
.img-circle { |
||||
|
border-radius: 50%; |
||||
|
} |
||||
|
|
||||
|
.profile-user-img { |
||||
|
border: 3px solid #adb5bd; |
||||
|
margin: 0 auto; |
||||
|
padding: 3px; |
||||
|
width: 100px; |
||||
|
} |
||||
|
|
||||
|
.img-fluid { |
||||
|
max-width: 100%; |
||||
|
height: auto; |
||||
|
} |
||||
|
|
||||
|
img { |
||||
|
vertical-align: middle; |
||||
|
border-style: none; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
.text-center { |
||||
|
text-align: center !important; |
||||
|
} |
||||
|
|
||||
|
.card-title { |
||||
|
float: left; |
||||
|
font-size: 1.1rem; |
||||
|
font-weight: 400; |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
.card-title { |
||||
|
margin-bottom: .75rem; |
||||
|
} |
||||
|
|
||||
|
.card { |
||||
|
position: relative; |
||||
|
display: -ms-flexbox; |
||||
|
display: flex; |
||||
|
-ms-flex-direction: column; |
||||
|
flex-direction: column; |
||||
|
min-width: 0; |
||||
|
word-wrap: break-word; |
||||
|
background-color: #fff; |
||||
|
background-clip: border-box; |
||||
|
border: 0 solid rgba(0,0,0,.125); |
||||
|
border-radius: .25rem; |
||||
|
} |
||||
|
|
||||
|
.card { |
||||
|
position: relative; |
||||
|
display: -ms-flexbox; |
||||
|
display: flex; |
||||
|
-ms-flex-direction: column; |
||||
|
flex-direction: column; |
||||
|
min-width: 0; |
||||
|
word-wrap: break-word; |
||||
|
background-color: #fff; |
||||
|
background-clip: border-box; |
||||
|
border: 0 solid rgba(0,0,0,.125); |
||||
|
border-radius: .25rem; |
||||
|
padding-top: 30px; |
||||
|
} |
||||
|
|
||||
|
.ui-button.btn-primary, .btn-primary.ui-paginator-element { |
||||
|
background-color: #0152cc; |
||||
|
} |
||||
|
|
||||
|
.logo { |
||||
|
display: block; |
||||
|
width: 100px; |
||||
|
margin: 0px auto; |
||||
|
border-radius: 20px; |
||||
|
background: #222; |
||||
|
padding: 10px; |
||||
|
} |
||||
|
|
||||
|
.columns { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
width: 100%; |
||||
|
} |
||||
|
@media only screen and (max-width: 600px) { |
||||
|
.columns { |
||||
|
display: flex; |
||||
|
flex-direction:row; |
||||
|
flex-wrap: wrap; |
||||
|
width: 100%; |
||||
|
} |
||||
|
.col-md-9{ |
||||
|
flex: 30%; |
||||
|
font-size: medium; |
||||
|
margin: 10px; |
||||
|
padding: 10px; |
||||
|
} |
||||
|
} |
||||
|
.column { |
||||
|
flex: 50%; |
||||
|
font-size: medium; |
||||
|
} |
||||
|
.col-md-3{ |
||||
|
flex: 30%; |
||||
|
font-size: medium; |
||||
|
margin: 10px; |
||||
|
padding: 10px; |
||||
|
} |
||||
|
.col-md-9{ |
||||
|
flex: 70%; |
||||
|
font-size: medium; |
||||
|
margin: 10px; |
||||
|
padding: 10px; |
||||
|
} |
@ -1,15 +1,75 @@ |
|||||
import { Component, OnInit } from '@angular/core'; |
import { Component, OnInit, ViewEncapsulation } from '@angular/core'; |
||||
|
import { MatDialog } from '@angular/material/dialog'; |
||||
|
import { Router } from '@angular/router'; |
||||
|
import { ConfirmBoxEvokeService } from '@costlydeveloper/ngx-awesome-popup'; |
||||
|
import { tesoMediaWatcherService } from '@teso/services/media-watcher'; |
||||
|
import { UserService } from 'app/core/user/user.service'; |
||||
|
import { TesoBusinessDetail } from 'app/models/businessModel'; |
||||
|
import { TesoUserDetails } from 'app/models/userModel'; |
||||
|
import { environment } from 'environments/environment'; |
||||
|
import { Subject } from 'rxjs'; |
||||
|
import { takeUntil } from 'rxjs/operators'; |
||||
|
import { FollowersService } from '../Followers/followers.service'; |
||||
|
import { DescriptionDialogComponent } from './DescriptionDialog/description-dialog.component'; |
||||
|
import { ProfileService } from './profile.service'; |
||||
|
|
||||
@Component({ |
@Component({ |
||||
selector: 'app-profile', |
selector: 'app-profile', |
||||
templateUrl: './profile.component.html', |
templateUrl: './profile.component.html', |
||||
styleUrls: ['./profile.component.scss'] |
styleUrls: ['./profile.component.scss'], |
||||
|
encapsulation: ViewEncapsulation.ShadowDom, |
||||
}) |
}) |
||||
export class ProfileComponent implements OnInit { |
export class ProfileComponent implements OnInit { |
||||
|
profile: TesoBusinessDetail = {}; |
||||
constructor() { } |
subscribers:TesoUserDetails[]=[]; |
||||
|
newProfilePicture:any; |
||||
|
private _unsubscribeAll: Subject<any> = new Subject<any>(); |
||||
|
constructor(private router: Router, private _tesoMediaWatcherService: tesoMediaWatcherService, |
||||
|
private _profileService: ProfileService, private _userService: UserService, |
||||
|
private _followersService: FollowersService, public dialog: MatDialog, |
||||
|
private confirmBoxEvokeService: ConfirmBoxEvokeService,) { } |
||||
|
|
||||
ngOnInit(): void { |
ngOnInit(): void { |
||||
|
this._userService.user$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { |
||||
|
this.profile = d; |
||||
|
}); |
||||
|
this._followersService.data$.pipe(takeUntil(this._unsubscribeAll)).subscribe((d) => { |
||||
|
this.subscribers = d; |
||||
|
}); |
||||
|
} |
||||
|
imageLoader(path: string): string { |
||||
|
return environment.apiURL + `shoplogo/${path}`; |
||||
|
} |
||||
|
editShopDescription(){ |
||||
|
const dialogReference = this.dialog.open(DescriptionDialogComponent,{ |
||||
|
disableClose: true, |
||||
|
hasBackdrop: true, |
||||
|
data: { details: this.profile.businessDescription }, |
||||
|
}); |
||||
|
|
||||
|
dialogReference.afterClosed().subscribe((d)=> { |
||||
|
this.profile.businessDescription = d; |
||||
|
}); |
||||
} |
} |
||||
|
onFileSelected(event) { |
||||
|
|
||||
|
const file: File = event.target.files[0]; |
||||
|
if (file.type.includes("image")) { |
||||
|
this.newProfilePicture = { |
||||
|
file: file, |
||||
|
highlight: true, |
||||
|
imageSRC: "", |
||||
|
}; |
||||
|
let reader = new FileReader(); |
||||
|
reader.onload = (event: any) => { |
||||
|
this.newProfilePicture.imageSRC = event.target.result; |
||||
|
} |
||||
|
reader.readAsDataURL(file); |
||||
|
} else { |
||||
|
this.confirmBoxEvokeService.danger("Incompatible File", "Only images can be added", "OK").subscribe(); |
||||
|
} |
||||
|
} |
||||
|
clear(){ |
||||
|
this.newProfilePicture={}; |
||||
|
} |
||||
} |
} |
||||
|
@ -1,12 +1,66 @@ |
|||||
import { NgModule } from '@angular/core'; |
import { NgModule } from '@angular/core'; |
||||
import { CommonModule } from '@angular/common'; |
import { CommonModule } from '@angular/common'; |
||||
|
import { ProfileComponent } from './profile.component'; |
||||
|
import { RouterModule } from '@angular/router'; |
||||
|
import { QuillModule } from 'ngx-quill'; |
||||
|
import { MatButtonModule } from '@angular/material/button'; |
||||
|
import { MatButtonToggleModule } from '@angular/material/button-toggle'; |
||||
|
import { MatRippleModule } from '@angular/material/core'; |
||||
|
import { MatDialogModule } from '@angular/material/dialog'; |
||||
|
import { MatDividerModule } from '@angular/material/divider'; |
||||
|
import { MatFormFieldModule } from '@angular/material/form-field'; |
||||
|
import { MatIconModule } from '@angular/material/icon'; |
||||
|
import { MatInputModule } from '@angular/material/input'; |
||||
|
import { MatMenuModule } from '@angular/material/menu'; |
||||
|
import { MatPaginatorModule } from '@angular/material/paginator'; |
||||
|
import { MatProgressBarModule } from '@angular/material/progress-bar'; |
||||
|
import { MatSelectModule } from '@angular/material/select'; |
||||
|
import { MatSidenavModule } from '@angular/material/sidenav'; |
||||
|
import { MatSortModule } from '@angular/material/sort'; |
||||
|
import { MatTableModule } from '@angular/material/table'; |
||||
|
import { MatTabsModule } from '@angular/material/tabs'; |
||||
|
import { MatTooltipModule } from '@angular/material/tooltip'; |
||||
|
import { TranslocoModule } from '@ngneat/transloco'; |
||||
|
import { SharedModule } from 'app/shared/shared.module'; |
||||
|
import { profileRoutes } from './profile.routing'; |
||||
|
import { InformationComponent } from './Information/information.component'; |
||||
|
import { SettingsComponent } from './Settings/settings.component'; |
||||
|
import { DescriptionDialogComponent } from './DescriptionDialog/description-dialog.component'; |
||||
|
import { InformationDialogComponent } from './InfoDialog/information-dialog.component'; |
||||
|
|
||||
|
|
||||
|
|
||||
@NgModule({ |
@NgModule({ |
||||
declarations: [], |
declarations: [ |
||||
|
ProfileComponent, |
||||
|
InformationComponent, |
||||
|
SettingsComponent, |
||||
|
DescriptionDialogComponent, |
||||
|
InformationDialogComponent |
||||
|
], |
||||
imports: [ |
imports: [ |
||||
CommonModule |
RouterModule.forChild(profileRoutes), |
||||
|
QuillModule.forRoot(), |
||||
|
CommonModule, |
||||
|
MatButtonModule, |
||||
|
MatButtonToggleModule, |
||||
|
MatDividerModule, |
||||
|
MatIconModule, |
||||
|
MatFormFieldModule, |
||||
|
MatInputModule, |
||||
|
MatMenuModule, |
||||
|
MatProgressBarModule, |
||||
|
MatRippleModule, |
||||
|
MatSidenavModule, |
||||
|
MatSortModule, |
||||
|
MatTableModule, |
||||
|
MatTabsModule, |
||||
|
MatDialogModule, |
||||
|
TranslocoModule, |
||||
|
SharedModule, |
||||
|
MatPaginatorModule, |
||||
|
MatSelectModule, |
||||
|
MatTooltipModule, |
||||
] |
] |
||||
}) |
}) |
||||
export class ProfileModule { } |
export class ProfileModule { } |
||||
|
@ -0,0 +1,35 @@ |
|||||
|
import { Route } from '@angular/router'; |
||||
|
import { EditProductComponent } from '../Products/EditProduct/edit-product.component'; |
||||
|
import { NewProductComponent } from '../Products/NewProduct/new-product.component'; |
||||
|
import { ProfileComponent } from './profile.component'; |
||||
|
|
||||
|
export const profileRoutes: Route[] = [ |
||||
|
{ |
||||
|
path : '', |
||||
|
component: ProfileComponent, |
||||
|
// resolve : {
|
||||
|
// data: DesiresResolver
|
||||
|
// },
|
||||
|
// children : [
|
||||
|
// {
|
||||
|
// path : '',
|
||||
|
// pathMatch: 'full',
|
||||
|
// component: ListDesiresComponent,
|
||||
|
// },
|
||||
|
// {
|
||||
|
// path : 'create-product',
|
||||
|
// component: NewProductComponent,
|
||||
|
// // resolve : {
|
||||
|
// // course: AcademyCourseResolver
|
||||
|
// // }
|
||||
|
// },
|
||||
|
// // {
|
||||
|
// // path : ':id',
|
||||
|
// // component: EditProductComponent,
|
||||
|
// // // resolve : {
|
||||
|
// // // course: AcademyCourseResolver
|
||||
|
// // // }
|
||||
|
// // }
|
||||
|
// ]
|
||||
|
} |
||||
|
]; |
@ -0,0 +1,64 @@ |
|||||
|
import { Injectable } from '@angular/core'; |
||||
|
import { HttpClient } from '@angular/common/http'; |
||||
|
import { BehaviorSubject, Observable } from 'rxjs'; |
||||
|
import { tap } from 'rxjs/operators'; |
||||
|
import { analytics as analyticsData } from 'app/mock-api/dashboards/analytics/data'; |
||||
|
import { environment } from 'environments/environment'; |
||||
|
import { BusinessCategory, TesoBusinessDetail } from 'app/models/businessModel'; |
||||
|
|
||||
|
@Injectable({ |
||||
|
providedIn: 'root' |
||||
|
}) |
||||
|
export class ProfileService { |
||||
|
private _data: BehaviorSubject<any> = new BehaviorSubject(null); |
||||
|
private business: TesoBusinessDetail[] = []; |
||||
|
private _businessCategory: BehaviorSubject<BusinessCategory[]> = new BehaviorSubject(null); |
||||
|
|
||||
|
/** |
||||
|
* Constructor |
||||
|
*/ |
||||
|
constructor(private _httpClient: HttpClient) { |
||||
|
} |
||||
|
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
// @ Accessors
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
|
||||
|
/** |
||||
|
* Getter for data |
||||
|
*/ |
||||
|
get data$(): Observable<any> { |
||||
|
return this._data.asObservable(); |
||||
|
} |
||||
|
get categories$(): Observable<BusinessCategory[]> { |
||||
|
return this._businessCategory.asObservable(); |
||||
|
} |
||||
|
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
// @ Public methods
|
||||
|
// -----------------------------------------------------------------------------------------------------
|
||||
|
|
||||
|
/** |
||||
|
* Get data |
||||
|
*/ |
||||
|
getData(business: string = ".null."): Observable<any> { |
||||
|
return this._httpClient.get(environment.apiURL + `business/search/filter?businessName=${business}`).pipe( |
||||
|
tap((response: any) => { |
||||
|
this._data.next(response); |
||||
|
this.business = response; |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
getBusiness(id: string): TesoBusinessDetail { |
||||
|
const found = this.business.find(item => item.businessId === id); |
||||
|
return found; |
||||
|
} |
||||
|
|
||||
|
getCategories(): Observable<BusinessCategory[]> { |
||||
|
return this._httpClient.get(environment.apiURL + `api/businesscategories1`).pipe( |
||||
|
tap((response: BusinessCategory[]) => { |
||||
|
this._businessCategory.next(response); |
||||
|
})); |
||||
|
} |
||||
|
} |
@ -1 +0,0 @@ |
|||||
<p>settings works!</p> |
|
@ -1,15 +0,0 @@ |
|||||
import { Component, OnInit } from '@angular/core'; |
|
||||
|
|
||||
@Component({ |
|
||||
selector: 'app-settings', |
|
||||
templateUrl: './settings.component.html', |
|
||||
styleUrls: ['./settings.component.scss'] |
|
||||
}) |
|
||||
export class SettingsComponent implements OnInit { |
|
||||
|
|
||||
constructor() { } |
|
||||
|
|
||||
ngOnInit(): void { |
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,12 +0,0 @@ |
|||||
import { NgModule } from '@angular/core'; |
|
||||
import { CommonModule } from '@angular/common'; |
|
||||
|
|
||||
|
|
||||
|
|
||||
@NgModule({ |
|
||||
declarations: [], |
|
||||
imports: [ |
|
||||
CommonModule |
|
||||
] |
|
||||
}) |
|
||||
export class SettingsModule { } |
|
@ -1,105 +0,0 @@ |
|||||
<div class="flex flex-col sm:flex-row items-center md:items-start sm:justify-center md:justify-start flex-auto min-w-0"> |
|
||||
<div class="md:flex md:items-center md:justify-end w-full sm:w-auto md:h-full md:w-1/2 py-8 px-4 sm:p-12 md:p-16 sm:rounded-2xl md:rounded-none sm:shadow md:shadow-none sm:bg-card"> |
|
||||
<div class="w-full max-w-80 sm:w-80 mx-auto sm:mx-0"> |
|
||||
<!-- Logo --> |
|
||||
<div class="w-12"> |
|
||||
<img src="assets/images/logo/logo.svg"> |
|
||||
</div> |
|
||||
|
|
||||
<!-- Title --> |
|
||||
<div class="mt-8 text-4xl font-extrabold tracking-tight leading-tight">Unlock your session</div> |
|
||||
<div class="mt-0.5 font-medium">Your session is locked due to inactivity</div> |
|
||||
|
|
||||
<!-- Alert --> |
|
||||
<teso-alert class="mt-8 -mb-4" *ngIf="showAlert" [appearance]="'outline'" [showIcon]="false" [type]="alert.type" [@shake]="alert.type === 'error'"> |
|
||||
{{alert.message}} |
|
||||
</teso-alert> |
|
||||
|
|
||||
<!-- Unlock form --> |
|
||||
<form class="mt-8" [formGroup]="unlockSessionForm" #unlockSessionNgForm="ngForm"> |
|
||||
|
|
||||
<!-- Name field --> |
|
||||
<mat-form-field class="w-full"> |
|
||||
<mat-label>Full name</mat-label> |
|
||||
<input id="name" matInput [formControlName]="'name'"> |
|
||||
</mat-form-field> |
|
||||
|
|
||||
<!-- Password field --> |
|
||||
<mat-form-field class="w-full"> |
|
||||
<mat-label>Password</mat-label> |
|
||||
<input id="password" matInput type="password" [formControlName]="'password'" #passwordField> |
|
||||
<button mat-icon-button type="button" (click)="passwordField.type === 'password' ? passwordField.type = 'text' : passwordField.type = 'password'" matSuffix> |
|
||||
<mat-icon |
|
||||
class="icon-size-5" |
|
||||
*ngIf="passwordField.type === 'password'" |
|
||||
[svgIcon]="'heroicons_solid:eye'"></mat-icon> |
|
||||
<mat-icon |
|
||||
class="icon-size-5" |
|
||||
*ngIf="passwordField.type === 'text'" |
|
||||
[svgIcon]="'heroicons_solid:eye-off'"></mat-icon> |
|
||||
</button> |
|
||||
<mat-error> |
|
||||
Password is required |
|
||||
</mat-error> |
|
||||
</mat-form-field> |
|
||||
|
|
||||
<!-- Submit button --> |
|
||||
<button class="teso-mat-button-large w-full mt-3" mat-flat-button [color]="'primary'" [disabled]="unlockSessionForm.disabled" (click)="unlock()"> |
|
||||
<span *ngIf="!unlockSessionForm.disabled"> |
|
||||
Unlock your session |
|
||||
</span> |
|
||||
<mat-progress-spinner |
|
||||
*ngIf="unlockSessionForm.disabled" |
|
||||
[diameter]="24" |
|
||||
[mode]="'indeterminate'"></mat-progress-spinner> |
|
||||
</button> |
|
||||
|
|
||||
<!-- Form footer --> |
|
||||
<div class="mt-8 text-md font-medium text-secondary"> |
|
||||
<span>I'm not</span> |
|
||||
<a class="ml-1 text-primary-500 hover:underline" [routerLink]="['/sign-out']">{{name}}</a> |
|
||||
</div> |
|
||||
|
|
||||
</form> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="relative hidden md:flex flex-auto items-center justify-center w-1/2 h-full p-16 lg:px-28 overflow-hidden bg-gray-800 dark:border-l"> |
|
||||
<!-- Background - @formatter:off --> |
|
||||
<!-- Rings --> |
|
||||
<svg class="absolute inset-0 pointer-events-none" viewBox="0 0 960 540" width="100%" height="100%" preserveAspectRatio="xMidYMax slice" xmlns="http://www.w3.org/2000/svg"> |
|
||||
<g class="text-gray-700 opacity-25" fill="none" stroke="currentColor" stroke-width="100"> |
|
||||
<circle r="234" cx="196" cy="23"></circle> |
|
||||
<circle r="234" cx="790" cy="491"></circle> |
|
||||
</g> |
|
||||
</svg> |
|
||||
<!-- Dots --> |
|
||||
<svg class="absolute -top-16 -right-16 text-gray-700" viewBox="0 0 220 192" width="220" height="192" fill="none"> |
|
||||
<defs> |
|
||||
<pattern id="837c3e70-6c3a-44e6-8854-cc48c737b659" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse"> |
|
||||
<rect x="0" y="0" width="4" height="4" fill="currentColor"></rect> |
|
||||
</pattern> |
|
||||
</defs> |
|
||||
<rect width="220" height="192" fill="url(#837c3e70-6c3a-44e6-8854-cc48c737b659)"></rect> |
|
||||
</svg> |
|
||||
<!-- @formatter:on --> |
|
||||
<!-- Content --> |
|
||||
<div class="z-10 relative w-full max-w-2xl"> |
|
||||
<div class="text-7xl font-bold leading-none text-gray-100"> |
|
||||
<div>Welcome to</div> |
|
||||
<div>our community</div> |
|
||||
</div> |
|
||||
<div class="mt-6 text-lg tracking-tight leading-6 text-gray-400"> |
|
||||
teso helps developers to build organized and well coded dashboards full of beautiful and rich modules. Join us and start building your application today. |
|
||||
</div> |
|
||||
<div class="flex items-center mt-8"> |
|
||||
<div class="flex flex-0 items-center -space-x-1.5"> |
|
||||
<img class="flex-0 w-10 h-10 rounded-full ring-4 ring-offset-1 ring-gray-800 ring-offset-gray-800 object-cover" src="assets/images/avatars/female-18.jpg"> |
|
||||
<img class="flex-0 w-10 h-10 rounded-full ring-4 ring-offset-1 ring-gray-800 ring-offset-gray-800 object-cover" src="assets/images/avatars/female-11.jpg"> |
|
||||
<img class="flex-0 w-10 h-10 rounded-full ring-4 ring-offset-1 ring-gray-800 ring-offset-gray-800 object-cover" src="assets/images/avatars/male-09.jpg"> |
|
||||
<img class="flex-0 w-10 h-10 rounded-full ring-4 ring-offset-1 ring-gray-800 ring-offset-gray-800 object-cover" src="assets/images/avatars/male-16.jpg"> |
|
||||
</div> |
|
||||
<div class="ml-4 font-medium tracking-tight text-gray-400">More than 17k people joined us, it's your turn</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
@ -1,129 +0,0 @@ |
|||||
import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; |
|
||||
import { FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms'; |
|
||||
import { ActivatedRoute, Router } from '@angular/router'; |
|
||||
import { tesoAnimations } from '@teso/animations'; |
|
||||
import { AuthService } from 'app/core/auth/auth.service'; |
|
||||
import { UserService } from 'app/core/user/user.service'; |
|
||||
import { tesoAlertType } from '@teso/components/alert'; |
|
||||
|
|
||||
@Component({ |
|
||||
selector : 'auth-unlock-session', |
|
||||
templateUrl : './unlock-session.component.html', |
|
||||
encapsulation: ViewEncapsulation.None, |
|
||||
animations : tesoAnimations |
|
||||
}) |
|
||||
export class AuthUnlockSessionComponent implements OnInit |
|
||||
{ |
|
||||
@ViewChild('unlockSessionNgForm') unlockSessionNgForm: NgForm; |
|
||||
|
|
||||
alert: { type: tesoAlertType; message: string } = { |
|
||||
type : 'success', |
|
||||
message: '' |
|
||||
}; |
|
||||
name: string; |
|
||||
showAlert: boolean = false; |
|
||||
unlockSessionForm: FormGroup; |
|
||||
private _email: string; |
|
||||
|
|
||||
/** |
|
||||
* Constructor |
|
||||
*/ |
|
||||
constructor( |
|
||||
private _activatedRoute: ActivatedRoute, |
|
||||
private _authService: AuthService, |
|
||||
private _formBuilder: FormBuilder, |
|
||||
private _router: Router, |
|
||||
private _userService: UserService |
|
||||
) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
// -----------------------------------------------------------------------------------------------------
|
|
||||
// @ Lifecycle hooks
|
|
||||
// -----------------------------------------------------------------------------------------------------
|
|
||||
|
|
||||
/** |
|
||||
* On init |
|
||||
*/ |
|
||||
ngOnInit(): void |
|
||||
{ |
|
||||
// Get the user's name
|
|
||||
this._userService.user$.subscribe((user) => { |
|
||||
this.name = user.name; |
|
||||
this._email = user.email; |
|
||||
}); |
|
||||
|
|
||||
// Create the form
|
|
||||
this.unlockSessionForm = this._formBuilder.group({ |
|
||||
name : [ |
|
||||
{ |
|
||||
value : this.name, |
|
||||
disabled: true |
|
||||
} |
|
||||
], |
|
||||
password: ['', Validators.required] |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// -----------------------------------------------------------------------------------------------------
|
|
||||
// @ Public methods
|
|
||||
// -----------------------------------------------------------------------------------------------------
|
|
||||
|
|
||||
/** |
|
||||
* Unlock |
|
||||
*/ |
|
||||
unlock(): void |
|
||||
{ |
|
||||
// Return if the form is invalid
|
|
||||
if ( this.unlockSessionForm.invalid ) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// Disable the form
|
|
||||
this.unlockSessionForm.disable(); |
|
||||
|
|
||||
// Hide the alert
|
|
||||
this.showAlert = false; |
|
||||
|
|
||||
this._authService.unlockSession({ |
|
||||
email : this._email ?? '', |
|
||||
password: this.unlockSessionForm.get('password').value |
|
||||
}).subscribe( |
|
||||
() => { |
|
||||
|
|
||||
// Set the redirect url.
|
|
||||
// The '/signed-in-redirect' is a dummy url to catch the request and redirect the user
|
|
||||
// to the correct page after a successful sign in. This way, that url can be set via
|
|
||||
// routing file and we don't have to touch here.
|
|
||||
const redirectURL = this._activatedRoute.snapshot.queryParamMap.get('redirectURL') || '/signed-in-redirect'; |
|
||||
|
|
||||
// Navigate to the redirect url
|
|
||||
this._router.navigateByUrl(redirectURL); |
|
||||
|
|
||||
}, |
|
||||
(response) => { |
|
||||
|
|
||||
// Re-enable the form
|
|
||||
this.unlockSessionForm.enable(); |
|
||||
|
|
||||
// Reset the form
|
|
||||
this.unlockSessionNgForm.resetForm({ |
|
||||
name: { |
|
||||
value : this.name, |
|
||||
disabled: true |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// Set the alert
|
|
||||
this.alert = { |
|
||||
type : 'error', |
|
||||
message: 'Invalid password' |
|
||||
}; |
|
||||
|
|
||||
// Show the alert
|
|
||||
this.showAlert = true; |
|
||||
} |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,32 +0,0 @@ |
|||||
import { NgModule } from '@angular/core'; |
|
||||
import { RouterModule } from '@angular/router'; |
|
||||
import { MatButtonModule } from '@angular/material/button'; |
|
||||
import { MatFormFieldModule } from '@angular/material/form-field'; |
|
||||
import { MatIconModule } from '@angular/material/icon'; |
|
||||
import { MatInputModule } from '@angular/material/input'; |
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; |
|
||||
import { tesoCardModule } from '@teso/components/card'; |
|
||||
import { tesoAlertModule } from '@teso/components/alert'; |
|
||||
import { SharedModule } from 'app/shared/shared.module'; |
|
||||
import { AuthUnlockSessionComponent } from 'app/pages/auth/unlock-session/unlock-session.component'; |
|
||||
import { authUnlockSessionRoutes } from 'app/pages/auth/unlock-session/unlock-session.routing'; |
|
||||
|
|
||||
@NgModule({ |
|
||||
declarations: [ |
|
||||
AuthUnlockSessionComponent |
|
||||
], |
|
||||
imports : [ |
|
||||
RouterModule.forChild(authUnlockSessionRoutes), |
|
||||
MatButtonModule, |
|
||||
MatFormFieldModule, |
|
||||
MatIconModule, |
|
||||
MatInputModule, |
|
||||
MatProgressSpinnerModule, |
|
||||
tesoCardModule, |
|
||||
tesoAlertModule, |
|
||||
SharedModule |
|
||||
] |
|
||||
}) |
|
||||
export class AuthUnlockSessionModule |
|
||||
{ |
|
||||
} |
|
@ -1,9 +0,0 @@ |
|||||
import { Route } from '@angular/router'; |
|
||||
import { AuthUnlockSessionComponent } from 'app/pages/auth/unlock-session/unlock-session.component'; |
|
||||
|
|
||||
export const authUnlockSessionRoutes: Route[] = [ |
|
||||
{ |
|
||||
path : '', |
|
||||
component: AuthUnlockSessionComponent |
|
||||
} |
|
||||
]; |
|
@ -0,0 +1,13 @@ |
|||||
|
import { Pipe, PipeTransform } from '@angular/core'; |
||||
|
|
||||
|
@Pipe({ |
||||
|
name: 'couponWorth' |
||||
|
}) |
||||
|
export class CouponWorthPipe implements PipeTransform { |
||||
|
|
||||
|
transform(value: number,rate:number): string { |
||||
|
var worth = (rate * value)/100; |
||||
|
return `GH¢ ${worth}`; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
import { Pipe, PipeTransform } from "@angular/core"; |
||||
|
|
||||
|
@Pipe({ |
||||
|
name: 'productDescShort' |
||||
|
}) |
||||
|
export class ProductDescriptionShort implements PipeTransform { |
||||
|
|
||||
|
constructor( |
||||
|
) { } |
||||
|
|
||||
|
transform(value: string): string { |
||||
|
if(value.length > 57){ |
||||
|
return value.substring(0, 58) + "...."; |
||||
|
}else{ |
||||
|
return value; |
||||
|
} |
||||
|
} |
||||
|
} |
After Width: | Height: | Size: 568 B |
Loading…
Reference in new issue