initiate to sit
5
package-lock.json
generated
|
|
@ -12739,11 +12739,6 @@
|
|||
"integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
|
||||
"dev": true
|
||||
},
|
||||
"ng-pick-datetime": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ng-pick-datetime/-/ng-pick-datetime-7.0.0.tgz",
|
||||
"integrity": "sha512-SbS+zKX6gOlYpgH8zDSx2EL32ak0Z0y1Ksu1ECP/FiwVBM2mHgbzdfyDYhMmKFB0GKn5yCwXTandR1FCQXe62w=="
|
||||
},
|
||||
"ng2-ckeditor": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ng2-ckeditor/-/ng2-ckeditor-1.2.2.tgz",
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@
|
|||
"ionicons": "2.0.1",
|
||||
"leaflet": "1.2.0",
|
||||
"nebular-icons": "1.1.0",
|
||||
"ng-pick-datetime": "^7.0.0",
|
||||
"ng2-ckeditor": "^1.2.2",
|
||||
"ng2-smart-table": "^1.6.0",
|
||||
"ngx-echarts": "^4.2.2",
|
||||
|
|
|
|||
26
src/app/@core/data/partner-price-detail.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
export interface PartnerPriceDetail {
|
||||
agingType: string,
|
||||
createTmString: string,
|
||||
createdBy: string,
|
||||
createdTm: string,
|
||||
currencyCarry: string,
|
||||
currencyType: string,
|
||||
customerCode: string,
|
||||
customerName: string,
|
||||
destCode: string,
|
||||
flowType: string,
|
||||
id: number,
|
||||
invalidDate: string,
|
||||
invalidString: string,
|
||||
isSale: number,
|
||||
modifiedBy: string,
|
||||
modifiedTm: string,
|
||||
payCountry: string,
|
||||
priceCode: string,
|
||||
productCode: string,
|
||||
roundTrip: number,
|
||||
sourceCode: string,
|
||||
status: number,
|
||||
validDate: string,
|
||||
validString: string,
|
||||
}
|
||||
12
src/app/@core/data/partner-price.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
export interface PartnerPrice {
|
||||
createdBy: string,
|
||||
createdTm: Date,
|
||||
customerCode: string,
|
||||
customerName: string,
|
||||
id: number,
|
||||
modifiedBy: string,
|
||||
modifiedTm: Date,
|
||||
quotationDesc: string,
|
||||
quotationNo: string,
|
||||
status: number,
|
||||
}
|
||||
|
|
@ -8,53 +8,29 @@ import {
|
|||
NbRequestPasswordComponent,
|
||||
NbResetPasswordComponent,
|
||||
} from '@nebular/auth';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: 'pages',
|
||||
loadChildren: () => import('./pages/pages.module')
|
||||
.then(m => m.PagesModule),
|
||||
},
|
||||
// {
|
||||
// path: 'pages',
|
||||
// loadChildren: () => import('./pages/pages.module')
|
||||
// .then(m => m.PagesModule),
|
||||
// },
|
||||
{
|
||||
path: 'auth',
|
||||
component: NbAuthComponent,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: NbLoginComponent,
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
component: NbLoginComponent,
|
||||
},
|
||||
{
|
||||
path: 'register',
|
||||
component: NbRegisterComponent,
|
||||
},
|
||||
{
|
||||
path: 'logout',
|
||||
component: NbLogoutComponent,
|
||||
},
|
||||
{
|
||||
path: 'request-password',
|
||||
component: NbRequestPasswordComponent,
|
||||
},
|
||||
{
|
||||
path: 'reset-password',
|
||||
component: NbResetPasswordComponent,
|
||||
},
|
||||
],
|
||||
loadChildren: () => import('./auth/auth.module').then(m => m.AuthModule),
|
||||
},
|
||||
{ path: '', redirectTo: 'pages', pathMatch: 'full' },
|
||||
{ path: '**', redirectTo: 'pages' },
|
||||
// { path: '', redirectTo: 'auth', pathMatch: 'full' },
|
||||
// { path: '**', redirectTo: 'auth' },
|
||||
];
|
||||
|
||||
const config: ExtraOptions = {
|
||||
useHash: false,
|
||||
};
|
||||
// const config: ExtraOptions = {
|
||||
// useHash: false,
|
||||
// };
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, config)],
|
||||
// imports: [RouterModule.forRoot(routes, config)],
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AppRoutingModule {
|
||||
|
|
|
|||
|
|
@ -11,13 +11,5 @@ import { SeoService } from './@core/utils/seo.service';
|
|||
selector: 'ngx-app',
|
||||
template: '<router-outlet></router-outlet>',
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
|
||||
constructor(private analytics: AnalyticsService, private seoService: SeoService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.analytics.trackPageViews();
|
||||
this.seoService.trackCanonicalChanges();
|
||||
}
|
||||
export class AppComponent {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,13 @@ import {
|
|||
NbSidebarModule,
|
||||
NbToastrModule,
|
||||
NbWindowModule,
|
||||
NbLayoutModule,
|
||||
NbCardModule,
|
||||
NbButtonModule,
|
||||
} from '@nebular/theme';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NbAuthModule, NbDummyAuthStrategy } from '@nebular/auth';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
|
|
|||
48
src/app/auth/auth-routing.module.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Akveo. All Rights Reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*/
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { NbAuthComponent } from '@nebular/auth';
|
||||
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { AuthComponent } from './auth.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
|
||||
{
|
||||
path: '',
|
||||
component: AuthComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'login',
|
||||
component: LoginComponent,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'auth',
|
||||
loadChildren: () => import('./auth.module').then(m => m.AuthModule),
|
||||
},
|
||||
// {
|
||||
// path: '',
|
||||
// component: NbAuthComponent,
|
||||
// children: [
|
||||
// {
|
||||
// path: 'login',
|
||||
// component: LoginComponent,
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AuthRoutingModule {
|
||||
}
|
||||
24
src/app/auth/auth.component.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Akveo. All Rights Reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-app',
|
||||
template: `
|
||||
<nb-layout>
|
||||
<nb-layout-column>
|
||||
<nb-card>
|
||||
<nb-card-body>
|
||||
<a nbButton routerLink="auth/login">Login</a>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
</nb-layout-column>
|
||||
</nb-layout>
|
||||
`,
|
||||
})
|
||||
export class AuthComponent {
|
||||
}
|
||||
57
src/app/auth/auth.module.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Akveo. All Rights Reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*/
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
import { AuthRoutingModule } from './auth-routing.module';
|
||||
import { NbAuthModule, NbDummyAuthStrategy } from '@nebular/auth';
|
||||
import { NbAlertModule, NbButtonModule, NbCheckboxModule, NbInputModule, NbLayoutModule, NbCardModule } from '@nebular/theme';
|
||||
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { AppRoutingModule } from '../app-routing.module';
|
||||
import { AuthComponent } from './auth.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
NbAlertModule,
|
||||
NbInputModule,
|
||||
NbButtonModule,
|
||||
NbCheckboxModule,
|
||||
AuthRoutingModule,
|
||||
|
||||
NbAuthModule,
|
||||
|
||||
CommonModule,
|
||||
HttpClientModule,
|
||||
NbLayoutModule,
|
||||
NbCardModule,
|
||||
NbButtonModule,
|
||||
AppRoutingModule,
|
||||
|
||||
NbAuthModule.forRoot({
|
||||
strategies: [
|
||||
NbDummyAuthStrategy.setup({
|
||||
name: 'email',
|
||||
|
||||
alwaysFail: true,
|
||||
delay: 1000,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
declarations: [
|
||||
LoginComponent,
|
||||
AuthComponent,
|
||||
],
|
||||
})
|
||||
export class AuthModule {
|
||||
}
|
||||
105
src/app/auth/login/login.component.html
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<h1 id="title" class="title">Sign In</h1>
|
||||
|
||||
<nb-alert *ngIf="showMessages.error && errors?.length && !submitted" outline="danger" role="alert">
|
||||
<p class="alert-title"><b>Oh snap!</b></p>
|
||||
<ul class="alert-message-list">
|
||||
<li *ngFor="let error of errors" class="alert-message">{{ error }}</li>
|
||||
</ul>
|
||||
</nb-alert>
|
||||
|
||||
<nb-alert *ngIf="showMessages.success && messages?.length && !submitted" outline="success" role="alert">
|
||||
<p class="alert-title"><b>Hooray!</b></p>
|
||||
<ul class="alert-message-list">
|
||||
<li *ngFor="let message of messages" class="alert-message">{{ message }}</li>
|
||||
</ul>
|
||||
</nb-alert>
|
||||
|
||||
<form (ngSubmit)="login()" #form="ngForm" aria-labelledby="title">
|
||||
|
||||
<div class="form-control-group">
|
||||
<label class="label" for="input-email">Email address:</label>
|
||||
<input nbInput
|
||||
fullWidth
|
||||
[(ngModel)]="user.email"
|
||||
#email="ngModel"
|
||||
name="email"
|
||||
id="input-email"
|
||||
pattern=".+@.+\..+"
|
||||
placeholder="Email address"
|
||||
autofocus
|
||||
[status]="email.dirty ? (email.invalid ? 'danger' : 'success') : 'basic'"
|
||||
[required]="getConfigValue('forms.validation.email.required')"
|
||||
[attr.aria-invalid]="email.invalid && email.touched ? true : null">
|
||||
<ng-container *ngIf="email.invalid && email.touched">
|
||||
<p class="caption status-danger" *ngIf="email.errors?.required">
|
||||
Email is required!
|
||||
</p>
|
||||
<p class="caption status-danger" *ngIf="email.errors?.pattern">
|
||||
Email should be the real one!
|
||||
</p>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="form-control-group">
|
||||
<label class="label" for="input-password">Password:</label>
|
||||
<input nbInput
|
||||
fullWidth
|
||||
[(ngModel)]="user.password"
|
||||
#password="ngModel"
|
||||
name="password"
|
||||
type="password"
|
||||
id="input-password"
|
||||
placeholder="Password"
|
||||
[status]="password.dirty ? (password.invalid ? 'danger' : 'success') : 'basic'"
|
||||
[required]="getConfigValue('forms.validation.password.required')"
|
||||
[minlength]="getConfigValue('forms.validation.password.minLength')"
|
||||
[maxlength]="getConfigValue('forms.validation.password.maxLength')"
|
||||
[attr.aria-invalid]="password.invalid && password.touched ? true : null">
|
||||
<ng-container *ngIf="password.invalid && password.touched ">
|
||||
<p class="caption status-danger" *ngIf="password.errors?.required">
|
||||
Password is required!
|
||||
</p>
|
||||
<p class="caption status-danger" *ngIf="(password.errors?.minlength || password.errors?.maxlength)">
|
||||
Password should contains
|
||||
from {{ getConfigValue('forms.validation.password.minLength') }}
|
||||
to {{ getConfigValue('forms.validation.password.maxLength') }}
|
||||
characters
|
||||
</p>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="form-control-group accept-group">
|
||||
<nb-checkbox name="rememberMe" [(ngModel)]="user.rememberMe" *ngIf="rememberMe">Remember me</nb-checkbox>
|
||||
<a class="forgot-password" routerLink="../request-password">Forgot Password?</a>
|
||||
</div>
|
||||
|
||||
<button nbButton
|
||||
fullWidth
|
||||
status="success"
|
||||
[disabled]="submitted || !form.valid"
|
||||
[class.btn-pulse]="submitted">
|
||||
Sign In
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<section *ngIf="socialLinks && socialLinks.length > 0" class="links" aria-label="Social sign in">
|
||||
Or connect with:
|
||||
<div class="socials">
|
||||
<ng-container *ngFor="let socialLink of socialLinks">
|
||||
<a *ngIf="socialLink.link"
|
||||
[routerLink]="socialLink.link"
|
||||
[attr.target]="socialLink.target"
|
||||
[attr.class]="socialLink.icon"
|
||||
[class.with-icon]="socialLink.icon">{{ socialLink.title }}</a>
|
||||
<a *ngIf="socialLink.url"
|
||||
[attr.href]="socialLink.url"
|
||||
[attr.target]="socialLink.target"
|
||||
[attr.class]="socialLink.icon"
|
||||
[class.with-icon]="socialLink.icon">{{ socialLink.title }}</a>
|
||||
</ng-container>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="another-action" aria-label="Register">
|
||||
Don't have an account? <a class="text-link" routerLink="../register">Sign Up</a>
|
||||
</section>
|
||||
15
src/app/auth/login/login.component.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Akveo. All Rights Reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*/
|
||||
import { Component } from '@angular/core';
|
||||
import { NbLoginComponent } from '@nebular/auth';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-login',
|
||||
templateUrl: './login.component.html',
|
||||
})
|
||||
export class LoginComponent extends NbLoginComponent {
|
||||
}
|
||||
|
|
@ -1,85 +1,37 @@
|
|||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1" class="label">Partner Code</label>
|
||||
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.customerID">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">User Id</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.customerID">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1" class="label">Promo Type</label>
|
||||
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.promotionType">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Periode </label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.validDay">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1" class="label">Coupon Code</label>
|
||||
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.code">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Valid Start</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.validFrom">
|
||||
</div>
|
||||
</form>
|
||||
<div class="form-group row">
|
||||
<label class="label col-sm-3 col-form-label">Quotation No</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="email" nbInput placeholder="Email">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Valid End</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.validTo">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Promo</label>
|
||||
<input type="number" nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.value">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1" class="label">Max. Promo</label>
|
||||
<input type="number" nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.maximumDiscount">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1" class="label">Quota per User</label>
|
||||
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.maximumPerUser">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">User Type</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.customerType">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Coverage Pickup</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.coverage">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="label col-sm-3 col-form-label">Partner</label>
|
||||
<div class="col-sm-9">
|
||||
<input nbInput placeholder="Password">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Product</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.product">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Promo (IDR)</label>
|
||||
<input type="number" nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.value">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Promo (%)</label>
|
||||
<input type="number" nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.value">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Max. Promo</label>
|
||||
<input type="number" nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.maximumDiscountValue">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Budget</label>
|
||||
<input type="number" nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.maximumDiscount">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Quota</label>
|
||||
<input type="number" nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.quota">
|
||||
</div>
|
||||
<button (click)="onSubmit()" type="submit" nbButton status="primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="label col-sm-3 col-form-label">Status</label>
|
||||
<div class="col-sm-9">
|
||||
<label>*SDS price is for first 5kg, each additional kg is additional Rp.</label>
|
||||
<input nbInput placeholder="Password">
|
||||
<label>for shipment total up to 10kg</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="form-group row">
|
||||
<div class="offset-sm-3 col-sm-9">
|
||||
<button (click)="onSubmit()" type="submit" nbButton status="primary">Sign in</button>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="form-group row">
|
||||
<label class="label col-sm-3 col-form-label">Add</label>
|
||||
<div class="col-sm-9">
|
||||
<input nbInput placeholder="Password">
|
||||
<label>Flow(s)</label>
|
||||
<!-- (click)="onSubmit()" -->
|
||||
<button type="submit" nbButton status="primary">Go</button> </div>
|
||||
</div>
|
||||
|
||||
<ng2-smart-table [settings]="settings" [source]="data" (deleteConfirm)="onDeleteConfirm($event)"
|
||||
(createConfirm)="onCreateConfirm($event)" (userRowSelect)="openWindowForm($event)">
|
||||
</ng2-smart-table>
|
||||
|
|
@ -1,30 +1,132 @@
|
|||
import { Component, Inject, Input } from '@angular/core';
|
||||
import { NbWindowRef, NB_WINDOW_CONTEXT } from '@nebular/theme';
|
||||
import { PromotionList } from '../../../@core/data/promotion';
|
||||
import { Component, Inject, Input, OnInit } from '@angular/core';
|
||||
import { NbWindowRef, NB_WINDOW_CONTEXT, NbWindowService } from '@nebular/theme';
|
||||
import { PartnerPriceService } from '../partner-price.service';
|
||||
import { PartnerPriceDetail } from '../../../@core/data/partner-price-detail';
|
||||
import { PartnerPrice } from '../../../@core/data/partner-price';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-partner-price-detail',
|
||||
templateUrl: './partner-price-detail.component.html',
|
||||
styleUrls: ['partner-price-detail.component.scss'],
|
||||
})
|
||||
export class PartnerPriceDetailComponent {
|
||||
export class PartnerPriceDetailComponent implements OnInit {
|
||||
|
||||
@Input() data: PromotionList;
|
||||
settings = {
|
||||
mode: 'inline',
|
||||
pager: {
|
||||
perPage: 2,
|
||||
},
|
||||
add: {
|
||||
addButtonContent: '<i class="nb-plus"></i>',
|
||||
createButtonContent: '<i class="nb-checkmark"></i>',
|
||||
cancelButtonContent: '<i class="nb-close"></i>',
|
||||
confirmCreate: true,
|
||||
},
|
||||
edit: {
|
||||
editButtonContent: '<i class="nb-edit"></i>',
|
||||
saveButtonContent: '<i class="nb-checkmark"></i>',
|
||||
cancelButtonContent: '<i class="nb-close"></i>',
|
||||
},
|
||||
delete: {
|
||||
deleteButtonContent: '<i class="nb-trash"></i>',
|
||||
confirmDelete: true,
|
||||
},
|
||||
columns: {
|
||||
productCode: {
|
||||
title: 'Product',
|
||||
type: 'string',
|
||||
},
|
||||
flowType: {
|
||||
title: 'Pricing Level',
|
||||
type: 'string',
|
||||
},
|
||||
sourceCode: {
|
||||
title: 'Origin',
|
||||
type: 'string',
|
||||
},
|
||||
destCode: {
|
||||
title: 'Destination',
|
||||
type: 'string',
|
||||
},
|
||||
priceCode: {
|
||||
title: 'Price',
|
||||
type: 'string',
|
||||
},
|
||||
agingType: {
|
||||
title: 'SLA',
|
||||
type: 'string',
|
||||
},
|
||||
validDate: {
|
||||
title: 'Valid Date Start',
|
||||
type: 'string',
|
||||
},
|
||||
invalidDate: {
|
||||
title: 'Valid Date End',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
constructor(private service: PartnerPriceService, public windowRef: NbWindowRef, @Inject(NB_WINDOW_CONTEXT) context) {
|
||||
@Input() data: PartnerPriceDetail[];
|
||||
headerData: PartnerPrice;
|
||||
|
||||
constructor(private service: PartnerPriceService, public windowRef: NbWindowRef, @Inject(NB_WINDOW_CONTEXT) context, private windowService: NbWindowService) {
|
||||
if (context != null) {
|
||||
this.data = Object.assign({}, context.data);
|
||||
this.headerData = Object.assign({}, context.data);
|
||||
}
|
||||
}
|
||||
|
||||
onSubmit(){
|
||||
this.service.postPromotion(this.data).subscribe((value) => {
|
||||
this.close();
|
||||
filter: any = {
|
||||
customerCode: '',
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.filter.customerCode = this.headerData.customerCode;
|
||||
this.getData();
|
||||
}
|
||||
|
||||
getData(){
|
||||
this.service.getDetailPartnerPrice(this.filter).subscribe((result) => {
|
||||
this.data = Object.assign([], result);
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
// this.service.postPromotion(this.data).subscribe((value) => {
|
||||
// this.close();
|
||||
// });
|
||||
}
|
||||
|
||||
close() {
|
||||
this.windowRef.close();
|
||||
}
|
||||
|
||||
onCreateConfirm(event) {
|
||||
if (window.confirm('Are you sure you want to save?')) {
|
||||
event.newData.status = 1;
|
||||
this.service.getHeaderPartnerPrice(event.newData).subscribe((value) => {
|
||||
// this.initData();
|
||||
});
|
||||
event.confirm.resolve(event.newData);
|
||||
} else {
|
||||
event.confirm.reject();
|
||||
}
|
||||
}
|
||||
|
||||
onDeleteConfirm(event): void {
|
||||
if (window.confirm('Are you sure you want to delete?')) {
|
||||
event.confirm.resolve();
|
||||
} else {
|
||||
event.confirm.reject();
|
||||
}
|
||||
}
|
||||
|
||||
openWindowForm(event) {
|
||||
this.windowService.open(PartnerPriceDetailComponent, {
|
||||
title: 'Partner Price Detail',
|
||||
context: {
|
||||
data: event.data,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<nb-card>
|
||||
<nb-card-header>
|
||||
Partner Price
|
||||
Partner Price - dev 90%
|
||||
</nb-card-header>
|
||||
|
||||
<nb-card-body>
|
||||
<ng2-smart-table [settings]="settings" [source]="source" (deleteConfirm)="onDeleteConfirm($event)"
|
||||
(createConfirm)="onCreateConfirm($event)" (userRowSelect)="openWindowForm($event)">
|
||||
(createConfirm)="onCreateConfirm($event)" (userRowSelect)="openWindowForm($event)" (create)="openCreateDialog($event)">
|
||||
</ng2-smart-table>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { PromotionList } from '../../@core/data/promotion';
|
||||
import { PartnerPriceService } from './partner-price.service';
|
||||
import { PartnerPriceDetailComponent } from './partner-price-detail/partner-price-detail.component';
|
||||
import { NbWindowService } from '@nebular/theme';
|
||||
import { PartnerPrice } from '../../@core/data/partner-price';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-partner-price',
|
||||
|
|
@ -11,7 +11,7 @@ import { NbWindowService } from '@nebular/theme';
|
|||
})
|
||||
export class PartnerPriceComponent implements OnInit {
|
||||
settings = {
|
||||
mode: 'inline',
|
||||
mode: 'external',
|
||||
add: {
|
||||
addButtonContent: '<i class="nb-plus"></i>',
|
||||
createButtonContent: '<i class="nb-checkmark"></i>',
|
||||
|
|
@ -28,26 +28,39 @@ export class PartnerPriceComponent implements OnInit {
|
|||
confirmDelete: true,
|
||||
},
|
||||
columns: {
|
||||
code: {
|
||||
title: 'Coupon Code',
|
||||
customerCode: {
|
||||
title: 'Partner Code',
|
||||
type: 'string',
|
||||
},
|
||||
validDay: {
|
||||
title: 'Period',
|
||||
customerName: {
|
||||
title: 'Partner Name',
|
||||
type: 'string',
|
||||
},
|
||||
validFrom: {
|
||||
title: 'Valid Start',
|
||||
quotationNo: {
|
||||
title: 'Reference',
|
||||
type: 'string',
|
||||
},
|
||||
validTo: {
|
||||
title: 'Valid End',
|
||||
modifiedBy: {
|
||||
title: 'Modified by',
|
||||
type: 'string',
|
||||
},
|
||||
modifiedTm: {
|
||||
title: 'Modified Date',
|
||||
type: 'string',
|
||||
},
|
||||
status: {
|
||||
title: 'Status',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
source: PromotionList[] = [];
|
||||
filter: any = {
|
||||
start: 0,
|
||||
column: 'customerCode',
|
||||
}
|
||||
|
||||
source: PartnerPrice[] = [];
|
||||
|
||||
constructor(private service: PartnerPriceService, private windowService: NbWindowService) {
|
||||
|
||||
|
|
@ -57,8 +70,8 @@ export class PartnerPriceComponent implements OnInit {
|
|||
this.initData();
|
||||
}
|
||||
|
||||
initData(){
|
||||
this.service.getPromotion().subscribe((result) => {
|
||||
initData() {
|
||||
this.service.getHeaderPartnerPrice(this.filter).subscribe((result) => {
|
||||
this.source = Object.assign([], result);
|
||||
});
|
||||
}
|
||||
|
|
@ -66,7 +79,7 @@ export class PartnerPriceComponent implements OnInit {
|
|||
onCreateConfirm(event) {
|
||||
if (window.confirm('Are you sure you want to save?')) {
|
||||
event.newData.status = 1;
|
||||
this.service.postPromotion(event.newData).subscribe((value) => {
|
||||
this.service.getHeaderPartnerPrice(event.newData).subscribe((value) => {
|
||||
this.initData();
|
||||
});
|
||||
event.confirm.resolve(event.newData);
|
||||
|
|
@ -83,9 +96,14 @@ export class PartnerPriceComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
openCreateDialog(event) {
|
||||
|
||||
//logic
|
||||
}
|
||||
|
||||
openWindowForm(event) {
|
||||
this.windowService.open(PartnerPriceDetailComponent, {
|
||||
title: 'Promotion Detail',
|
||||
title: 'Partner Price Detail',
|
||||
context: {
|
||||
data: event.data,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import { Observable, throwError } from 'rxjs';
|
|||
import { map, catchError } from 'rxjs/operators';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { PromotionList } from '../../@core/data/promotion';
|
||||
import { PartnerPrice } from '../../@core/data/partner-price';
|
||||
import { PartnerPriceDetail } from '../../@core/data/partner-price-detail';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
|
@ -12,28 +14,40 @@ export class PartnerPriceService {
|
|||
constructor(private http: HttpClient) {
|
||||
}
|
||||
|
||||
getPromotion(): Observable<any> {
|
||||
const url = 'http://localhost:8011/api/promotions/all';
|
||||
return this.http.get(url).pipe(
|
||||
getHeaderPartnerPrice(filter: any): Observable<any> {
|
||||
const url = 'http://34.87.6.140:8011/api/productFlow/pagingFlowHeader?start=0&sort=asc&length=10&column=' + filter.column;
|
||||
return this.http.post(url, null).pipe(
|
||||
map(this.extractData),
|
||||
catchError(this.handleError),
|
||||
);
|
||||
}
|
||||
|
||||
postPromotion(data: PromotionList): Observable<any> {
|
||||
const headers = new HttpHeaders({
|
||||
'Content-Type': 'application/json',
|
||||
'X-Requested-Method': 'POST',
|
||||
});
|
||||
const options = { headers: headers };
|
||||
const url = 'http://localhost:8011/api/promotions/add';
|
||||
return this.http.post(url, JSON.stringify(data), options).pipe(
|
||||
getDetailPartnerPrice(filter: any): Observable<any> {
|
||||
const url = 'http://34.87.6.140:8011/api/productFlow/pagingByCustomerCode?start=0&sort=asc&length=10&column=modifiedTm&customerCode=' + filter.customerCode;
|
||||
return this.http.post(url, null).pipe(
|
||||
map(this.extractData),
|
||||
catchError(this.handleError),
|
||||
);
|
||||
}
|
||||
|
||||
private extractData(body: any): PromotionList[] {
|
||||
return Object.assign(body.promotionList);
|
||||
// postPromotion(data: PromotionList): Observable<any> {
|
||||
// const headers = new HttpHeaders({
|
||||
// 'Content-Type': 'application/json',
|
||||
// 'X-Requested-Method': 'POST',
|
||||
// });
|
||||
// const options = { headers: headers };
|
||||
// const url = 'http://localhost:8011/api/promotions/add';
|
||||
// return this.http.post(url, JSON.stringify(data), options).pipe(
|
||||
// catchError(this.handleError),
|
||||
// );
|
||||
// }
|
||||
|
||||
private extractData(body: any): PartnerPrice[] {
|
||||
return Object.assign(body.content);
|
||||
}
|
||||
|
||||
private extractDataDetail(body: any): PartnerPriceDetail[] {
|
||||
return Object.assign(body.content);
|
||||
}
|
||||
|
||||
private handleError(error: HttpErrorResponse | any) {
|
||||
|
|
|
|||
|
|
@ -1,80 +1,92 @@
|
|||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1" class="label">Partner Code</label>
|
||||
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.customerID">
|
||||
<label class="label">Partner Code</label>
|
||||
<input nbInput fullWidth [(ngModel)]="data.customerID">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">User Id</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.customerID">
|
||||
<label class="label">User Id</label>
|
||||
<input nbInput fullWidth [(ngModel)]="data.customerID">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1" class="label">Promo Type</label>
|
||||
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.promotionType">
|
||||
<label class="label">Promo Type</label>
|
||||
<!-- <input nbInput fullWidth [(ngModel)]="data.promotionType"> -->
|
||||
<nb-card-body>
|
||||
<nb-select [(selected)]="data.promotionType">
|
||||
<nb-option value="Discount">Discount</nb-option>
|
||||
<nb-option value="Cashback">Cashback</nb-option>
|
||||
</nb-select>
|
||||
</nb-card-body>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Periode </label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.validDay">
|
||||
<label class="label">Periode </label>
|
||||
<input nbInput fullWidth [(ngModel)]="data.validDay">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1" class="label">Coupon Code</label>
|
||||
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.code">
|
||||
<label class="label">Coupon Code</label>
|
||||
<input nbInput fullWidth [(ngModel)]="data.code">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Valid Start</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.validFrom">
|
||||
<label class="label">Valid Start</label>
|
||||
<!-- <input nbInput fullWidth [(ngModel)]="data.validFrom"> -->
|
||||
<input nbInput fullWidth placeholder="Select Date" [nbDatepicker]="formpickerstart" [(ngModel)]="data.validFrom">
|
||||
<nb-datepicker #formpickerstart></nb-datepicker>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Valid End</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.validTo">
|
||||
<label class="label">Valid End</label>
|
||||
<!-- <input nbInput fullWidth [(ngModel)]="data.validTo"> -->
|
||||
<input nbInput fullWidth placeholder="Select Date" [nbDatepicker]="formpickerend" [(ngModel)]="data.validTo">
|
||||
<nb-datepicker #formpickerend></nb-datepicker>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Promo</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.value">
|
||||
<label class="label">Quota per User</label>
|
||||
<input (keypress)="numberOnly($event)" nbInput fullWidth [(ngModel)]="data.maximumPerUser">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1" class="label">Max. Promo</label>
|
||||
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.maximumDiscount">
|
||||
<label class="label">User Type</label>
|
||||
<input nbInput fullWidth [(ngModel)]="data.customerType">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1" class="label">Quota per User</label>
|
||||
<input nbInput fullWidth id="exampleInputEmail1" [(ngModel)]="data.maximumPerUser">
|
||||
<label class="label">Coverage Pickup</label>
|
||||
<input nbInput fullWidth [(ngModel)]="data.coverage">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">User Type</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.customerType">
|
||||
<label class="label">Product</label>
|
||||
<!-- <input nbInput fullWidth [(ngModel)]="data.promotionType"> -->
|
||||
<nb-card-body>
|
||||
<nb-select [(selected)]="data.product">
|
||||
<nb-option value="Regular">Regular</nb-option>
|
||||
<nb-option value="Next Day">Next Day</nb-option>
|
||||
<nb-option value="Same Day">Same Day</nb-option>
|
||||
<nb-option value="Same Day Service">Same Day Service</nb-option>
|
||||
<nb-option value="Instant Delivery">Instant Delivery</nb-option>
|
||||
<nb-option value="Free Ongkir Regular">Free Ongkir Regular</nb-option>
|
||||
</nb-select>
|
||||
</nb-card-body>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Coverage Pickup</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.coverage">
|
||||
<label class="label">Promo (IDR)</label>
|
||||
<input (keypress)="numberOnly($event)" nbInput fullWidth [(ngModel)]="data.value">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Product</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.product">
|
||||
<label class="label">Promo (%)</label>
|
||||
<input (keypress)="numberOnly($event)" nbInput fullWidth [(ngModel)]="data.value">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Promo (IDR)</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.value">
|
||||
<label class="label">Max. Promo</label>
|
||||
<input (keypress)="numberOnly($event)" nbInput fullWidth [(ngModel)]="data.maximumDiscountValue">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Promo (%)</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.value">
|
||||
<label class="label">Budget</label>
|
||||
<input (keypress)="numberOnly($event)" nbInput fullWidth [(ngModel)]="data.maximumDiscount">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Max. Promo</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.maximumDiscountValue">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Budget</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.maximumDiscount">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1" class="label">Quota</label>
|
||||
<input nbInput fullWidth id="exampleInputPassword1" [(ngModel)]="data.quota">
|
||||
<label class="label">Quota</label>
|
||||
<input (keypress)="numberOnly($event)" nbInput fullWidth [(ngModel)]="data.quota">
|
||||
</div>
|
||||
<button (click)="onSubmit()" type="submit" nbButton status="primary">Submit</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { Component, Inject, Input } from '@angular/core';
|
|||
import { NbWindowRef, NB_WINDOW_CONTEXT } from '@nebular/theme';
|
||||
import { PromotionList } from '../../../@core/data/promotion';
|
||||
import { PromotionService } from '../promotion.service';
|
||||
import { NgForm } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-promotion-detail',
|
||||
|
|
@ -19,7 +18,7 @@ export class PromotionDetailComponent {
|
|||
}
|
||||
}
|
||||
|
||||
onSubmit(){
|
||||
onSubmit() {
|
||||
this.service.postPromotion(this.data).subscribe((value) => {
|
||||
this.close();
|
||||
});
|
||||
|
|
@ -28,4 +27,14 @@ export class PromotionDetailComponent {
|
|||
close() {
|
||||
this.windowRef.close();
|
||||
}
|
||||
|
||||
numberOnly(event): boolean {
|
||||
const charCode = (event.which) ? event.which : event.keyCode;
|
||||
if (charCode > 31 && (charCode < 48 || charCode > 57)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<nb-card>
|
||||
<nb-card-header>
|
||||
Promotion
|
||||
Promotion - dev 100%
|
||||
</nb-card-header>
|
||||
|
||||
<nb-card-body>
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ export class PromotionComponent implements OnInit {
|
|||
this.initData();
|
||||
}
|
||||
|
||||
initData(){
|
||||
initData() {
|
||||
this.service.getPromotion().subscribe((result) => {
|
||||
this.source = Object.assign([], result);
|
||||
});
|
||||
|
|
@ -78,6 +78,9 @@ export class PromotionComponent implements OnInit {
|
|||
|
||||
onDeleteConfirm(event): void {
|
||||
if (window.confirm('Are you sure you want to delete?')) {
|
||||
this.service.deletePromotion(event.data).subscribe((value) => {
|
||||
this.initData();
|
||||
});
|
||||
event.confirm.resolve();
|
||||
} else {
|
||||
event.confirm.reject();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import {
|
|||
NbTreeGridModule,
|
||||
NbButtonModule,
|
||||
NbCheckboxModule,
|
||||
NbRadioModule
|
||||
NbRadioModule,
|
||||
NbSelectModule,
|
||||
NbDatepickerModule
|
||||
} from '@nebular/theme';
|
||||
import { Ng2SmartTableModule } from 'ng2-smart-table';
|
||||
|
||||
|
|
@ -28,6 +30,8 @@ import { FormsModule } from '@angular/forms';
|
|||
NbCheckboxModule,
|
||||
Ng2SmartTableModule,
|
||||
FormsModule,
|
||||
NbSelectModule,
|
||||
NbDatepickerModule,
|
||||
],
|
||||
declarations: [
|
||||
PromotionComponent,
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ export class PromotionService {
|
|||
constructor(private http: HttpClient) {
|
||||
}
|
||||
|
||||
// 34.87.6.140
|
||||
|
||||
getPromotion(): Observable<any> {
|
||||
const url = 'http://localhost:8011/api/promotions/all';
|
||||
const url = 'http://34.87.6.140:8011/api/promotions/all';
|
||||
return this.http.get(url).pipe(
|
||||
map(this.extractData),
|
||||
catchError(this.handleError),
|
||||
|
|
@ -26,12 +28,25 @@ export class PromotionService {
|
|||
'X-Requested-Method': 'POST',
|
||||
});
|
||||
const options = { headers: headers };
|
||||
const url = 'http://localhost:8011/api/promotions/add';
|
||||
const url = 'http://34.87.6.140:8011/api/promotions/add';
|
||||
return this.http.post(url, JSON.stringify(data), options).pipe(
|
||||
catchError(this.handleError),
|
||||
);
|
||||
}
|
||||
|
||||
deletePromotion(data: PromotionList): Observable<any> {
|
||||
const id = data.id;
|
||||
const headers = new HttpHeaders({
|
||||
'Content-Type': 'application/json',
|
||||
'X-Requested-Method': 'PUT',
|
||||
});
|
||||
const options = { headers: headers };
|
||||
const url = 'http://34.87.6.140:8011/api/promotion/delete';
|
||||
return this.http.put(url, id, options).pipe(
|
||||
catchError(this.handleError),
|
||||
);
|
||||
}
|
||||
|
||||
private extractData(body: any): PromotionList[] {
|
||||
return Object.assign(body.promotionList);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { ServerDataSource } from 'ng2-smart-table';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-advanced-example-server',
|
||||
styleUrls: ['./examples.component.scss'],
|
||||
template: `
|
||||
<ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table>
|
||||
`,
|
||||
})
|
||||
export class AdvancedExampleServerComponent {
|
||||
|
||||
settings = {
|
||||
columns: {
|
||||
id: {
|
||||
title: 'ID',
|
||||
},
|
||||
albumId: {
|
||||
title: 'Album',
|
||||
},
|
||||
title: {
|
||||
title: 'Title',
|
||||
},
|
||||
url: {
|
||||
title: 'Url',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
source: ServerDataSource;
|
||||
|
||||
constructor(http: HttpClient) {
|
||||
this.source = new ServerDataSource(http, { endPoint: 'https://jsonplaceholder.typicode.com/photos' });
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
import { LocalDataSource } from 'ng2-smart-table';
|
||||
import { BasicExampleLoadService } from './basic-example-load.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-basic-example-load',
|
||||
providers: [BasicExampleLoadService],
|
||||
styleUrls: ['./examples.component.scss'],
|
||||
template: `
|
||||
<ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table>
|
||||
`,
|
||||
})
|
||||
export class BasicExampleLoadComponent {
|
||||
|
||||
source: LocalDataSource;
|
||||
|
||||
settings = {
|
||||
columns: {
|
||||
id: {
|
||||
title: 'ID',
|
||||
editable: false,
|
||||
addable: false,
|
||||
},
|
||||
|
||||
name: {
|
||||
title: 'Full Name',
|
||||
},
|
||||
username: {
|
||||
title: 'User Name',
|
||||
},
|
||||
email: {
|
||||
title: 'Email',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
constructor(protected service: BasicExampleLoadService) {
|
||||
this.source = new LocalDataSource();
|
||||
|
||||
this.service.getData().then((data) => {
|
||||
this.source.load(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class BasicExampleLoadService {
|
||||
|
||||
static DATA_SIZE = 500;
|
||||
|
||||
// emulating request to the server
|
||||
getData(): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve(this.generateData());
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
getNewExampleObj(n?: number): any {
|
||||
n = typeof n !== 'undefined' ? n : Math.random() * 1000;
|
||||
return {
|
||||
id: n,
|
||||
name: `Jack London ${n}`,
|
||||
username: `jack_london_${n}`,
|
||||
email: `jack_london_${n}@example.com`,
|
||||
};
|
||||
}
|
||||
|
||||
protected generateData(): Array<any> {
|
||||
const data = [];
|
||||
for (let i = 0; i < BasicExampleLoadService.DATA_SIZE; i++) {
|
||||
data.push(this.getNewExampleObj(i));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
.with-sidebar {
|
||||
position: relative;
|
||||
|
||||
.main-content-body {
|
||||
padding-left: 16rem;
|
||||
}
|
||||
|
||||
.fixed-sidebar {
|
||||
display: block;
|
||||
padding: 0 1rem;
|
||||
margin-top: 2rem;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
padding-top: 290px;
|
||||
width: 16rem;
|
||||
font-size: 0.875rem;
|
||||
|
||||
.back-top {
|
||||
display: none;
|
||||
margin-bottom: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 1rem;
|
||||
list-style: none;
|
||||
margin-bottom: 0.875rem;
|
||||
}
|
||||
|
||||
&.scrolled {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
||||
.back-top {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.examples-menu {
|
||||
a.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 64em) {
|
||||
.with-sidebar {
|
||||
.fixed-sidebar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 42em) and (max-width: 64em) {
|
||||
.with-sidebar {
|
||||
padding: 2rem 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 42em) {
|
||||
.with-sidebar {
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {ServerExamplesComponent} from './server-examples.component';
|
||||
import {BasicExampleLoadComponent} from './basic-example-load.component';
|
||||
import {AdvancedExampleServerComponent} from './advanced-example-server.component';
|
||||
import {Ng2SmartTableModule} from 'ng2-smart-table';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
Ng2SmartTableModule,
|
||||
],
|
||||
declarations: [
|
||||
ServerExamplesComponent,
|
||||
BasicExampleLoadComponent,
|
||||
AdvancedExampleServerComponent,
|
||||
],
|
||||
})
|
||||
export class SampleModule { }
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<h2>Get data from external source example</h2>
|
||||
<h3><a id="from-server" class="anchor" href="#from-server" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Loading From Server Example</h3>
|
||||
<p>
|
||||
This example shows how to pass the data loaded from some API to the table DataSource.
|
||||
</p>
|
||||
<div class="with-source">
|
||||
<ngx-basic-example-load></ngx-basic-example-load>
|
||||
</div>
|
||||
|
||||
<h3><a id="server" class="anchor" href="#server" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Server Data Source Example</h3>
|
||||
<p>
|
||||
An example on how to load data user Server DataSource:
|
||||
</p>
|
||||
<div class="with-source">
|
||||
<ngx-advanced-example-server></ngx-advanced-example-server>
|
||||
</div>
|
||||
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-server-examples',
|
||||
styleUrls: ['./examples.component.scss'],
|
||||
templateUrl: './server-examples.component.html',
|
||||
})
|
||||
export class ServerExamplesComponent { }
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { LocalDataSource } from 'ng2-smart-table';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class CustomServerDataSource extends LocalDataSource {
|
||||
|
||||
lastRequestCount: number = 0;
|
||||
|
||||
constructor(protected http: HttpClient) {
|
||||
super();
|
||||
}
|
||||
|
||||
count(): number {
|
||||
return this.lastRequestCount;
|
||||
}
|
||||
|
||||
getElements(): Promise<any> {
|
||||
let url = 'https://jsonplaceholder.typicode.com/photos?';
|
||||
|
||||
if (this.sortConf) {
|
||||
this.sortConf.forEach((fieldConf) => {
|
||||
url += `_sort=${fieldConf.field}&_order=${fieldConf.direction.toUpperCase()}&`;
|
||||
});
|
||||
}
|
||||
|
||||
if (this.pagingConf && this.pagingConf['page'] && this.pagingConf['perPage']) {
|
||||
url += `_page=${this.pagingConf['page']}&_limit=${this.pagingConf['perPage']}&`;
|
||||
}
|
||||
|
||||
if (this.filterConf.filters) {
|
||||
this.filterConf.filters.forEach((fieldConf) => {
|
||||
if (fieldConf['search']) {
|
||||
url += `${fieldConf['field']}_like=${fieldConf['search']}&`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this.http.get(url, { observe: 'response' })
|
||||
.pipe(
|
||||
map(res => {
|
||||
this.lastRequestCount = +res.headers.get('x-total-count');
|
||||
return res.body;
|
||||
}),
|
||||
).toPromise();
|
||||
}
|
||||
}
|
||||
BIN
symfonia/src/main/resources/static/Roboto-Black.eot
Normal file
10968
symfonia/src/main/resources/static/Roboto-Black.svg
Normal file
|
After Width: | Height: | Size: 732 KiB |
BIN
symfonia/src/main/resources/static/Roboto-Black.ttf
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Black.woff
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Black.woff2
Normal file
BIN
symfonia/src/main/resources/static/Roboto-BlackItalic.eot
Normal file
11086
symfonia/src/main/resources/static/Roboto-BlackItalic.svg
Normal file
|
After Width: | Height: | Size: 758 KiB |
BIN
symfonia/src/main/resources/static/Roboto-BlackItalic.ttf
Normal file
BIN
symfonia/src/main/resources/static/Roboto-BlackItalic.woff
Normal file
BIN
symfonia/src/main/resources/static/Roboto-BlackItalic.woff2
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Bold.eot
Normal file
11010
symfonia/src/main/resources/static/Roboto-Bold.svg
Normal file
|
After Width: | Height: | Size: 738 KiB |
BIN
symfonia/src/main/resources/static/Roboto-Bold.ttf
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Bold.woff
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Bold.woff2
Normal file
BIN
symfonia/src/main/resources/static/Roboto-BoldItalic.eot
Normal file
11096
symfonia/src/main/resources/static/Roboto-BoldItalic.svg
Normal file
|
After Width: | Height: | Size: 760 KiB |
BIN
symfonia/src/main/resources/static/Roboto-BoldItalic.ttf
Normal file
BIN
symfonia/src/main/resources/static/Roboto-BoldItalic.woff
Normal file
BIN
symfonia/src/main/resources/static/Roboto-BoldItalic.woff2
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Light.eot
Normal file
10564
symfonia/src/main/resources/static/Roboto-Light.svg
Normal file
|
After Width: | Height: | Size: 726 KiB |
BIN
symfonia/src/main/resources/static/Roboto-Light.ttf
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Light.woff
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Light.woff2
Normal file
BIN
symfonia/src/main/resources/static/Roboto-LightItalic.eot
Normal file
10646
symfonia/src/main/resources/static/Roboto-LightItalic.svg
Normal file
|
After Width: | Height: | Size: 750 KiB |
BIN
symfonia/src/main/resources/static/Roboto-LightItalic.ttf
Normal file
BIN
symfonia/src/main/resources/static/Roboto-LightItalic.woff
Normal file
BIN
symfonia/src/main/resources/static/Roboto-LightItalic.woff2
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Medium.eot
Normal file
10999
symfonia/src/main/resources/static/Roboto-Medium.svg
Normal file
|
After Width: | Height: | Size: 736 KiB |
BIN
symfonia/src/main/resources/static/Roboto-Medium.ttf
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Medium.woff
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Medium.woff2
Normal file
BIN
symfonia/src/main/resources/static/Roboto-MediumItalic.eot
Normal file
11100
symfonia/src/main/resources/static/Roboto-MediumItalic.svg
Normal file
|
After Width: | Height: | Size: 761 KiB |
BIN
symfonia/src/main/resources/static/Roboto-MediumItalic.ttf
Normal file
BIN
symfonia/src/main/resources/static/Roboto-MediumItalic.woff
Normal file
BIN
symfonia/src/main/resources/static/Roboto-MediumItalic.woff2
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Regular.eot
Normal file
10520
symfonia/src/main/resources/static/Roboto-Regular.svg
Normal file
|
After Width: | Height: | Size: 716 KiB |
BIN
symfonia/src/main/resources/static/Roboto-Regular.ttf
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Regular.woff
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Regular.woff2
Normal file
BIN
symfonia/src/main/resources/static/Roboto-RegularItalic.eot
Normal file
10629
symfonia/src/main/resources/static/Roboto-RegularItalic.svg
Normal file
|
After Width: | Height: | Size: 743 KiB |
BIN
symfonia/src/main/resources/static/Roboto-RegularItalic.ttf
Normal file
BIN
symfonia/src/main/resources/static/Roboto-RegularItalic.woff
Normal file
BIN
symfonia/src/main/resources/static/Roboto-RegularItalic.woff2
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Thin.eot
Normal file
10617
symfonia/src/main/resources/static/Roboto-Thin.svg
Normal file
|
After Width: | Height: | Size: 726 KiB |
BIN
symfonia/src/main/resources/static/Roboto-Thin.ttf
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Thin.woff
Normal file
BIN
symfonia/src/main/resources/static/Roboto-Thin.woff2
Normal file
BIN
symfonia/src/main/resources/static/Roboto-ThinItalic.eot
Normal file
10726
symfonia/src/main/resources/static/Roboto-ThinItalic.svg
Normal file
|
After Width: | Height: | Size: 756 KiB |
BIN
symfonia/src/main/resources/static/Roboto-ThinItalic.ttf
Normal file
BIN
symfonia/src/main/resources/static/Roboto-ThinItalic.woff
Normal file
BIN
symfonia/src/main/resources/static/Roboto-ThinItalic.woff2
Normal file
352
symfonia/src/main/resources/static/assets/data/news.json
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
[
|
||||
{
|
||||
"title": "Fan of Angular-In-Depth and my writings? Support us on Twitter!",
|
||||
"link": "https://blog.angularindepth.com/fan-of-angular-in-depth-and-my-writings-support-us-on-twitter-e3bfcbabb4b1",
|
||||
"text": "A few weeks ago I ran a poll on Twitter to understand why Angular account has 280k followers on Twitter while Angular-In-Depth has only a fraction of that on Medium (11k). The poll showed that 50% of those who responded don’t use Medium, 17% find stories too complicated, 27% have no time to read and there are people (7%) who find stories not interesting."
|
||||
},
|
||||
{
|
||||
"title": "Boosting performance of Angular applications with manual change detection",
|
||||
"link": "https://blog.angularindepth.com/boosting-performance-of-angular-applications-with-manual-change-detection-42cb396110fb",
|
||||
"text": "Angular uses NgZone/Zone.js to know when to trigger UI update (change detection) when our app data state changes. It brilliantly utilized the events emitted by Zone.js when async operations are performed to detect when to run a change detection cycle."
|
||||
},
|
||||
{
|
||||
"title": "Learn how Angular Elements transmits Component’s @Outputs outside Angular",
|
||||
"link": "https://blog.angularindepth.com/how-angular-elements-uses-custom-events-mechanism-to-transmit-components-outputs-outside-angular-7b469386f6e2",
|
||||
"text": "In our last article we described how Angular Elements works under the hood. We identified that Angular Elements is a bridge to connect Custom Elements to Angular Components."
|
||||
},
|
||||
{
|
||||
"title": "Angular CDK Tables",
|
||||
"link": "https://blog.angularindepth.com/angular-cdk-tables-1537774d7c99",
|
||||
"text": "In this article: Angular CDK Tables, Bootstrap 4 with Angular CDK Tables, Client Side searching/paging/sorting."
|
||||
},
|
||||
{
|
||||
"title": "One-way template expression binding mechanism in Angular",
|
||||
"link": "https://blog.angularindepth.com/becoming-an-angular-environmentalist-45a48f7c20d8",
|
||||
"text": "Angular is the most popular and widely used JavaScript framework after React.js. It abstracts many complexities away from developers to enable them to develop apps with ease."
|
||||
},
|
||||
{
|
||||
"title": "The Extensive Guide to Creating Streams in RxJS",
|
||||
"link": "https://blog.angularindepth.com/how-to-unit-test-angular-components-with-fake-ngrx-teststore-f0500cc5fc26",
|
||||
"text": "For most developers the first contact with RxJS is established by libraries, like Angular. Some functions return streams and to make use of them the focus naturally is on operators."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: Avoiding Unbound Methods",
|
||||
"link": "https://blog.angularindepth.com/rxjs-avoiding-unbound-methods-fcf2648a805",
|
||||
"text": "When unbound methods are passed to RxJS, they will be invoked with an unexpected context for this. If the method implementations don’t use this, they will behave as you would expect."
|
||||
},
|
||||
{
|
||||
"title": "Angular Elements: how does this magic work under the hood?",
|
||||
"link": "https://blog.angularindepth.com/angular-elements-how-does-this-magic-work-under-the-hood-3684a0b2be95",
|
||||
"text": "The Angular Elements project is generating lots of hype in the community right now, and rightly so! Angular Elements provides a wealth of awesome features out of the box."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: Testing with Fake Time",
|
||||
"link": "https://blog.angularindepth.com/rxjs-testing-with-fake-time-94114271eed2",
|
||||
"text": "Angular, Jasmine, Jest and Sinon.JS all provide APIs for running tests with fake time. Their APIs differ, but they are broadly similar. Running tests with fake time avoids having to wait for actual time to elapse and it also makes the tests much simpler, as they run synchronously. So what does this have to do with RxJS?"
|
||||
},
|
||||
{
|
||||
"title": "How do CDK Portals work?",
|
||||
"link": "https://blog.angularindepth.com/how-do-cdk-portals-work-7c097c14a494",
|
||||
"text": "In the last article we were exploring how to leverage the Angular Material CDK portals for placing some piece of template from a component to some other location within our app. CDK portals make this a no-brainer. Wondering how they work? In this article we dive deeper to uncover how its internals work and how we could simply implement it by ourselves."
|
||||
},
|
||||
{
|
||||
"title": "How I test my NgRx selectors",
|
||||
"link": "https://blog.angularindepth.com/how-i-test-my-ngrx-selectors-c50b1dc556bc",
|
||||
"text": "In this post I’m going to show you how I test my selectors by putting the selectors from a previous post “Clean NgRx reducers using Immer”, where we created a small shopping cart application, under test. In the application there is a collection of products (the catalog) and the cart items, together they form the state of the application."
|
||||
},
|
||||
{
|
||||
"title": "Angular 5 or Angular 6? Yes please!",
|
||||
"link": "https://blog.angularindepth.com/angular-5-or-angular-6-yes-please-d71b08b5e59b",
|
||||
"text": "And, I’m glad you asked: YES, you should move all your projects to Angular 6 now or sooner! But … and it is a big but. Like me, you may be in the situation where you are working on multiple projects and many of them are going to be stuck in Angular 5 for a while. So, you need to support a development environment where you can work on and even create new Angular applications in both Angular 5 and Angular 6."
|
||||
},
|
||||
{
|
||||
"title": "Total Guide To Dynamic Angular Animations That Can Be Customized At Runtime",
|
||||
"link": "https://blog.angularindepth.com/total-guide-to-dynamic-angular-animations-that-can-be-toggled-at-runtime-be5bb6778a0a",
|
||||
"text": "From route transitions to small details like feedback when clicking on a button or displaying a tooltip, animations give your project that nice sleek look. Well crafted animations communicate that you or your organization care enough to put effort into details and create best possible experience for your users."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: How to Observe an Object",
|
||||
"link": "https://blog.angularindepth.com/rxjs-how-to-observe-an-object-20c47cf51571",
|
||||
"text": "A while ago, John Lindquist published a package named rx-handler. With it, you can create event handler functions that are also observables. When it was published, I noticed a few queries about whether something similar could be done with Angular’s Input properties — so that they, too, could be treated as observables."
|
||||
},
|
||||
{
|
||||
"title": "A curious case of the @Host decorator and Element Injectors in Angular",
|
||||
"link": "https://blog.angularindepth.com/a-curios-case-of-the-host-decorator-and-element-injectors-in-angular-582562abcf0a",
|
||||
"text": "As you know, Angular’s dependency injection mechanism includes a bunch of decorators like @Optional and @Self which impact the way dependencies are resolved. And while most of them are pretty straightforward and self-explanatory, the @Host decorator has puzzled me for a long time."
|
||||
},
|
||||
{
|
||||
"title": "Simple state mutations in NGXS with Immer",
|
||||
"link": "https://blog.angularindepth.com/simple-state-mutations-in-ngxs-with-immer-48b908874a5e",
|
||||
"text": "NGXS is a state management pattern + library for Angular. Just like Redux and NgRx it’s modeled after the CQRS pattern. NGXS uses TypeScript functionality to its fullest extent and because of this it may feel more Angular-y."
|
||||
},
|
||||
{
|
||||
"title": "Upgrading a project without CLI to Angular 6",
|
||||
"link": "https://blog.angularindepth.com/upgrading-a-project-without-cli-to-angular-6-b07b105adc02",
|
||||
"text": "In the following article, I’m going to describe the challenging process of updating an Angular application with custom Webpack configuration, which our team had to pull through 3 weeks ago. I guess our experience would be useful for those who use Angular with acustom Webpack config. For others, it is an illustration of where modern front-end could lead us and how to live with that."
|
||||
},
|
||||
{
|
||||
"title": "Power of RxJS when using exponential backoff",
|
||||
"link": "https://blog.angularindepth.com/power-of-rxjs-when-using-exponential-backoff-a4b8bde276b0",
|
||||
"text": "Most of the modern-day Angular web apps make Ajax requests to the servers. These requests involve multiple network components (such as routers, switches, etc) as well as servers’ state and everything has to go just right for them to succeed. However, sometimes it doesn’t."
|
||||
},
|
||||
{
|
||||
"title": "Clean NgRx reducers using Immer",
|
||||
"link": "https://blog.angularindepth.com/clean-ngrx-reducers-using-immer-7fe4a0d43508",
|
||||
"text": "This weeks post is inspired by another great This Dot Media event and the topic this time was state management. There was a small segment about Immer which I found interesting (video is linked at the bottom of this post), so I decided to give it a shot with NgRx."
|
||||
},
|
||||
{
|
||||
"title": "The Angular Library Series - Creating a Library with the Angular CLI",
|
||||
"link": "https://blog.angularindepth.com/creating-a-library-in-angular-6-87799552e7e5",
|
||||
"text": "Angular 6 was just released. Many of the improvements were to the Angular CLI. The one I have really been looking forward to is the integration of the Angular CLI with ng-packagr to generate and build Angular libraries. ng-packagr is a fantastic tool created by David Herges that transpiles your library to the Angular Package Format."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: Avoiding takeUntil Leaks",
|
||||
"link": "https://blog.angularindepth.com/rxjs-avoiding-takeuntil-leaks-fb5182d047ef",
|
||||
"text": "Using the takeUntil operator to automatically unsubscribe from an observable is a mechanism that’s explained in Ben Lesh’s Don’t Unsubscribe article. It’s also the basis of a generally-accepted pattern for unsubscribing upon an Angular component’s destruction."
|
||||
},
|
||||
{
|
||||
"title": "Use <ng-template>",
|
||||
"link": "https://blog.angularindepth.com/use-ng-template-c72852c37fba",
|
||||
"text": "Render Props have been making waves in the React community recently, but the corresponding pattern in the Angular world hasn’t been getting nearly as much press. I’ve written before that TemplateRefs are Angular’s Render Props and I hope to give you a good simple example of that here."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: Improving the Static pipe Function",
|
||||
"link": "https://blog.angularindepth.com/rxjs-improving-the-static-pipe-function-81146fbb14b6",
|
||||
"text": "My previous article looked at using the static pipe function to compose reusable combinations of operators. Most of the time, the pipe function’s TypeScript overload signatures will infer the desired type for the returned function. However, sometimes it’s desirable to have a generic type inferred and the current overload signatures will not do that."
|
||||
},
|
||||
{
|
||||
"title": "Angular Ivy change detection execution: are you prepared?",
|
||||
"link": "https://blog.angularindepth.com/angular-ivy-change-detection-execution-are-you-prepared-ab68d4231f2c",
|
||||
"text": "While new Ivy renderer is not feature completely yet, many people wonder how it will work and what changes it prepares for us. In this article I am going to visualize Ivy change detection mechanism, show some things I am really excited about and also build simple app based on instructions, similar to angular Ivy instructions, from scratch."
|
||||
},
|
||||
{
|
||||
"title": "Ivy engine in Angular: first in-depth look at compilation, runtime and change detection",
|
||||
"link": "https://blog.angularindepth.com/ivy-engine-in-angular-first-in-depth-look-at-compilation-runtime-and-change-detection-876751edd9fd",
|
||||
"text": "I usually finish my talks with the philosophical phrase that nothing stays the same. And as you probably know it’s more then true with Angular. The current rendering engine is being rewritten with the new much enhanced version called Ivy. The current status of Ivy can be tracked here."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: Combining Operators",
|
||||
"link": "https://blog.angularindepth.com/rxjs-combining-operators-397bad0628d0",
|
||||
"text": "In version 5.5, pipeable operators were added to RxJS. And in version 6, their non-pipeable namesakes were removed. Pipeable operators have numerous advantages. The most obvious is that they are easier to write. A less obvious advantage is that they can be composed into reusable combinations."
|
||||
},
|
||||
{
|
||||
"title": "A modern solution to lazy loading images using Intersection Observer",
|
||||
"link": "https://blog.angularindepth.com/a-modern-solution-to-lazy-loading-using-intersection-observer-9280c149bbc",
|
||||
"text": "Performance of a web application has become a key factor in deciding conversion rates for e-commerce websites. The faster a page loads, the better the conversion rate. According to the recent mobile page speed benchmarks released by Google, the bounce probability increases as page load time increases."
|
||||
},
|
||||
{
|
||||
"title": "Working with DOM in Angular: unexpected consequences and optimization techniques",
|
||||
"link": "https://blog.angularindepth.com/working-with-dom-in-angular-unexpected-consequences-and-optimization-techniques-682ac09f6866",
|
||||
"text": "I recently gave a talk on advanced DOM manipulations in Angular in a form of a workshop at NgConf. I went from the basics like using template references and DOM queries to access DOM elements to using a view container to render templates and components dynamically."
|
||||
},
|
||||
{
|
||||
"title": "The benefits of application state normalization in Angular",
|
||||
"link": "https://blog.angularindepth.com/the-benefits-of-application-state-normalization-in-angular-f93392ca9f44",
|
||||
"text": "Imagine we have a recursive data structure in the store, let us say, information about a product’s category in an e-commerce application. Category is the classification of which type of product it is. For example, Mobile Phones category can have subcategories such as Google, Apple, Samsung and so on and each subcategory can in turn have further subcategories..."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: TSLint Rules for Version 6",
|
||||
"link": "https://blog.angularindepth.com/rxjs-tslint-rules-for-version-6-d10e2482292d",
|
||||
"text": "Earlier this week, RxJS version 6 was released and, with its release, managing RxJS imports has become much, much easier. Last year, I wrote a bunch of TSLint rules for managing RxJS imports. They’re distributed in the rxjs-tslint-rules package."
|
||||
},
|
||||
{
|
||||
"title": "Angular Universal & Firebase functions: The missing guide",
|
||||
"link": "https://blog.angularindepth.com/angular-5-universal-firebase-4c85a7d00862",
|
||||
"text": "Lucky you, I’ve written this simplified guide to configure Angular 5 Universal in your Angular project. Moreover, I’m gonna give you also a bonus track on how to run Universal in a serverless environment like Firebase Cloud Functions."
|
||||
},
|
||||
{
|
||||
"title": "Angular and Internet Explorer",
|
||||
"link": "https://blog.angularindepth.com/angular-and-internet-explorer-5e59bb6fb4e9",
|
||||
"text": "You installed the Angular CLI and used it to generate your new application. But, when you try to view it in Internet Explorer (IE), you see nothing. Now what? The bad news: Angular CLI applications require a few more steps in order to support Internet Explorer."
|
||||
},
|
||||
{
|
||||
"title": "Gestures in an Angular Application",
|
||||
"link": "https://blog.angularindepth.com/gestures-in-an-angular-application-dde71804c0d0",
|
||||
"text": "In this post I will attempt to explain how to use hammerjs gesture recognizers provided by the @angular/platform-browser package. I’ll be referencing @angular/platform-browser@5.2.0 within my code samples, but there are some changes coming to 6.0.0 that will be discussed later."
|
||||
},
|
||||
{
|
||||
"title": "Deploy an Angular Application to IIS",
|
||||
"link": "https://blog.angularindepth.com/deploy-an-angular-application-to-iis-60a0897742e7",
|
||||
"text": "The Angular Router is a fantastic module for Single Page Apps. However, to deploy it in a Production scenario you will typically need to do some configuration to make it work. This article details the steps necessary to deploy an Angular Router application anywhere on Internet Information Services (IIS)."
|
||||
},
|
||||
{
|
||||
"title": "Super Charging an Angular CLI App",
|
||||
"link": "https://blog.angularindepth.com/super-charging-an-angular-cli-app-fc496a6c100",
|
||||
"text": "A standard Angular CLI application comes with a terrific set of of tooling to prepare you to get developing quickly. However, there’s a few additional steps you should take to really prepare your project for success. In this article I’ll break down all the additional features you can add to your project without ejecting (exporting the WebPack Config)."
|
||||
},
|
||||
{
|
||||
"title": "What you always wanted to know about Angular Dependency Injection tree",
|
||||
"link": "https://blog.angularindepth.com/angular-dependency-injection-and-tree-shakeable-tokens-4588a8f70d5d",
|
||||
"text": "If you didn’t dive deep into angular dependency injection mechanism, your mental model should be that in angular application we have some root injector with all merged providers, every component has its own injector and lazy loaded module introduces new injector."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: When to Use switchMap",
|
||||
"link": "https://blog.angularindepth.com/when-to-use-switchmap-dfe84ac5a1ff",
|
||||
"text": "In a response to RxJS: Avoiding switchMap-Related Bugs, Martin Hochel mentioned a classic use case for switchMap. For the use case to which he referred, switchMap is not only valid; it’s optimal. And it’s worth looking at why."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: Understanding Expand",
|
||||
"link": "https://blog.angularindepth.com/rxjs-understanding-expand-a5f8b41a3602",
|
||||
"text": "RxJS has a lot of operators. Lots and lots of them. It takes time to learn what they all do and how they can be used. Some operators are straightforward; others, less so. One operator that developers often find confusing is expand."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: Composing Subscriptions",
|
||||
"link": "https://blog.angularindepth.com/rxjs-composing-subscriptions-b53ab22f1fd5",
|
||||
"text": "RxJS code involves making subscriptions to observables. Lots of subscriptions. If each subscription is assigned to its own variable or property, the situation can be difficult to manage."
|
||||
},
|
||||
{
|
||||
"title": "Handle Template Reference Variables with Directives",
|
||||
"link": "https://blog.angularindepth.com/handle-template-reference-variables-with-directives-223081bc70c2",
|
||||
"text": "I’ve been using template reference variables pretty liberally in my examples so far, and it’s high time I dive in a bit into how to use them to reference specific directives."
|
||||
},
|
||||
{
|
||||
"title": "Avoid Namespace Clashes with Directives",
|
||||
"link": "https://blog.angularindepth.com/avoid-namespace-clashes-with-directives-1f00d62de445",
|
||||
"text": "Not only can the selector for a directive clash with another directive, but Inputs and Outputs for those directives can clash with each other. When they have the same name, Angular doesn’t complain — it just applies the logic to both directives. In some cases, this is exactly what we want. However, sometimes it can cause unexpected behavior."
|
||||
},
|
||||
{
|
||||
"title": "Dynamically Loading Components with Angular CLI",
|
||||
"link": "https://blog.angularindepth.com/dynamically-loading-components-with-angular-cli-92a3c69bcd28",
|
||||
"text": "When moving from a multi-page application to a SPA, one of the problems that presents itself is the payload size upon initial load. By default, in an Angular application everything is bundled into one payload, which means as the application grows, so does the time that it takes to load."
|
||||
},
|
||||
{
|
||||
"title": "Insider’s guide into interceptors and HttpClient mechanics in Angular",
|
||||
"link": "https://blog.angularindepth.com/insiders-guide-into-interceptors-and-httpclient-mechanics-in-angular-103fbdb397bf",
|
||||
"text": "You probably know that Angular introduced a new powerful HTTP client in version 4.3. One of its major features was request interception — the ability to declare interceptors which sit in between your application and the backend."
|
||||
},
|
||||
{
|
||||
"title": "Enhance Components with Directives",
|
||||
"link": "https://blog.angularindepth.com/enhance-components-with-directives-58f16c4ca1f",
|
||||
"text": "One element of part 4 of Kent C. Dodds’ series that I didn’t touch on in the previous article is the fact that the withToggle higher order component is able to pull common logic out of the <toggle-on>, <toggle-off>, and <toggle-button> components. There wasn’t very much logic happening in those components in the last article, but what if there were?"
|
||||
},
|
||||
{
|
||||
"title": "Communicate Between Components Using Dependency Injection",
|
||||
"link": "https://blog.angularindepth.com/communicate-between-components-using-dependency-injection-d7280567faa7",
|
||||
"text": "There is another problem we’ve found with our <toggle> component. We can’t have more than one <toggle-on> or <toggle-button> component in the same <toggle> and a <toggle-on> that is inside of another custom component won’t be picked up by the @ContentChild decorator."
|
||||
},
|
||||
{
|
||||
"title": "Build a Toggle Component",
|
||||
"link": "https://blog.angularindepth.com/build-a-toggle-component-6e8f44889c2c",
|
||||
"text": "Just like in Kent C. Dodds’ Advanced React Component Patterns, we will use a relatively simple <toggle> component to illustrate these patterns. The <toggle> component is responsible for managing a singleboolean property: on."
|
||||
},
|
||||
{
|
||||
"title": "Introducing Advanced Angular Component Patterns",
|
||||
"link": "https://blog.angularindepth.com/introducing-advanced-angular-component-patterns-13e102e6bbfc",
|
||||
"text": "This series of posts is my small attempt to broaden my own view by providing a translation of Kent C. Dodds’ Advanced React Patterns in Angular. My goal is to foster learning and sharing rather than criticism."
|
||||
},
|
||||
{
|
||||
"title": "Top 10 Angular articles in 2017 from Angular-In-Depth you really want to read",
|
||||
"link": "https://blog.angularindepth.com/top-10-angular-articles-in-2017-from-angularindepth-you-really-want-to-read-153ae6e497d4",
|
||||
"text": "Almost one year ago I started Angular-In-Depth medium publication with the goal to become the largest and most technical Angular publication on medium. I was lucky to get on board very talented and knowledgeable guys Uri Shaked, Nicholas Jamieson and Chaz Gatian."
|
||||
},
|
||||
{
|
||||
"title": "Practical RxJS In The Wild 🦁— Requests with concatMap() vs mergeMap() vs forkJoin() 🥊",
|
||||
"link": "https://blog.angularindepth.com/practical-rxjs-in-the-wild-requests-with-concatmap-vs-mergemap-vs-forkjoin-11e5b2efe293",
|
||||
"text": "I would like to share with you experience acquired by working on a yet another Hacker News client (code name HAKAFAKA 😂 still in alpha). I have been on the road for couple months now and realized that a small coding project wouldn’t hurt."
|
||||
},
|
||||
{
|
||||
"title": "He who thinks change detection is depth-first and he who thinks it’s breadth-first are both usually right",
|
||||
"link": "https://blog.angularindepth.com/he-who-thinks-change-detection-is-depth-first-and-he-who-thinks-its-breadth-first-are-both-usually-8b6bf24a63e6",
|
||||
"text": "I was once asked if change detection in Angular is depth or breadth first. This basically means whether Angular first checks siblings of the current component (breadth-first) or its children (depth-first). I hadn’t given any prior thought to this question so I just went with my gut and the knowledge of internals."
|
||||
},
|
||||
{
|
||||
"title": "Learn to combine RxJs sequences with super intuitive interactive diagrams",
|
||||
"link": "https://blog.angularindepth.com/learn-to-combine-rxjs-sequences-with-super-intuitive-interactive-diagrams-20fce8e6511",
|
||||
"text": "When working on a sufficiently complex application you usually have data coming from more than one data source. It can be some multiple external data points like Firebase or several UI widgets interacting with a user. Sequence composition is a technique that enables you to create complex queries."
|
||||
},
|
||||
{
|
||||
"title": "React Call Return in Angular",
|
||||
"link": "https://blog.angularindepth.com/react-call-return-in-angular-32a1c9751d6",
|
||||
"text": "This article continues in the theme of taking React articles and reimagining them in Angular. See TemplateRefs are Angular’s Render Props and Content Directives Are Angular’s Prop Getters."
|
||||
},
|
||||
{
|
||||
"title": "Do you really know what unidirectional data flow means in Angular",
|
||||
"link": "https://blog.angularindepth.com/do-you-really-know-what-unidirectional-data-flow-means-in-angular-a6f55cefdc63",
|
||||
"text": "Most architectural patterns are not easy to grasp especially when the information that describes them is scarce. One of such patterns in Angular is unidirectional data flow. There’s no clear explanation of what that means in the official documentation and it’s only briefly mentioned in the expression guidelines and template statements sections."
|
||||
},
|
||||
{
|
||||
"title": "How to Reduce Action Boilerplate",
|
||||
"link": "https://blog.angularindepth.com/how-to-reduce-action-boilerplate-90dc3d389e2b",
|
||||
"text": "I use Redux for my application development and, to take advantage of RxJS, I use NgRx in Angular projects and redux-observable in React projects. I also use TypeScript."
|
||||
},
|
||||
{
|
||||
"title": "These 5 articles will make you an Angular Change Detection expert",
|
||||
"link": "https://blog.angularindepth.com/these-5-articles-will-make-you-an-angular-change-detection-expert-ed530d28930",
|
||||
"text": "In the last 8 months I’ve spent most of my free time reverse-engineering Angular. The topic that fascinated me the most was change detection. I’d argue that it’s the most important part of the framework since it’s responsible for the “visible” job like DOM updates, input bindings and query list updates."
|
||||
},
|
||||
{
|
||||
"title": "Angular CDK Portals",
|
||||
"link": "https://blog.angularindepth.com/angular-cdk-portals-b02f66dd020c",
|
||||
"text": "The @angular/cdk contains a concept called portals. In this post I’ll attempt to explain the concepts of a Portal, and when they should be applied. The example code in this post is referencing @angular/cdk@2.0.0-beta.12."
|
||||
},
|
||||
{
|
||||
"title": "Content Directives Are Angular’s Prop Getters",
|
||||
"link": "https://blog.angularindepth.com/content-directives-are-angulars-prop-getters-360fdae60576",
|
||||
"text": "Kent C. Dodds wrote a piece about using prop getters in React. Along with render props (see TemplateRefs Are Angular’s Render Props), prop getters allow component library authors to give users as much control of the rendering as possible — the component only needs to do its job."
|
||||
},
|
||||
{
|
||||
"title": "Using TransferState API in an Angular v5 Universal App",
|
||||
"link": "https://blog.angularindepth.com/using-transferstate-api-in-an-angular-5-universal-app-130f3ada9e5b",
|
||||
"text": "You can get a more up-to-date version at https://leanpub.com/angular-universal. Let’s illustrate this article with a concrete example. We have a weather app, displaying a list of cities in its sidebar. When you click on a city name, the app displays the current weather in this city."
|
||||
},
|
||||
{
|
||||
"title": "Do you still think that NgZone (zone.js) is required for change detection in Angular?",
|
||||
"link": "https://blog.angularindepth.com/do-you-still-think-that-ngzone-zone-js-is-required-for-change-detection-in-angular-16f7a575afef",
|
||||
"text": "Most articles I have seen strongly associate Zone(zone.js) and NgZone with change detection in Angular. And although they are definitely related, technically they are not part of one whole. Yes, Zone and NgZone is used to automatically trigger change detection as a result of async operations."
|
||||
},
|
||||
{
|
||||
"title": "As busy as a bee — lazy loading in the Angular CLI",
|
||||
"link": "https://blog.angularindepth.com/as-busy-as-a-bee-lazy-loading-in-the-angular-cli-d2812141637f",
|
||||
"text": "Angular has a programmatic API for lazy loading NgModule’s. In the Angular CLI, it has a direct dependency upon webpack’s underlying toolchain for chunk splitting and lazy loading. It’s thus (almost) impossible to use it outside of an ordinary router set-up. Custom lazy loading strategies need to use SystemJS."
|
||||
},
|
||||
{
|
||||
"title": "TemplateRefs are Angular’s Render Props",
|
||||
"link": "https://blog.angularindepth.com/templaterefs-are-angulars-render-props-a2b97cbcc362",
|
||||
"text": "As a developer that spends most of my time building Angular apps, I still love reading about what the React community is doing. We’re generally solving the same problems and innovation in one community can be leveraged in another."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: How to Use Lettable Operators with Promises",
|
||||
"link": "https://blog.angularindepth.com/rxjs-how-to-use-lettable-operators-and-promises-2e717313bf76",
|
||||
"text": "Converting observables to promises is an antipattern. Unless you are integrating observables with a promise-based API, there is no reason to convert an observable into a promise."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: Pipelining Lettable Operators",
|
||||
"link": "https://blog.angularindepth.com/rxjs-pipelining-lettable-operators-f92f6843d817",
|
||||
"text": "Earlier this week, a TC39 proposal for a pipeline operator moved to stage-1. If the proposal is eventually accepted and included in the ECMAScript standard — it has a long way to go — it will offer a new syntax for lettable operators."
|
||||
},
|
||||
{
|
||||
"title": "I reverse-engineered Zones (zone.js) and here is what I’ve found",
|
||||
"link": "https://blog.angularindepth.com/i-reverse-engineered-zones-zone-js-and-here-is-what-ive-found-1f48dc87659b",
|
||||
"text": "Zones is a new mechanism that helps developers work with multiple logically-connected async operations. Zones work by associating each async operation with a zone."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: Understanding Lettable Operators",
|
||||
"link": "https://blog.angularindepth.com/rxjs-understanding-lettable-operators-fe74dda186d3",
|
||||
"text": "Lettable operators offer a new way of composing observable chains and they have advantages for both application developers and library authors. Let’s look briefly at the existing composition mechanisms in RxJS and then look at lettable operators in more detail."
|
||||
},
|
||||
{
|
||||
"title": "The essential difference between Constructor and ngOnInit in Angular",
|
||||
"link": "https://blog.angularindepth.com/the-essential-difference-between-constructor-and-ngoninit-in-angular-c9930c209a42",
|
||||
"text": "One of the most popular Angular questions on stackoverflow is Difference between Constructor and ngOnInit with over 100k views. I gave my answer to this question there but also decided to expand on it in this article."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: How to Use refCount",
|
||||
"link": "https://blog.angularindepth.com/rxjs-how-to-use-refcount-73a0c6619a4e",
|
||||
"text": "My previous article — Understanding the publish and share Operators — looked only briefly at the refCount method. Let’s look at it more closely here."
|
||||
},
|
||||
{
|
||||
"title": "The essential difference between pure and impure pipes in Angular and why that matters",
|
||||
"link": "https://blog.angularindepth.com/the-essential-difference-between-pure-and-impure-pipes-and-why-that-matters-999818aa068",
|
||||
"text": "When writing a custom pipe in Angular you can specify whether you define a pure or an impure pipe. Angular has a pretty good documentation on pipes that you can find here. But as it often happens with documentation the clearly reasoning for division is missing."
|
||||
},
|
||||
{
|
||||
"title": "RxJS: Understanding the publish and share Operators",
|
||||
"link": "https://blog.angularindepth.com/rxjs-understanding-the-publish-and-share-operators-16ea2f446635",
|
||||
"text": "I’m often asked questions that relate to the publish operator: What’s the difference between publish and share? How do I import the refCount operator? When should I use an AsyncSubject? Let’s answer these questions — and more — by starting with the basics."
|
||||
},
|
||||
{
|
||||
"title": "If you think `ngDoCheck` means your component is being checked — read this article",
|
||||
"link": "https://blog.angularindepth.com/if-you-think-ngdocheck-means-your-component-is-being-checked-read-this-article-36ce63a3f3e5",
|
||||
"text": "There’s one question that comes up again and again on stackoverflow. The question is about ngDoCheck lifecycle hook that is triggered for a component that implements OnPush change detection strategy."
|
||||
}
|
||||
]
|
||||
BIN
symfonia/src/main/resources/static/assets/images/alan.png
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
symfonia/src/main/resources/static/assets/images/camera1.jpg
Normal file
|
After Width: | Height: | Size: 214 KiB |
BIN
symfonia/src/main/resources/static/assets/images/camera2.jpg
Normal file
|
After Width: | Height: | Size: 201 KiB |
BIN
symfonia/src/main/resources/static/assets/images/camera3.jpg
Normal file
|
After Width: | Height: | Size: 218 KiB |
BIN
symfonia/src/main/resources/static/assets/images/camera4.jpg
Normal file
|
After Width: | Height: | Size: 227 KiB |