mirror of
https://github.com/Nezumi-2711/foodify-frontend.git
synced 2026-09-23 04:09:52 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { content } from './shared/routes/content-routes';
|
||||
import { ContentLayoutComponent } from './shared/layout/content-layout/content-layout.component';
|
||||
import { LoginComponent } from './components/auth/login/login.component';
|
||||
import { SignupComponent } from './components/auth/signup/signup.component';
|
||||
import { ForbiddenComponent } from './components/auth/forbidden/forbidden.component';
|
||||
import { ResetPasswordComponent } from './components/auth/reset-password/reset-password.component';
|
||||
import { NotLogged } from './shared/guard/not-logged.service';
|
||||
import { EmailVerifiedComponent } from './components/auth/email-verified/email-verified.component';
|
||||
import { EmailNotVerifiedComponent } from './components/auth/email-not-verified/email-not-verified.component';
|
||||
import { SendEmailComponent } from './components/auth/send-email/send-email.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: '/dashboard/default',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: ContentLayoutComponent,
|
||||
children: content
|
||||
},
|
||||
{
|
||||
path: 'auth/login',
|
||||
canActivate: [NotLogged],
|
||||
component: LoginComponent,
|
||||
},
|
||||
{
|
||||
path: 'auth/signup',
|
||||
canActivate: [NotLogged],
|
||||
component: SignupComponent
|
||||
},
|
||||
{
|
||||
path: 'auth/send-email',
|
||||
canActivate: [NotLogged],
|
||||
component: SendEmailComponent
|
||||
},
|
||||
{
|
||||
path: 'auth/forbidden',
|
||||
canActivate: [NotLogged],
|
||||
component: ForbiddenComponent
|
||||
},
|
||||
{
|
||||
path: 'auth/reset-password',
|
||||
canActivate: [NotLogged],
|
||||
component: ResetPasswordComponent
|
||||
},
|
||||
{
|
||||
path: 'auth/email-verified',
|
||||
canActivate: [NotLogged],
|
||||
component: EmailVerifiedComponent
|
||||
},
|
||||
{
|
||||
path: 'auth/email-not-verified',
|
||||
canActivate: [NotLogged],
|
||||
component: EmailNotVerifiedComponent
|
||||
},
|
||||
{
|
||||
path: '**', // Navigate to Home Page if not found any page
|
||||
redirectTo: '/dashboard/default',
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, {
|
||||
scrollPositionRestoration: 'enabled'
|
||||
})],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
@@ -0,0 +1 @@
|
||||
<router-outlet></router-outlet>
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'foodify-backend';
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AgGridModule } from '@ag-grid-community/angular';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
import { DashboardModule } from './components/dashboard/dashboard.module';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
import { ProductsModule } from './components/products/products.module';
|
||||
import { SalesModule } from './components/sales/sales.module';
|
||||
import { CouponsModule } from './components/coupons/coupons.module';
|
||||
import { PagesModule } from './components/pages/pages.module';
|
||||
import { MediaModule } from './components/media/media.module';
|
||||
import { MenusModule } from './components/menus/menus.module';
|
||||
import { VendorsModule } from './components/vendors/vendors.module';
|
||||
import { UsersModule } from './components/users/users.module';
|
||||
import { LocalizationModule } from './components/localization/localization.module';
|
||||
import { InvoiceModule } from './components/invoice/invoice.module';
|
||||
import { SettingModule } from './components/setting/setting.module';
|
||||
import { ReportsModule } from './components/reports/reports.module';
|
||||
import { AuthModule } from './components/auth/auth.module';
|
||||
import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http";
|
||||
import { ModalModule } from "ngx-bootstrap/modal";
|
||||
import { AngularFireStorageModule } from "@angular/fire/compat/storage";
|
||||
import { AngularFireModule } from "@angular/fire/compat";
|
||||
import { environment } from "../environments/environment";
|
||||
import { FirebaseService } from "./shared/service/firebase.service";
|
||||
import { AuthInterceptor } from "./shared/inceptor/auth-interceptor";
|
||||
import { NgImageSliderModule } from 'ng-image-slider'
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserAnimationsModule,
|
||||
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||
AppRoutingModule,
|
||||
DashboardModule,
|
||||
InvoiceModule,
|
||||
SettingModule,
|
||||
ReportsModule,
|
||||
AuthModule,
|
||||
SharedModule,
|
||||
LocalizationModule,
|
||||
ProductsModule,
|
||||
SalesModule,
|
||||
// NgImageSliderModule,
|
||||
VendorsModule,
|
||||
CouponsModule,
|
||||
PagesModule,
|
||||
MediaModule,
|
||||
MenusModule,
|
||||
UsersModule,
|
||||
AgGridModule,
|
||||
HttpClientModule,
|
||||
AngularFireStorageModule,
|
||||
AngularFireModule.initializeApp(environment.firebaseConfig, 'cloud'),
|
||||
ModalModule.forRoot()
|
||||
|
||||
],
|
||||
providers: [
|
||||
FirebaseService,
|
||||
{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
useClass: AuthInterceptor,
|
||||
multi: true
|
||||
}
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { Routes, RouterModule } from "@angular/router";
|
||||
import { LoginComponent } from "./login/login.component";
|
||||
import { ForbiddenComponent } from "./forbidden/forbidden.component";
|
||||
import { SignupComponent } from "./signup/signup.component";
|
||||
import { ResetPasswordComponent } from "./reset-password/reset-password.component";
|
||||
import { SendEmailComponent } from "./send-email/send-email.component";
|
||||
import { EmailVerifiedComponent } from "./email-verified/email-verified.component";
|
||||
|
||||
const routes: Routes = [
|
||||
// {
|
||||
// path: "login",
|
||||
// component: LoginComponent,
|
||||
// },
|
||||
// {
|
||||
// path: "forbidden",
|
||||
// component: ForbiddenComponent,
|
||||
// },
|
||||
// {
|
||||
// path: "signup",
|
||||
// component: SignupComponent,
|
||||
// },
|
||||
// {
|
||||
// path: "send-email",
|
||||
// component: SendEmailComponent,
|
||||
// },
|
||||
// {
|
||||
// path: "reset-password",
|
||||
// component: ResetPasswordComponent,
|
||||
// },
|
||||
// {
|
||||
// path: "email-verified",
|
||||
// component: EmailVerifiedComponent,
|
||||
// },
|
||||
// {
|
||||
// path: "email-not-verified",
|
||||
// component: EmailVerifiedComponent,
|
||||
// }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AuthRoutingModule { }
|
||||
@@ -0,0 +1,29 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { CommonModule } from "@angular/common";
|
||||
|
||||
import { AuthRoutingModule } from "./auth-routing.module";
|
||||
import { LoginComponent } from "./login/login.component";
|
||||
|
||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
|
||||
import { CarouselModule } from "ngx-owl-carousel-o";
|
||||
import { SharedModule } from "../../shared/shared.module";
|
||||
import { ForbiddenComponent } from "./forbidden/forbidden.component";
|
||||
import { SignupComponent } from "./signup/signup.component";
|
||||
import { ResetPasswordComponent } from './reset-password/reset-password.component';
|
||||
import { SendEmailComponent } from './send-email/send-email.component';
|
||||
import { EmailVerifiedComponent } from './email-verified/email-verified.component';
|
||||
import { EmailNotVerifiedComponent } from './email-not-verified/email-not-verified.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [LoginComponent, SignupComponent, ForbiddenComponent, ResetPasswordComponent, SendEmailComponent, EmailVerifiedComponent, EmailNotVerifiedComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AuthRoutingModule,
|
||||
ReactiveFormsModule,
|
||||
CarouselModule,
|
||||
SharedModule,
|
||||
NgbModule,
|
||||
FormsModule],
|
||||
})
|
||||
export class AuthModule { }
|
||||
@@ -0,0 +1,15 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="backgound-food d-flex justify-content-center align-items-center">
|
||||
<div class="forbidden-box d-flex flex-column justify-content-center align-items-center responsive-box">
|
||||
<img class="forbidden-img" src="../../../../assets/images/dashboard/403-forbidden.png">
|
||||
<div class="forbidden-text"><span>Email</span> Not Verified</div>
|
||||
<div class="forbidden-desc mt-1 mb-4">
|
||||
<div>Tài khoản của bạn chưa được xác minh.</div>
|
||||
<div>Vui lòng kiểm tra email để kích hoạt tài khoản.</div>
|
||||
</div>
|
||||
<div class="forbidden-btn">
|
||||
<button class="btn btn-primary return-btn" (click)="goToGmail()">Đi tới mail</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,60 @@
|
||||
.backgound-food {
|
||||
background-image: url("../../../../assets/images/background-login.jpg");
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
z-index: -99;
|
||||
|
||||
.forbidden-box {
|
||||
width: 822px;
|
||||
// height: 536px;
|
||||
background-color: #fff;
|
||||
padding: 16px 110px 40px;
|
||||
border-radius: 10px;
|
||||
box-shadow: rgb(38, 57, 77) 0px 20px 30px -10px;
|
||||
|
||||
.forbidden-img {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.forbidden-text {
|
||||
font-family: 'Sedgwick Ave Display', cursive;
|
||||
text-transform: uppercase;
|
||||
font-size: 50px;
|
||||
|
||||
span {
|
||||
color: lightcoral;
|
||||
font-size: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.forbidden-desc {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
font-family: 'Nunito', sans-serif;
|
||||
}
|
||||
|
||||
.forbidden-btn {}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width:768px) {
|
||||
.backgound-food {
|
||||
background-image: none;
|
||||
|
||||
.responsive-box {
|
||||
width: 86%;
|
||||
padding: 16px 20px 40px;
|
||||
|
||||
.forbidden-img {
|
||||
max-width: 248px;
|
||||
}
|
||||
|
||||
.forbidden-text {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EmailNotVerifiedComponent } from './email-not-verified.component';
|
||||
|
||||
describe('EmailNotVerifiedComponent', () => {
|
||||
let component: EmailNotVerifiedComponent;
|
||||
let fixture: ComponentFixture<EmailNotVerifiedComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ EmailNotVerifiedComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(EmailNotVerifiedComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-email-not-verified',
|
||||
templateUrl: './email-not-verified.component.html',
|
||||
styleUrls: ['./email-not-verified.component.scss']
|
||||
})
|
||||
export class EmailNotVerifiedComponent {
|
||||
goToGmail() {
|
||||
window.location.assign("https://mail.google.com/");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="backgound-food d-flex justify-content-center align-items-center">
|
||||
<div class="forbidden-box d-flex flex-column justify-content-center align-items-center responsive-box">
|
||||
<img class="forbidden-img" src="../../../../assets/images/dashboard/verified-vuong.png">
|
||||
<div class="forbidden-text"><span>Email</span> Verified</div>
|
||||
<div class="forbidden-desc mt-1 mb-4">
|
||||
<div>Tài khoản của bạn đã được xác minh</div>
|
||||
</div>
|
||||
<div class="forbidden-btn w-100 d-flex justify-content-around align-items-center ">
|
||||
<button class="btn btn-primary return-btn" [routerLink]="['/auth', 'login']">Đăng nhập cửa hàng</button>
|
||||
<a href="https://foodify-fpt.vercel.app/home/login"><button class="btn btn-info return-btn">Đăng nhập
|
||||
người dùng</button></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,60 @@
|
||||
.backgound-food {
|
||||
background-image: url("../../../../assets/images/background-login.jpg");
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
z-index: -99;
|
||||
|
||||
.forbidden-box {
|
||||
width: 822px;
|
||||
// height: 536px;
|
||||
background-color: #fff;
|
||||
padding: 16px 110px 40px;
|
||||
border-radius: 10px;
|
||||
box-shadow: rgb(38, 57, 77) 0px 20px 30px -10px;
|
||||
|
||||
.forbidden-img {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.forbidden-text {
|
||||
font-family: 'Sedgwick Ave Display', cursive;
|
||||
text-transform: uppercase;
|
||||
font-size: 50px;
|
||||
|
||||
span {
|
||||
color: rgb(128, 240, 186);
|
||||
font-size: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.forbidden-desc {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
font-family: 'Nunito', sans-serif;
|
||||
}
|
||||
|
||||
.forbidden-btn {}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width:768px) {
|
||||
.backgound-food {
|
||||
background-image: none;
|
||||
|
||||
.responsive-box {
|
||||
width: 86%;
|
||||
padding: 16px 20px 40px;
|
||||
|
||||
.forbidden-img {
|
||||
max-width: 248px;
|
||||
}
|
||||
|
||||
.forbidden-text {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EmailVerifiedComponent } from './email-verified.component';
|
||||
|
||||
describe('EmailVerifiedComponent', () => {
|
||||
let component: EmailVerifiedComponent;
|
||||
let fixture: ComponentFixture<EmailVerifiedComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ EmailVerifiedComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(EmailVerifiedComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-email-verified',
|
||||
templateUrl: './email-verified.component.html',
|
||||
styleUrls: ['./email-verified.component.scss']
|
||||
})
|
||||
export class EmailVerifiedComponent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="backgound-food d-flex justify-content-center align-items-center">
|
||||
<div class="forbidden-box d-flex flex-column justify-content-center align-items-center responsive-box">
|
||||
<img class="forbidden-img" src="../../../../assets/images/dashboard/403-forbidden.png">
|
||||
<div class="forbidden-text"><span>403</span> Forbidden</div>
|
||||
<div class="forbidden-desc mt-1 mb-4">
|
||||
<div>Bạn không có quyền truy cập vào trang web này</div>
|
||||
<div>hoặc tài khoản của bạn đã bị khoá.</div>
|
||||
</div>
|
||||
<div class="forbidden-btn">
|
||||
<button class="btn btn-primary return-btn" [routerLink]="['/auth', 'login']">Quay về</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
|
||||
.backgound-food {
|
||||
background-image: url("../../../../assets/images/background-login.jpg");
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
z-index: -99;
|
||||
|
||||
.forbidden-box {
|
||||
width: 822px;
|
||||
// height: 536px;
|
||||
background-color: #fff;
|
||||
padding: 16px 110px 40px;
|
||||
border-radius: 10px;
|
||||
box-shadow: rgb(38, 57, 77) 0px 20px 30px -10px;
|
||||
|
||||
.forbidden-img {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.forbidden-text {
|
||||
font-family: 'Sedgwick Ave Display', cursive;
|
||||
text-transform: uppercase;
|
||||
font-size: 50px;
|
||||
|
||||
span {
|
||||
color: lightcoral;
|
||||
font-size: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.forbidden-desc {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
font-family: 'Nunito', sans-serif;
|
||||
}
|
||||
|
||||
.forbidden-btn {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width:768px) {
|
||||
.backgound-food {
|
||||
background-image: none;
|
||||
|
||||
.responsive-box {
|
||||
width: 86%;
|
||||
padding: 16px 20px 40px;
|
||||
|
||||
.forbidden-img {
|
||||
max-width: 248px;
|
||||
}
|
||||
|
||||
.forbidden-text {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-forbidden',
|
||||
templateUrl: './forbidden.component.html',
|
||||
styleUrls: ['./forbidden.component.scss']
|
||||
})
|
||||
export class ForbiddenComponent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<div class="page-wrapper">
|
||||
<div class="authentication-box backgound-food">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-5 p-0 card-left disable-pink">
|
||||
<div class="card bg-primary">
|
||||
<div class="svg-icon">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 468.52 468.52"
|
||||
style="enable-background:new 0 0 468.52 468.52;" xml:space="preserve">
|
||||
<g>
|
||||
<path style="fill:#E6E6E6;" d="M468.52,102.595v251.88c0,11.14-7.571,20.52-17.86,23.25c-1.98,0.531-4.06,0.81-6.2,0.81H38.58
|
||||
c-13.29,0-24.061-10.77-24.061-24.06v-251.88c0-13.29,10.771-24.06,24.061-24.06h405.88c2.14,0,4.22,0.28,6.2,0.81
|
||||
C460.949,82.075,468.52,91.455,468.52,102.595" />
|
||||
<path style="fill:#B3B3B3;" d="M468.518,102.591v23.94h-454v-23.94c0-13.29,10.771-24.06,24.06-24.06h405.88
|
||||
C457.747,78.532,468.518,89.302,468.518,102.591" />
|
||||
<rect x="42.099" y="141.002" style="fill:#999999;" width="118.901"
|
||||
height="16.532" />
|
||||
<polyline style="fill:#FFFFFF;"
|
||||
points="442.383,222.765 256.652,222.765 256.652,354.496 442.383,354.496 442.383,222.765 " />
|
||||
<polyline style="fill:#13c9ca;"
|
||||
points="434.552,329.733 265.552,329.733 265.552,346.265 434.552,346.265 434.552,329.733 " />
|
||||
<polyline style="fill:#a5a5a5;"
|
||||
points="286.552,297.744 265.552,297.744 265.552,314.276 286.552,314.276 286.552,297.744 " />
|
||||
<polyline style="fill:#ffbc58;"
|
||||
points="350.552,265.754 265.552,265.754 265.552,282.286 350.552,282.286 350.552,265.754 " />
|
||||
<polyline style="fill:#999999;"
|
||||
points="411.552,233.765 265.552,233.765 265.552,250.297 411.552,250.297 411.552,233.765 " />
|
||||
<circle style="fill:#a5a5a5;" cx="47.517" cy="102.532" r="15" />
|
||||
<circle style="fill:#ffbc58;" cx="87.517" cy="102.532" r="15" />
|
||||
<path style="fill:#EEEEEE;" d="M463.696,88.139c3.027,4.024,4.822,9.029,4.822,14.453v23.94h-23.321
|
||||
c0.04,35.899-19.42,70.584-53.444,88.347c-2.102,0.921-4.223,1.779-6.365,2.574l2.774,5.313h54.222v131.731H298.563l7.326,24.038
|
||||
h138.57c2.14,0,4.22-0.28,6.2-0.81c10.29-2.73,17.86-12.11,17.86-23.25v-251.88C468.52,97.17,466.725,92.164,463.696,88.139
|
||||
M463.657,88.088c0.012,0.016,0.023,0.031,0.036,0.047C463.681,88.12,463.669,88.103,463.657,88.088 M463.621,88.04
|
||||
c0.008,0.01,0.015,0.02,0.023,0.03C463.636,88.06,463.629,88.05,463.621,88.04 M463.59,87.999c0.003,0.004,0.006,0.008,0.009,0.012
|
||||
C463.596,88.007,463.593,88.003,463.59,87.999" />
|
||||
<path style="fill:#CBCBCB;" d="M444.46,78.535h-11.739c0.396,0.716,0.784,1.437,1.165,2.167c7.655,14.662,11.293,30.36,11.31,45.83
|
||||
h23.321v-23.94c0-5.424-1.795-10.429-4.822-14.453l0,0c-0.001-0.001-0.002-0.002-0.003-0.003l0,0
|
||||
c-0.012-0.016-0.023-0.031-0.036-0.047l0,0c-0.005-0.006-0.009-0.012-0.014-0.018l0,0c-0.008-0.01-0.015-0.02-0.023-0.03l0,0
|
||||
c-0.007-0.01-0.014-0.019-0.022-0.029l0,0c-0.003-0.004-0.006-0.008-0.009-0.012l0,0c-3.187-4.171-7.708-7.269-12.93-8.654
|
||||
C448.68,78.815,446.6,78.535,444.46,78.535" />
|
||||
<path style="fill:#FFFFFF;" d="M442.383,222.765h-54.222l5.326,10.202c-0.519,0.271-1.04,0.537-1.561,0.798h19.626v16.532H266.807
|
||||
l4.711,15.457h79.034v16.532h-73.996l4.711,15.458h5.285v16.532h-0.246l4.711,15.457h143.536v16.532H296.055l2.509,8.231h143.819
|
||||
V222.765" />
|
||||
<polyline style="fill:#6EC6EB;"
|
||||
points="434.552,329.733 291.016,329.733 296.055,346.265 434.552,346.265 434.552,329.733 " />
|
||||
<polyline style="fill:#FDA589;"
|
||||
points="286.552,297.744 281.267,297.744 286.306,314.276 286.552,314.276 286.552,297.744 " />
|
||||
<polyline style="fill:#FBE98E;"
|
||||
points="350.552,265.754 271.518,265.754 276.556,282.286 350.552,282.286 350.552,265.754 " />
|
||||
<path style="fill:#EEEEEE;" d="M246.265,126.531h-17.178l29.329,96.234h62.908c-1.465-0.282-2.926-0.591-4.384-0.929l-1.001-0.232
|
||||
c-24.282-7.625-45.651-24.51-58.362-48.855C249.857,157.962,246.221,142.128,246.265,126.531" />
|
||||
<path style="fill:#CBCBCB;" d="M258.73,78.535h-44.27l14.628,47.996h17.178c0.028-10.08,1.594-20.062,4.587-29.594
|
||||
C252.852,90.569,255.489,84.4,258.73,78.535" />
|
||||
<path style="fill:#FFFFFF;" d="M321.324,222.765h-62.908l7.136,23.415v-12.415h53.983l3.362-10.708
|
||||
C322.373,222.963,321.848,222.866,321.324,222.765" />
|
||||
<path style="fill:#BABABA;" d="M411.552,233.765h-19.626c-14.25,7.129-29.421,10.514-44.383,10.514
|
||||
c-10.173,0-20.251-1.566-29.869-4.586l1.861-5.928h-53.983v12.415l1.255,4.117h144.745V233.765" />
|
||||
<path style="fill:#CECECE;"
|
||||
d="M385.387,217.453c-6.196,2.3-12.551,4.073-18.994,5.313h21.767L385.387,217.453" />
|
||||
<path style="fill:#DBDBDB;"
|
||||
d="M388.161,222.765h-21.767c-7.428,1.428-14.973,2.147-22.536,2.147
|
||||
c-6.999,0-14.015-0.616-20.959-1.855l-3.362,10.708h72.391c0.521-0.261,1.042-0.527,1.561-0.798L388.161,222.765" />
|
||||
<path style="fill:#A4A4A4;" d="M391.926,233.765h-72.391l-1.861,5.928c9.618,3.02,19.696,4.586,29.869,4.586
|
||||
C362.505,244.279,377.677,240.894,391.926,233.765" />
|
||||
<path style="fill:#DBDBDB;" d="M345.64,27.261c-15.507,0-31.24,3.637-45.936,11.309c-17.955,9.374-31.855,23.466-40.973,39.961
|
||||
h173.987c-12.828-23.18-33.641-39.288-57.199-46.682C365.898,28.829,355.817,27.261,345.64,27.261" />
|
||||
<polyline style="fill:#9F9F9F;"
|
||||
points="432.719,78.532 258.732,78.532 258.73,78.535 432.721,78.535 432.719,78.532 " />
|
||||
<path style="fill:#CECECE;"
|
||||
d="M445.196,126.531H246.265c-0.044,15.597,3.592,31.43,11.312,46.218
|
||||
c12.711,24.345,34.08,41.23,58.362,48.855l1.001,0.232c1.458,0.338,2.919,0.647,4.384,0.929h45.069
|
||||
c6.443-1.239,12.797-3.012,18.994-5.313c2.142-0.795,4.263-1.653,6.365-2.574C425.776,197.115,445.237,162.431,445.196,126.531" />
|
||||
<path style="fill:#B2B2B2;" d="M432.721,78.535H258.73c-3.241,5.865-5.878,12.033-7.878,18.402
|
||||
c-2.993,9.532-4.559,19.515-4.587,29.594h198.931c-0.017-15.469-3.656-31.168-11.31-45.83
|
||||
C433.505,79.972,433.117,79.251,432.721,78.535" />
|
||||
<path style="fill:#DBDBDB;" d="M366.393,222.765h-45.069c0.524,0.101,1.049,0.198,1.574,0.292
|
||||
c6.945,1.239,13.96,1.855,20.959,1.855C351.42,224.912,358.965,224.193,366.393,222.765" />
|
||||
<path style="fill:#ffbc58;" d="M345.73,113.225l-5.24,16.69l-24.55,78.18c-24.28-7.62-45.65-24.51-58.36-48.85
|
||||
c-12.71-24.35-14.35-51.53-6.73-75.81l83.46,26.2L345.73,113.225z" />
|
||||
<path style="fill:#13c9ca;" d="M393.49,219.465c-7.48,3.9-15.23,6.76-23.08,8.63c-17.7,4.21-35.91,3.38-52.74-1.91l26.47-84.29
|
||||
l3.32-10.59L393.49,219.465z" />
|
||||
<path style="fill:#a5a5a5;" d="M391.75,201.375l-46.02-88.15l-11.42-3.59l-83.46-26.2c7.63-24.29,24.51-45.66,48.85-58.37
|
||||
c2.55-1.33,5.12-2.54,7.73-3.62c22.27-9.33,46.35-9.93,68.09-3.1c24.29,7.62,45.66,24.5,58.37,48.85
|
||||
C459.3,115.885,440.44,175.955,391.75,201.375z" />
|
||||
<polyline style="fill:#FFFFFF;"
|
||||
points="226.383,168.765 40.652,168.765 40.652,354.496 226.383,354.496 226.383,168.765 " />
|
||||
<polyline style="fill:#2BE0C6;"
|
||||
points="93.6,254.001 57.602,254.001 57.602,343.996 93.6,343.996 93.6,254.001 " />
|
||||
<polyline style="fill:#ffbc58;"
|
||||
points="151.517,224.002 115.518,224.002 115.518,343.996 151.517,343.996 151.517,224.002 " />
|
||||
<polyline style="fill:#a5a5a5;"
|
||||
points="209.432,191.004 173.435,191.004 173.435,343.996 209.432,343.996 209.432,191.004 " />
|
||||
<polyline style="fill:#13c9ca;"
|
||||
points="242.83,325.765 242.83,454.765 0,454.765 0,325.765 242.83,325.765 " />
|
||||
<polyline style="fill:#F2F2F2;"
|
||||
points="15,439.765 15,340.765 227.826,340.765 227.826,439.765 15,439.765 " />
|
||||
<polyline style="fill:#a5a5a5;"
|
||||
points="108.064,430.978 87.408,408.6 78.664,420.466 71.9,415.483 86.68,395.426 106.349,416.734
|
||||
137.239,360.959 160.785,398.8 188.393,349.552 195.722,353.66 161.162,415.307 137.661,377.538 108.064,430.978 " />
|
||||
<rect x="26" y="357.765" style="fill:#999999;" width="27" height="11" />
|
||||
<rect x="26" y="384.765" style="fill:#999999;" width="27" height="11" />
|
||||
<rect x="26" y="411.765" style="fill:#999999;" width="27" height="11" />
|
||||
<polyline style="fill:#6EC6EB;" points="242.83,325.765 209.432,325.765 173.435,325.765 151.517,325.765 115.518,325.765
|
||||
93.6,325.765 92.88,325.765 97.451,340.765 227.826,340.765 227.826,439.765 127.619,439.765 132.19,454.765 242.83,454.765
|
||||
242.83,325.765 " />
|
||||
<polyline style="fill:#F6F6F6;" points="227.826,340.765 97.451,340.765 115.542,400.135 137.239,360.959 160.785,398.8
|
||||
188.393,349.552 195.722,353.66 161.162,415.307 137.661,377.538 118.951,411.32 127.619,439.765 227.826,439.765 227.826,340.765
|
||||
" />
|
||||
<polyline style="fill:#FDA589;" points="188.393,349.552 160.785,398.8 137.239,360.959 115.542,400.135 118.951,411.32
|
||||
137.661,377.538 161.162,415.307 195.722,353.66 188.393,349.552 " />
|
||||
<path style="fill:#CCCCCC;"
|
||||
d="M393.695,417.909l8.485-20.662l25.251,25.251c1.464,1.464,3.387,2.199,5.303,2.192
|
||||
c1.923,0,3.84-0.728,5.303-2.192c2.934-2.934,2.927-7.679,0-10.607l-25.251-25.251l20.669-8.492l-78.559-38.792L393.695,417.909z" />
|
||||
<path style="fill:#808080;" d="M393.695,417.909l8.485-20.662l25.251,25.251c1.464,1.464,3.387,2.199,5.303,2.192
|
||||
c1.923,0,3.84-0.728,5.303-2.192l-83.142-83.142L393.695,417.909z" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="single-item">
|
||||
<owl-carousel-o [options]="owlcarouselOptions">
|
||||
<ng-container *ngFor="let data of owlcarousel">
|
||||
<ng-template carouselSlide class="item">
|
||||
<div>
|
||||
<h3>{{data.title}}</h3>
|
||||
<p>{{data.desc}}</p>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</owl-carousel-o>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7 p-0 card-right">
|
||||
<div class="card tab2-card">
|
||||
<div style="padding: 46px;" class="card-body">
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
|
||||
<li [ngbNavItem]="1">
|
||||
<h3 style="color: lightcoral" ngbNavLink>Đăng nhập</h3>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="login-img-box d-flex justify-content-center align-items-center">
|
||||
<img class="login-img"
|
||||
src="../../../../assets/images/dashboard/Logo web.png">
|
||||
</div>
|
||||
<div class="tab-pane fade active show" id="account" role="tabpanel"
|
||||
aria-labelledby="account-tab">
|
||||
<form [formGroup]="loginForm" class="form-horizontal auth-form"
|
||||
novalidate="">
|
||||
<div class="form-group">
|
||||
<input required="" name="login[username]" type="email"
|
||||
class="form-control" placeholder="Email" formControlName="email"
|
||||
id="exampleInputEmail1">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-control input-password-holder">
|
||||
<input formControlName="password" class="input-password"
|
||||
type="{{ showPassword ? 'text' : 'password' }}" #pwc
|
||||
placeholder="Xác nhận mật khẩu" />
|
||||
<span class="icon-holder-eye"
|
||||
(click)="showPassword = !showPassword">
|
||||
<i
|
||||
class="{{ showPassword ? 'fa fa-eye-slash' : 'fa fa-eye' }}"></i>
|
||||
</span>
|
||||
</div>
|
||||
<!-- <input required="" name="login[password]" type="password"
|
||||
class="form-control" placeholder="Password"
|
||||
formControlName="password"> -->
|
||||
</div>
|
||||
<div class="form-button d-flex justify-content-end">
|
||||
<button class="btn btn-primary w-100 mt-0" type="submit"
|
||||
(click)="onSignIn()">Đăng nhập</button>
|
||||
</div>
|
||||
<div class="form-terms mt-1">
|
||||
<div class="d-flex justify-content-end">
|
||||
<!-- <a routerLink="/reset-password" routerLinkActive="active" class="">Quên mật khẩu</a> -->
|
||||
<a [routerLink]="['/auth/send-email']" routerLinkActive="active"
|
||||
class="">Quên mật khẩu ?</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-center align-items-center mt-2">
|
||||
<p class="mb-0 text-secondary">Trở thành người bán hàng của Foodify?
|
||||
</p>
|
||||
<a class="ml-2" [routerLink]="['/auth', 'signup']">Đăng ký ngay</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #error_modal>
|
||||
<div class="modal-body text-center">
|
||||
<p>Sai tên tài khoản hoặc mật khẩu. Vui lòng kiểm tra lại !</p>
|
||||
<button type="button" class="btn btn-primary" (click)="closeLayer1()">Tiếp tục</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,39 @@
|
||||
.backgound-food {
|
||||
background-image: url('../../../../assets/images/background-login.jpg');
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
z-index: -99;
|
||||
|
||||
|
||||
@media (max-width:768px) {
|
||||
.disable-pink {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.login-img-box {
|
||||
width: 100%;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.login-img {
|
||||
max-width: 442px;
|
||||
}
|
||||
}
|
||||
|
||||
.input-password-holder {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.input-password {
|
||||
border: none;
|
||||
width: 100%;
|
||||
}
|
||||
.icon-holder-eye {
|
||||
color: #999a9b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { FirebaseService } from "../../../shared/service/firebase.service";
|
||||
import { Router } from '@angular/router';
|
||||
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.scss']
|
||||
})
|
||||
export class LoginComponent implements OnInit {
|
||||
|
||||
public loginForm: UntypedFormGroup;
|
||||
public registerForm: UntypedFormGroup;
|
||||
public active = 1;
|
||||
|
||||
// Password
|
||||
showPassword = false;
|
||||
|
||||
//Modal
|
||||
@ViewChild('error_modal') errorModal: TemplateRef<any>
|
||||
layer1: BsModalRef;
|
||||
|
||||
constructor(private formBuilder: UntypedFormBuilder,
|
||||
private firebaseAuthService: FirebaseService,
|
||||
private modalService: BsModalService,
|
||||
private router: Router) {
|
||||
this.createLoginForm();
|
||||
this.createRegisterForm();
|
||||
}
|
||||
|
||||
owlcarousel = [
|
||||
{
|
||||
title: "Chào mừng đến với Foodify",
|
||||
desc: "Foodify - Một trong những ứng dụng đặt đồ ăn tốt nhất hiện nay, được phát triển bởi Pyramide team",
|
||||
},
|
||||
{
|
||||
title: "Foodify - xu hướng mới",
|
||||
desc: "Với mục đích tạo ra những sản phẩm tốt nhất cho người dùng, chúng mình đã quyết định sử dụng các công nghệ mới nhất vào Foodify",
|
||||
},
|
||||
{
|
||||
title: "Về Pyramide Team",
|
||||
desc: "Pyramide team là một nhóm sinh viên trường Đại học FPT, team nghiên cứu và phát triển sản phẩm theo công nghệ Angular và Spring Boot",
|
||||
}
|
||||
]
|
||||
owlcarouselOptions = {
|
||||
loop: true,
|
||||
items: 1,
|
||||
dots: true
|
||||
};
|
||||
|
||||
createLoginForm() {
|
||||
this.loginForm = this.formBuilder.group({
|
||||
email: [''],
|
||||
password: [''],
|
||||
});
|
||||
}
|
||||
|
||||
createRegisterForm() {
|
||||
this.registerForm = this.formBuilder.group({
|
||||
email: [''],
|
||||
password: [''],
|
||||
confirmPassword: [''],
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
|
||||
}
|
||||
|
||||
onSignUp() {
|
||||
this.firebaseAuthService.signUp(this.getSignUpEmail, this.getSignUpPassword);
|
||||
}
|
||||
|
||||
onSignIn() {
|
||||
this.firebaseAuthService.signIn(this.getSignInEmail, this.getSignInPassword).then((result) => {
|
||||
if (result) {
|
||||
|
||||
}
|
||||
else {
|
||||
this.layer1 = this.modalService.show(this.errorModal, { class: "modal-sm" });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
closeLayer1() {
|
||||
this.layer1.hide();
|
||||
}
|
||||
|
||||
get getSignUpEmail() {
|
||||
return this.registerForm.get('email').getRawValue();
|
||||
}
|
||||
|
||||
get getSignUpPassword() {
|
||||
return this.registerForm.get('password').getRawValue();
|
||||
}
|
||||
|
||||
get getSignInEmail() {
|
||||
return this.loginForm.get('email').getRawValue();
|
||||
}
|
||||
|
||||
get getSignInPassword() {
|
||||
return this.loginForm.get('password').getRawValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<div class="page-wrapper">
|
||||
<div class="authentication-box backgound-food">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-5 p-0 card-left disable-pink">
|
||||
<div class="card bg-primary">
|
||||
<div class="svg-icon">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 468.52 468.52"
|
||||
style="enable-background: new 0 0 468.52 468.52" xml:space="preserve">
|
||||
<g>
|
||||
<path style="fill: #e6e6e6" d="M468.52,102.595v251.88c0,11.14-7.571,20.52-17.86,23.25c-1.98,0.531-4.06,0.81-6.2,0.81H38.58
|
||||
c-13.29,0-24.061-10.77-24.061-24.06v-251.88c0-13.29,10.771-24.06,24.061-24.06h405.88c2.14,0,4.22,0.28,6.2,0.81
|
||||
C460.949,82.075,468.52,91.455,468.52,102.595" />
|
||||
<path style="fill: #b3b3b3" d="M468.518,102.591v23.94h-454v-23.94c0-13.29,10.771-24.06,24.06-24.06h405.88
|
||||
C457.747,78.532,468.518,89.302,468.518,102.591" />
|
||||
<rect x="42.099" y="141.002" style="fill: #999999" width="118.901" height="16.532" />
|
||||
<polyline style="fill: #ffffff"
|
||||
points="442.383,222.765 256.652,222.765 256.652,354.496 442.383,354.496 442.383,222.765 " />
|
||||
<polyline style="fill: #13c9ca"
|
||||
points="434.552,329.733 265.552,329.733 265.552,346.265 434.552,346.265 434.552,329.733 " />
|
||||
<polyline style="fill: #a5a5a5"
|
||||
points="286.552,297.744 265.552,297.744 265.552,314.276 286.552,314.276 286.552,297.744 " />
|
||||
<polyline style="fill: #ffbc58"
|
||||
points="350.552,265.754 265.552,265.754 265.552,282.286 350.552,282.286 350.552,265.754 " />
|
||||
<polyline style="fill: #999999"
|
||||
points="411.552,233.765 265.552,233.765 265.552,250.297 411.552,250.297 411.552,233.765 " />
|
||||
<circle style="fill: #a5a5a5" cx="47.517" cy="102.532" r="15" />
|
||||
<circle style="fill: #ffbc58" cx="87.517" cy="102.532" r="15" />
|
||||
<path style="fill: #eeeeee" d="M463.696,88.139c3.027,4.024,4.822,9.029,4.822,14.453v23.94h-23.321
|
||||
c0.04,35.899-19.42,70.584-53.444,88.347c-2.102,0.921-4.223,1.779-6.365,2.574l2.774,5.313h54.222v131.731H298.563l7.326,24.038
|
||||
h138.57c2.14,0,4.22-0.28,6.2-0.81c10.29-2.73,17.86-12.11,17.86-23.25v-251.88C468.52,97.17,466.725,92.164,463.696,88.139
|
||||
M463.657,88.088c0.012,0.016,0.023,0.031,0.036,0.047C463.681,88.12,463.669,88.103,463.657,88.088 M463.621,88.04
|
||||
c0.008,0.01,0.015,0.02,0.023,0.03C463.636,88.06,463.629,88.05,463.621,88.04 M463.59,87.999c0.003,0.004,0.006,0.008,0.009,0.012
|
||||
C463.596,88.007,463.593,88.003,463.59,87.999" />
|
||||
<path style="fill: #cbcbcb" d="M444.46,78.535h-11.739c0.396,0.716,0.784,1.437,1.165,2.167c7.655,14.662,11.293,30.36,11.31,45.83
|
||||
h23.321v-23.94c0-5.424-1.795-10.429-4.822-14.453l0,0c-0.001-0.001-0.002-0.002-0.003-0.003l0,0
|
||||
c-0.012-0.016-0.023-0.031-0.036-0.047l0,0c-0.005-0.006-0.009-0.012-0.014-0.018l0,0c-0.008-0.01-0.015-0.02-0.023-0.03l0,0
|
||||
c-0.007-0.01-0.014-0.019-0.022-0.029l0,0c-0.003-0.004-0.006-0.008-0.009-0.012l0,0c-3.187-4.171-7.708-7.269-12.93-8.654
|
||||
C448.68,78.815,446.6,78.535,444.46,78.535" />
|
||||
<path style="fill: #ffffff" d="M442.383,222.765h-54.222l5.326,10.202c-0.519,0.271-1.04,0.537-1.561,0.798h19.626v16.532H266.807
|
||||
l4.711,15.457h79.034v16.532h-73.996l4.711,15.458h5.285v16.532h-0.246l4.711,15.457h143.536v16.532H296.055l2.509,8.231h143.819
|
||||
V222.765" />
|
||||
<polyline style="fill: #6ec6eb"
|
||||
points="434.552,329.733 291.016,329.733 296.055,346.265 434.552,346.265 434.552,329.733 " />
|
||||
<polyline style="fill: #fda589"
|
||||
points="286.552,297.744 281.267,297.744 286.306,314.276 286.552,314.276 286.552,297.744 " />
|
||||
<polyline style="fill: #fbe98e"
|
||||
points="350.552,265.754 271.518,265.754 276.556,282.286 350.552,282.286 350.552,265.754 " />
|
||||
<path style="fill: #eeeeee" d="M246.265,126.531h-17.178l29.329,96.234h62.908c-1.465-0.282-2.926-0.591-4.384-0.929l-1.001-0.232
|
||||
c-24.282-7.625-45.651-24.51-58.362-48.855C249.857,157.962,246.221,142.128,246.265,126.531" />
|
||||
<path style="fill: #cbcbcb" d="M258.73,78.535h-44.27l14.628,47.996h17.178c0.028-10.08,1.594-20.062,4.587-29.594
|
||||
C252.852,90.569,255.489,84.4,258.73,78.535" />
|
||||
<path style="fill: #ffffff" d="M321.324,222.765h-62.908l7.136,23.415v-12.415h53.983l3.362-10.708
|
||||
C322.373,222.963,321.848,222.866,321.324,222.765" />
|
||||
<path style="fill: #bababa" d="M411.552,233.765h-19.626c-14.25,7.129-29.421,10.514-44.383,10.514
|
||||
c-10.173,0-20.251-1.566-29.869-4.586l1.861-5.928h-53.983v12.415l1.255,4.117h144.745V233.765" />
|
||||
<path style="fill: #cecece"
|
||||
d="M385.387,217.453c-6.196,2.3-12.551,4.073-18.994,5.313h21.767L385.387,217.453" />
|
||||
<path style="fill: #dbdbdb"
|
||||
d="M388.161,222.765h-21.767c-7.428,1.428-14.973,2.147-22.536,2.147
|
||||
c-6.999,0-14.015-0.616-20.959-1.855l-3.362,10.708h72.391c0.521-0.261,1.042-0.527,1.561-0.798L388.161,222.765" />
|
||||
<path style="fill: #a4a4a4" d="M391.926,233.765h-72.391l-1.861,5.928c9.618,3.02,19.696,4.586,29.869,4.586
|
||||
C362.505,244.279,377.677,240.894,391.926,233.765" />
|
||||
<path style="fill: #dbdbdb" d="M345.64,27.261c-15.507,0-31.24,3.637-45.936,11.309c-17.955,9.374-31.855,23.466-40.973,39.961
|
||||
h173.987c-12.828-23.18-33.641-39.288-57.199-46.682C365.898,28.829,355.817,27.261,345.64,27.261" />
|
||||
<polyline style="fill: #9f9f9f"
|
||||
points="432.719,78.532 258.732,78.532 258.73,78.535 432.721,78.535 432.719,78.532 " />
|
||||
<path style="fill: #cecece"
|
||||
d="M445.196,126.531H246.265c-0.044,15.597,3.592,31.43,11.312,46.218
|
||||
c12.711,24.345,34.08,41.23,58.362,48.855l1.001,0.232c1.458,0.338,2.919,0.647,4.384,0.929h45.069
|
||||
c6.443-1.239,12.797-3.012,18.994-5.313c2.142-0.795,4.263-1.653,6.365-2.574C425.776,197.115,445.237,162.431,445.196,126.531" />
|
||||
<path style="fill: #b2b2b2" d="M432.721,78.535H258.73c-3.241,5.865-5.878,12.033-7.878,18.402
|
||||
c-2.993,9.532-4.559,19.515-4.587,29.594h198.931c-0.017-15.469-3.656-31.168-11.31-45.83
|
||||
C433.505,79.972,433.117,79.251,432.721,78.535" />
|
||||
<path style="fill: #dbdbdb" d="M366.393,222.765h-45.069c0.524,0.101,1.049,0.198,1.574,0.292
|
||||
c6.945,1.239,13.96,1.855,20.959,1.855C351.42,224.912,358.965,224.193,366.393,222.765" />
|
||||
<path style="fill: #ffbc58" d="M345.73,113.225l-5.24,16.69l-24.55,78.18c-24.28-7.62-45.65-24.51-58.36-48.85
|
||||
c-12.71-24.35-14.35-51.53-6.73-75.81l83.46,26.2L345.73,113.225z" />
|
||||
<path style="fill: #13c9ca" d="M393.49,219.465c-7.48,3.9-15.23,6.76-23.08,8.63c-17.7,4.21-35.91,3.38-52.74-1.91l26.47-84.29
|
||||
l3.32-10.59L393.49,219.465z" />
|
||||
<path style="fill: #a5a5a5" d="M391.75,201.375l-46.02-88.15l-11.42-3.59l-83.46-26.2c7.63-24.29,24.51-45.66,48.85-58.37
|
||||
c2.55-1.33,5.12-2.54,7.73-3.62c22.27-9.33,46.35-9.93,68.09-3.1c24.29,7.62,45.66,24.5,58.37,48.85
|
||||
C459.3,115.885,440.44,175.955,391.75,201.375z" />
|
||||
<polyline style="fill: #ffffff"
|
||||
points="226.383,168.765 40.652,168.765 40.652,354.496 226.383,354.496 226.383,168.765 " />
|
||||
<polyline style="fill: #2be0c6"
|
||||
points="93.6,254.001 57.602,254.001 57.602,343.996 93.6,343.996 93.6,254.001 " />
|
||||
<polyline style="fill: #ffbc58"
|
||||
points="151.517,224.002 115.518,224.002 115.518,343.996 151.517,343.996 151.517,224.002 " />
|
||||
<polyline style="fill: #a5a5a5"
|
||||
points="209.432,191.004 173.435,191.004 173.435,343.996 209.432,343.996 209.432,191.004 " />
|
||||
<polyline style="fill: #13c9ca"
|
||||
points="242.83,325.765 242.83,454.765 0,454.765 0,325.765 242.83,325.765 " />
|
||||
<polyline style="fill: #f2f2f2"
|
||||
points="15,439.765 15,340.765 227.826,340.765 227.826,439.765 15,439.765 " />
|
||||
<polyline style="fill: #a5a5a5"
|
||||
points="108.064,430.978 87.408,408.6 78.664,420.466 71.9,415.483 86.68,395.426 106.349,416.734
|
||||
137.239,360.959 160.785,398.8 188.393,349.552 195.722,353.66 161.162,415.307 137.661,377.538 108.064,430.978 " />
|
||||
<rect x="26" y="357.765" style="fill: #999999" width="27" height="11" />
|
||||
<rect x="26" y="384.765" style="fill: #999999" width="27" height="11" />
|
||||
<rect x="26" y="411.765" style="fill: #999999" width="27" height="11" />
|
||||
<polyline style="fill: #6ec6eb" points="242.83,325.765 209.432,325.765 173.435,325.765 151.517,325.765 115.518,325.765
|
||||
93.6,325.765 92.88,325.765 97.451,340.765 227.826,340.765 227.826,439.765 127.619,439.765 132.19,454.765 242.83,454.765
|
||||
242.83,325.765 " />
|
||||
<polyline style="fill: #f6f6f6" points="227.826,340.765 97.451,340.765 115.542,400.135 137.239,360.959 160.785,398.8
|
||||
188.393,349.552 195.722,353.66 161.162,415.307 137.661,377.538 118.951,411.32 127.619,439.765 227.826,439.765 227.826,340.765
|
||||
" />
|
||||
<polyline style="fill: #fda589" points="188.393,349.552 160.785,398.8 137.239,360.959 115.542,400.135 118.951,411.32
|
||||
137.661,377.538 161.162,415.307 195.722,353.66 188.393,349.552 " />
|
||||
<path style="fill: #cccccc"
|
||||
d="M393.695,417.909l8.485-20.662l25.251,25.251c1.464,1.464,3.387,2.199,5.303,2.192
|
||||
c1.923,0,3.84-0.728,5.303-2.192c2.934-2.934,2.927-7.679,0-10.607l-25.251-25.251l20.669-8.492l-78.559-38.792L393.695,417.909z" />
|
||||
<path style="fill: #808080" d="M393.695,417.909l8.485-20.662l25.251,25.251c1.464,1.464,3.387,2.199,5.303,2.192
|
||||
c1.923,0,3.84-0.728,5.303-2.192l-83.142-83.142L393.695,417.909z" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="single-item">
|
||||
<owl-carousel-o [options]="owlcarouselOptions">
|
||||
<ng-container *ngFor="let data of owlcarousel">
|
||||
<ng-template carouselSlide class="item">
|
||||
<div>
|
||||
<h3>{{ data.title }}</h3>
|
||||
<p>{{ data.desc }}</p>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</owl-carousel-o>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7 p-0 card-right">
|
||||
<div class="card tab2-card">
|
||||
<div class="card-body">
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
|
||||
<li [ngbNavItem]="1">
|
||||
<h3 style="color: lightcoral" ngbNavLink>Thay đổi mật khẩu mới</h3>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<div class="reset-img-box d-flex justify-content-center align-items-center">
|
||||
<img class="reset-img" src="../../../../assets/images/dashboard/reset-password.png">
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade active show" id="account" role="tabpanel" aria-labelledby="account-tab">
|
||||
<form [formGroup]="resetPasswordForm" class="form-horizontal auth-form" novalidate="">
|
||||
<div class="form-group">
|
||||
<div class="form-control input-password-holder">
|
||||
<input formControlName="password" class="input-password"
|
||||
type="{{ showPassword ? 'text' : 'password' }}" placeholder="Mật khẩu mới" />
|
||||
<span class="icon-holder-eye" (click)="showPassword = !showPassword">
|
||||
<i class="{{ showPassword ? 'fa fa-eye-slash' : 'fa fa-eye' }}"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="password?.invalid && (password?.dirty || password?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="password?.errors?.['required']">Vui lòng không được bỏ trống mật khẩu.</div>
|
||||
<div *ngIf="password?.errors?.['pattern']">
|
||||
Mật khẩu phải chứa ít nhất 8 ký tự, trong đó gồm một ký tự số, một chữ hoa, một chữ thường
|
||||
và một ký tự đặc biệt.
|
||||
<br />
|
||||
Mật khẩu không được chứa khoảng trắng.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-control input-password-holder">
|
||||
<input formControlName="confirmPassword" class="input-password"
|
||||
type="{{ showPassword ? 'text' : 'password' }}" #pwc placeholder="Xác nhận mật khẩu" />
|
||||
<span class="icon-holder-eye" (click)="showPassword = !showPassword">
|
||||
<i class="{{ showPassword ? 'fa fa-eye-slash' : 'fa fa-eye' }}"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="
|
||||
confirmPassword?.invalid &&
|
||||
(confirmPassword?.dirty || confirmPassword?.touched)
|
||||
" class="alert alert-danger mt-1">
|
||||
<div *ngIf="confirmPassword?.errors?.['required']">Vui lòng xác nhận mật khẩu.</div>
|
||||
<div *ngIf="confirmPassword?.errors?.['pattern']">
|
||||
Mật khẩu phải chứa ít nhất 8 ký tự, trong đó gồm một ký tự số, một chữ hoa, một chữ thường
|
||||
và một ký tự đặc biệt.
|
||||
<br />
|
||||
Mật khẩu không được chứa khoảng trắng.
|
||||
</div>
|
||||
<div *ngIf="confirmPassword?.errors?.['confirmedValidator']">
|
||||
Mật khẩu xác nhận phải giống với mật khẩu đã nhập
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-button">
|
||||
<button class="btn btn-primary" type="submit"
|
||||
(click)="changePassword(changePasswordCompleted)">Thay đổi</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #changePasswordCompleted>
|
||||
<div class="modal-body text-center">
|
||||
<p>Thay đổi mật khẩu thành công</p>
|
||||
<button type="button" class="btn btn-primary" (click)="backToLogin()"> Đăng nhập ngay
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,39 @@
|
||||
.backgound-food {
|
||||
background-image: url('../../../../assets/images/background-login.jpg');
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
z-index: -99;
|
||||
|
||||
@media (max-width:768px) {
|
||||
.disable-pink {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.reset-img-box {
|
||||
.reset-img {
|
||||
max-width: 265px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.input-password-holder {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.input-password {
|
||||
border: none;
|
||||
width: 100%;
|
||||
}
|
||||
.icon-holder-eye {
|
||||
color: #999a9b;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { FormControl, FormGroup, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { FirebaseService } from "../../../shared/service/firebase.service";
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Validation } from 'src/app/constants/Validation';
|
||||
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
|
||||
|
||||
@Component({
|
||||
selector: "app-reset-password",
|
||||
templateUrl: "./reset-password.component.html",
|
||||
styleUrls: ["./reset-password.component.scss"],
|
||||
})
|
||||
export class ResetPasswordComponent {
|
||||
public resetPasswordForm: FormGroup
|
||||
public active = 1;
|
||||
|
||||
// Password
|
||||
action: string = '';
|
||||
oobCode: string = '';
|
||||
showPassword = false;
|
||||
|
||||
//Modal
|
||||
layer1: BsModalRef;
|
||||
|
||||
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private firebaseService: FirebaseService,
|
||||
private modalService: BsModalService,
|
||||
private router: Router
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
owlcarousel = [
|
||||
{
|
||||
title: "Chào mừng đến với Foodify",
|
||||
desc: "Foodify - Một trong những ứng dụng đặt đồ ăn tốt nhất hiện nay, được phát triển bởi Pyramide team",
|
||||
},
|
||||
{
|
||||
title: "Foodify - xu hướng mới",
|
||||
desc: "Với mục đích tạo ra những sản phẩm tốt nhất cho người dùng, chúng mình đã quyết định sử dụng các công nghệ mới nhất vào Foodify",
|
||||
},
|
||||
{
|
||||
title: "Về Pyramide Team",
|
||||
desc: "Pyramide team là một nhóm sinh viên trường Đại học FPT, team nghiên cứu và phát triển sản phẩm theo công nghệ Angular và Spring Boot",
|
||||
},
|
||||
];
|
||||
owlcarouselOptions = {
|
||||
loop: true,
|
||||
items: 1,
|
||||
dots: true,
|
||||
};
|
||||
|
||||
ngOnInit() {
|
||||
//Get Token from link
|
||||
this.oobCode = this.activatedRoute.snapshot.queryParamMap.get('oobCode');
|
||||
this.action = this.activatedRoute.snapshot.queryParamMap.get('mode');
|
||||
|
||||
if (this.action == 'verifyEmail') {
|
||||
this.firebaseService.confirmVerifyEmail(this.oobCode)
|
||||
this.router.navigate(['/auth/email-verified'])
|
||||
}
|
||||
|
||||
if (this.oobCode) {
|
||||
this.resetPasswordForm = this.formBuilder.group({
|
||||
password: new FormControl("", [Validators.required, Validators.pattern(Validation.Regex.Password)]),
|
||||
confirmPassword: new FormControl("", [Validators.required, Validators.pattern(Validation.Regex.Password)]),
|
||||
},
|
||||
{
|
||||
validator: this.ConfirmedValidator("password", "confirmPassword"),
|
||||
})
|
||||
}
|
||||
else {
|
||||
this.router.navigate(['/auth/forbidden']);
|
||||
}
|
||||
}
|
||||
|
||||
//Change password
|
||||
changePassword(success: TemplateRef<any>) {
|
||||
if (this.resetPasswordForm.invalid) {
|
||||
this.resetPasswordForm.markAllAsTouched();
|
||||
return;
|
||||
}
|
||||
|
||||
this.firebaseService.confirmPasswordReset(this.oobCode, this.password.value);
|
||||
this.layer1 = this.modalService.show(success, { class: 'modal-sm' })
|
||||
}
|
||||
|
||||
backToLogin() {
|
||||
this.layer1.hide();
|
||||
this.router.navigate(['/auth/login']);
|
||||
}
|
||||
|
||||
// Validation for password and confirm password
|
||||
ConfirmedValidator(controlName: string, matchingControlName: string) {
|
||||
return (formGroup: FormGroup) => {
|
||||
const control = formGroup.controls[controlName];
|
||||
const matchingControl = formGroup.controls[matchingControlName];
|
||||
if (matchingControl.errors && !matchingControl.errors.confirmedValidator) {
|
||||
return;
|
||||
}
|
||||
if (control.value !== matchingControl.value) {
|
||||
matchingControl.setErrors({ confirmedValidator: true });
|
||||
} else {
|
||||
matchingControl.setErrors(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
get password() {
|
||||
return this.resetPasswordForm.get("password");
|
||||
}
|
||||
|
||||
get confirmPassword() {
|
||||
return this.resetPasswordForm.get("confirmPassword");
|
||||
}
|
||||
|
||||
// onSignUp() {
|
||||
// this.firebaseAuthService.signUp(this.getSignUpEmail, this.getSignUpPassword);
|
||||
// }
|
||||
|
||||
// onSignIn() {
|
||||
// this.firebaseAuthService.signIn(this.getSignInEmail, this.getSignInPassword);
|
||||
// }
|
||||
|
||||
|
||||
// get getSignUpPassword() {
|
||||
// return this.registerForm.get("password").getRawValue();
|
||||
// }
|
||||
|
||||
// get getSignInEmail() {
|
||||
// return this.loginForm.get("email").getRawValue();
|
||||
// }
|
||||
|
||||
// get getSignInPassword() {
|
||||
// return this.loginForm.get("password").getRawValue();
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
<div class="page-wrapper">
|
||||
<div class="authentication-box backgound-food">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-5 p-0 card-left disable-pink">
|
||||
<div class="card bg-primary">
|
||||
<div class="svg-icon">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 468.52 468.52"
|
||||
style="enable-background: new 0 0 468.52 468.52" xml:space="preserve">
|
||||
<g>
|
||||
<path style="fill: #e6e6e6" d="M468.52,102.595v251.88c0,11.14-7.571,20.52-17.86,23.25c-1.98,0.531-4.06,0.81-6.2,0.81H38.58
|
||||
c-13.29,0-24.061-10.77-24.061-24.06v-251.88c0-13.29,10.771-24.06,24.061-24.06h405.88c2.14,0,4.22,0.28,6.2,0.81
|
||||
C460.949,82.075,468.52,91.455,468.52,102.595" />
|
||||
<path style="fill: #b3b3b3" d="M468.518,102.591v23.94h-454v-23.94c0-13.29,10.771-24.06,24.06-24.06h405.88
|
||||
C457.747,78.532,468.518,89.302,468.518,102.591" />
|
||||
<rect x="42.099" y="141.002" style="fill: #999999" width="118.901" height="16.532" />
|
||||
<polyline style="fill: #ffffff"
|
||||
points="442.383,222.765 256.652,222.765 256.652,354.496 442.383,354.496 442.383,222.765 " />
|
||||
<polyline style="fill: #13c9ca"
|
||||
points="434.552,329.733 265.552,329.733 265.552,346.265 434.552,346.265 434.552,329.733 " />
|
||||
<polyline style="fill: #a5a5a5"
|
||||
points="286.552,297.744 265.552,297.744 265.552,314.276 286.552,314.276 286.552,297.744 " />
|
||||
<polyline style="fill: #ffbc58"
|
||||
points="350.552,265.754 265.552,265.754 265.552,282.286 350.552,282.286 350.552,265.754 " />
|
||||
<polyline style="fill: #999999"
|
||||
points="411.552,233.765 265.552,233.765 265.552,250.297 411.552,250.297 411.552,233.765 " />
|
||||
<circle style="fill: #a5a5a5" cx="47.517" cy="102.532" r="15" />
|
||||
<circle style="fill: #ffbc58" cx="87.517" cy="102.532" r="15" />
|
||||
<path style="fill: #eeeeee" d="M463.696,88.139c3.027,4.024,4.822,9.029,4.822,14.453v23.94h-23.321
|
||||
c0.04,35.899-19.42,70.584-53.444,88.347c-2.102,0.921-4.223,1.779-6.365,2.574l2.774,5.313h54.222v131.731H298.563l7.326,24.038
|
||||
h138.57c2.14,0,4.22-0.28,6.2-0.81c10.29-2.73,17.86-12.11,17.86-23.25v-251.88C468.52,97.17,466.725,92.164,463.696,88.139
|
||||
M463.657,88.088c0.012,0.016,0.023,0.031,0.036,0.047C463.681,88.12,463.669,88.103,463.657,88.088 M463.621,88.04
|
||||
c0.008,0.01,0.015,0.02,0.023,0.03C463.636,88.06,463.629,88.05,463.621,88.04 M463.59,87.999c0.003,0.004,0.006,0.008,0.009,0.012
|
||||
C463.596,88.007,463.593,88.003,463.59,87.999" />
|
||||
<path style="fill: #cbcbcb" d="M444.46,78.535h-11.739c0.396,0.716,0.784,1.437,1.165,2.167c7.655,14.662,11.293,30.36,11.31,45.83
|
||||
h23.321v-23.94c0-5.424-1.795-10.429-4.822-14.453l0,0c-0.001-0.001-0.002-0.002-0.003-0.003l0,0
|
||||
c-0.012-0.016-0.023-0.031-0.036-0.047l0,0c-0.005-0.006-0.009-0.012-0.014-0.018l0,0c-0.008-0.01-0.015-0.02-0.023-0.03l0,0
|
||||
c-0.007-0.01-0.014-0.019-0.022-0.029l0,0c-0.003-0.004-0.006-0.008-0.009-0.012l0,0c-3.187-4.171-7.708-7.269-12.93-8.654
|
||||
C448.68,78.815,446.6,78.535,444.46,78.535" />
|
||||
<path style="fill: #ffffff" d="M442.383,222.765h-54.222l5.326,10.202c-0.519,0.271-1.04,0.537-1.561,0.798h19.626v16.532H266.807
|
||||
l4.711,15.457h79.034v16.532h-73.996l4.711,15.458h5.285v16.532h-0.246l4.711,15.457h143.536v16.532H296.055l2.509,8.231h143.819
|
||||
V222.765" />
|
||||
<polyline style="fill: #6ec6eb"
|
||||
points="434.552,329.733 291.016,329.733 296.055,346.265 434.552,346.265 434.552,329.733 " />
|
||||
<polyline style="fill: #fda589"
|
||||
points="286.552,297.744 281.267,297.744 286.306,314.276 286.552,314.276 286.552,297.744 " />
|
||||
<polyline style="fill: #fbe98e"
|
||||
points="350.552,265.754 271.518,265.754 276.556,282.286 350.552,282.286 350.552,265.754 " />
|
||||
<path style="fill: #eeeeee" d="M246.265,126.531h-17.178l29.329,96.234h62.908c-1.465-0.282-2.926-0.591-4.384-0.929l-1.001-0.232
|
||||
c-24.282-7.625-45.651-24.51-58.362-48.855C249.857,157.962,246.221,142.128,246.265,126.531" />
|
||||
<path style="fill: #cbcbcb" d="M258.73,78.535h-44.27l14.628,47.996h17.178c0.028-10.08,1.594-20.062,4.587-29.594
|
||||
C252.852,90.569,255.489,84.4,258.73,78.535" />
|
||||
<path style="fill: #ffffff" d="M321.324,222.765h-62.908l7.136,23.415v-12.415h53.983l3.362-10.708
|
||||
C322.373,222.963,321.848,222.866,321.324,222.765" />
|
||||
<path style="fill: #bababa" d="M411.552,233.765h-19.626c-14.25,7.129-29.421,10.514-44.383,10.514
|
||||
c-10.173,0-20.251-1.566-29.869-4.586l1.861-5.928h-53.983v12.415l1.255,4.117h144.745V233.765" />
|
||||
<path style="fill: #cecece"
|
||||
d="M385.387,217.453c-6.196,2.3-12.551,4.073-18.994,5.313h21.767L385.387,217.453" />
|
||||
<path style="fill: #dbdbdb"
|
||||
d="M388.161,222.765h-21.767c-7.428,1.428-14.973,2.147-22.536,2.147
|
||||
c-6.999,0-14.015-0.616-20.959-1.855l-3.362,10.708h72.391c0.521-0.261,1.042-0.527,1.561-0.798L388.161,222.765" />
|
||||
<path style="fill: #a4a4a4" d="M391.926,233.765h-72.391l-1.861,5.928c9.618,3.02,19.696,4.586,29.869,4.586
|
||||
C362.505,244.279,377.677,240.894,391.926,233.765" />
|
||||
<path style="fill: #dbdbdb" d="M345.64,27.261c-15.507,0-31.24,3.637-45.936,11.309c-17.955,9.374-31.855,23.466-40.973,39.961
|
||||
h173.987c-12.828-23.18-33.641-39.288-57.199-46.682C365.898,28.829,355.817,27.261,345.64,27.261" />
|
||||
<polyline style="fill: #9f9f9f"
|
||||
points="432.719,78.532 258.732,78.532 258.73,78.535 432.721,78.535 432.719,78.532 " />
|
||||
<path style="fill: #cecece"
|
||||
d="M445.196,126.531H246.265c-0.044,15.597,3.592,31.43,11.312,46.218
|
||||
c12.711,24.345,34.08,41.23,58.362,48.855l1.001,0.232c1.458,0.338,2.919,0.647,4.384,0.929h45.069
|
||||
c6.443-1.239,12.797-3.012,18.994-5.313c2.142-0.795,4.263-1.653,6.365-2.574C425.776,197.115,445.237,162.431,445.196,126.531" />
|
||||
<path style="fill: #b2b2b2" d="M432.721,78.535H258.73c-3.241,5.865-5.878,12.033-7.878,18.402
|
||||
c-2.993,9.532-4.559,19.515-4.587,29.594h198.931c-0.017-15.469-3.656-31.168-11.31-45.83
|
||||
C433.505,79.972,433.117,79.251,432.721,78.535" />
|
||||
<path style="fill: #dbdbdb" d="M366.393,222.765h-45.069c0.524,0.101,1.049,0.198,1.574,0.292
|
||||
c6.945,1.239,13.96,1.855,20.959,1.855C351.42,224.912,358.965,224.193,366.393,222.765" />
|
||||
<path style="fill: #ffbc58" d="M345.73,113.225l-5.24,16.69l-24.55,78.18c-24.28-7.62-45.65-24.51-58.36-48.85
|
||||
c-12.71-24.35-14.35-51.53-6.73-75.81l83.46,26.2L345.73,113.225z" />
|
||||
<path style="fill: #13c9ca" d="M393.49,219.465c-7.48,3.9-15.23,6.76-23.08,8.63c-17.7,4.21-35.91,3.38-52.74-1.91l26.47-84.29
|
||||
l3.32-10.59L393.49,219.465z" />
|
||||
<path style="fill: #a5a5a5" d="M391.75,201.375l-46.02-88.15l-11.42-3.59l-83.46-26.2c7.63-24.29,24.51-45.66,48.85-58.37
|
||||
c2.55-1.33,5.12-2.54,7.73-3.62c22.27-9.33,46.35-9.93,68.09-3.1c24.29,7.62,45.66,24.5,58.37,48.85
|
||||
C459.3,115.885,440.44,175.955,391.75,201.375z" />
|
||||
<polyline style="fill: #ffffff"
|
||||
points="226.383,168.765 40.652,168.765 40.652,354.496 226.383,354.496 226.383,168.765 " />
|
||||
<polyline style="fill: #2be0c6"
|
||||
points="93.6,254.001 57.602,254.001 57.602,343.996 93.6,343.996 93.6,254.001 " />
|
||||
<polyline style="fill: #ffbc58"
|
||||
points="151.517,224.002 115.518,224.002 115.518,343.996 151.517,343.996 151.517,224.002 " />
|
||||
<polyline style="fill: #a5a5a5"
|
||||
points="209.432,191.004 173.435,191.004 173.435,343.996 209.432,343.996 209.432,191.004 " />
|
||||
<polyline style="fill: #13c9ca"
|
||||
points="242.83,325.765 242.83,454.765 0,454.765 0,325.765 242.83,325.765 " />
|
||||
<polyline style="fill: #f2f2f2"
|
||||
points="15,439.765 15,340.765 227.826,340.765 227.826,439.765 15,439.765 " />
|
||||
<polyline style="fill: #a5a5a5"
|
||||
points="108.064,430.978 87.408,408.6 78.664,420.466 71.9,415.483 86.68,395.426 106.349,416.734
|
||||
137.239,360.959 160.785,398.8 188.393,349.552 195.722,353.66 161.162,415.307 137.661,377.538 108.064,430.978 " />
|
||||
<rect x="26" y="357.765" style="fill: #999999" width="27" height="11" />
|
||||
<rect x="26" y="384.765" style="fill: #999999" width="27" height="11" />
|
||||
<rect x="26" y="411.765" style="fill: #999999" width="27" height="11" />
|
||||
<polyline style="fill: #6ec6eb" points="242.83,325.765 209.432,325.765 173.435,325.765 151.517,325.765 115.518,325.765
|
||||
93.6,325.765 92.88,325.765 97.451,340.765 227.826,340.765 227.826,439.765 127.619,439.765 132.19,454.765 242.83,454.765
|
||||
242.83,325.765 " />
|
||||
<polyline style="fill: #f6f6f6" points="227.826,340.765 97.451,340.765 115.542,400.135 137.239,360.959 160.785,398.8
|
||||
188.393,349.552 195.722,353.66 161.162,415.307 137.661,377.538 118.951,411.32 127.619,439.765 227.826,439.765 227.826,340.765
|
||||
" />
|
||||
<polyline style="fill: #fda589" points="188.393,349.552 160.785,398.8 137.239,360.959 115.542,400.135 118.951,411.32
|
||||
137.661,377.538 161.162,415.307 195.722,353.66 188.393,349.552 " />
|
||||
<path style="fill: #cccccc"
|
||||
d="M393.695,417.909l8.485-20.662l25.251,25.251c1.464,1.464,3.387,2.199,5.303,2.192
|
||||
c1.923,0,3.84-0.728,5.303-2.192c2.934-2.934,2.927-7.679,0-10.607l-25.251-25.251l20.669-8.492l-78.559-38.792L393.695,417.909z" />
|
||||
<path style="fill: #808080" d="M393.695,417.909l8.485-20.662l25.251,25.251c1.464,1.464,3.387,2.199,5.303,2.192
|
||||
c1.923,0,3.84-0.728,5.303-2.192l-83.142-83.142L393.695,417.909z" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="single-item">
|
||||
<owl-carousel-o [options]="owlcarouselOptions">
|
||||
<ng-container *ngFor="let data of owlcarousel">
|
||||
<ng-template carouselSlide class="item">
|
||||
<div>
|
||||
<h3>{{ data.title }}</h3>
|
||||
<p>{{ data.desc }}</p>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</owl-carousel-o>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7 p-0 card-right">
|
||||
<div class="card tab2-card">
|
||||
<div class="card-body">
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
|
||||
<li [ngbNavItem]="1">
|
||||
<h3 style="color: lightcoral" ngbNavLink>Quên mật khẩu</h3>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<div class="email-img-box d-flex justify-content-center align-items-center">
|
||||
<img class="email-img" src="../../../../assets/images/dashboard/email.png">
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade active show" id="account" role="tabpanel" aria-labelledby="account-tab">
|
||||
<form [formGroup]="sendEmailForm" class="form-horizontal auth-form" novalidate="">
|
||||
<div class="form-group d-flex justify-content-center align-items-center ">
|
||||
<i class="fa fa-envelope-o" aria-hidden="true"></i>
|
||||
<input type="email" class="form-control" placeholder="Vui lòng nhập Email"
|
||||
formControlName="email" />
|
||||
</div>
|
||||
<div *ngIf="email?.invalid && (email?.dirty || email?.touched)" class="alert alert-danger">
|
||||
<div *ngIf="email?.errors?.['required']">Vui lòng không được bỏ trống email</div>
|
||||
<div *ngIf="email?.errors?.email">Email không hợp lệ, vui lòng nhập lại</div>
|
||||
</div>
|
||||
<div class="form-button d-flex justify-content-around">
|
||||
<button class="btn btn-primary" type="submit" (click)="sendPasswordResetMail()">Tiếp
|
||||
tục</button>
|
||||
<button type="button" class="btn btn-danger btn-back" [routerLink]="['/auth', 'login']">Quay
|
||||
lại</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #sendMailCompleted>
|
||||
<div class="modal-body text-center">
|
||||
<p>Đã gửi đường dẫn thay đổi mật khẩu đến email của bạn</p>
|
||||
<p>Vui lòng kiểm tra hòm thư</p>
|
||||
<button type="button" class="btn btn-primary" (click)="gmail()">Gmail</button>
|
||||
<button type="button" class="btn btn-secondary" (click)="closeLayer1()">Đóng</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,38 @@
|
||||
.backgound-food {
|
||||
background-image: url("../../../../assets/images/background-login.jpg");
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
z-index: -99;
|
||||
|
||||
@media (max-width:768px) {
|
||||
.disable-pink {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.email-img-box {
|
||||
|
||||
.email-img {
|
||||
max-width: 351px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.fa-envelope-o {
|
||||
font-size: 38px;
|
||||
margin-right: 12px;
|
||||
color: #8b8b8b;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-back {
|
||||
border-radius: 25px;
|
||||
margin-top: 20px;
|
||||
font-weight: 400;
|
||||
padding: 11px 45px;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import { Component, OnInit, TemplateRef, ViewChild } from "@angular/core";
|
||||
import { FormControl, FormGroup, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
|
||||
import { FirebaseService } from "../../../shared/service/firebase.service";
|
||||
import { Router } from "@angular/router";
|
||||
import { Validation } from "src/app/constants/Validation";
|
||||
import { BsModalRef, BsModalService } from "ngx-bootstrap/modal";
|
||||
|
||||
@Component({
|
||||
selector: "app-send-email",
|
||||
templateUrl: "./send-email.component.html",
|
||||
styleUrls: ["./send-email.component.scss"],
|
||||
})
|
||||
|
||||
// @ViewChild('sendMailCompleted') sendMailCompleted: TemplateRef<any>;
|
||||
|
||||
|
||||
export class SendEmailComponent {
|
||||
public sendEmailForm: FormGroup;
|
||||
public active = 1;
|
||||
|
||||
@ViewChild('sendMailCompleted') sendMailCompleted: TemplateRef<any>
|
||||
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private firebaseService: FirebaseService,
|
||||
private modalService: BsModalService,
|
||||
private router: Router
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
layer1: BsModalRef;
|
||||
|
||||
owlcarousel = [
|
||||
{
|
||||
title: "Chào mừng đến với Foodify",
|
||||
desc: "Foodify - Một trong những ứng dụng đặt đồ ăn tốt nhất hiện nay, được phát triển bởi Pyramide team",
|
||||
},
|
||||
{
|
||||
title: "Foodify - xu hướng mới",
|
||||
desc: "Với mục đích tạo ra những sản phẩm tốt nhất cho người dùng, chúng mình đã quyết định sử dụng các công nghệ mới nhất vào Foodify",
|
||||
},
|
||||
{
|
||||
title: "Về Pyramide Team",
|
||||
desc: "Pyramide team là một nhóm sinh viên trường Đại học FPT, team nghiên cứu và phát triển sản phẩm theo công nghệ Angular và Spring Boot",
|
||||
},
|
||||
];
|
||||
owlcarouselOptions = {
|
||||
loop: true,
|
||||
items: 1,
|
||||
dots: true,
|
||||
};
|
||||
|
||||
ngOnInit() {
|
||||
this.sendEmailForm = this.formBuilder.group({
|
||||
email: new FormControl("", [Validators.required, Validators.email]),
|
||||
});
|
||||
}
|
||||
|
||||
sendPasswordResetMail() {
|
||||
if (this.sendEmailForm.invalid) {
|
||||
this.sendEmailForm.markAllAsTouched();
|
||||
return;
|
||||
}
|
||||
this.firebaseService.resetPassword(this.email.value);
|
||||
this.layer1 = this.modalService.show(this.sendMailCompleted, { class: "modal-sm" });
|
||||
}
|
||||
|
||||
gmail() {
|
||||
this.layer1.hide();
|
||||
window.location.assign("https://mail.google.com/");
|
||||
}
|
||||
|
||||
closeLayer1() {
|
||||
this.layer1.hide();
|
||||
}
|
||||
|
||||
get email() {
|
||||
return this.sendEmailForm.get("email");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="backgound-food" [ngClass]="{'change-height': active === 2}">
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<div class="form-shop-box">
|
||||
<div class="d-flex justify-content-center align-items-center ">
|
||||
<div class="card tab2-card form-shop-item">
|
||||
<div class="card-header">
|
||||
<h2 style="color: lightcoral" class="font-weight-bold">Đăng ký shop</h2>
|
||||
</div>
|
||||
<div class="card-body tab2-card">
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
|
||||
<li [ngbNavItem]="1">
|
||||
<a ngbNavLink>Tài khoản</a>
|
||||
<ng-template ngbNavContent>
|
||||
<form [formGroup]="addUserForm" class="needs-validation user-add" novalidate>
|
||||
<h4>Chi tiết tài khoản</h4>
|
||||
<!-- avatar -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom0"><span>*</span> Ảnh đại diện</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<div class="d-flex align-items-center">
|
||||
<button *ngIf="!userImageChoosen" type="submit" class="btn btn-success"
|
||||
(click)="chooseImg(chooseUserImage)">
|
||||
Chọn ảnh
|
||||
</button>
|
||||
<button *ngIf="userImageChoosen" type="submit" class="btn btn-secondary"
|
||||
(click)="chooseImg(chooseUserImage)">
|
||||
Thay đổi ảnh
|
||||
</button>
|
||||
<img class="rounded-circle border border-dark" [src]="avatar" *ngIf="avatar"
|
||||
style="width: 160px; height: 160px; margin-left: 20px" />
|
||||
</div>
|
||||
<!-- <div *ngIf="!userImageChoosen && (addUserForm.get('image')?.dirty || addUserForm.get('image')?.touched)" class="alert alert-danger mt-1">
|
||||
<div *ngIf="addUserForm.get('image').errors?.['required']">Vui lòng chọn ảnh đại diện.</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- full name -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom0"><span>*</span> Họ và tên</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<input formControlName="fullName" class="form-control" type="text" placeholder="Họ và tên" />
|
||||
<div *ngIf="userFullName?.invalid && (userFullName?.dirty || userFullName?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="userFullName?.errors?.['required']">Vui lòng không được bỏ trống
|
||||
tên.</div>
|
||||
<div *ngIf="userFullName?.errors?.['minlength']">Tên phải có ít nhất 2 kí tự
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- email -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom2"><span>*</span> Email</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<input formControlName="email" class="form-control" type="email" placeholder="Email" />
|
||||
<div *ngIf="userEmail?.invalid && (userEmail?.dirty || userEmail?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="userEmail?.errors?.['required']">Vui lòng không được bỏ trống
|
||||
email.</div>
|
||||
<div *ngIf="userEmail?.errors?.email">Email không hợp lệ, vui lòng nhập lại
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- date of birth -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom2"><span>*</span> Ngày Sinh</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<input type="date" formControlName="dateOfBirth" class="form-control" placeholder="Ngày sinh" />
|
||||
<div *ngIf="userDateOfBirth?.invalid && (userDateOfBirth?.dirty || userDateOfBirth?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="userDateOfBirth?.errors?.['required']">Vui lòng không được bỏ
|
||||
trống ngày sinh.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- phone number -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom2"><span>*</span> Số Điện Thoại</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<input formControlName="phoneNumber" class="form-control" type="tel"
|
||||
placeholder="Số điện thoại" />
|
||||
<div *ngIf="userPhoneNumber?.invalid && (userPhoneNumber?.dirty || userPhoneNumber?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="userPhoneNumber?.errors?.['required']">Vui lòng không được bỏ
|
||||
trống số điện thoại.</div>
|
||||
<div *ngIf="userPhoneNumber?.errors?.['pattern']">Số điện thoại phải có tối
|
||||
đa 10 số</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- identified code -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom2"><span>*</span> CMND/CCCD</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<input formControlName="identifiedCode" class="form-control" type="number"
|
||||
placeholder="CMND/CCCD" />
|
||||
<div
|
||||
*ngIf="userIdentifiedCode?.invalid && (userIdentifiedCode?.dirty || userIdentifiedCode?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="userIdentifiedCode?.errors?.['required']">Vui lòng không được bỏ
|
||||
trống CMND/CCCD.</div>
|
||||
<div *ngIf="userIdentifiedCode?.errors?.['pattern']">CMND phải có tối đa 9
|
||||
số hoặc CCCD phải có tối đa 12 số</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Address -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom2"><span>*</span>Địa chỉ</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<input formControlName="address" class="form-control" type="text" placeholder="Địa chỉ" />
|
||||
<div *ngIf="userAddress?.invalid && (userAddress?.dirty || userAddress?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="userAddress?.errors?.['required']">Vui lòng không được bỏ
|
||||
trống địa chỉ</div>
|
||||
<div *ngIf="userAddress?.errors?.['minlength']">Tên phải có ít nhất 8 kí tự</div>
|
||||
<!-- <div *ngIf="userAddress?.errors?.['pattern']">CMND phải có tối đa 9
|
||||
số hoặc CCCD phải có tối đa 12 số</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="needs-validation user-add">
|
||||
<!-- District -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom2"><span>*</span> Quận</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<select [(ngModel)]="district" class="form-control" (change)="onDistrictSelected()">
|
||||
<option [selected]="true" [ngValue]="'none'">--- Chọn quận ---</option>
|
||||
<option *ngFor="let district of districts" [ngValue]="district.name">{{
|
||||
district.name }}
|
||||
</option>
|
||||
</select>
|
||||
<div *ngIf="!validDistrict" class="alert alert-danger mt-1">
|
||||
Vui lòng không được bỏ trống quận
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Wards -->
|
||||
<div *ngIf="isHaveDistrict" class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom2"><span>*</span> Phường</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<select [(ngModel)]="ward" class="form-control">
|
||||
<option [ngValue]="'none'">--- Chọn phường ---</option>
|
||||
<option *ngFor="let ward of wards">{{ ward.name }}
|
||||
</option>
|
||||
</select>
|
||||
<div *ngIf="!validWard" class="alert alert-danger mt-1">
|
||||
Vui lòng không được bỏ trống phường
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form [formGroup]="passwordForm" class="needs-validation user-add" novalidate>
|
||||
<!-- Password -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom2"><span>*</span> Mật khẩu</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<div class="form-control input-password-holder">
|
||||
<input formControlName="password" class="input-password"
|
||||
type="{{ showPassword ? 'text' : 'password' }}" placeholder="Mật khẩu" />
|
||||
<span class="icon-holder-eye" (click)="showPassword = !showPassword">
|
||||
<i class="{{ showPassword ? 'fa fa-eye-slash' : 'fa fa-eye' }}"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="userPassword?.invalid && (userPassword?.dirty || userPassword?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="userPassword?.errors?.['required']">Vui lòng không được bỏ trống mật khẩu</div>
|
||||
<div *ngIf="userPassword?.errors?.['pattern']">
|
||||
Mật khẩu phải chứa ít nhất 8 ký tự, trong đó gồm một ký tự số, một
|
||||
chữ hoa, một chữ thường và một ký
|
||||
tự đặc biệt.
|
||||
<br />
|
||||
Mật khẩu không được chứa khoảng trắng.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confirm Password -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom2"><span>*</span>Nhập lại mật khẩu</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<div class="form-control input-password-holder">
|
||||
<input formControlName="confirmPassword" class="input-password"
|
||||
type="{{ showPassword ? 'text' : 'password' }}" placeholder="Xác nhận mật khẩu" />
|
||||
<span class="icon-holder-eye" (click)="showPassword = !showPassword">
|
||||
<i class="{{ showPassword ? 'fa fa-eye-slash' : 'fa fa-eye' }}"></i>
|
||||
</span>
|
||||
</div>
|
||||
<!-- <input formControlName="confirmPassword" class="form-control" type="text" placeholder="Xác nhận mật khẩu"/> -->
|
||||
<div
|
||||
*ngIf="userConfirmPassword?.invalid && (userConfirmPassword?.dirty || userConfirmPassword?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="userConfirmPassword?.errors?.['required']">Vui lòng không được bỏ trống mật khẩu
|
||||
</div>
|
||||
<div *ngIf="userConfirmPassword?.errors?.['pattern']">
|
||||
Mật khẩu phải chứa ít nhất 8 ký tự, trong đó gồm một ký tự số, một
|
||||
chữ hoa, một chữ thường và một ký
|
||||
tự đặc biệt.
|
||||
<br />
|
||||
Mật khẩu không được chứa khoảng trắng.
|
||||
</div>
|
||||
<div *ngIf="userConfirmPassword?.errors?.['confirmedValidator']">
|
||||
Mật khẩu xác nhận phải giống với mật khẩu đã nhập
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</ng-template>
|
||||
</li>
|
||||
<!-- --------- SHOP ----------- -->
|
||||
<li [ngbNavItem]="2">
|
||||
<a ngbNavLink>Shop đăng ký</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="row product-adding">
|
||||
<div class="col-xl-5">
|
||||
<div class="add-product">
|
||||
<div class="row">
|
||||
<div class="col-xl-9 xl-50 col-sm-6 col-9">
|
||||
<img [src]="shopBanner" *ngIf="shopBanner" alt="" class="img-fluid image_zoom_1">
|
||||
</div>
|
||||
<!-- <div class="col-xl-3 xl-50 col-sm-6 col-3">
|
||||
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-7">
|
||||
<form [formGroup]="shopForm" class="needs-validation user-add" novalidate="">
|
||||
|
||||
<!-- Shop Banner -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom0"><span>*</span> Banner shop</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<div class="d-flex align-items-center">
|
||||
<button *ngIf="!shopImageChoosen" type="submit" class="btn btn-success"
|
||||
(click)="chooseImg(chooseShopImage)">Chọn
|
||||
ảnh</button>
|
||||
<button *ngIf="shopImageChoosen" type="submit" class="btn btn-secondary"
|
||||
(click)="chooseImg(chooseShopImage)">Thay đổi ảnh</button>
|
||||
</div>
|
||||
<!-- <div *ngIf="!shopImageChoosen && (shopForm.get('banner')?.dirty || shopForm.get('banner')?.touched)" class="alert alert-danger mt-1">
|
||||
<div *ngIf="shopForm.get('banner').errors?.['required']">Vui lòng chọn ảnh banner của shop.</div>
|
||||
</div> -->
|
||||
<!-- <div *ngIf="imageUrl?.invalid && (imageUrl?.dirty || imageUrl?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="imageUrl?.errors?.['required']">Vui lòng chọn ảnh đại diện.
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shop Name -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom2"><span>*</span>Tên shop</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<input formControlName="name" class="form-control" type="text" placeholder="Tên shop" />
|
||||
<div *ngIf="shopName?.invalid && (shopName?.dirty || shopName?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="shopName?.errors?.['required']">Vui lòng không được bỏ trống tên shop</div>
|
||||
<div *ngIf="shopName?.errors?.['minlength']">Tên shop phải có ít nhất
|
||||
2 kí tự</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shop Description -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom2"><span>*</span>Mô tả shop</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<textarea formControlName="description" class="form-control" type="text"
|
||||
placeholder="Mô tả về shop"></textarea>
|
||||
<div
|
||||
*ngIf="shopDescription?.invalid && (shopDescription?.dirty || shopDescription?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="shopDescription?.errors?.['required']">Vui lòng không được bỏ
|
||||
trống phần mô tả shop</div>
|
||||
<div *ngIf="shopDescription?.errors?.['minlength']">Mô tả phải có ít nhất 8 kí tự</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Is Student Shop -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom2"><span>*</span> Loại shop</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-md-7">
|
||||
<button *ngIf="isStudent" type="button" class="btn btn-secondary" (click)="swap()">Shop sinh
|
||||
viên
|
||||
</button>
|
||||
<button *ngIf="!isStudent" type="button" class="btn btn-primary" (click)="swap()">Shop
|
||||
thường
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
<div class="pull-right">
|
||||
<button type="button" class="btn btn-danger mr-3" (click)="goBackToLogin()">Quay lại</button>
|
||||
<button type="submit" class="btn btn-primary" (click)="createUser(createSuccess)">Tạo tài khoản</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
|
||||
<ng-template #chooseUserImage>
|
||||
<div class="modal-body text-center">
|
||||
<p>Vui lòng chọn 1 ảnh</p>
|
||||
<input class="input-image" type="file" accept="image/*" (change)="onFileSelected($event)" />
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #chooseShopImage>
|
||||
<div class="modal-body text-center">
|
||||
<p>Vui lòng chọn 1 ảnh</p>
|
||||
<input class="input-image" type="file" accept="image/*" (change)="onShopFileSelected($event)" />
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #createSuccess>
|
||||
<div class="modal-body text-center">
|
||||
<p>Tạo tài khoản thành công. Vui lòng kiểm tra hòm thư để xác thực tài khoản</p>
|
||||
<button type="button" class="btn btn-primary" (click)="continue()">Tiếp tục
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #error_modal>
|
||||
<div class="modal-body text-center">
|
||||
<p>{{ errorItem }} đã tồn tại. Vui lòng chọn một {{ errorItem.toLowerCase() }} khác.</p>
|
||||
<button type="button" class="btn btn-primary" (click)="closeLayer2()">Tiếp tục</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,40 @@
|
||||
.backgound-food {
|
||||
background-image: url('../../../../assets/images/background-login.jpg');
|
||||
width: 100vw;
|
||||
height: 100%;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
z-index: -99;
|
||||
}
|
||||
|
||||
.change-height {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
|
||||
.form-shop-box {
|
||||
width: 1300px;
|
||||
margin: 80px 0;
|
||||
|
||||
.form-shop-item {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.input-password-holder {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.input-password {
|
||||
border: none;
|
||||
width: 100%;
|
||||
}
|
||||
.icon-holder-eye {
|
||||
color: #999a9b;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
import { Component, TemplateRef, ViewChild } from "@angular/core";
|
||||
import { AngularFireStorage } from "@angular/fire/compat/storage";
|
||||
import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms";
|
||||
import { Router } from "@angular/router";
|
||||
import { BsModalRef, BsModalService } from "ngx-bootstrap/modal";
|
||||
import { EMPTY, finalize, switchMap } from "rxjs";
|
||||
import { Observable } from "rxjs-compat";
|
||||
import { Validation } from "src/app/constants/Validation";
|
||||
import { AuthService } from "src/app/shared/service/auth.service";
|
||||
import { DistrictService } from "src/app/shared/service/district.service";
|
||||
import { FirebaseService } from "src/app/shared/service/firebase.service";
|
||||
import { ShopService } from "src/app/shared/service/shop.service";
|
||||
import { UserService } from "src/app/shared/service/user.service";
|
||||
import { Address } from "src/app/shared/tables/address";
|
||||
import { District } from "src/app/shared/tables/district";
|
||||
import { Shop } from "src/app/shared/tables/shop";
|
||||
import { User } from "src/app/shared/tables/user";
|
||||
import { Ward } from "src/app/shared/tables/ward";
|
||||
import { environment } from "src/environments/environment";
|
||||
|
||||
@Component({
|
||||
selector: "app-signup",
|
||||
templateUrl: "./signup.component.html",
|
||||
styleUrls: ["./signup.component.scss"],
|
||||
})
|
||||
export class SignupComponent {
|
||||
// form
|
||||
public active = 1;
|
||||
addUserForm: FormGroup;
|
||||
passwordForm: FormGroup;
|
||||
shopForm: FormGroup;
|
||||
|
||||
// image
|
||||
avatar: string;
|
||||
shopBanner: string;
|
||||
defaultUserImg = environment.userDefaultImg;
|
||||
defaultShopImg = environment.shopDefaultImg;
|
||||
|
||||
// file
|
||||
downloadURL: Observable<string>;
|
||||
userImageFile: File;
|
||||
shopImageFile: File;
|
||||
imageLink;
|
||||
|
||||
// password
|
||||
showPassword = false;
|
||||
|
||||
// address
|
||||
isHaveDistrict: boolean;
|
||||
district: string = "none";
|
||||
ward: string = "none";
|
||||
validDistrict = true;
|
||||
validWard = true;
|
||||
|
||||
districts: District[];
|
||||
wards: Ward[];
|
||||
|
||||
userImageChoosen: boolean = false;
|
||||
shopImageChoosen: boolean = false;
|
||||
isStudent: boolean = true;
|
||||
|
||||
//Modal
|
||||
errorItem = '';
|
||||
layer1: BsModalRef;
|
||||
layer2: BsModalRef;
|
||||
@ViewChild('error_modal') errorModal: TemplateRef<any>;
|
||||
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
private authService: AuthService,
|
||||
private userService: UserService,
|
||||
private districtService: DistrictService,
|
||||
private storage: AngularFireStorage,
|
||||
private shopService: ShopService,
|
||||
private modalService: BsModalService,
|
||||
private router: Router,
|
||||
private firebaseService: FirebaseService
|
||||
) {
|
||||
this.createUserForm();
|
||||
this.createPasswordForm();
|
||||
this.createShopForm();
|
||||
}
|
||||
|
||||
createUserForm() {
|
||||
this.addUserForm = this.formBuilder.group({
|
||||
fullName: new FormControl("", [Validators.required, Validators.minLength(2)]),
|
||||
email: new FormControl("", [Validators.required, Validators.email]),
|
||||
dateOfBirth: new FormControl("", [Validators.required]),
|
||||
phoneNumber: new FormControl("", [Validators.required, Validators.pattern(Validation.Regex.Phone)]),
|
||||
identifiedCode: new FormControl("", [Validators.required, Validators.pattern(Validation.Regex.IdentifiedCode)]),
|
||||
address: new FormControl("", [Validators.required, Validators.minLength(8)]),
|
||||
});
|
||||
}
|
||||
|
||||
createPasswordForm() {
|
||||
this.passwordForm = this.formBuilder.group(
|
||||
{
|
||||
password: new FormControl("", [Validators.required, Validators.pattern(Validation.Regex.Password)]),
|
||||
confirmPassword: new FormControl("", [Validators.required, Validators.pattern(Validation.Regex.Password)]),
|
||||
},
|
||||
{
|
||||
validator: this.ConfirmedValidator("password", "confirmPassword"),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Validation for password and confirm password
|
||||
ConfirmedValidator(controlName: string, matchingControlName: string) {
|
||||
return (formGroup: FormGroup) => {
|
||||
const control = formGroup.controls[controlName];
|
||||
const matchingControl = formGroup.controls[matchingControlName];
|
||||
if (matchingControl.errors && !matchingControl.errors.confirmedValidator) {
|
||||
return;
|
||||
}
|
||||
if (control.value !== matchingControl.value) {
|
||||
matchingControl.setErrors({ confirmedValidator: true });
|
||||
} else {
|
||||
matchingControl.setErrors(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
createShopForm() {
|
||||
this.shopForm = this.formBuilder.group({
|
||||
name: new FormControl("", [Validators.required, Validators.minLength(2)]),
|
||||
description: new FormControl("", [Validators.required, Validators.minLength(8)]),
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.getAllDistrict();
|
||||
}
|
||||
|
||||
/**
|
||||
* create User from Form
|
||||
*/
|
||||
createUser(template: TemplateRef<any>): Promise<void> {
|
||||
const newUser = new User();
|
||||
const newAddress = new Address();
|
||||
const newShop = new Shop();
|
||||
|
||||
newUser.fullName = this.userFullName.value;
|
||||
newUser.email = this.userEmail.value;
|
||||
newUser.dateOfBirth = this.userDateOfBirth.value;
|
||||
newUser.phoneNumber = this.userPhoneNumber.value;
|
||||
newUser.identifiedCode = this.userIdentifiedCode.value;
|
||||
newUser.defaultAddress = 0;
|
||||
newUser.isLocked = false;
|
||||
newUser.roleName = 'ROLE_SHOP';
|
||||
|
||||
if (this.district === 'none' || this.ward === 'none') {
|
||||
this.validDistrict = this.district !== 'none';
|
||||
this.validWard = this.ward !== 'none';
|
||||
} else {
|
||||
// Tiếp tục với quá trình tạo tài khoản
|
||||
this.validDistrict = true;
|
||||
this.validWard = true;
|
||||
}
|
||||
|
||||
|
||||
if (this.addUserForm.invalid || this.passwordForm.invalid || this.shopForm.invalid || this.district == 'none') {
|
||||
this.addUserForm.markAllAsTouched();
|
||||
this.passwordForm.markAllAsTouched();
|
||||
this.shopForm.markAllAsTouched();
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
newShop.name = this.shopName.value
|
||||
newShop.description = this.shopDescription.value;
|
||||
newShop.isStudent = this.isStudent;
|
||||
newShop.isEnabled = true;
|
||||
newShop.lat = '1';
|
||||
newShop.lng = '1';
|
||||
|
||||
|
||||
newAddress.address = this.userAddress.value;
|
||||
newAddress.district = this.district;
|
||||
if (this.district != "Huyện Hoàng Sa") newAddress.ward = this.ward;
|
||||
|
||||
this.authService.checkEmailOrPhoneNumberExist(newUser).subscribe((error) => {
|
||||
if (error.title == 'emailExist') {
|
||||
this.errorItem = 'Email';
|
||||
this.layer2 = this.modalService.show(this.errorModal, { class: 'modal-sm' });
|
||||
return Promise.reject();
|
||||
}
|
||||
else if (error.title == 'phoneNumExist') {
|
||||
this.errorItem = 'Số điện thoại';
|
||||
this.layer2 = this.modalService.show(this.errorModal, { class: 'modal-sm' });
|
||||
return Promise.reject();
|
||||
}
|
||||
else {
|
||||
if (this.userImageChoosen && this.shopImageChoosen) {
|
||||
this.uploadUserImage(this.userImageFile).then((url) => {
|
||||
newUser.imageUrl = url
|
||||
|
||||
this.authService.SignUpUser(newUser).pipe(
|
||||
switchMap((user) => {
|
||||
this.uploadShopImage(this.shopImageFile).then((url) => {
|
||||
newShop.userId = user.id
|
||||
newShop.imageUrl = url;
|
||||
this.authService.SignUpUserAddress(user.id, newAddress).subscribe({
|
||||
next: () => {
|
||||
this.firebaseService.signUp(newUser.email, this.userPassword.value);
|
||||
this.authService.SignUpShop(newShop).subscribe(() => {
|
||||
this.layer1 = this.modalService.show(template, { class: 'modal-sm' })
|
||||
})
|
||||
}
|
||||
});
|
||||
})
|
||||
return EMPTY; // Return an empty observable to prevent nested subscriptions
|
||||
})
|
||||
).subscribe({
|
||||
next: () => { },
|
||||
error: () => {
|
||||
this.errorItem = 'CCCD/CMND';
|
||||
this.layer2 = this.modalService.show(this.errorModal, { class: 'modal-sm' });
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
else if (this.userImageChoosen && !this.shopImageChoosen) {
|
||||
this.uploadUserImage(this.userImageFile).then((url) => {
|
||||
newUser.imageUrl = url
|
||||
|
||||
this.authService.SignUpUser(newUser).pipe(
|
||||
switchMap((user) => {
|
||||
newShop.userId = user.id
|
||||
newShop.imageUrl = this.defaultShopImg;
|
||||
this.authService.SignUpUserAddress(user.id, newAddress).subscribe({
|
||||
next: () => {
|
||||
this.firebaseService.signUp(newUser.email, this.userPassword.value);
|
||||
this.authService.SignUpShop(newShop).subscribe(() => {
|
||||
this.layer1 = this.modalService.show(template, { class: 'modal-sm' })
|
||||
})
|
||||
}
|
||||
});
|
||||
return EMPTY; // Return an empty observable to prevent nested subscriptions
|
||||
})
|
||||
).subscribe({
|
||||
next: () => { },
|
||||
error: () => {
|
||||
this.errorItem = 'CCCD/CMND';
|
||||
this.layer2 = this.modalService.show(this.errorModal, { class: 'modal-sm' });
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
else if (!this.userImageChoosen && this.shopImageChoosen) {
|
||||
newUser.imageUrl = this.defaultUserImg;
|
||||
|
||||
this.authService.SignUpUser(newUser).pipe(
|
||||
switchMap((user) => {
|
||||
this.uploadShopImage(this.shopImageFile).then((url) => {
|
||||
newShop.userId = user.id
|
||||
newShop.imageUrl = url;
|
||||
this.authService.SignUpUserAddress(user.id, newAddress).subscribe({
|
||||
next: () => {
|
||||
this.firebaseService.signUp(newUser.email, this.userPassword.value);
|
||||
this.authService.SignUpShop(newShop).subscribe(() => {
|
||||
this.layer1 = this.modalService.show(template, { class: 'modal-sm' })
|
||||
})
|
||||
}
|
||||
});
|
||||
})
|
||||
return EMPTY; // Return an empty observable to prevent nested subscriptions
|
||||
})
|
||||
).subscribe({
|
||||
next: () => { },
|
||||
error: () => {
|
||||
this.errorItem = 'CCCD/CMND';
|
||||
this.layer2 = this.modalService.show(this.errorModal, { class: 'modal-sm' });
|
||||
}
|
||||
})
|
||||
}
|
||||
else {
|
||||
newUser.imageUrl = this.defaultUserImg;
|
||||
|
||||
this.authService.SignUpUser(newUser).pipe(
|
||||
switchMap((user) => {
|
||||
newShop.userId = user.id
|
||||
newShop.imageUrl = this.defaultShopImg;
|
||||
this.authService.SignUpUserAddress(user.id, newAddress).subscribe({
|
||||
next: () => {
|
||||
this.firebaseService.signUp(newUser.email, this.userPassword.value);
|
||||
this.authService.SignUpShop(newShop).subscribe(() => {
|
||||
this.layer1 = this.modalService.show(template, { class: 'modal-sm' })
|
||||
})
|
||||
}
|
||||
});
|
||||
return EMPTY; // Return an empty observable to prevent nested subscriptions
|
||||
})
|
||||
).subscribe({
|
||||
next: () => { },
|
||||
error: () => {
|
||||
this.errorItem = 'CCCD/CMND';
|
||||
this.layer2 = this.modalService.show(this.errorModal, { class: 'modal-sm' });
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//Districts and Wards
|
||||
getAllDistrict() {
|
||||
this.districtService.getAllDistricts().subscribe((distrs) => {
|
||||
this.districts = distrs;
|
||||
});
|
||||
}
|
||||
|
||||
onDistrictSelected() {
|
||||
this.isHaveDistrict = false;
|
||||
this.districts.forEach((element: District) => {
|
||||
if (this.district == element.name && this.district != "Huyện Hoàng Sa") {
|
||||
this.isHaveDistrict = true;
|
||||
this.wards = element.wards;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
swap() {
|
||||
if (this.isStudent) {
|
||||
this.isStudent = false;
|
||||
} else {
|
||||
this.isStudent = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Image Selected
|
||||
onFileSelected(event) {
|
||||
this.userImageChoosen = true;
|
||||
this.userImageFile = event.target.files[0];
|
||||
|
||||
const reader = new FileReader();
|
||||
if (event.target.files && event.target.files.length) {
|
||||
const [file] = event.target.files;
|
||||
reader.readAsDataURL(file);
|
||||
|
||||
reader.onload = () => {
|
||||
this.avatar = reader.result as string;
|
||||
localStorage.setItem("image", this.avatar);
|
||||
};
|
||||
}
|
||||
this.layer1.hide();
|
||||
}
|
||||
|
||||
//Shop Image Selected
|
||||
onShopFileSelected(event) {
|
||||
this.shopImageChoosen = true;
|
||||
this.shopImageFile = event.target.files[0];
|
||||
|
||||
const reader = new FileReader();
|
||||
if (event.target.files && event.target.files.length) {
|
||||
const [file] = event.target.files;
|
||||
reader.readAsDataURL(file);
|
||||
|
||||
reader.onload = () => {
|
||||
this.shopBanner = reader.result as string;
|
||||
localStorage.setItem("image", this.shopBanner);
|
||||
};
|
||||
}
|
||||
this.layer1.hide();
|
||||
}
|
||||
|
||||
uploadUserImage(fileUpload: File): Promise<string> {
|
||||
return new Promise<string>((resolve) => {
|
||||
let n = Date.now();
|
||||
const filePath = `UserImages/${n}`;
|
||||
|
||||
const fileRef = this.storage.ref(filePath);
|
||||
const task = this.storage.upload(`UserImages/${n}`, fileUpload);
|
||||
task
|
||||
.snapshotChanges()
|
||||
.pipe(
|
||||
finalize(() => {
|
||||
this.downloadURL = fileRef.getDownloadURL();
|
||||
this.downloadURL.subscribe((url) => {
|
||||
if (url) {
|
||||
resolve(url);
|
||||
}
|
||||
});
|
||||
})
|
||||
)
|
||||
.subscribe((url) => { });
|
||||
});
|
||||
}
|
||||
|
||||
uploadShopImage(fileUpload: File): Promise<string> {
|
||||
return new Promise<string>((resolve) => {
|
||||
let n = Date.now();
|
||||
const filePath = `Shops/${n}`;
|
||||
|
||||
const fileRef = this.storage.ref(filePath);
|
||||
const task = this.storage.upload(`Shops/${n}`, fileUpload);
|
||||
task
|
||||
.snapshotChanges()
|
||||
.pipe(
|
||||
finalize(() => {
|
||||
this.downloadURL = fileRef.getDownloadURL();
|
||||
this.downloadURL.subscribe((url) => {
|
||||
if (url) {
|
||||
resolve(url);
|
||||
}
|
||||
});
|
||||
})
|
||||
)
|
||||
.subscribe((url) => { });
|
||||
});
|
||||
}
|
||||
|
||||
chooseImg(template: TemplateRef<any>) {
|
||||
this.layer1 = this.modalService.show(template, { class: "modal-sm" });
|
||||
}
|
||||
|
||||
continue() {
|
||||
this.layer1.hide();
|
||||
this.router.navigate(["/vendors/list"]);
|
||||
}
|
||||
|
||||
closeLayer2() {
|
||||
this.layer2.hide();
|
||||
}
|
||||
|
||||
goBackToLogin() {
|
||||
this.router.navigate(['/auth/login']);
|
||||
}
|
||||
|
||||
//Getter
|
||||
get image() {
|
||||
return this.addUserForm.get("image");
|
||||
}
|
||||
get userFullName() {
|
||||
return this.addUserForm.get("fullName");
|
||||
}
|
||||
get userEmail() {
|
||||
return this.addUserForm.get("email");
|
||||
}
|
||||
get userDateOfBirth() {
|
||||
return this.addUserForm.get("dateOfBirth");
|
||||
}
|
||||
get userPhoneNumber() {
|
||||
return this.addUserForm.get("phoneNumber");
|
||||
}
|
||||
get userIdentifiedCode() {
|
||||
return this.addUserForm.get("identifiedCode");
|
||||
}
|
||||
get userAddress() {
|
||||
return this.addUserForm.get("address");
|
||||
}
|
||||
get userDistrict() {
|
||||
return this.addUserForm.get("district");
|
||||
}
|
||||
get userWard() {
|
||||
return this.addUserForm.get("ward");
|
||||
}
|
||||
|
||||
get userPassword() {
|
||||
return this.passwordForm.get("password");
|
||||
}
|
||||
get userConfirmPassword() {
|
||||
return this.passwordForm.get("confirmPassword");
|
||||
}
|
||||
|
||||
//Shop
|
||||
get banner() {
|
||||
return this.shopForm.get("banner");
|
||||
}
|
||||
get shopName() {
|
||||
return this.shopForm.get("name");
|
||||
}
|
||||
get shopDescription() {
|
||||
return this.shopForm.get("description");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { ListCouponComponent } from './list-coupon/list-coupon.component';
|
||||
import { CreateCouponComponent } from './create-coupon/create-coupon.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
children: [
|
||||
{
|
||||
path: 'list-coupons',
|
||||
component: ListCouponComponent,
|
||||
data: {
|
||||
title: "List Coupons",
|
||||
breadcrumb: "List Coupons"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'create-coupons',
|
||||
component: CreateCouponComponent,
|
||||
data: {
|
||||
title: "Create Coupon",
|
||||
breadcrumb: "Create Coupons"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class CouponsRoutingModule { }
|
||||
@@ -0,0 +1,21 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { CouponsRoutingModule } from './coupons-routing.module';
|
||||
import { ListCouponComponent } from './list-coupon/list-coupon.component';
|
||||
import { CreateCouponComponent } from './create-coupon/create-coupon.component';
|
||||
import { SharedModule } from 'src/app/shared/shared.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ListCouponComponent, CreateCouponComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
CouponsRoutingModule,
|
||||
NgbModule,
|
||||
ReactiveFormsModule,
|
||||
SharedModule,
|
||||
FormsModule
|
||||
]
|
||||
})
|
||||
export class CouponsModule { }
|
||||
@@ -0,0 +1,184 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Discount Coupon Details</h5>
|
||||
</div>
|
||||
<div class="card-body tab2-card">
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
|
||||
<li [ngbNavItem]="1">
|
||||
<a ngbNavLink>General</a>
|
||||
<ng-template ngbNavContent>
|
||||
<form [formGroup]="generalForm" class="needs-validation" novalidate="">
|
||||
<h4>General</h4>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom0"><span>*</span>
|
||||
Coupan Title</label>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<input class="form-control" formControlName="name"
|
||||
id="validationCustom0" type="text" required="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom1"><span>*</span>
|
||||
Coupan Code</label>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<input class="form-control" formControlName="code"
|
||||
id="validationCustom1" type="text" required="">
|
||||
</div>
|
||||
<div class="valid-feedback">Please Provide a Valid Coupon Code.</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label>Start Date</label>
|
||||
</div>
|
||||
<div class="col-md-7 ">
|
||||
<div class="input-group input-grp-p">
|
||||
<input class="datepicker-here form-control digits" ngbDatepicker
|
||||
formControlName="start_date" #f="ngbDatepicker">
|
||||
<div class="input-group-append">
|
||||
<button class="btn calendar" (click)="f.toggle()" type="button"><i
|
||||
class="fa fa-calendar"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label>End Date</label>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="input-group input-grp-p">
|
||||
<input class="datepicker-here form-control digits" ngbDatepicker
|
||||
formControlName="end_date" #c="ngbDatepicker">
|
||||
<div class="input-group-append">
|
||||
<button class="btn calendar" (click)="c.toggle()" type="button"><i
|
||||
class="fa fa-calendar"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label>Free Shipping</label>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="checkbox checkbox-primary">
|
||||
<input id="checkbox-primary-1" type="checkbox"
|
||||
formControlName="free_shipping" data-original-title="" title="">
|
||||
<label for="checkbox-primary-1">Allow Free Shipping</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4"><label>Quantity</label></div>
|
||||
<div class="col-md-7">
|
||||
<input class="form-control" formControlName="quantity" type="number"
|
||||
required="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4"><label>Discount Type</label></div>
|
||||
<div class="col-md-7">
|
||||
<select class="custom-select form-select" required="">
|
||||
<option value="">--Select--</option>
|
||||
<option value="1">Percent</option>
|
||||
<option value="2">Fixed</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label>Status</label>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="checkbox checkbox-primary">
|
||||
<input id="checkbox-primary-2" formControlName="status" type="checkbox"
|
||||
data-original-title="" title="">
|
||||
<label for="checkbox-primary-2">Enable the Coupon</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li [ngbNavItem]="2">
|
||||
<a ngbNavLink>Restriction</a>
|
||||
<ng-template ngbNavContent>
|
||||
<form [formGroup]="restrictionForm" class="needs-validation" novalidate="">
|
||||
<h4>Restriction</h4>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4"><label for="validationCustom3">Products</label></div>
|
||||
<div class="col-md-7"><input class="form-control" formControlName="products" id="validationCustom3"
|
||||
type="text" required="">
|
||||
</div>
|
||||
<div class="valid-feedback">Please Provide a Product Name.</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4"><label>Category</label></div>
|
||||
<div class="col-md-7">
|
||||
<select class="custom-select form-select" formControlName="category" required="">
|
||||
<option value="">--Select--</option>
|
||||
<option value="1">Electronics</option>
|
||||
<option value="2">Clothes</option>
|
||||
<option value="2">Shoes</option>
|
||||
<option value="2">Digital</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4"><label for="validationCustom4">Minimum Spend</label></div>
|
||||
<div class="col-md-7">
|
||||
<input class="form-control" formControlName="min" id="validationCustom4"
|
||||
type="number">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4">
|
||||
<label for="validationCustom5">Maximum Spend</label>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<input class="form-control" formControlName="max" id="validationCustom5"
|
||||
type="number">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li [ngbNavItem]="3">
|
||||
<a ngbNavLink>Usage</a>
|
||||
<ng-template ngbNavContent>
|
||||
<form [formGroup]="usageForm" class="needs-validation" novalidate="">
|
||||
<h4>Usage Limits</h4>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4"><label for="validationCustom6">Per Limit</label></div>
|
||||
<div class="col-md-7"><input class="form-control" formControlName="limit" id="validationCustom6"
|
||||
type="number">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4"><label for="validationCustom7">Per Customer</label></div>
|
||||
<div class="col-md-7"><input class="form-control" formControlName="customer" id="validationCustom7"
|
||||
type="number">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
<div class="pull-right">
|
||||
<button type="button" class="btn btn-primary">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,61 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { NgbDateStruct, NgbDate, NgbCalendar, NgbDatepickerConfig } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-coupon',
|
||||
templateUrl: './create-coupon.component.html',
|
||||
styleUrls: ['./create-coupon.component.scss']
|
||||
})
|
||||
export class CreateCouponComponent implements OnInit {
|
||||
public generalForm: UntypedFormGroup;
|
||||
public restrictionForm: UntypedFormGroup;
|
||||
public usageForm: UntypedFormGroup;
|
||||
public model: NgbDateStruct;
|
||||
public date: { year: number, month: number };
|
||||
public modelFooter: NgbDateStruct;
|
||||
public active = 1;
|
||||
|
||||
constructor(private formBuilder: UntypedFormBuilder, private calendar: NgbCalendar) {
|
||||
this.createGeneralForm();
|
||||
this.createRestrictionForm();
|
||||
this.createUsageForm();
|
||||
}
|
||||
|
||||
selectToday() {
|
||||
this.model = this.calendar.getToday();
|
||||
}
|
||||
|
||||
createGeneralForm() {
|
||||
this.generalForm = this.formBuilder.group({
|
||||
name: [''],
|
||||
code: [''],
|
||||
start_date: [''],
|
||||
end_date: [''],
|
||||
free_shipping: [''],
|
||||
quantity: [''],
|
||||
discount_type: [''],
|
||||
status: [''],
|
||||
});
|
||||
}
|
||||
|
||||
createRestrictionForm() {
|
||||
this.restrictionForm = this.formBuilder.group({
|
||||
products: [''],
|
||||
category: [''],
|
||||
min: [''],
|
||||
max: ['']
|
||||
})
|
||||
}
|
||||
|
||||
createUsageForm() {
|
||||
this.usageForm = this.formBuilder.group({
|
||||
limit: [''],
|
||||
customer: ['']
|
||||
})
|
||||
}
|
||||
ngOnInit() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Products Category</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="custom-datatable">
|
||||
<button class="btn btn-primary me-1 mb-3" style="float: right;">Delete</button>
|
||||
<div class="clearfix"></div>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" sortable="title" (sort)="onSort($event)">Title</th>
|
||||
<th scope="col" sortable="code" (sort)="onSort($event)">Code</th>
|
||||
<th scope="col" sortable="discount" (sort)="onSort($event)">Discoount</th>
|
||||
<th scope="col" sortable="status" (sort)="onSort($event)">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of tableItem$ | async ">
|
||||
|
||||
<th scope="row">
|
||||
<input type="checkbox" name="title_select" onclick="onSelect(item.id)">
|
||||
{{ item.title }}
|
||||
</th>
|
||||
<td>
|
||||
{{item.code}}
|
||||
</td>
|
||||
<td>{{item.discount}}</td>
|
||||
<td><i class="fa fa-circle {{item.status}} f-12"></i></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="d-flex justify-content-center p-2">
|
||||
<ngb-pagination
|
||||
[collectionSize]="(total$ | async)!" [(page)]="service.page" [pageSize]="service.pageSize">
|
||||
</ngb-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,51 @@
|
||||
import {DecimalPipe} from '@angular/common';
|
||||
import {Component, OnInit, QueryList, ViewChildren} from '@angular/core';
|
||||
import {Observable} from 'rxjs';
|
||||
import {NgbdSortableHeader, SortEvent} from 'src/app/shared/directives/NgbdSortableHeader';
|
||||
import {TableService} from 'src/app/shared/service/table.service';
|
||||
import {LISTCOUPLEDB, ListCouponsDB} from 'src/app/shared/tables/list-coupon';
|
||||
|
||||
@Component({
|
||||
selector: 'app-list-coupon',
|
||||
templateUrl: './list-coupon.component.html',
|
||||
styleUrls: ['./list-coupon.component.scss'],
|
||||
providers: [TableService, DecimalPipe]
|
||||
})
|
||||
export class ListCouponComponent implements OnInit {
|
||||
|
||||
public selected = [];
|
||||
|
||||
public tableItem$: Observable<ListCouponsDB[]>;
|
||||
public searchText;
|
||||
total$: Observable<number>;
|
||||
|
||||
constructor(public service: TableService) {
|
||||
this.tableItem$ = service.tableItem$;
|
||||
this.total$ = service.total$;
|
||||
this.service.setUserData(LISTCOUPLEDB);
|
||||
}
|
||||
|
||||
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
|
||||
|
||||
onSort({column, direction}: SortEvent) {
|
||||
// resetting other headers
|
||||
this.headers.forEach((header) => {
|
||||
if (header.sortable !== column) {
|
||||
header.direction = '';
|
||||
}
|
||||
});
|
||||
|
||||
this.service.sortColumn = column;
|
||||
this.service.sortDirection = direction;
|
||||
|
||||
}
|
||||
|
||||
onSelect({selected}) {
|
||||
this.selected.splice(0, this.selected.length);
|
||||
this.selected.push(...selected);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { DashboardComponent } from './dashboard.component';
|
||||
import { AuthGuard } from 'src/app/shared/guard/auth-guard.service';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
children: [
|
||||
{
|
||||
path: 'default',
|
||||
component: DashboardComponent,
|
||||
canActivate: [AuthGuard],
|
||||
data: {
|
||||
title: "Trang chủ",
|
||||
breadcrumb: "Trang chủ"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class DashboardRoutingModule { }
|
||||
@@ -0,0 +1,726 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xl-3 col-md-6 xl-50">
|
||||
<div class="card o-hidden widget-cards">
|
||||
<div class="bg-warning card-body">
|
||||
<div class="media static-top-widget row">
|
||||
<div class="icons-widgets col-4">
|
||||
<div class="align-self-center text-center">
|
||||
<app-feather-icons *ngIf="isShop" [icon]="'dollar-sign'"
|
||||
class="font-warning"></app-feather-icons>
|
||||
<app-feather-icons *ngIf="!isShop" [icon]="'user'"
|
||||
class="font-warning"></app-feather-icons>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="isShop" class="media-body col-8"><span class="m-0">Thu nhập 30 ngày qua</span>
|
||||
<h3 class="mb-0">
|
||||
<span class="counter" [CountTo]="totalRevenue" [from]="0" [duration]="2">{{ totalRevenue
|
||||
| currency : "VND"
|
||||
}}</span>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!isShop" class="media-body col-8"><span class="m-0">Người dùng đăng ký mới</span>
|
||||
<h3 class="mb-0">
|
||||
<span class="counter" [CountTo]="totalUser" [from]="0" [duration]="2">{{ totalUser
|
||||
}}</span><small>
|
||||
người dùng</small>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6 xl-50">
|
||||
<div class="card o-hidden widget-cards">
|
||||
<div class="bg-secondary card-body">
|
||||
<div class="media static-top-widget row">
|
||||
<div class="icons-widgets col-4">
|
||||
<div class="align-self-center text-center">
|
||||
<app-feather-icons [icon]="'package'" class="font-secondary"></app-feather-icons>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media-body col-8"><span class="m-0">Tổng sản phẩm đã bán</span>
|
||||
<h3 class="mb-0"><span class="counter" [CountTo]="totalProducts" [from]="0"
|
||||
[duration]="2">{{ totalProducts }}</span><small> sản phẩm</small></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6 xl-50">
|
||||
<div class="card o-hidden widget-cards">
|
||||
<div class="bg-primary card-body">
|
||||
<div class="media static-top-widget row">
|
||||
<div class="icons-widgets col-4">
|
||||
<div class="align-self-center text-center">
|
||||
<app-feather-icons [icon]="'message-square'" class="font-primary"></app-feather-icons>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media-body col-8"><span class="m-0">Số lượt đánh giá sản phẩm</span>
|
||||
<h3 class="mb-0"><span class="counter" [CountTo]="totalReviewCount" [from]="0"
|
||||
[duration]="2">{{ totalReviewCount }}</span><small> lượt</small></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6 xl-50">
|
||||
<div class="card o-hidden widget-cards">
|
||||
<div class="bg-danger card-body">
|
||||
<div class="media static-top-widget row">
|
||||
<div class="icons-widgets col-4">
|
||||
<div class="align-self-center text-center">
|
||||
<app-feather-icons [icon]="'star'" class="font-danger"></app-feather-icons>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media-body col-8"><span class="m-0">Đánh giá trung bình</span>
|
||||
<h3 class="mb-0"><span class="counter" [CountTo]="totalAverageRating" [from]="0"
|
||||
[duration]="2">{{ totalAverageRating }}</span><small> /5</small></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="col-xl-6 xl-100">-->
|
||||
<!-- <div class="card height-equal">-->
|
||||
<!-- <div class="card-header">-->
|
||||
<!-- <h5>Market Value</h5>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="card-body">-->
|
||||
<!-- <div class="market-chart">-->
|
||||
<!-- <x-chartist class="board-chart ct-golden-section" [data]="chart3.data" [type]="chart3.type"-->
|
||||
<!-- [options]="chart3.options" [events]="chart3.events">-->
|
||||
<!-- </x-chartist>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<div class="col-xl-12 xl-100">
|
||||
<div class="card height-equal">
|
||||
<div class="card-header">
|
||||
<h5 *ngIf="isShop">Đơn hàng của bạn</h5>
|
||||
<h5 *ngIf="!isShop">Các đơn hàng gần đây</h5>
|
||||
</div>
|
||||
<div class="card-body" *ngIf="orders.length">
|
||||
<div class=" user-status table-responsive latest-order-table">
|
||||
<table class="table table-bordernone">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Mã đơn hàng</th>
|
||||
<th scope="col">Tổng giá trị đơn hàng</th>
|
||||
<th scope="col">Phương thức thanh toán</th>
|
||||
<th scope="col">Trạng thái đơn hàng</th>
|
||||
<th scope="col">Địa chỉ</th>
|
||||
<th scope="col">Shipper</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let order of orders; let i = index">
|
||||
<td>{{ order?.orderTrackingNumber }}</td>
|
||||
<td>{{ order?.total | currency : "VND" }}</td>
|
||||
<td><span [ngClass]="{
|
||||
badge: true,
|
||||
'badge-info': order.paymentMethod === 'ZALO PAY',
|
||||
'badge-success': order.paymentMethod === 'CASH'
|
||||
}">
|
||||
{{ order.paymentMethod === "CASH"
|
||||
? "Tiền mặt"
|
||||
: order.paymentMethod === "ZALO PAY"
|
||||
? "Zalo pay"
|
||||
: ""
|
||||
}}
|
||||
</span></td>
|
||||
<td><span [ngClass]="{
|
||||
badge: true,
|
||||
'badge-dark': order.status === 'AWAITING',
|
||||
'badge-warning': order.status === 'CONFIRMED',
|
||||
'badge-info': order.status === 'SHIPPING',
|
||||
'badge-success': order.status === 'COMPLETED',
|
||||
'badge-primary': order.status === 'CANCELED' || order.status === 'REJECT_DELIVERY'
|
||||
}">
|
||||
{{
|
||||
order.status === "AWAITING"
|
||||
? "Chờ xác nhận"
|
||||
: order.status === "CONFIRMED"
|
||||
? "Đã xác nhận"
|
||||
: order.status === "SHIPPING"
|
||||
? "Đang giao hàng"
|
||||
: order.status === "COMPLETED"
|
||||
? "Giao thành công"
|
||||
: order.status === "CANCELED"
|
||||
? "Đã huỷ đơn"
|
||||
: order.status === "REJECT_DELIVERY"
|
||||
? "Không nhận hàng"
|
||||
: ""
|
||||
}}
|
||||
</span></td>
|
||||
<td>{{ order?.address }}</td>
|
||||
<td *ngIf="order?.shipper?.user?.fullName != null">{{ order?.shipper?.user?.fullName
|
||||
}}</td>
|
||||
<td *ngIf="order?.shipper?.user?.fullName == null"><strong>Đang chờ...</strong></td>
|
||||
<td>
|
||||
<a [routerLink]="['/sales', 'detail-order', 'user', order.user.id, 'order', order.id]"
|
||||
style="cursor: pointer;">
|
||||
<i style="color: lightseagreen;" class="fa fa-eye f-12"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- <tr>
|
||||
<td>2</td>
|
||||
<td class="digits">$90.00</td>
|
||||
<td class="font-secondary">Ewallets</td>
|
||||
<td class="digits">Delivered</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td class="digits">$240.00</td>
|
||||
<td class="font-warning">Cash</td>
|
||||
<td class="digits">Delivered</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td class="digits">$120.00</td>
|
||||
<td class="font-primary">Direct Deposit</td>
|
||||
<td class="digits">$6523</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td class="digits">$50.00</td>
|
||||
<td class="font-primary">Bank Transfers</td>
|
||||
<td class="digits">Delivered</td>
|
||||
</tr> -->
|
||||
</tbody>
|
||||
</table>
|
||||
<a [routerLink]="['/sales', 'orders']" class="btn btn-primary">Xem tất cả đơn hàng</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class=" user-status table-responsive latest-order-table" *ngIf="!orders.length">
|
||||
<div class="d-flex justify-content-center p-2">
|
||||
<img src="../../../assets/images/error/no-order.png" alt="" srcset="">
|
||||
</div>
|
||||
<div class="d-flex justify-content-center p-2">
|
||||
<h4>Bạn chưa có đơn hàng nào</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="col-xl-3 col-md-6 xl-50">
|
||||
<div class="card order-graph sales-carousel">
|
||||
<div class="card-header">
|
||||
<h6>Total Sales</h6>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="small-chartjs">
|
||||
<canvas baseChart id="small-graph-1" height="600" width="400"
|
||||
[datasets]="smallLineChartData" [labels]="smallLineChartLabels"
|
||||
[options]="smallLineChartOptions" [legend]="smallLineChartLegend"
|
||||
[type]="smallLineChartType" (chartHover)="chartHovered($event)"
|
||||
(chartClick)="chartClicked($event)"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="value-graph">
|
||||
<h3>42% <span><i class="fa fa-angle-up font-primary"></i></span></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="media-body">
|
||||
<span>Sales Last Month</span>
|
||||
<h2 class="mb-0">9054</h2>
|
||||
<p>0.25% <span><i class="fa fa-angle-up"></i></span></p>
|
||||
<h5 class="f-w-600">Gross sales of August</h5>
|
||||
<p>Lorem Ipsum is simply dummy text of the printing and typesetting</p>
|
||||
</div>
|
||||
<div class="bg-primary b-r-8">
|
||||
<div class="small-box">
|
||||
<i data-feather="briefcase"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6 xl-50">
|
||||
<div class="card order-graph sales-carousel">
|
||||
<div class="card-header">
|
||||
<h6>Total purchase</h6>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="small-chartjs">
|
||||
<canvas baseChart id="small-graph-2" height="60vw" width="140vw"
|
||||
[datasets]="smallLine2ChartData" [labels]="smallLine2ChartLabels"
|
||||
[options]="smallLine2ChartOptions" [legend]="smallLine2ChartLegend"
|
||||
[type]="smallLine2ChartType" (chartHover)="chartHovered($event)"
|
||||
(chartClick)="chartClicked($event)"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="value-graph">
|
||||
<h3>20% <span><i class="fa fa-angle-up font-secondary"></i></span></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="media-body">
|
||||
<span>Monthly purchase</span>
|
||||
<h2 class="mb-0">2154</h2>
|
||||
<p>0.13% <span><i class="fa fa-angle-up"></i></span></p>
|
||||
<h5 class="f-w-600">Avg Gross purchase</h5>
|
||||
<p>Lorem Ipsum is simply dummy text of the printing and typesetting</p>
|
||||
</div>
|
||||
<div class="bg-secondary b-r-8">
|
||||
<div class="small-box">
|
||||
<i data-feather="credit-card"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6 xl-50">
|
||||
<div class="card order-graph sales-carousel">
|
||||
<div class="card-header">
|
||||
<h6>Total cash transaction</h6>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="small-chartjs">
|
||||
<canvas baseChart id="small-graph-3" height="60vw" width="140vw"
|
||||
[datasets]="smallLine3ChartData" [labels]="smallLine3ChartLabels"
|
||||
[options]="smallLine3ChartOptions" [legend]="smallLine3ChartLegend"
|
||||
[type]="smallLine3ChartType" (chartHover)="chartHovered($event)"
|
||||
(chartClick)="chartClicked($event)"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="value-graph">
|
||||
<h3>28% <span><i class="fa fa-angle-up font-warning"></i></span></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="media-body">
|
||||
<span>Cash on hand</span>
|
||||
<h2 class="mb-0">4672</h2>
|
||||
<p>0.8% <span><i class="fa fa-angle-up"></i></span></p>
|
||||
<h5 class="f-w-600">Details about cash</h5>
|
||||
<p>Lorem Ipsum is simply dummy text of the printing and typesetting</p>
|
||||
</div>
|
||||
<div class="bg-warning b-r-8">
|
||||
<div class="small-box">
|
||||
<i data-feather="shopping-cart"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6 xl-50">
|
||||
<div class="card order-graph sales-carousel">
|
||||
<div class="card-header">
|
||||
<h6>Daily Deposits</h6>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="small-chartjs">
|
||||
<canvas baseChart id="small-graph-4" height="60vw" width="140vw"
|
||||
[datasets]="smallLine4ChartData" [labels]="smallLine4ChartLabels"
|
||||
[options]="smallLine4ChartOptions" [legend]="smallLine4ChartLegend"
|
||||
[type]="smallLine4ChartType" (chartHover)="chartHovered($event)"
|
||||
(chartClick)="chartClicked($event)"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="value-graph">
|
||||
<h3>75% <span><i class="fa fa-angle-up font-danger"></i></span></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start">
|
||||
<div class="media-body">
|
||||
<span>Security Deposits</span>
|
||||
<h2 class="mb-0">0782</h2>
|
||||
<p>0.25% <span><i class="fa fa-angle-up"></i></span></p>
|
||||
<h5 class="f-w-600">Gross sales of June</h5>
|
||||
<p>Lorem Ipsum is simply dummy text of the printing and typesetting</p>
|
||||
</div>
|
||||
<div class="bg-danger b-r-8">
|
||||
<div class="small-box">
|
||||
<i data-feather="calendar"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<!-- <div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Buy / Sell</h5>
|
||||
</div>
|
||||
<div class="card-body sell-graph">
|
||||
<canvas baseChart id="myGraph" [datasets]="lineChartData" [labels]="lineChartLabels"
|
||||
[options]="lineChartOptions" [legend]="lineChartLegend" [type]="lineChartType"
|
||||
(chartHover)="chartHovered($event)" (chartClick)="chartClicked($event)"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<!-- <div class="col-xl-12 xl-100">-->
|
||||
<!-- <div class="card height-equal">-->
|
||||
<!-- <div class="card-header">-->
|
||||
<!-- <h5>Products Cart</h5>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="card-body">-->
|
||||
<!-- <div class="user-status table-responsive products-table">-->
|
||||
<!-- <table class="table table-bordernone mb-0">-->
|
||||
<!-- <thead>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <th scope="col">Details</th>-->
|
||||
<!-- <th scope="col">Quantity</th>-->
|
||||
<!-- <th scope="col">Status</th>-->
|
||||
<!-- <th scope="col">Price</th>-->
|
||||
<!-- </tr>-->
|
||||
<!-- </thead>-->
|
||||
<!-- <tbody>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <td>Simply dummy text of the printing</td>-->
|
||||
<!-- <td class="digits">1</td>-->
|
||||
<!-- <td class="font-primary">Pending</td>-->
|
||||
<!-- <td class="digits">$6523</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <td>Long established</td>-->
|
||||
<!-- <td class="digits">5</td>-->
|
||||
<!-- <td class="font-secondary">Cancle</td>-->
|
||||
<!-- <td class="digits">$6523</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <td>sometimes by accident</td>-->
|
||||
<!-- <td class="digits">10</td>-->
|
||||
<!-- <td class="font-secondary">Cancle</td>-->
|
||||
<!-- <td class="digits">$6523</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <td>Classical Latin literature</td>-->
|
||||
<!-- <td class="digits">9</td>-->
|
||||
<!-- <td class="font-primary">Return</td>-->
|
||||
<!-- <td class="digits">$6523</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <td>keep the site on the Internet</td>-->
|
||||
<!-- <td class="digits">8</td>-->
|
||||
<!-- <td class="font-primary">Pending</td>-->
|
||||
<!-- <td class="digits">$6523</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <td>Molestiae consequatur</td>-->
|
||||
<!-- <td class="digits">3</td>-->
|
||||
<!-- <td class="font-secondary">Cancle</td>-->
|
||||
<!-- <td class="digits">$6523</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <td>Pain can procure</td>-->
|
||||
<!-- <td class="digits">8</td>-->
|
||||
<!-- <td class="font-primary">Return</td>-->
|
||||
<!-- <td class="digits">$6523</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- </tbody>-->
|
||||
<!-- </table>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="col-xl-6 xl-100">-->
|
||||
<!-- <div class="card height-equal">-->
|
||||
<!-- <div class="card-header">-->
|
||||
<!-- <h5>Empolyee Status</h5>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="card-body">-->
|
||||
<!-- <div class="user-status table-responsive products-table">-->
|
||||
<!-- <table class="table table-bordernone mb-0">-->
|
||||
<!-- <thead>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <th scope="col">Name</th>-->
|
||||
<!-- <th scope="col">Designation</th>-->
|
||||
<!-- <th scope="col">Skill Level</th>-->
|
||||
<!-- <th scope="col">Experience</th>-->
|
||||
<!-- </tr>-->
|
||||
<!-- </thead>-->
|
||||
<!-- <tbody>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <td class="bd-t-none u-s-tb">-->
|
||||
<!-- <div class="align-middle image-sm-size"><img-->
|
||||
<!-- class="img-radius align-top m-r-15 rounded-circle"-->
|
||||
<!-- src="assets/images/dashboard/user2.jpg" alt="" data-original-title=""-->
|
||||
<!-- title="">-->
|
||||
<!-- <div class="d-inline-block">-->
|
||||
<!-- <h6>John Deo <span class="text-muted digits">(14+ Online)</span></h6>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td>Designer</td>-->
|
||||
<!-- <td>-->
|
||||
<!-- <div class="progress-showcase">-->
|
||||
<!-- <div class="progress" style="height: 8px;">-->
|
||||
<!-- <div class="progress-bar bg-primary" role="progressbar"-->
|
||||
<!-- style="width: 30%" aria-valuenow="50" aria-valuemin="0"-->
|
||||
<!-- aria-valuemax="100"></div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="digits">2 Year</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <td class="bd-t-none u-s-tb">-->
|
||||
<!-- <div class="align-middle image-sm-size"><img-->
|
||||
<!-- class="img-radius align-top m-r-15 rounded-circle"-->
|
||||
<!-- src="assets/images/dashboard/user1.jpg" alt="" data-original-title=""-->
|
||||
<!-- title="">-->
|
||||
<!-- <div class="d-inline-block">-->
|
||||
<!-- <h6>Holio Mako <span class="text-muted digits">(250+ Online)</span></h6>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td>Developer</td>-->
|
||||
<!-- <td>-->
|
||||
<!-- <div class="progress-showcase">-->
|
||||
<!-- <div class="progress" style="height: 8px;">-->
|
||||
<!-- <div class="progress-bar bg-secondary" role="progressbar"-->
|
||||
<!-- style="width: 70%" aria-valuenow="50" aria-valuemin="0"-->
|
||||
<!-- aria-valuemax="100"></div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="digits">3 Year</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <td class="bd-t-none u-s-tb">-->
|
||||
<!-- <div class="align-middle image-sm-size"><img-->
|
||||
<!-- class="img-radius align-top m-r-15 rounded-circle"-->
|
||||
<!-- src="assets/images/dashboard/man.png" alt="" data-original-title=""-->
|
||||
<!-- title="">-->
|
||||
<!-- <div class="d-inline-block">-->
|
||||
<!-- <h6>Mohsib lara<span class="text-muted digits">(99+ Online)</span></h6>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td>Tester</td>-->
|
||||
<!-- <td>-->
|
||||
<!-- <div class="progress-showcase">-->
|
||||
<!-- <div class="progress" style="height: 8px;">-->
|
||||
<!-- <div class="progress-bar bg-primary" role="progressbar"-->
|
||||
<!-- style="width: 60%" aria-valuenow="50" aria-valuemin="0"-->
|
||||
<!-- aria-valuemax="100"></div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="digits">5 Month</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <td class="bd-t-none u-s-tb">-->
|
||||
<!-- <div class="align-middle image-sm-size"><img-->
|
||||
<!-- class="img-radius align-top m-r-15 rounded-circle"-->
|
||||
<!-- src="assets/images/dashboard/user.png" alt="" data-original-title=""-->
|
||||
<!-- title="">-->
|
||||
<!-- <div class="d-inline-block">-->
|
||||
<!-- <h6>Hileri Soli <span class="text-muted digits">(150+ Online)</span>-->
|
||||
<!-- </h6>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td>Designer</td>-->
|
||||
<!-- <td>-->
|
||||
<!-- <div class="progress-showcase">-->
|
||||
<!-- <div class="progress" style="height: 8px;">-->
|
||||
<!-- <div class="progress-bar bg-secondary" role="progressbar"-->
|
||||
<!-- style="width: 30%" aria-valuenow="50" aria-valuemin="0"-->
|
||||
<!-- aria-valuemax="100"></div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="digits">3 Month</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <td class="bd-t-none u-s-tb">-->
|
||||
<!-- <div class="align-middle image-sm-size"><img-->
|
||||
<!-- class="img-radius align-top m-r-15 rounded-circle"-->
|
||||
<!-- src="assets/images/dashboard/designer.jpg" alt=""-->
|
||||
<!-- data-original-title="" title="">-->
|
||||
<!-- <div class="d-inline-block">-->
|
||||
<!-- <h6>Pusiz bia <span class="text-muted digits">(14+ Online)</span></h6>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td>Designer</td>-->
|
||||
<!-- <td>-->
|
||||
<!-- <div class="progress-showcase">-->
|
||||
<!-- <div class="progress" style="height: 8px;">-->
|
||||
<!-- <div class="progress-bar bg-primary" role="progressbar"-->
|
||||
<!-- style="width: 90%" aria-valuenow="50" aria-valuemin="0"-->
|
||||
<!-- aria-valuemax="100"></div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="digits">5 Year</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- </tbody>-->
|
||||
<!-- </table>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Tổng quan đơn hàng</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-xl-6 col-sm-6 xl-50">
|
||||
<div class="order-graph overflow-hidden">
|
||||
<h6>Vị trí đặt đơn</h6>
|
||||
<div class="chart-block chart-vertical-center">
|
||||
<ngx-charts-pie-chart class="pie-colours-1" [view]="view"
|
||||
[scheme]="doughnutChartColorScheme" [results]="pieData" [explodeSlices]="false"
|
||||
[labels]="doughnutChartShowLabels" [arcWidth]=0.50 [doughnut]="true"
|
||||
[animations]="false" [tooltipDisabled]="doughnutChartTooltip"
|
||||
[gradient]="doughnutChartGradient">
|
||||
</ngx-charts-pie-chart>
|
||||
</div>
|
||||
<div class="order-graph-bottom">
|
||||
<div class="media" *ngIf="thanhKheValue > 0">
|
||||
<div class="order-color-primary"></div>
|
||||
<div class="media-body">
|
||||
<h6 class="mb-0">Quận Thanh Khê <span class="pull-right">{{ thanhKheValue
|
||||
}} đơn hàng</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media" *ngIf="sonTraValue > 0">
|
||||
<div class="order-color-secondary"></div>
|
||||
<div class="media-body">
|
||||
<h6 class="mb-0">Quận Sơn Trà <span class="pull-right">{{ sonTraValue
|
||||
}} đơn hàng</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media" *ngIf="camLeValue > 0">
|
||||
<div class="order-color-danger"></div>
|
||||
<div class="media-body">
|
||||
<h6 class="mb-0">Quận Cẩm Lệ<span class="pull-right">{{ camLeValue }} đơn
|
||||
hàng</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media" *ngIf="lienChieuValue > 0">
|
||||
<div class="order-color-warning"></div>
|
||||
<div class="media-body">
|
||||
<h6 class="mb-0">Quận Liên Chiểu<span class="pull-right">{{ lienChieuValue
|
||||
}} đơn hàng</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media" *ngIf="haiChauValue > 0">
|
||||
<div class="order-color-success" style="background-color: #32CD32;"></div>
|
||||
<div class="media-body">
|
||||
<h6 class="mb-0">Quận Hải Châu<span class="pull-right">{{ haiChauValue }}
|
||||
đơn hàng</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media" *ngIf="hoaVangValue > 0">
|
||||
<div class="order-color-success" style="background-color: mediumpurple;"></div>
|
||||
<div class="media-body">
|
||||
<h6 class="mb-0">Huyện Hoà Vang<span class="pull-right">{{ hoaVangValue }}
|
||||
đơn hàng</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media" *ngIf="nguHanhSonValue > 0">
|
||||
<div class="order-color-success" style="background-color: deepskyblue;"></div>
|
||||
<div class="media-body">
|
||||
<h6 class="mb-0">Quận Ngũ Hành Sơn<span class="pull-right">{{
|
||||
nguHanhSonValue }} đơn hàng</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media" *ngIf="hoangSaValue > 0">
|
||||
<div class="order-color-success" style="background-color: olive;"></div>
|
||||
<div class="media-body">
|
||||
<h6 class="mb-0">Huyện Hoàng Sa<span class="pull-right">{{ hoangSaValue }}
|
||||
đơn hàng</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-6 col-sm-6 xl-50">
|
||||
<div class="order-graph sm-order-space overflow-hidden">
|
||||
<h6>Top sản phẩm bán chạy</h6>
|
||||
<div class="peity-chart-dashboard text-center chart-vertical-center">
|
||||
<ngx-charts-pie-chart *ngIf="!isShop || (isShop && isData)" class="pie-colours-1"
|
||||
[view]="view" [scheme]="doughnutChartColorScheme" [results]="doughnutData"
|
||||
[explodeSlices]="false" [labels]="doughnutChartShowLabels" [arcWidth]=0.99
|
||||
[doughnut]="true" [animations]="false" [tooltipDisabled]="doughnutChartTooltip"
|
||||
[gradient]="doughnutChartGradient">
|
||||
</ngx-charts-pie-chart>
|
||||
</div>
|
||||
<div class="order-graph-bottom sales-location">
|
||||
<div class="media" *ngIf="products.length > 0">
|
||||
<div class="order-shape-primary"></div>
|
||||
<div class="media-body">
|
||||
<h6 class="mb-0 me-0">{{ products[0]?.name }} <span class="pull-right">{{
|
||||
products[0]?.sold }} lượt</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media" *ngIf="products.length > 1">
|
||||
<div class="order-shape-secondary"></div>
|
||||
<div class="media-body">
|
||||
<h6 class="mb-0 me-0">{{ products[1]?.name }} <span class="pull-right">{{
|
||||
products[1]?.sold }} lượt</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media" *ngIf="products.length > 2">
|
||||
<div class="order-shape-danger"></div>
|
||||
<div class="media-body">
|
||||
<h6 class="mb-0 me-0">{{ products[2]?.name }} <span class="pull-right">{{
|
||||
products[2]?.sold }} lượt</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media" *ngIf="products.length > 3">
|
||||
<div class="order-shape-warning"></div>
|
||||
<div class="media-body">
|
||||
<h6 class="mb-0 me-0">{{ products[3]?.name }} <span class="pull-right">{{
|
||||
products[3]?.sold }} lượt</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media" *ngIf="products.length > 4">
|
||||
<div class="order-shape-success"></div>
|
||||
<div class="media-body">
|
||||
<h6 class="mb-0 me-0">{{ products[4]?.name }} <span class="pull-right">{{
|
||||
products[4]?.sold }} lượt</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="col-xl-6 xl-100">-->
|
||||
<!-- <div class="order-graph xl-space">-->
|
||||
<!-- <h6>Revenue for last month</h6>-->
|
||||
<!-- <div class="ct-4 flot-chart-container">-->
|
||||
<!-- <x-chartist [type]="chart5.type" [data]="ch art5.data" [options]="chart5.options">-->
|
||||
<!-- </x-chartist>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,3 @@
|
||||
th, td {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
import { AfterViewInit, Component, OnInit } from '@angular/core';
|
||||
import * as chartData from '../../shared/data/chart';
|
||||
import { doughnutData, pieData } from '../../shared/data/chart';
|
||||
import { ShopService } from 'src/app/shared/service/shop.service';
|
||||
import { ProductService } from 'src/app/shared/service/product.service';
|
||||
import { Product } from 'src/app/shared/tables/product';
|
||||
import { Order } from 'src/app/shared/tables/order-list';
|
||||
import { OrderService } from 'src/app/shared/service/order.service';
|
||||
import { resolve } from 'path';
|
||||
import { forkJoin } from 'rxjs';
|
||||
import { UserService } from 'src/app/shared/service/user.service';
|
||||
import { FirebaseService } from 'src/app/shared/service/firebase.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrls: ['./dashboard.component.scss']
|
||||
})
|
||||
export class DashboardComponent implements OnInit, AfterViewInit {
|
||||
//Log-in
|
||||
isShop: boolean = false;
|
||||
loggedId: number = Number(localStorage.getItem('user-id'))
|
||||
loggedRole = localStorage.getItem('user-role');
|
||||
shopId: number;
|
||||
|
||||
//Shop Properties
|
||||
totalRevenue: number = 0;
|
||||
totalProducts: number = 0;
|
||||
totalReviewCount: number = 0;
|
||||
totalAverageRating: number = 0;
|
||||
|
||||
//Orders
|
||||
orders: Order[] = [];
|
||||
|
||||
//Pagination Properties
|
||||
thePageNumber = 1;
|
||||
thePageSize = 5;
|
||||
sortBy = "orderTime";
|
||||
sortDir = "desc";
|
||||
theTotalElements = 0;
|
||||
|
||||
//District value
|
||||
thanhKheValue: number;
|
||||
sonTraValue: number;
|
||||
camLeValue: number;
|
||||
lienChieuValue: number;
|
||||
haiChauValue: number;
|
||||
hoaVangValue: number;
|
||||
nguHanhSonValue: number;
|
||||
hoangSaValue: number;
|
||||
|
||||
//Products
|
||||
products: Product[] = [];
|
||||
isData: boolean = false;
|
||||
|
||||
//Admin
|
||||
totalUser: number = 0;
|
||||
day: number = 30;
|
||||
|
||||
public doughnutData: Array<Object> = [];
|
||||
public pieData;
|
||||
|
||||
constructor(
|
||||
private userService: UserService,
|
||||
private shopService: ShopService,
|
||||
private productService: ProductService,
|
||||
private orderService: OrderService,
|
||||
private firebaseService: FirebaseService
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
if (this.loggedRole != 'ROLE_ADMIN') {
|
||||
this.isShop = true;
|
||||
// this.shopId = Number(localStorage.getItem('shop-id'))
|
||||
|
||||
const shopIdPromise = new Promise<number>((resolve) => {
|
||||
const shopId = Number(localStorage.getItem('shop-id'));
|
||||
resolve(shopId);
|
||||
})
|
||||
|
||||
shopIdPromise.then((shopId) => {
|
||||
this.shopId = shopId;
|
||||
|
||||
this.productService.getProductsByShopIdNoPageable(this.shopId).subscribe((products) => {
|
||||
let i = 0;
|
||||
products.forEach(product => {
|
||||
this.totalReviewCount = this.totalReviewCount + product.reviewCount;
|
||||
this.totalProducts = this.totalProducts + product.sold;
|
||||
if (product.averageRating != 0) {
|
||||
this.totalAverageRating = this.totalAverageRating + product.averageRating;
|
||||
i++;
|
||||
}
|
||||
});
|
||||
if (i != 0) {
|
||||
this.totalAverageRating = this.totalAverageRating / i;
|
||||
}
|
||||
})
|
||||
|
||||
this.productService.getProductsByShopIdAndSort(this.shopId, 0, 5, 'sold', 'desc').subscribe((data) => {
|
||||
this.products = data.products;
|
||||
if (this.products.length > 4) {
|
||||
this.isData = true;
|
||||
}
|
||||
this.doughtnutData(data.products);
|
||||
})
|
||||
|
||||
this.orderService.getOrdersByShopId(this.shopId, this.thePageNumber - 1, this.thePageSize, this.sortBy, this.sortDir)
|
||||
.subscribe(this.processResult());
|
||||
this.shopService.getShopRevenue(this.shopId, 30).subscribe((res) => {
|
||||
this.totalRevenue = res;
|
||||
});
|
||||
this.valueOfShopDistrict();
|
||||
})
|
||||
}
|
||||
else {
|
||||
//ADMIN DO HERE
|
||||
|
||||
this.userService.countNewUser(this.day).subscribe((res) => {
|
||||
this.totalUser = res;
|
||||
})
|
||||
this.productService.getProductsNoPagination().subscribe((products) => {
|
||||
let i = 0;
|
||||
products.forEach(product => {
|
||||
this.totalReviewCount = this.totalReviewCount + product.reviewCount;
|
||||
this.totalProducts = this.totalProducts + product.sold;
|
||||
if (product.averageRating != 0) {
|
||||
this.totalAverageRating = this.totalAverageRating + product.averageRating;
|
||||
i++;
|
||||
}
|
||||
});
|
||||
this.totalAverageRating = this.totalAverageRating / i;
|
||||
})
|
||||
|
||||
this.productService.getProductsPaginationAndSort(0, 5, 'sold', 'desc').subscribe((data) => {
|
||||
this.products = data.products;
|
||||
this.doughtnutData(data.products)
|
||||
})
|
||||
|
||||
this.orderService
|
||||
.getOrdersPagination(this.thePageNumber - 1, this.thePageSize, this.sortBy, this.sortDir)
|
||||
.subscribe(this.processResult());
|
||||
|
||||
this.valueOfAdminDistrict();
|
||||
}
|
||||
}
|
||||
|
||||
processResult() {
|
||||
return (data: any) => {
|
||||
this.orders = data.orders;
|
||||
this.thePageNumber = data.page.pageNo + 1;
|
||||
this.thePageSize = data.page.pageSize;
|
||||
this.theTotalElements = data.page.totalElements;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// doughnut 2
|
||||
public view = chartData.view;
|
||||
public doughnutChartColorScheme = chartData.doughnutChartcolorScheme;
|
||||
public doughnutChartShowLabels = chartData.doughnutChartShowLabels;
|
||||
public doughnutChartGradient = chartData.doughnutChartGradient;
|
||||
public doughnutChartTooltip = chartData.doughnutChartTooltip;
|
||||
|
||||
public chart5 = chartData.chart5;
|
||||
|
||||
|
||||
// lineChart
|
||||
public lineChartData = chartData.lineChartData;
|
||||
public lineChartLabels = chartData.lineChartLabels;
|
||||
public lineChartOptions = chartData.lineChartOptions;
|
||||
public lineChartColors = chartData.lineChartColors;
|
||||
public lineChartLegend = chartData.lineChartLegend;
|
||||
public lineChartType = chartData.lineChartType;
|
||||
|
||||
// lineChart
|
||||
public smallLineChartData = chartData.smallLineChartData;
|
||||
public smallLineChartLabels = chartData.smallLineChartLabels;
|
||||
public smallLineChartOptions = chartData.smallLineChartOptions;
|
||||
public smallLineChartLegend = chartData.smallLineChartLegend;
|
||||
public smallLineChartType = chartData.smallLineChartType;
|
||||
|
||||
// lineChart
|
||||
public smallLine2ChartData = chartData.smallLine2ChartData;
|
||||
public smallLine2ChartLabels = chartData.smallLine2ChartLabels;
|
||||
public smallLine2ChartOptions = chartData.smallLine2ChartOptions;
|
||||
public smallLine2ChartLegend = chartData.smallLine2ChartLegend;
|
||||
public smallLine2ChartType = chartData.smallLine2ChartType;
|
||||
|
||||
// lineChart
|
||||
public smallLine3ChartData = chartData.smallLine3ChartData;
|
||||
public smallLine3ChartLabels = chartData.smallLine3ChartLabels;
|
||||
public smallLine3ChartOptions = chartData.smallLine3ChartOptions;
|
||||
public smallLine3ChartLegend = chartData.smallLine3ChartLegend;
|
||||
public smallLine3ChartType = chartData.smallLine3ChartType;
|
||||
|
||||
// lineChart
|
||||
public smallLine4ChartData = chartData.smallLine4ChartData;
|
||||
public smallLine4ChartLabels = chartData.smallLine4ChartLabels;
|
||||
public smallLine4ChartOptions = chartData.smallLine4ChartOptions;
|
||||
public smallLine4ChartColors = chartData.smallLine4ChartColors;
|
||||
public smallLine4ChartLegend = chartData.smallLine4ChartLegend;
|
||||
public smallLine4ChartType = chartData.smallLine4ChartType;
|
||||
|
||||
public chart3 = chartData.chart3;
|
||||
|
||||
|
||||
// events
|
||||
public chartClicked(e: any): void {
|
||||
}
|
||||
|
||||
public chartHovered(e: any): void {
|
||||
}
|
||||
|
||||
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
}
|
||||
|
||||
valueOfAdminDistrict() {
|
||||
const requests = [
|
||||
this.orderService.countOrderByDistrict('Thanh Khê'),
|
||||
this.orderService.countOrderByDistrict('Sơn Trà'),
|
||||
this.orderService.countOrderByDistrict('Cẩm Lệ'),
|
||||
this.orderService.countOrderByDistrict('Liên Chiểu'),
|
||||
this.orderService.countOrderByDistrict('Hải Châu'),
|
||||
this.orderService.countOrderByDistrict('Hoà Vang'),
|
||||
this.orderService.countOrderByDistrict('Ngũ Hành Sơn'),
|
||||
this.orderService.countOrderByDistrict('Hoàng Sa')
|
||||
];
|
||||
|
||||
forkJoin(requests).subscribe(
|
||||
(results: number[]) => {
|
||||
this.thanhKheValue = results[0];
|
||||
this.sonTraValue = results[1];
|
||||
this.camLeValue = results[2];
|
||||
this.lienChieuValue = results[3];
|
||||
this.haiChauValue = results[4];
|
||||
this.hoaVangValue = results[5];
|
||||
this.nguHanhSonValue = results[6];
|
||||
this.hoangSaValue = results[7];
|
||||
|
||||
this.pieData = [
|
||||
{
|
||||
value: this.thanhKheValue,
|
||||
name: "Quận Thanh Khê"
|
||||
|
||||
},
|
||||
{
|
||||
value: this.sonTraValue,
|
||||
name: "Quận Sơn Trà"
|
||||
},
|
||||
{
|
||||
value: this.camLeValue,
|
||||
name: "Quận Cẩm Lệ"
|
||||
},
|
||||
{
|
||||
value: this.lienChieuValue,
|
||||
name: "Quận Liên Chiểu"
|
||||
},
|
||||
{
|
||||
value: this.haiChauValue,
|
||||
name: "Quận Hải Châu"
|
||||
},
|
||||
{
|
||||
value: this.hoaVangValue,
|
||||
name: "Huyện Hoà Vang"
|
||||
},
|
||||
{
|
||||
value: this.nguHanhSonValue,
|
||||
name: "Quận Ngũ Hành Sơn"
|
||||
},
|
||||
{
|
||||
value: this.hoangSaValue,
|
||||
name: "Huyện Hoàng Sa"
|
||||
}
|
||||
]
|
||||
},
|
||||
(error) => {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
valueOfShopDistrict() {
|
||||
const requests = [
|
||||
this.orderService.countShopOrderByDistrict(this.shopId, 'Thanh Khê'),
|
||||
this.orderService.countShopOrderByDistrict(this.shopId, 'Sơn Trà'),
|
||||
this.orderService.countShopOrderByDistrict(this.shopId, 'Cẩm Lệ'),
|
||||
this.orderService.countShopOrderByDistrict(this.shopId, 'Liên Chiểu'),
|
||||
this.orderService.countShopOrderByDistrict(this.shopId, 'Hải Châu'),
|
||||
this.orderService.countShopOrderByDistrict(this.shopId, 'Hoà Vang'),
|
||||
this.orderService.countShopOrderByDistrict(this.shopId, 'Ngũ Hành Sơn'),
|
||||
this.orderService.countShopOrderByDistrict(this.shopId, 'Hoàng Sa')
|
||||
];
|
||||
|
||||
forkJoin(requests).subscribe(
|
||||
(results: number[]) => {
|
||||
this.thanhKheValue = results[0];
|
||||
this.sonTraValue = results[1];
|
||||
this.camLeValue = results[2];
|
||||
this.lienChieuValue = results[3];
|
||||
this.haiChauValue = results[4];
|
||||
this.hoaVangValue = results[5];
|
||||
this.nguHanhSonValue = results[6];
|
||||
this.hoangSaValue = results[7];
|
||||
|
||||
this.pieData = [
|
||||
{
|
||||
value: this.thanhKheValue,
|
||||
name: "Quận Thanh Khê"
|
||||
|
||||
},
|
||||
{
|
||||
value: this.sonTraValue,
|
||||
name: "Quận Sơn Trà"
|
||||
},
|
||||
{
|
||||
value: this.camLeValue,
|
||||
name: "Quận Cẩm Lệ"
|
||||
},
|
||||
{
|
||||
value: this.lienChieuValue,
|
||||
name: "Quận Liên Chiểu"
|
||||
},
|
||||
{
|
||||
value: this.haiChauValue,
|
||||
name: "Quận Hải Châu"
|
||||
},
|
||||
{
|
||||
value: this.hoaVangValue,
|
||||
name: "Huyện Hoà Vang"
|
||||
},
|
||||
{
|
||||
value: this.nguHanhSonValue,
|
||||
name: "Quận Ngũ Hành Sơn"
|
||||
},
|
||||
{
|
||||
value: this.hoangSaValue,
|
||||
name: "Huyện Hoàng Sa"
|
||||
}
|
||||
]
|
||||
},
|
||||
(error) => {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
doughtnutData(products: Product[]) {
|
||||
// products.forEach((product) => {
|
||||
// this.doughnutData.push({ value: product.sold, name: product.name });
|
||||
// })
|
||||
|
||||
this.doughnutData = [
|
||||
{
|
||||
value: products[0]?.sold,
|
||||
name: products[0]?.name
|
||||
|
||||
},
|
||||
{
|
||||
value: products[1]?.sold,
|
||||
name: products[1]?.name
|
||||
},
|
||||
{
|
||||
value: products[2]?.sold,
|
||||
name: products[2]?.name
|
||||
},
|
||||
{
|
||||
value: products[3]?.sold,
|
||||
name: products[3]?.name
|
||||
},
|
||||
{
|
||||
value: products[4]?.sold,
|
||||
name: products[4]?.name
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { DashboardRoutingModule } from './dashboard-routing.module';
|
||||
import { DashboardComponent } from './dashboard.component';
|
||||
import { CountToModule } from 'angular-count-to';
|
||||
import { NgChartsModule } from 'ng2-charts';
|
||||
import { Ng2GoogleChartsModule } from 'ng2-google-charts';
|
||||
import { NgxChartsModule } from '@swimlane/ngx-charts';
|
||||
import { ChartistModule } from 'ng-chartist';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [DashboardComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
DashboardRoutingModule,
|
||||
CountToModule,
|
||||
SharedModule,
|
||||
NgChartsModule,
|
||||
Ng2GoogleChartsModule,
|
||||
NgxChartsModule,
|
||||
ChartistModule
|
||||
]
|
||||
})
|
||||
export class DashboardModule { }
|
||||
@@ -0,0 +1,21 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { InvoiceComponent } from './invoice.component';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: InvoiceComponent,
|
||||
data: {
|
||||
title: "Invoice",
|
||||
breadcrumb: "Invoice"
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class InvoiceRoutingModule { }
|
||||
@@ -0,0 +1,57 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Invoice List</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="batchDelete" class="custom-datatable">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" sortable="no">No</th>
|
||||
<th scope="col" sortable="invoice">Invoice</th>
|
||||
<th scope="col" sortable="name" >Date</th>
|
||||
<th scope="col" sortable="name" >Shipping</th>
|
||||
<th scope="col" sortable="name" >Amount</th>
|
||||
<th scope="col" sortable="name" >Tax</th>
|
||||
<th scope="col" sortable="name" >Total</th>
|
||||
<th scope="col" sortable="name" >Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of tableItem$ | async ">
|
||||
<td scope="row">
|
||||
<span>{{item.no}}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{item.invoice}}
|
||||
</td>
|
||||
<td>{{item.date}}</td>
|
||||
<td>{{item.shipping}}</td>
|
||||
<td>{{item.amount}}</td>
|
||||
<td>{{item.tax}}</td>
|
||||
<td>{{item.total}}</td>
|
||||
<td>
|
||||
<a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>
|
||||
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="d-flex justify-content-center p-2">
|
||||
<ngb-pagination
|
||||
[collectionSize]="(total$ | async)!" [(page)]="service.page" [pageSize]="service.pageSize">
|
||||
</ngb-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,45 @@
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
|
||||
import { TableService } from 'src/app/shared/service/table.service';
|
||||
import { InvoiceDB, INVOICEDB } from '../../shared/tables/invoice';
|
||||
|
||||
@Component({
|
||||
selector: 'app-invoice',
|
||||
templateUrl: './invoice.component.html',
|
||||
styleUrls: ['./invoice.component.scss'],
|
||||
providers: [TableService, DecimalPipe]
|
||||
})
|
||||
export class InvoiceComponent implements OnInit {
|
||||
|
||||
public tableItem$: Observable<InvoiceDB[]>;
|
||||
public searchText;
|
||||
total$: Observable<number>;
|
||||
|
||||
constructor(public service: TableService) {
|
||||
this.tableItem$ = service.tableItem$;
|
||||
this.total$ = service.total$;
|
||||
this.service.setUserData(INVOICEDB)
|
||||
}
|
||||
|
||||
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
|
||||
|
||||
onSort({ column, direction }: SortEvent) {
|
||||
// resetting other headers
|
||||
this.headers.forEach((header) => {
|
||||
if (header.sortable !== column) {
|
||||
header.direction = '';
|
||||
}
|
||||
});
|
||||
|
||||
this.service.sortColumn = column;
|
||||
this.service.sortDirection = direction;
|
||||
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { InvoiceRoutingModule } from './invoice-routing.module';
|
||||
import { InvoiceComponent } from './invoice.component';
|
||||
import { SharedModule } from 'src/app/shared/shared.module';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
@NgModule({
|
||||
declarations: [InvoiceComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
InvoiceRoutingModule,
|
||||
SharedModule,
|
||||
NgbModule
|
||||
]
|
||||
})
|
||||
export class InvoiceModule { }
|
||||
@@ -0,0 +1,39 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { RatesComponent } from './rates/rates.component';
|
||||
import { TranslationsComponent } from './translations/translations.component';
|
||||
import { TaxesComponent } from './taxes/taxes.component';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'translations',
|
||||
component: TranslationsComponent,
|
||||
data: {
|
||||
title: "Translations",
|
||||
breadcrumb: "Translations"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'currency-rates',
|
||||
component: RatesComponent,
|
||||
data: {
|
||||
title: "Currency Rates",
|
||||
breadcrumb: "Currency Rates"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'taxes',
|
||||
component: TaxesComponent,
|
||||
data: {
|
||||
title: 'Taxes',
|
||||
breadcrumb: 'Taxes'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class LocalizationRoutingModule { }
|
||||
@@ -0,0 +1,21 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { LocalizationRoutingModule } from './localization-routing.module';
|
||||
import { TranslationsComponent } from './translations/translations.component';
|
||||
import { RatesComponent } from './rates/rates.component';
|
||||
import { TaxesComponent } from './taxes/taxes.component';
|
||||
import { SharedModule } from 'src/app/shared/shared.module';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [TranslationsComponent, RatesComponent, TaxesComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
LocalizationRoutingModule,
|
||||
SharedModule,
|
||||
NgbModule
|
||||
]
|
||||
})
|
||||
export class LocalizationModule { }
|
||||
@@ -0,0 +1,46 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Currency Details</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="batchDelete" class="custom-datatable">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" sortable="title" (sort)="onSort($event)">Currency Title</th>
|
||||
<th scope="col" sortable="usd" (sort)="onSort($event)">USD</th>
|
||||
<th scope="col" sortable="code" (sort)="onSort($event)">Code</th>
|
||||
<th scope="col" sortable="rate" (sort)="onSort($event)">Exchange Rate</th>
|
||||
<th scope="col" >Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of tableItem$ | async ">
|
||||
<td scope="row">
|
||||
<span>{{item.title}}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{item.usd}}
|
||||
</td>
|
||||
<td>{{item.code}}</td>
|
||||
<td>{{item.rate}}</td>
|
||||
<td>
|
||||
<a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>
|
||||
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,46 @@
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
|
||||
import { TableService } from 'src/app/shared/service/table.service';
|
||||
import { RateDB, RATEDB } from '../../../shared/tables/rate';
|
||||
|
||||
@Component({
|
||||
selector: 'app-rates',
|
||||
templateUrl: './rates.component.html',
|
||||
styleUrls: ['./rates.component.scss'],
|
||||
providers: [TableService, DecimalPipe]
|
||||
})
|
||||
export class RatesComponent implements OnInit {
|
||||
|
||||
public tableItem$: Observable<RateDB[]>;
|
||||
public searchText;
|
||||
total$: Observable<number>;
|
||||
|
||||
constructor(public service: TableService) {
|
||||
this.tableItem$ = service.tableItem$;
|
||||
this.total$ = service.total$;
|
||||
this.service.setUserData(RATEDB)
|
||||
}
|
||||
|
||||
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
|
||||
|
||||
onSort({ column, direction }: SortEvent) {
|
||||
// resetting other headers
|
||||
this.headers.forEach((header) => {
|
||||
if (header.sortable !== column) {
|
||||
header.direction = '';
|
||||
}
|
||||
});
|
||||
|
||||
this.service.sortColumn = column;
|
||||
this.service.sortDirection = direction;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Tax Details</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="batchDelete" class="custom-datatable">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" sortable="detail" (sort)="onSort($event)">Currency Title</th>
|
||||
<th scope="col" sortable="tax_schedule" (sort)="onSort($event)">USD</th>
|
||||
<th scope="col" sortable="rate" (sort)="onSort($event)" >Code</th>
|
||||
<th scope="col" sortable="total_amount" (sort)="onSort($event)">Exchange Rate</th>
|
||||
<th scope="col" (sort)="onSort($event)">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of tableItem$ | async ">
|
||||
<td scope="row">
|
||||
<span>{{item.detail}}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{item.tax_schedule}}
|
||||
</td>
|
||||
<td>{{item.rate}}</td>
|
||||
<td>{{item.total_amount}}</td>
|
||||
<td>
|
||||
<a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>
|
||||
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,43 @@
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
|
||||
import { TableService } from 'src/app/shared/service/table.service';
|
||||
import { TaxesDB, TAXESDB } from '../../../shared/tables/taxes';
|
||||
|
||||
@Component({
|
||||
selector: 'app-taxes',
|
||||
templateUrl: './taxes.component.html',
|
||||
styleUrls: ['./taxes.component.scss'],
|
||||
providers: [TableService, DecimalPipe]
|
||||
})
|
||||
export class TaxesComponent implements OnInit {
|
||||
public tableItem$: Observable<TaxesDB[]>;
|
||||
public searchText;
|
||||
total$: Observable<number>;
|
||||
|
||||
constructor(public service: TableService) {
|
||||
this.tableItem$ = service.tableItem$;
|
||||
this.total$ = service.total$;
|
||||
this.service.setUserData(TAXESDB)
|
||||
}
|
||||
|
||||
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
|
||||
|
||||
onSort({ column, direction }: SortEvent) {
|
||||
// resetting other headers
|
||||
this.headers.forEach((header) => {
|
||||
if (header.sortable !== column) {
|
||||
header.direction = '';
|
||||
}
|
||||
});
|
||||
|
||||
this.service.sortColumn = column;
|
||||
this.service.sortDirection = direction;
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Translations</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="batchDelete" class="custom-datatable">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" sortable="user_key" (sort)="onSort($event)">User Key</th>
|
||||
<th scope="col" sortable="russian" (sort)="onSort($event)">Russian</th>
|
||||
<th scope="col" sortable="arabic" (sort)="onSort($event)">Arbic</th>
|
||||
<th scope="col" sortable="english" (sort)="onSort($event)">English</th>
|
||||
<th scope="col" >Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of tableItem$ | async ">
|
||||
<td scope="row">
|
||||
<span>{{item.user_key}}</span>
|
||||
</td>
|
||||
<td>
|
||||
{{item.russian}}
|
||||
</td>
|
||||
<td>{{item.arabic}}</td>
|
||||
<td>{{item.english}}</td>
|
||||
<td>
|
||||
<a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>
|
||||
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="d-flex justify-content-center p-2">
|
||||
<ngb-pagination
|
||||
[collectionSize]="(total$ | async)!" [(page)]="service.page" [pageSize]="service.pageSize">
|
||||
</ngb-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,45 @@
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
|
||||
import { TableService } from 'src/app/shared/service/table.service';
|
||||
import { TRANSLATIONDB, TranslationsDB } from '../../../shared/tables/translations';
|
||||
|
||||
@Component({
|
||||
selector: 'app-translations',
|
||||
templateUrl: './translations.component.html',
|
||||
styleUrls: ['./translations.component.scss'],
|
||||
providers: [TableService, DecimalPipe]
|
||||
})
|
||||
export class TranslationsComponent implements OnInit {
|
||||
|
||||
public tableItem$: Observable<TranslationsDB[]>;
|
||||
public searchText;
|
||||
total$: Observable<number>;
|
||||
|
||||
constructor(public service: TableService) {
|
||||
this.tableItem$ = service.tableItem$;
|
||||
this.total$ = service.total$;
|
||||
this.service.setUserData(TRANSLATIONDB)
|
||||
}
|
||||
|
||||
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
|
||||
|
||||
onSort({ column, direction }: SortEvent) {
|
||||
// resetting other headers
|
||||
this.headers.forEach((header) => {
|
||||
if (header.sortable !== column) {
|
||||
header.direction = '';
|
||||
}
|
||||
});
|
||||
|
||||
this.service.sortColumn = column;
|
||||
this.service.sortDirection = direction;
|
||||
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { MediaComponent } from './media/media.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: MediaComponent,
|
||||
data: {
|
||||
title: "Media",
|
||||
breadcrumb: "Media"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class MediaRoutingModule { }
|
||||
@@ -0,0 +1,21 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { MediaRoutingModule } from './media-routing.module';
|
||||
import { MediaComponent } from './media/media.component';
|
||||
import { NgxDropzoneModule } from 'ngx-dropzone';
|
||||
import { SharedModule } from 'src/app/shared/shared.module';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [MediaComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MediaRoutingModule,
|
||||
NgxDropzoneModule,
|
||||
SharedModule,
|
||||
NgbModule
|
||||
]
|
||||
})
|
||||
export class MediaModule { }
|
||||
@@ -0,0 +1,67 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid bulk-cate">
|
||||
<div class="card ">
|
||||
<div class="card-header">
|
||||
<h5>Dropzone Media</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="dropzone-custom">
|
||||
<ngx-dropzone class="dropzone-border" (change)="onSelect($event)">
|
||||
<ngx-dropzone-label>
|
||||
<div class="dz-message needsclick">
|
||||
<i class="fa fa-cloud-upload"></i>
|
||||
<h4 class="mb-0 f-w-600">Drop files here or click to upload.</h4>
|
||||
</div>
|
||||
</ngx-dropzone-label>
|
||||
<ngx-dropzone-preview *ngFor="let f of files" [removable]="true" (removed)="onRemove(f)">
|
||||
<ngx-dropzone-label>{{ f.name }} ({{ f.type }})</ngx-dropzone-label>
|
||||
</ngx-dropzone-preview>
|
||||
</ngx-dropzone>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Media File List</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="batchDelete" class="category-table custom-datatable media-datatable">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" sortable="action">Actions</th>
|
||||
<th scope="col" sortable="img" (sort)="onSort($event)">Image</th>
|
||||
<th scope="col" sortable="file_name" (sort)="onSort($event)">File Name</th>
|
||||
<th scope="col" sortable="url" (sort)="onSort($event)">URL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of tableItem$ | async ">
|
||||
<td>
|
||||
<a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>
|
||||
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a>
|
||||
</td>
|
||||
<td scope="row">
|
||||
<img [src]="item.img" class="imgTable" style="width: 20px">
|
||||
</td>
|
||||
<td>
|
||||
{{item.file_name}}
|
||||
</td>
|
||||
<td>{{item.url}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
<div class="d-flex justify-content-center p-2 border-top-0">
|
||||
<ngb-pagination
|
||||
[collectionSize]="(total$ | async)!" [(page)]="service.page" [pageSize]="service.pageSize">
|
||||
</ngb-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,55 @@
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { DropzoneConfigInterface } from 'ngx-dropzone-wrapper';
|
||||
import { Observable } from 'rxjs';
|
||||
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
|
||||
import { TableService } from 'src/app/shared/service/table.service';
|
||||
import { MediaDB, MEDIADB } from 'src/app/shared/tables/media';
|
||||
|
||||
@Component({
|
||||
selector: 'app-media',
|
||||
templateUrl: './media.component.html',
|
||||
styleUrls: ['./media.component.scss'],
|
||||
providers: [TableService, DecimalPipe]
|
||||
})
|
||||
export class MediaComponent implements OnInit {
|
||||
|
||||
public tableItem$: Observable<MediaDB[]>;
|
||||
public searchText;
|
||||
total$: Observable<number>;
|
||||
|
||||
constructor(public service: TableService) {
|
||||
this.tableItem$ = service.tableItem$;
|
||||
this.total$ = service.total$;
|
||||
this.service.setUserData(MEDIADB)
|
||||
}
|
||||
|
||||
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
|
||||
|
||||
onSort({ column, direction }: SortEvent) {
|
||||
// resetting other headers
|
||||
this.headers.forEach((header) => {
|
||||
if (header.sortable !== column) {
|
||||
header.direction = '';
|
||||
}
|
||||
});
|
||||
|
||||
this.service.sortColumn = column;
|
||||
this.service.sortDirection = direction;
|
||||
|
||||
}
|
||||
|
||||
files: File[] = [];
|
||||
|
||||
onSelect(event) {
|
||||
this.files.push(...event.addedFiles);
|
||||
}
|
||||
|
||||
onRemove(event) {
|
||||
this.files.splice(this.files.indexOf(event), 1);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Add Menu</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="needs-validation">
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4"><label for="validationCustom0"><span>*</span> Menu Name</label></div>
|
||||
<div class="col-md-8"><input class="form-control" id="validationCustom0" type="text" required=""></div>
|
||||
</div>
|
||||
<div clalass="form-group row">
|
||||
<label class="col-xl-3 col-md-4">Status</label>
|
||||
<div class="col-xl-9 col-md-8">
|
||||
<div class="checkbox checkbox-primary">
|
||||
<input id="checkbox-primary-2" type="checkbox" data-original-title="" title="">
|
||||
<label for="checkbox-primary-2">Enable the Menu</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-menu',
|
||||
templateUrl: './create-menu.component.html',
|
||||
styleUrls: ['./create-menu.component.scss']
|
||||
})
|
||||
export class CreateMenuComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Menu Lists</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="custom-datatable">
|
||||
<button class="btn btn-primary me-1 mb-3" style="float: right;" >Delete</button>
|
||||
<div class="clearfix"></div>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th scope="col" sortable="name" (sort)="onSort($event)">Name</th>
|
||||
<th scope="col" sortable="status" (sort)="onSort($event)">Status</th>
|
||||
<th scope="col" sortable="created_on" (sort)="onSort($event)">Created On</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of tableItem$ | async ">
|
||||
<th> <input type="checkbox" name="title_select" onclick="onSelect(item.id)"></th>
|
||||
<td scope="row">
|
||||
{{item.name}}
|
||||
</td>
|
||||
<td>
|
||||
<i class="fa fa-circle {{item.status}} f-12"></i>
|
||||
</td>
|
||||
<td>{{item.created_on}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="d-flex justify-content-center p-2">
|
||||
<ngb-pagination
|
||||
[collectionSize]="(total$ | async)!" [(page)]="service.page" [pageSize]="service.pageSize">
|
||||
</ngb-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,51 @@
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
|
||||
import { TableService } from 'src/app/shared/service/table.service';
|
||||
import { ListPagesDB } from 'src/app/shared/tables/list-pages';
|
||||
import { MENUDB, MenuDB } from 'src/app/shared/tables/menu';
|
||||
|
||||
@Component({
|
||||
selector: 'app-list-menu',
|
||||
templateUrl: './list-menu.component.html',
|
||||
styleUrls: ['./list-menu.component.scss'],
|
||||
providers: [TableService, DecimalPipe]
|
||||
})
|
||||
export class ListMenuComponent implements OnInit {
|
||||
|
||||
public selected = [];
|
||||
|
||||
public tableItem$: Observable<ListPagesDB[]>;
|
||||
public searchText;
|
||||
total$: Observable<number>;
|
||||
|
||||
constructor(public service: TableService) {
|
||||
this.tableItem$ = service.tableItem$;
|
||||
this.total$ = service.total$;
|
||||
this.service.setUserData(MENUDB)
|
||||
}
|
||||
|
||||
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
|
||||
|
||||
onSort({ column, direction }: SortEvent) {
|
||||
// resetting other headers
|
||||
this.headers.forEach((header) => {
|
||||
if (header.sortable !== column) {
|
||||
header.direction = '';
|
||||
}
|
||||
});
|
||||
|
||||
this.service.sortColumn = column;
|
||||
this.service.sortDirection = direction;
|
||||
|
||||
}
|
||||
|
||||
onSelect({ selected }) {
|
||||
this.selected.splice(0, this.selected.length);
|
||||
this.selected.push(...selected);
|
||||
}
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { ListMenuComponent } from './list-menu/list-menu.component';
|
||||
import { CreateMenuComponent } from './create-menu/create-menu.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
children: [
|
||||
{
|
||||
path: 'list-menu',
|
||||
component: ListMenuComponent,
|
||||
data: {
|
||||
title: "Menu Lists",
|
||||
breadcrumb: "Menu Lists"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'create-menu',
|
||||
component: CreateMenuComponent,
|
||||
data: {
|
||||
title: "Create Menu",
|
||||
breadcrumb: "Create Menu"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class MenusRoutingModule { }
|
||||
@@ -0,0 +1,21 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
// import { NgxDatatableModule } from '@swimlane/ngx-datatable';
|
||||
|
||||
import { MenusRoutingModule } from './menus-routing.module';
|
||||
import { ListMenuComponent } from './list-menu/list-menu.component';
|
||||
import { CreateMenuComponent } from './create-menu/create-menu.component';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SharedModule } from 'src/app/shared/shared.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ListMenuComponent, CreateMenuComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MenusRoutingModule,
|
||||
NgbModule,
|
||||
SharedModule
|
||||
]
|
||||
})
|
||||
export class MenusModule { }
|
||||
@@ -0,0 +1,79 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Add Page</h5>
|
||||
</div>
|
||||
<div class="card-body tab2-card">
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
|
||||
<li [ngbNavItem]="1">
|
||||
<a ngbNavLink>General</a>
|
||||
<ng-template ngbNavContent>
|
||||
<form [formGroup]="generalForm" class="needs-validation" novalidate="">
|
||||
<h4>General</h4>
|
||||
<div class="form-group row align-items-center">
|
||||
<div class="col-xl-3 col-md-4"><label for="validationCustom0"><span>*</span> Name</label></div>
|
||||
<div class="col-xl-8 col-md-7"><input class="form-control" formControlName="name"
|
||||
id="validationCustom0" type="text" required="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row editor-label align-items-center">
|
||||
<div class="col-xl-3 col-md-4"><label>Description</label></div>
|
||||
<div class="col-xl-8 col-md-7"><textarea class="w-100" rows="4" formControlName="desc"></textarea></div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4"><label>Status</label></div>
|
||||
<div class="col-xl-8 col-md-7"><div class="checkbox checkbox-primary">
|
||||
<input id="checkbox-primary-2" formControlName="status" type="checkbox"
|
||||
data-original-title="" title="">
|
||||
<label for="checkbox-primary-2">Enable the Page</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li [ngbNavItem]="2">
|
||||
<a ngbNavLink>SEO</a>
|
||||
<ng-template ngbNavContent>
|
||||
<form [formGroup]="seoForm" class="needs-validation" novalidate="">
|
||||
<h4>SEO</h4>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-md-4"><label for="validationCustom2">Meta Title</label></div>
|
||||
<div class="col-xl-8 col-md-7"><input class="form-control" formControlName="title"
|
||||
id="validationCustom2" type="text" required=""></div>
|
||||
</div>
|
||||
<div class="form-group row align-items-center editor-label">
|
||||
<div class="col-xl-3 col-md-4"><label>Meta Description</label></div>
|
||||
<div class="col-xl-8 col-md-7"><textarea rows="4" class="w-100" formControlName="meta_desc"></textarea></div>
|
||||
</div>
|
||||
</form>
|
||||
</ng-template>
|
||||
</li>
|
||||
<!-- <li [ngbNavItem]="3">
|
||||
<a ngbNavLink>Usage</a>
|
||||
<ng-template ngbNavContent>
|
||||
<form [formGroup]="usageForm" class="needs-validation" novalidate="">
|
||||
<h4>Usage Limits</h4>
|
||||
<div class="form-group row">
|
||||
<label for="validationCustom6" class="col-xl-3 col-md-4">Per Limit</label>
|
||||
<input class="form-control col-md-7" formControlName="limit" id="validationCustom6"
|
||||
type="number">
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="validationCustom7" class="col-xl-3 col-md-4">Per Customer</label>
|
||||
<input class="form-control col-md-7" formControlName="customer" id="validationCustom7"
|
||||
type="number">
|
||||
</div>
|
||||
</form>
|
||||
</ng-template>
|
||||
</li> -->
|
||||
</ul>
|
||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
<div class="pull-right">
|
||||
<button type="button" class="btn btn-primary">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-page',
|
||||
templateUrl: './create-page.component.html',
|
||||
styleUrls: ['./create-page.component.scss']
|
||||
})
|
||||
export class CreatePageComponent implements OnInit {
|
||||
public generalForm: UntypedFormGroup;
|
||||
public seoForm: UntypedFormGroup;
|
||||
public active = 1;
|
||||
|
||||
constructor(private formBuilder: UntypedFormBuilder) {
|
||||
this.createGeneralForm();
|
||||
this.createSeoForm();
|
||||
}
|
||||
|
||||
createGeneralForm() {
|
||||
this.generalForm = this.formBuilder.group({
|
||||
name: [''],
|
||||
desc: [''],
|
||||
status: ['']
|
||||
})
|
||||
}
|
||||
createSeoForm() {
|
||||
this.seoForm = this.formBuilder.group({
|
||||
title: [''],
|
||||
keyword: [''],
|
||||
meta_desc: ['']
|
||||
})
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Page Details</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="custom-datatable">
|
||||
<button class="btn btn-primary me-1 mb-3" style="float: right;">Delete</button>
|
||||
<div class="clearfix"></div>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th scope="col" sortable="name" (sort)="onSort($event)">Name</th>
|
||||
<th scope="col" sortable="status" (sort)="onSort($event)">Status</th>
|
||||
<th scope="col" sortable="created_on" (sort)="onSort($event)">Created On</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of tableItem$ | async ">
|
||||
<th> <input type="checkbox" name="title_select" onclick="onSelect(item.id)"></th>
|
||||
<th scope="row">
|
||||
{{ item.name }}</th>
|
||||
<td><i class="fa fa-circle {{item.status}} f-12"></i></td>
|
||||
<td>{{item.created_on}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="d-flex justify-content-center p-2">
|
||||
<ngb-pagination
|
||||
[collectionSize]="(total$ | async)!" [(page)]="service.page" [pageSize]="service.pageSize">
|
||||
</ngb-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,49 @@
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { NgbdSortableHeader, SortEvent } from 'src/app/shared/directives/NgbdSortableHeader';
|
||||
import { TableService } from 'src/app/shared/service/table.service';
|
||||
import { LISTPAGEDB, ListPagesDB } from 'src/app/shared/tables/list-pages';
|
||||
|
||||
@Component({
|
||||
selector: 'app-list-page',
|
||||
templateUrl: './list-page.component.html',
|
||||
styleUrls: ['./list-page.component.scss'],
|
||||
providers: [TableService, DecimalPipe]
|
||||
})
|
||||
export class ListPageComponent implements OnInit {
|
||||
public selected = [];
|
||||
public tableItem$: Observable<ListPagesDB[]>;
|
||||
public searchText;
|
||||
total$: Observable<number>;
|
||||
|
||||
constructor(public service: TableService) {
|
||||
this.tableItem$ = service.tableItem$;
|
||||
this.total$ = service.total$;
|
||||
this.service.setUserData(LISTPAGEDB)
|
||||
}
|
||||
|
||||
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
|
||||
|
||||
onSort({ column, direction }: SortEvent) {
|
||||
// resetting other headers
|
||||
this.headers.forEach((header) => {
|
||||
if (header.sortable !== column) {
|
||||
header.direction = '';
|
||||
}
|
||||
});
|
||||
|
||||
this.service.sortColumn = column;
|
||||
this.service.sortDirection = direction;
|
||||
|
||||
}
|
||||
|
||||
onSelect({ selected }) {
|
||||
this.selected.splice(0, this.selected.length);
|
||||
this.selected.push(...selected);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { NgModule, Component } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { ListPageComponent } from './list-page/list-page.component';
|
||||
import { CreatePageComponent } from './create-page/create-page.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
children: [
|
||||
{
|
||||
path: 'list-page',
|
||||
component: ListPageComponent,
|
||||
data: {
|
||||
title: "List Page",
|
||||
breadcrumb: "List Page"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'create-page',
|
||||
component: CreatePageComponent,
|
||||
data: {
|
||||
title: "Create Page",
|
||||
breadcrumb: "Create Page"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class PagesRoutingModule { }
|
||||
@@ -0,0 +1,21 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { PagesRoutingModule } from './pages-routing.module';
|
||||
import { ListPageComponent } from './list-page/list-page.component';
|
||||
import { CreatePageComponent } from './create-page/create-page.component';
|
||||
import { SharedModule } from 'src/app/shared/shared.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ListPageComponent, CreatePageComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
PagesRoutingModule,
|
||||
NgbModule,
|
||||
ReactiveFormsModule,
|
||||
SharedModule
|
||||
]
|
||||
})
|
||||
export class PagesModule { }
|
||||
@@ -0,0 +1,108 @@
|
||||
<div class="container-fluid">
|
||||
<div class="row product-adding">
|
||||
<div class="col-xl-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>General</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="digital-add needs-validation">
|
||||
<div class="form-group">
|
||||
<label for="validationCustom01" class="col-form-label pt-0"><span>*</span> Title</label>
|
||||
<input class="form-control" id="validationCustom01" type="text" required="">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="validationCustomtitle" class="col-form-label pt-0"><span>*</span> SKU</label>
|
||||
<input class="form-control" id="validationCustomtitle" type="text" required="">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-form-label"><span>*</span> Categories</label>
|
||||
<select class="custom-select form-select" required="">
|
||||
<option value="">--Select--</option>
|
||||
<option value="1">eBooks</option>
|
||||
<option value="2">Graphic Design</option>
|
||||
<option value="3">3D Impact</option>
|
||||
<option value="4">Application</option>
|
||||
<option value="5">Websites</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-form-label">Sort Summary</label>
|
||||
<textarea rows="5" cols="12"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="validationCustom02" class="col-form-label"><span>*</span> Product Price</label>
|
||||
<input class="form-control" id="validationCustom02" type="text" required="">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-form-label"><span>*</span> Status</label>
|
||||
<div class="m-checkbox-inline mb-0 custom-radio-ml d-flex radio-animated">
|
||||
<label class="d-block" for="edo-ani">
|
||||
<input class="radio_animated" id="edo-ani" type="radio" name="rdo-ani">
|
||||
Enable
|
||||
</label>
|
||||
<label class="d-block" for="edo-ani1">
|
||||
<input class="radio_animated" id="edo-ani1" type="radio" name="rdo-ani">
|
||||
Disable
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<label class="col-form-label pt-0"> Product Upload</label>
|
||||
<div class="card-body dropzone-custom p-0">
|
||||
<ngx-dropzone class="dropzone-border" (change)="onSelect($event)">
|
||||
<ngx-dropzone-label>
|
||||
<div class="dz-message needsclick">
|
||||
<i class="fa fa-cloud-upload"></i>
|
||||
<h4 class="mb-0 f-w-600">Drop files here or click to upload.</h4>
|
||||
</div>
|
||||
</ngx-dropzone-label>
|
||||
<ngx-dropzone-preview *ngFor="let f of files" [removable]="true" (removed)="onRemove(f)">
|
||||
<ngx-dropzone-label>{{ f.name }} ({{ f.type }})</ngx-dropzone-label>
|
||||
</ngx-dropzone-preview>
|
||||
</ngx-dropzone>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-6">
|
||||
<div class="card h-auto">
|
||||
<div class="card-header">
|
||||
<h5>Add Description</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="digital-add needs-validation">
|
||||
<div class="form-group mb-0">
|
||||
<div class="description-sm">
|
||||
<ckeditor [editor]="Editor" data="<p>Hello, world!</p>"></ckeditor>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Meta Data</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="digital-add needs-validation">
|
||||
<div class="form-group">
|
||||
<label for="validationCustom05" class="col-form-label pt-0"> Meta Title</label>
|
||||
<input class="form-control" id="validationCustom05" type="text" required="">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-form-label">Meta Description</label>
|
||||
<textarea rows="4" cols="12"></textarea>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<div class="product-buttons text-end mt-3">
|
||||
<button type="button" class="btn btn-primary">Add</button>
|
||||
<button type="button" class="btn btn-light">Discard</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
|
||||
|
||||
@Component({
|
||||
selector: 'app-digital-add',
|
||||
templateUrl: './digital-add.component.html',
|
||||
styleUrls: ['./digital-add.component.scss']
|
||||
})
|
||||
|
||||
|
||||
export class DigitalAddComponent implements OnInit {
|
||||
public Editor = ClassicEditor;
|
||||
constructor() { }
|
||||
|
||||
files: File[] = [];
|
||||
|
||||
onSelect(event) {
|
||||
this.files.push(...event.addedFiles);
|
||||
}
|
||||
|
||||
onRemove(event) {
|
||||
this.files.splice(this.files.indexOf(event), 1);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Digital Products</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="btn-popup pull-right">
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-original-title="test"
|
||||
data-target="#exampleModal" (click)="open(content)">Add Category</button>
|
||||
<ng-template #content let-modal>
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title f-w-600" id="exampleModalLabel">Add Digital Product</h5>
|
||||
<button type="button" class="close" aria-label="Close"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="needs-validation">
|
||||
<div class="form">
|
||||
<div class="form-group">
|
||||
<label for="validationCustom01" class="mb-1">Category Name :</label>
|
||||
<input class="form-control" id="validationCustom01" type="text">
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<label for="validationCustom02" class="mb-1">Category Image :</label>
|
||||
<input class="form-control" id="validationCustom02" type="file">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" >Save</button>
|
||||
<button class="btn btn-secondary" type="button" data-dismiss="modal"
|
||||
(click)="modal.dismiss('Cross click')">Close</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div id="batchDelete" class="category-table custom-datatable">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col" sortable="img" (sort)="onSort($event)" >Image</th>
|
||||
<th scope="col" sortable="product_name" (sort)="onSort($event)">Name</th>
|
||||
<th scope="col" sortable="price" (sort)="onSort($event)" >Price</th>
|
||||
<th scope="col" sortable="status" (sort)="onSort($event)">Status</th>
|
||||
<th scope="col" sortable="category" (sort)="onSort($event)" >Category</th>
|
||||
<th scope="col" sortable="action" (sort)="onSort($event)" >Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let catgory of tableItem$ | async">
|
||||
<th scope="row">{{ catgory.id }}</th>
|
||||
<td>
|
||||
<img [src]="catgory.img" class="imgTable" style="width: 20px">
|
||||
</td>
|
||||
<td>{{ catgory.product_name }}</td>
|
||||
<td>{{ catgory.price }}</td>
|
||||
<td><i class='fa fa-circle {{catgory.status}} f-12'></i></td>
|
||||
<td>{{ catgory.category}}</td>
|
||||
<td>
|
||||
<a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>
|
||||
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { DIGITALCATEGORY, DigitalCategoryDB } from 'src/app/shared/tables/digital-category';
|
||||
import { NgbModal, ModalDismissReasons } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SortEvent } from 'src/app/shared/directives/shorting.directive';
|
||||
import { NgbdSortableHeader } from "src/app/shared/directives/NgbdSortableHeader";
|
||||
import { TableService } from 'src/app/shared/service/table.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-digital-category',
|
||||
templateUrl: './digital-category.component.html',
|
||||
styleUrls: ['./digital-category.component.scss'],
|
||||
providers: [TableService, DecimalPipe],
|
||||
})
|
||||
export class DigitalCategoryComponent implements OnInit {
|
||||
public closeResult: string;
|
||||
tableItem$: Observable<DigitalCategoryDB[]>;
|
||||
public digital_categories = []
|
||||
|
||||
constructor(public service: TableService, private modalService: NgbModal) {
|
||||
this.tableItem$ = service.tableItem$;
|
||||
this.service.setUserData(DIGITALCATEGORY)
|
||||
}
|
||||
|
||||
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
|
||||
|
||||
onSort({ column, direction }: SortEvent) {
|
||||
// resetting other headers
|
||||
this.headers.forEach((header) => {
|
||||
if (header.sortable !== column) {
|
||||
header.direction = '';
|
||||
}
|
||||
});
|
||||
|
||||
this.service.sortColumn = column;
|
||||
this.service.sortDirection = direction;
|
||||
|
||||
}
|
||||
|
||||
open(content) {
|
||||
this.modalService.open(content, { ariaLabelledBy: 'modal-basic-title' }).result.then((result) => {
|
||||
this.closeResult = `Closed with: ${result}`;
|
||||
}, (reason) => {
|
||||
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
|
||||
});
|
||||
}
|
||||
private getDismissReason(reason: any): string {
|
||||
if (reason === ModalDismissReasons.ESC) {
|
||||
return 'by pressing ESC';
|
||||
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
|
||||
return 'by clicking on a backdrop';
|
||||
} else {
|
||||
return `with: ${reason}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Product Lists</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="batchDelete" class="custom-datatable">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col" sortable="img" (sort)="onSort($event)">Image</th>
|
||||
<th scope="col" sortable="title" (sort)="onSort($event)">Product Title</th>
|
||||
<th scope="col" sortable="entry_type" (sort)="onSort($event)">Empty Type</th>
|
||||
<th scope="col" sortable="quantity" (sort)="onSort($event)">Quantity</th>
|
||||
<th scope="col" sortable="action" (sort)="onSort($event)">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let catgory of tableItem$ | async ">
|
||||
<th scope="row">{{ catgory.id }}</th>
|
||||
<td>
|
||||
<img [src]="catgory.img" class="imgTable" style="width: 20px">
|
||||
</td>
|
||||
<td>{{ catgory.title }}</td>
|
||||
<td>{{ catgory.entry_type }}</td>
|
||||
<td>{{ catgory.quantity}}</td>
|
||||
<td>
|
||||
<a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>
|
||||
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,43 @@
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Observable } from 'rxjs';
|
||||
import { SortEvent } from 'src/app/shared/directives/shorting.directive';
|
||||
import { NgbdSortableHeader } from "src/app/shared/directives/NgbdSortableHeader";
|
||||
import { TableService } from 'src/app/shared/service/table.service';
|
||||
import { DigitalCategoryDB } from 'src/app/shared/tables/digital-category';
|
||||
import { DIGITALLIST, DigitalListDB } from 'src/app/shared/tables/digital-list';
|
||||
@Component({
|
||||
selector: 'app-digital-list',
|
||||
templateUrl: './digital-list.component.html',
|
||||
styleUrls: ['./digital-list.component.scss'],
|
||||
providers: [TableService, DecimalPipe],
|
||||
})
|
||||
export class DigitalListComponent implements OnInit {
|
||||
tableItem$: Observable<DigitalListDB[]>;
|
||||
public digital_categories = []
|
||||
|
||||
constructor(public service: TableService, private modalService: NgbModal) {
|
||||
this.tableItem$ = service.tableItem$;
|
||||
this.service.setUserData(DIGITALLIST)
|
||||
}
|
||||
|
||||
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
|
||||
|
||||
onSort({ column, direction }: SortEvent) {
|
||||
|
||||
// resetting other headers
|
||||
this.headers.forEach((header) => {
|
||||
if (header.sortable !== column) {
|
||||
header.direction = '';
|
||||
}
|
||||
});
|
||||
|
||||
this.service.sortColumn = column;
|
||||
this.service.sortDirection = direction;
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Digital Sub Category</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="btn-popup pull-right">
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-original-title="test"
|
||||
data-target="#exampleModal" (click)="open(content)">Add Sub Category</button>
|
||||
<ng-template #content let-modal>
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title f-w-600" id="exampleModalLabel">Add Digital Product</h5>
|
||||
<button type="button" class="close" aria-label="Close"
|
||||
(click)="modal.dismiss('Cross click')">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="needs-validation">
|
||||
<div class="form">
|
||||
<div class="form-group">
|
||||
<label for="validationCustom01" class="mb-1">Sub Category Name :</label>
|
||||
<input class="form-control" id="validationCustom01" type="text">
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<label for="validationCustom02" class="mb-1">Sub Category Image :</label>
|
||||
<input class="form-control" id="validationCustom02" type="file">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" >Save</button>
|
||||
<button class="btn btn-secondary" type="button" data-dismiss="modal"
|
||||
(click)="modal.dismiss('Cross click')">Close</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div id="batchDelete" class="category-table custom-datatable">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col" sortable="img" (sort)="onSort($event)">Image</th>
|
||||
<th scope="col" sortable="product_name" (sort)="onSort($event)" >Name</th>
|
||||
<th scope="col" sortable="price" (sort)="onSort($event)">Price</th>
|
||||
<th scope="col" sortable="status" (sort)="onSort($event)">Status</th>
|
||||
<th scope="col" sortable="category" (sort)="onSort($event)">Category</th>
|
||||
<th scope="col" sortable="action" (sort)="onSort($event)">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let catgory of tableItem$ | async">
|
||||
<th scope="row">{{ catgory.id }}</th>
|
||||
<td>
|
||||
<img [src]="catgory.img" class="imgTable" style="width: 20px">
|
||||
</td>
|
||||
<td>{{ catgory.product_name }}</td>
|
||||
<td>{{ catgory.price }}</td>
|
||||
<td><i class='fa fa-circle {{catgory.status}} f-12'></i></td>
|
||||
<td>{{ catgory.category}}</td>
|
||||
<td>
|
||||
<a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>
|
||||
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { DIGITALSUBCATEGORY, DigitalSubCategoryDB } from 'src/app/shared/tables/digital-sub-category';
|
||||
import { NgbModal, ModalDismissReasons } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SortEvent } from 'src/app/shared/directives/shorting.directive';
|
||||
import { NgbdSortableHeader } from "src/app/shared/directives/NgbdSortableHeader";
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { TableService } from 'src/app/shared/service/table.service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-digital-sub-category',
|
||||
templateUrl: './digital-sub-category.component.html',
|
||||
styleUrls: ['./digital-sub-category.component.scss'],
|
||||
providers: [TableService, DecimalPipe],
|
||||
})
|
||||
|
||||
export class DigitalSubCategoryComponent implements OnInit {
|
||||
public closeResult: string;
|
||||
tableItem$: Observable<DigitalSubCategoryDB[]>;
|
||||
public digital_categories = []
|
||||
|
||||
constructor(public service: TableService, private modalService: NgbModal) {
|
||||
this.tableItem$ = service.tableItem$;
|
||||
this.service.setUserData(DIGITALSUBCATEGORY)
|
||||
}
|
||||
|
||||
open(content) {
|
||||
this.modalService.open(content, { ariaLabelledBy: 'modal-basic-title' }).result.then((result) => {
|
||||
this.closeResult = `Closed with: ${result}`;
|
||||
}, (reason) => {
|
||||
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
|
||||
});
|
||||
}
|
||||
|
||||
private getDismissReason(reason: any): string {
|
||||
if (reason === ModalDismissReasons.ESC) {
|
||||
return 'by pressing ESC';
|
||||
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
|
||||
return 'by clicking on a backdrop';
|
||||
} else {
|
||||
return `with: ${reason}`;
|
||||
}
|
||||
}
|
||||
|
||||
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;
|
||||
|
||||
onSort({ column, direction }: SortEvent) {
|
||||
// resetting other headers
|
||||
this.headers.forEach((header) => {
|
||||
if (header.sortable !== column) {
|
||||
header.direction = '';
|
||||
}
|
||||
});
|
||||
|
||||
this.service.sortColumn = column;
|
||||
this.service.sortDirection = direction;
|
||||
|
||||
}
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Add Product</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row product-adding">
|
||||
<div class="col-xl-5">
|
||||
<div class="add-product">
|
||||
<div class="row">
|
||||
<div class="col-xl-9 xl-50 col-sm-6 col-9">
|
||||
<img src="assets/images/pro3/1.jpg" alt="" class="img-fluid image_zoom_1">
|
||||
</div>
|
||||
<div class="col-xl-3 xl-50 col-sm-6 col-3">
|
||||
<ul class="file-upload-product">
|
||||
<li *ngFor="let item of url; let i=index">
|
||||
<div class="box-input-file">
|
||||
<input class="upload" type="file" (change)="readUrl($event,i)">
|
||||
<img class="imgUpload" alt="" [src]="item.img">
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-7">
|
||||
<form class="needs-validation" [formGroup]="productForm" novalidate>
|
||||
<div class="add-product-form">
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-sm-4">
|
||||
<label for="validationCustom01" class="mb-0">Name</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-sm-7">
|
||||
<input class="form-control" id="validationCustom01" type="text"
|
||||
[formControl]="productForm.controls['name']" required>
|
||||
<div *ngIf="productForm.controls.name.touched && productForm.controls.name.errors?.required"
|
||||
class="text text-danger">
|
||||
Title is required.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-sm-4">
|
||||
<label for="validationCustom02" class="mb-0">Price</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-sm-7">
|
||||
<input class="form-control" id="validationCustom02" type="number"
|
||||
[formControl]="productForm.controls['price']" required="">
|
||||
<div *ngIf="productForm.controls.price.touched && productForm.controls.price.errors?.required"
|
||||
class="text text-danger">
|
||||
Price is required.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-sm-4">
|
||||
<label for="validationCustom03" class="mb-0">Category</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-sm-7">
|
||||
<input class="form-control" id="validationCustom03"
|
||||
[formControl]="productForm.controls['category']" required="">
|
||||
<div *ngIf="productForm.controls.category.touched && productForm.controls.category.errors?.required"
|
||||
class="text text-danger">
|
||||
Category is required.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="form-group row">-->
|
||||
<!-- <div class="col-xl-3 col-sm-4">-->
|
||||
<!-- <label for="exampleFormControlSelect1" class="mb-0">Select-->
|
||||
<!-- Size-->
|
||||
<!-- :</label>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="col-xl-8 col-sm-7">-->
|
||||
<!-- <select class="form-control digits" id="exampleFormControlSelect1">-->
|
||||
<!-- <option>Small</option>-->
|
||||
<!-- <option>Medium</option>-->
|
||||
<!-- <option>Large</option>-->
|
||||
<!-- <option>Extra Large</option>-->
|
||||
<!-- </select>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-sm-4">
|
||||
<label class="mb-0">Total Products</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-sm-7">
|
||||
<div class="qty-box1">
|
||||
<div class="input-group">
|
||||
<i class="fa fa-minus btnGtr1" style="cursor: pointer;"
|
||||
(click)=decrement()></i>
|
||||
<input #productQuantity class="touchspin1 text-center"
|
||||
name="counter" [value]="counter" type="text">
|
||||
<i class="fa fa-plus btnLess1" style="cursor: pointer;"
|
||||
(click)=increment()></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-sm-4"><label>Add Description</label></div>
|
||||
<div class="col-xl-8 col-sm-7">
|
||||
<div class="ps-0 description-sm">
|
||||
<ckeditor #ckEditor [editor]="Editor" [data]="description"
|
||||
(change)="onChange($event)"></ckeditor>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="form-group row mb-0">
|
||||
<div class="col-sm-8 offset-xl-3 offset-sm-4">
|
||||
<div class="product-buttons">
|
||||
<button type="button" class="btn btn-primary me-1" (click)="onAddNewProduct()">
|
||||
Add
|
||||
</button>
|
||||
<button type="button" class="btn btn-light" (click)="openModal(template)">
|
||||
Discard
|
||||
</button>
|
||||
<ng-template #template>
|
||||
<div class="modal-body text-center">
|
||||
<p>Do you want to discard?</p>
|
||||
<button type="button" class="btn btn-default">Yes
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary">No
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
|
||||
import { ChangeEvent } from "@ckeditor/ckeditor5-angular";
|
||||
import { BsModalRef, BsModalService } from "ngx-bootstrap/modal";
|
||||
import { Product } from "../../../../shared/tables/product";
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-product-category',
|
||||
templateUrl: './add-product-category.component.html',
|
||||
styleUrls: ['./add-product-category.component.scss']
|
||||
})
|
||||
export class AddProductCategoryComponent {
|
||||
public productForm: UntypedFormGroup;
|
||||
public Editor = ClassicEditor;
|
||||
public counter: number = 1;
|
||||
public url = [{
|
||||
img: "assets/images/user.png",
|
||||
},
|
||||
{
|
||||
img: "assets/images/user.png",
|
||||
},
|
||||
{
|
||||
img: "assets/images/user.png",
|
||||
},
|
||||
{
|
||||
img: "assets/images/user.png",
|
||||
},
|
||||
{
|
||||
img: "assets/images/user.png",
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@ViewChild('productQuantity') quantity;
|
||||
@ViewChild('ckEditor') ckEditor: any;
|
||||
|
||||
description: string = '';
|
||||
|
||||
modalRef: BsModalRef;
|
||||
message: string;
|
||||
|
||||
constructor(private fb: UntypedFormBuilder,
|
||||
private modalService: BsModalService) {
|
||||
this.productForm = this.fb.group({
|
||||
name: ['', [Validators.required, Validators.pattern('[a-zA-Z][a-zA-Z ]+[a-zA-Z]$')]],
|
||||
price: ['', [Validators.required, Validators.pattern('[a-zA-Z][a-zA-Z ]+[a-zA-Z]$')]],
|
||||
category: ['', [Validators.required, Validators.pattern('[a-zA-Z][a-zA-Z ]+[a-zA-Z]$')]],
|
||||
code: ['', [Validators.required, Validators.pattern('[a-zA-Z][a-zA-Z ]+[a-zA-Z]$')]],
|
||||
quantity: ['', [Validators.required, Validators.pattern('[a-zA-Z][a-zA-Z ]+[a-zA-Z]$')]],
|
||||
// size: ['', Validators.required]
|
||||
});
|
||||
}
|
||||
|
||||
onAddNewProduct() {
|
||||
// const product = new Product(
|
||||
// this.productName, this.description, true, 20,
|
||||
// this.productPrice,
|
||||
// [this.productCategory], [''], 30);
|
||||
this.productForm.reset();
|
||||
}
|
||||
|
||||
onChange({ editor }: ChangeEvent) {
|
||||
let data = editor.getData();
|
||||
// const data = EDITTORC.instances.Editor.document.getBody().getText();
|
||||
this.description = data;
|
||||
}
|
||||
|
||||
get productName() {
|
||||
return this.productForm.get('name').value;
|
||||
}
|
||||
|
||||
get productCategory() {
|
||||
return this.productForm.get('category').value;
|
||||
}
|
||||
|
||||
get productPrice() {
|
||||
return this.productForm.get('price').value;
|
||||
}
|
||||
|
||||
get productQuantity() {
|
||||
return this.productForm.get('quantity').value;
|
||||
}
|
||||
|
||||
// get productName() {
|
||||
// return this.productForm.get('name').value;
|
||||
// }
|
||||
|
||||
increment() {
|
||||
this.counter += 1;
|
||||
}
|
||||
|
||||
decrement() {
|
||||
this.counter -= 1;
|
||||
}
|
||||
|
||||
// FileUpload
|
||||
readUrl(event: any, i) {
|
||||
if (event.target.files.length === 0) {
|
||||
return;
|
||||
}
|
||||
// Image upload validation
|
||||
var mimeType = event.target.files[0].type;
|
||||
if (mimeType.match(/image\/*/) == null) {
|
||||
return;
|
||||
}
|
||||
// Image upload
|
||||
var reader = new FileReader();
|
||||
reader.readAsDataURL(event.target.files[0]);
|
||||
reader.onload = (_event) => {
|
||||
this.url[i].img = reader.result.toString();
|
||||
};
|
||||
}
|
||||
|
||||
openModal(template: TemplateRef<any>) {
|
||||
this.modalRef = this.modalService.show(template, { class: 'modal-sm' });
|
||||
}
|
||||
|
||||
confirm(): void {
|
||||
this.message = 'Confirmed!';
|
||||
this.modalRef.hide();
|
||||
this.productForm.reset();
|
||||
|
||||
// this.ckEditor.instance.setData('');
|
||||
// this.ckEditor.setData('');
|
||||
}
|
||||
|
||||
decline(): void {
|
||||
this.message = 'Declined!';
|
||||
this.modalRef.hide();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
<!-- Container-fluid starts-->
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Thêm sản phẩm</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row product-adding">
|
||||
<div class="col-xl-5">
|
||||
<div class="add-product">
|
||||
<div class="row">
|
||||
<div class="col-xl-9 xl-50 col-sm-6 col-9">
|
||||
<img src="{{adminImg}}" alt="" class="img-fluid image_zoom_1">
|
||||
</div>
|
||||
<div class="col-xl-3 xl-50 col-sm-6 col-3">
|
||||
<!-- <ul class="file-upload-product">
|
||||
<li *ngFor="let item of url; let i=index">
|
||||
<div class="box-input-file">
|
||||
<input class="upload" type="file" (change)="readUrl($event,i)">
|
||||
<img class="imgUpload" alt="" [src]="item.img">
|
||||
</div>
|
||||
</li>
|
||||
</ul> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-7">
|
||||
|
||||
<!-- Form Start -->
|
||||
<form class="needs-validation" [formGroup]="productForm" novalidate>
|
||||
|
||||
<!-- Product Name -->
|
||||
<div class="add-product-form">
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-sm-4">
|
||||
<label for="validationCustom01" class="mb-0">Tên sản phẩm</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-sm-7">
|
||||
<input class="form-control" id="validationCustom01" type="text"
|
||||
[formControl]="productForm.controls['name']" required>
|
||||
<!-- <div class="alert alert-danger mt-1" *ngIf="productForm.controls.name.touched && productForm.controls.name.errors?.required"
|
||||
class="text text-danger">
|
||||
Tên sản phẩm không được bỏ trống.
|
||||
</div> -->
|
||||
<div *ngIf="productName?.invalid && (productName?.dirty || productName?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="productName?.errors?.['required']">Tên sản phẩm không được
|
||||
bỏ trống.</div>
|
||||
<div *ngIf="productName?.errors?.['minlength']">Tên sản phẩm phải có ít
|
||||
nhất 2 kí tự.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Product Price -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-sm-4">
|
||||
<label for="validationCustom02" class="mb-0">Giá sản phẩm</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-sm-7">
|
||||
<input class="form-control" id="validationCustom02" type="number"
|
||||
[formControl]="productForm.controls['price']" required="">
|
||||
<!-- <div *ngIf="productForm.controls.price.touched && productForm.controls.price.errors?.required"
|
||||
class="text text-danger">
|
||||
Giá sản phẩm không được bỏ trống.
|
||||
</div> -->
|
||||
<div *ngIf="productPrice?.invalid && (productPrice?.dirty || productPrice?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="productPrice?.errors?.['required']">Giá sản phẩm không được
|
||||
bỏ trống.</div>
|
||||
<div *ngIf="productPrice?.errors?.['pattern']">Giá sản phẩm phải là ký
|
||||
tự số.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Product Categories -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-sm-4">
|
||||
<label for="validationCustom03" class="mb-0">Loại sản phẩm</label>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-8 col-sm-7" formArrayName="categories">
|
||||
<div *ngFor="let item of categories.controls; let i = index">
|
||||
<div formGroupName="{{i}}">
|
||||
<div class="d-flex mb-2 mt-2">
|
||||
<input class="form-control" id="validationCustom03"
|
||||
formControlName="categoryName">
|
||||
<button type="button" class="btn btn-light ml-2"
|
||||
(click)="deleteCategory(i)">
|
||||
Xoá
|
||||
</button>
|
||||
</div>
|
||||
<div *ngIf="categories.controls[i].get('categoryName').invalid &&
|
||||
(categories.controls[i].get('categoryName').dirty || categories.controls[i].get('categoryName').touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div
|
||||
*ngIf="categories.controls[i].get('categoryName')?.errors?.['required']">
|
||||
Không được để trống loại sản phẩm
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-primary me-1"
|
||||
(click)="addNewCategory()">
|
||||
Thêm loại
|
||||
</button>
|
||||
</div>
|
||||
<!-- <div class="col-xl-8 col-sm-7">
|
||||
<input class="form-control" id="validationCustom03"
|
||||
[formControl]="productForm.controls['category']" required="">
|
||||
<div *ngIf="productForm.controls.category.touched && productForm.controls.category.errors?.required"
|
||||
class="text text-danger">
|
||||
Loại sản phẩm không được bỏ trống.
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Product Images -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-sm-4">
|
||||
<label for="validationCustom03" class="mb-0">Hình ảnh sản phẩm</label>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-8 col-sm-7" formArrayName="images">
|
||||
<div *ngFor="let item of images.controls; let i = index">
|
||||
<div formGroupName="{{i}}">
|
||||
<div class="d-flex mb-2 mt-2">
|
||||
<input class="form-control" id="image"
|
||||
formControlName="image_File" type="file" autocomplete="off"
|
||||
(change)="onFileSelected($event, i)" />
|
||||
<button type="button" class="btn btn-light ml-2"
|
||||
(click)="deleteImage(i)">
|
||||
Xoá
|
||||
</button>
|
||||
</div>
|
||||
<!-- <div
|
||||
*ngIf="images.controls[i].get('images').invalid &&
|
||||
(images.controls[i].get('images').dirty || images.controls[i].get('images').touched)" class="alert alert-danger mt-1">
|
||||
<div *ngIf="images.controls[i].get('images')?.errors?.['required']">
|
||||
Ảnh không được bỏ trống
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-primary me-1" (click)="addNewImage()">
|
||||
Thêm ảnh
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="form-group row">-->
|
||||
<!-- <div class="col-xl-3 col-sm-4">-->
|
||||
<!-- <label for="exampleFormControlSelect1" class="mb-0">Select-->
|
||||
<!-- Size-->
|
||||
<!-- :</label>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="col-xl-8 col-sm-7">-->
|
||||
<!-- <select class="form-control digits" id="exampleFormControlSelect1">-->
|
||||
<!-- <option>Small</option>-->
|
||||
<!-- <option>Medium</option>-->
|
||||
<!-- <option>Large</option>-->
|
||||
<!-- <option>Extra Large</option>-->
|
||||
<!-- </select>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<!-- Product Description -->
|
||||
<div class="form-group row">
|
||||
<div class="col-xl-3 col-sm-4">
|
||||
<label for="validationCustom02" class="mb-0">Mô tả sản phẩm</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-sm-7">
|
||||
<textarea class="form-control" id="validationCustom02" type="number"
|
||||
[formControl]="productForm.controls['descriptions']"
|
||||
required=""></textarea>
|
||||
<!-- <div *ngIf="productForm.controls.descriptions.touched && productForm.controls.descriptions.errors?.required"
|
||||
class="text text-danger">
|
||||
Mô tả không được bỏ trống.
|
||||
</div> -->
|
||||
<div *ngIf="descriptions?.invalid && (descriptions?.dirty || descriptions?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="descriptions?.errors?.['required']">Mô tả sản phẩm không
|
||||
được bỏ trống.</div>
|
||||
<div *ngIf="descriptions?.errors?.['minlength']">Mô tả sản phẩm phải có
|
||||
ít nhất 8 kí tự.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Product Shop Id -->
|
||||
<div *ngIf="!isShop" class="form-group row">
|
||||
<div class="col-xl-3 col-sm-4">
|
||||
<label for="validationCustom03" class="mb-0">Id shop</label>
|
||||
</div>
|
||||
<div class="col-xl-8 col-sm-7">
|
||||
<input class="form-control" id="validationCustom03"
|
||||
[formControl]="productForm.controls['shopId']" type="number">
|
||||
<!-- <div *ngIf="productForm.controls.shopId.touched && productForm.controls.shopId.errors?.required"
|
||||
class="text text-danger">
|
||||
Id shop không được bỏ trống.
|
||||
</div> -->
|
||||
<div *ngIf="productShopId?.invalid && (productShopId?.dirty || productShopId?.touched)"
|
||||
class="alert alert-danger mt-1">
|
||||
<div *ngIf="productShopId?.errors?.['required']">Id shop không được bỏ
|
||||
trống.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="form-group row mb-0">
|
||||
<div class="col-sm-8 offset-xl-3 offset-sm-4">
|
||||
<div class="product-buttons">
|
||||
<button type="button" class="btn btn-primary me-1"
|
||||
(click)="openModal(add_product)">
|
||||
Thêm sản phẩm
|
||||
</button>
|
||||
<!-- <button type="button" class="btn btn-light" (click)="openModal(add_product)">
|
||||
Discard
|
||||
</button> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Container-fluid Ends-->
|
||||
<ng-template #add_product>
|
||||
<div class="modal-body text-center">
|
||||
<h5 class="text-dark font-weight-bold mb-4">Bạn chắc chắc muốn thêm sản phẩm này chứ ?</h5>
|
||||
<div class="modal-btn-option">
|
||||
<button type="button" class="btn btn-default width-btn" (click)="confirm()">Có
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary width-btn" (click)="decline()">Không
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template #completedModal>
|
||||
<div class="modal-body text-center">
|
||||
<h5 class="text-dark font-weight-bold mb-4">Thêm sản phẩm thành công</h5>
|
||||
<button type="button" class="btn btn-primary" (click)="completed()">Tiếp
|
||||
tục
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #errorModal>
|
||||
<div class="modal-body text-center">
|
||||
<h5 class="text-dark font-weight-bold mb-4">Vui lòng thêm ít nhất một {{ item }} vào trong sản phẩm của bạn.
|
||||
</h5>
|
||||
<button type="button" class="btn btn-primary" (click)="closeModal()">Tiếp
|
||||
tục
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #errorShopModal>
|
||||
<div class="modal-body text-center">
|
||||
<h5 class="text-dark font-weight-bold mb-4">Shop bạn thêm vào không tồn tại. Vui lòng chọn một shop khác.</h5>
|
||||
<button type="button" class="btn btn-primary" (click)="closeModal()">Tiếp
|
||||
tục
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,11 @@
|
||||
.modal-btn-option {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.width-btn {
|
||||
width: 110px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { FormArray, FormControl, FormGroup, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
|
||||
import { ChangeEvent } from "@ckeditor/ckeditor5-angular";
|
||||
import { BsModalRef, BsModalService } from "ngx-bootstrap/modal";
|
||||
import { Product } from "../../../../shared/tables/product";
|
||||
import { Category } from "../../../../shared/tables/category";
|
||||
import { ProductService } from 'src/app/shared/service/product.service';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { concatMap, finalize, Observable, switchMap } from 'rxjs';
|
||||
import { AngularFireStorage } from '@angular/fire/compat/storage';
|
||||
import { resolve } from 'path';
|
||||
import { rejects } from 'assert';
|
||||
import { ProductImageService } from 'src/app/shared/service/product-image.service';
|
||||
import { ProductImage } from 'src/app/shared/tables/product-image';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { error } from 'console';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-product',
|
||||
templateUrl: './add-product.component.html',
|
||||
styleUrls: ['./add-product.component.scss'],
|
||||
providers: []
|
||||
})
|
||||
export class AddProductComponent implements OnInit {
|
||||
public adminImage = environment.adminImg;
|
||||
public productForm: UntypedFormGroup;
|
||||
public Editor = ClassicEditor;
|
||||
|
||||
//Log-in
|
||||
isShop: boolean = false;
|
||||
loggedId: number = Number(localStorage.getItem('user-id'))
|
||||
loggedRole = localStorage.getItem('user-role');
|
||||
shopId: number;
|
||||
|
||||
adminImg = environment.adminImg;
|
||||
|
||||
item: string = '';
|
||||
description: string = '';
|
||||
message: string;
|
||||
cats!: FormArray;
|
||||
imgs!: FormArray;
|
||||
createdId: number;
|
||||
imageUrls: string[] = [];
|
||||
|
||||
@ViewChild('errorModal') errorModal: TemplateRef<any>
|
||||
@ViewChild('errorShopModal') errorShopModal: TemplateRef<any>
|
||||
@ViewChild('completedModal') completedModal: TemplateRef<any>
|
||||
|
||||
constructor(
|
||||
private fb: UntypedFormBuilder,
|
||||
private productService: ProductService,
|
||||
private productImageService: ProductImageService,
|
||||
private modalService: BsModalService,
|
||||
private storage: AngularFireStorage,
|
||||
private toastService: ToastrService) {
|
||||
this.productForm = this.fb.group({
|
||||
name: new FormControl("", [Validators.required, Validators.minLength(2)]),
|
||||
price: new FormControl("", [Validators.required, Validators.pattern("^[0-9]*$")]),
|
||||
shopId: new FormControl("", [Validators.required, Validators.pattern("^[0-9]*$")]),
|
||||
descriptions: new FormControl("", [Validators.required, Validators.minLength(8)]),
|
||||
categories: new FormArray([]),
|
||||
images: new FormArray([])
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.loggedRole != 'ROLE_ADMIN') {
|
||||
this.isShop = true;
|
||||
this.shopId = Number(localStorage.getItem('shop-id'))
|
||||
}
|
||||
this.productForm.patchValue({
|
||||
shopId: this.shopId
|
||||
})
|
||||
}
|
||||
|
||||
onAddNewProduct(): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
const catArray = this.productForm.get('categories') as FormArray
|
||||
if (catArray.length == 0) {
|
||||
this.item = 'thể loại'
|
||||
this.modalRef.hide();
|
||||
this.modalRef = this.modalService.show(this.errorModal, { class: 'modal-sm' });
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
const imgArray = this.productForm.get('images') as FormArray
|
||||
if (imgArray.length == 0) {
|
||||
this.item = 'ảnh'
|
||||
this.modalRef.hide();
|
||||
this.modalRef = this.modalService.show(this.errorModal, { class: 'modal-sm' });
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
const categoryNames: string[] = this.cats.controls.map(control => control.get('categoryName').value);
|
||||
const uploadFiles: File[] = this.imgs.controls.map(control => control.get('image_File').value);
|
||||
|
||||
const product = new Product();
|
||||
product.name = this.productName.value;
|
||||
product.description = this.descriptions.value;
|
||||
product.isEnabled = true;
|
||||
product.discountPercent = 0;
|
||||
product.cost = this.productPrice.value;
|
||||
product.averageRating = 0;
|
||||
product.reviewCount = 0;
|
||||
product.categoryNames = categoryNames;
|
||||
|
||||
if (this.isShop) {
|
||||
product.shopId = this.shopId;
|
||||
}
|
||||
else {
|
||||
product.shopId = this.productShopId.value;
|
||||
}
|
||||
|
||||
const uploadPromises = uploadFiles.map((file) => {
|
||||
return this.uploadImage(file).then((imageUrl: string) => {
|
||||
this.imageUrls.push(imageUrl)
|
||||
})
|
||||
})
|
||||
|
||||
this.productService.addProduct(product).subscribe({
|
||||
next: (product) => {
|
||||
this.createdId = product.id;
|
||||
Promise.all(uploadPromises).then(() => {
|
||||
this.imageUrls.forEach(url => {
|
||||
const img = new ProductImage();
|
||||
img.id = 0;
|
||||
img.imageUrl = url;
|
||||
img.productId = product.id;
|
||||
this.productImageService.addProductImage(img, img.productId).subscribe();
|
||||
});
|
||||
this.modalRef = this.modalService.show(this.completedModal, { class: 'modal-sm' });
|
||||
resolve();
|
||||
})
|
||||
},
|
||||
error: () => {
|
||||
this.modalRef = this.modalService.show(this.errorShopModal, { class: 'modal-sm' });
|
||||
}
|
||||
})
|
||||
|
||||
// Promise.all(uploadPromises).then(() => {
|
||||
|
||||
// this.productService.addProduct(product).pipe(
|
||||
// switchMap((product) => {
|
||||
// this.createdId = product.id;
|
||||
// return this.imageUrls;
|
||||
// }),
|
||||
// concatMap((url) => {
|
||||
// const img = new ProductImage();
|
||||
// img.id = 0;
|
||||
// img.imageUrl = url;
|
||||
// img.productId = this.createdId;
|
||||
// return this.productImageService.addProductImage(img, img.productId);
|
||||
// })
|
||||
|
||||
|
||||
// resolve();
|
||||
// })
|
||||
})
|
||||
}
|
||||
|
||||
// Category Form Array
|
||||
addNewCategory() {
|
||||
this.cats = this.productForm.get('categories') as FormArray;
|
||||
this.cats.push(this.generateNewCategory());
|
||||
}
|
||||
|
||||
generateNewCategory(): FormGroup {
|
||||
return new FormGroup({
|
||||
categoryName: new FormControl('', Validators.required)
|
||||
});
|
||||
}
|
||||
|
||||
deleteCategory(index: number) {
|
||||
this.cats = this.productForm.get('categories') as FormArray;
|
||||
this.cats.removeAt(index);
|
||||
|
||||
}
|
||||
|
||||
//Image Form Array
|
||||
addNewImage() {
|
||||
this.imgs = this.productForm.get('images') as FormArray;
|
||||
this.imgs.push(this.generateNewImage())
|
||||
}
|
||||
|
||||
generateNewImage(): FormGroup {
|
||||
return new FormGroup({
|
||||
image_File: new FormControl('', Validators.required)
|
||||
})
|
||||
}
|
||||
|
||||
deleteImage(index: number) {
|
||||
this.imgs = this.productForm.get('images') as FormArray;
|
||||
this.imgs.removeAt(index)
|
||||
}
|
||||
|
||||
//Getter
|
||||
get productName() { return this.productForm.get('name') }
|
||||
|
||||
get productCategory() {
|
||||
const numberStr: string = this.productForm.get('category').value;
|
||||
const idArr: number[] = numberStr.split(",").map(Number);
|
||||
return idArr;
|
||||
}
|
||||
|
||||
get productPrice() { return this.productForm.get('price') }
|
||||
|
||||
get productShopId() { return this.productForm.get('shopId') }
|
||||
|
||||
get categories() {
|
||||
return this.productForm.get('categories') as FormArray;
|
||||
}
|
||||
|
||||
get images() {
|
||||
return this.productForm.get('images') as FormArray;
|
||||
}
|
||||
|
||||
get descriptions() { return this.productForm.get('descriptions') }
|
||||
|
||||
|
||||
//Modal
|
||||
modalRef: BsModalRef;
|
||||
|
||||
openModal(template: TemplateRef<any>) {
|
||||
if (this.productForm.invalid) {
|
||||
this.productForm.markAllAsTouched()
|
||||
return;
|
||||
}
|
||||
this.modalRef = this.modalService.show(template, { class: 'modal-sm' });
|
||||
}
|
||||
|
||||
confirm(): void {
|
||||
this.modalRef.hide();
|
||||
this.onAddNewProduct().then(() => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
decline(): void {
|
||||
this.modalRef.hide();
|
||||
}
|
||||
|
||||
completed(): void {
|
||||
this.modalRef.hide();
|
||||
this.productForm.reset();
|
||||
}
|
||||
|
||||
closeModal() {
|
||||
this.modalRef.hide();
|
||||
}
|
||||
|
||||
//Image Upload
|
||||
|
||||
//File
|
||||
imageFile: File;
|
||||
imageUrl: string;
|
||||
downloadURL: Observable<string>;
|
||||
|
||||
onFileSelected(event, index: number) {
|
||||
this.imageFile = event.target.files[0]
|
||||
|
||||
this.imgs = this.productForm.get('images') as FormArray;
|
||||
this.imgs.at(index).patchValue({ image_File: this.imageFile });
|
||||
}
|
||||
|
||||
uploadImage(fileUpload: File): Promise<string> {
|
||||
return new Promise<string>((resolve) => {
|
||||
let n = Date.now();
|
||||
const filePath = `Products/${n}`;
|
||||
|
||||
const fileRef = this.storage.ref(filePath);
|
||||
const task = this.storage.upload(`Products/${n}`, fileUpload);
|
||||
task
|
||||
.snapshotChanges()
|
||||
.pipe(
|
||||
finalize(() => {
|
||||
this.downloadURL = fileRef.getDownloadURL();
|
||||
this.downloadURL.subscribe(url => {
|
||||
if (url) {
|
||||
resolve(url);
|
||||
}
|
||||
|
||||
});
|
||||
})
|
||||
)
|
||||
.subscribe(url => {
|
||||
}
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// // FileUpload
|
||||
// readUrl(event: any, i) {
|
||||
// if (event.target.files.length === 0) {
|
||||
// return;
|
||||
// }
|
||||
// // Image upload validation
|
||||
// var mimeType = event.target.files[0].type;
|
||||
// if (mimeType.match(/image\/*/) == null) {
|
||||
// return;
|
||||
// }
|
||||
// // Image upload
|
||||
// var reader = new FileReader();
|
||||
// reader.readAsDataURL(event.target.files[0]);
|
||||
// reader.onload = (_event) => {
|
||||
// this.url[i].img = reader.result.toString();
|
||||
// };
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Product Lists</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="batchDelete" class="custom-datatable">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Ảnh</th>
|
||||
<th scope="col">Tên sản phẩm</th>
|
||||
<th scope="col">Giá</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let product of products; let i = index ">
|
||||
<th scope="row">{{ i + 1 }}</th>
|
||||
<td>
|
||||
<img [src]="product.images[0].imageUrl" class="imgTable"
|
||||
style="width: 20px">
|
||||
</td>
|
||||
<td>{{ product.name }}</td>
|
||||
<td>{{ product.cost - product.cost * product.discountPercent / 100 }}</td>
|
||||
<td>
|
||||
<a [routerLink]="['/products','product-detail', product.id]"><i
|
||||
class="fa fa-eye"></i></a>
|
||||
<!-- <a href="javascript:void(0)"><i class='fa fa-edit f-12'></i></a>
|
||||
<a href="javascript:void(0)"><i class="fa fa-trash-o"></i></a> -->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="d-flex justify-content-between p-2">
|
||||
<ngb-pagination [collectionSize]="theTotalElements" [(page)]="thePageNumber"
|
||||
[maxSize]="5" [pageSize]="thePageSize" [rotate]="true" [boundaryLinks]="true"
|
||||
(pageChange)="getProductByCategories()">
|
||||
</ngb-pagination>
|
||||
<select class="form-select" style="width: auto" name="pageSize"
|
||||
(change)="getProductByCategories()" [(ngModel)]="thePageSize">
|
||||
<option [value]="10" [selected]="true" [ngValue]="10">10 items per page</option>
|
||||
<option [ngValue]="15">15 items per page</option>
|
||||
<option [ngValue]="20">20 items per page</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user