Moved to Angular 6.0
---- Pending * Table width for the points column in incentive * Linting * keyboard navigation where it was used earlier * can remove the unused totals calculated serverside in productledger * spinner and loading bars * Activate Guard for Employee Function tabs * Progress for Fingerprint uploads * deleted reconcile and receipe features as they were not being used * focus the right control on component load
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import {DataSource} from '@angular/cdk/collections';
|
||||
import {Observable} from 'rxjs';
|
||||
import {Incentive} from '../journal/voucher';
|
||||
|
||||
|
||||
export class IncentiveDataSource extends DataSource<Incentive> {
|
||||
|
||||
constructor(private data: Observable<Incentive[]>) {
|
||||
super();
|
||||
}
|
||||
|
||||
connect(): Observable<Incentive[]> {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import {inject, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {IncentiveResolver} from './incentive-resolver.service';
|
||||
|
||||
describe('IncentiveResolver', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [IncentiveResolver]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([IncentiveResolver], (service: IncentiveResolver) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router';
|
||||
import {Observable} from 'rxjs/internal/Observable';
|
||||
import {Voucher} from '../journal/voucher';
|
||||
import {VoucherService} from '../journal/voucher.service';
|
||||
import {tap} from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class IncentiveResolver implements Resolve<Voucher> {
|
||||
|
||||
constructor(private ser: VoucherService) {
|
||||
}
|
||||
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Voucher> {
|
||||
const id = route.paramMap.get('id');
|
||||
if (id === null) {
|
||||
return this.ser.getOfType('Service Charge');
|
||||
} else {
|
||||
return this.ser.get(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import {IncentiveRoutingModule} from './incentive-routing.module';
|
||||
|
||||
describe('IncentiveRoutingModule', () => {
|
||||
let incentiveRoutingModule: IncentiveRoutingModule;
|
||||
|
||||
beforeEach(() => {
|
||||
incentiveRoutingModule = new IncentiveRoutingModule();
|
||||
});
|
||||
|
||||
it('should create an instance', () => {
|
||||
expect(incentiveRoutingModule).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {IncentiveResolver} from './incentive-resolver.service';
|
||||
import {AuthGuard} from '../auth/auth-guard.service';
|
||||
import {IncentiveComponent} from './incentive.component';
|
||||
|
||||
const incentiveRoutes: Routes = [
|
||||
{
|
||||
path: 'Incentive',
|
||||
component: IncentiveComponent,
|
||||
canActivate: [AuthGuard],
|
||||
data: {
|
||||
permission: 'Service Charge'
|
||||
},
|
||||
resolve: {
|
||||
voucher: IncentiveResolver
|
||||
},
|
||||
runGuardsAndResolvers: 'always'
|
||||
},
|
||||
{
|
||||
path: 'Incentive/:id',
|
||||
component: IncentiveComponent,
|
||||
canActivate: [AuthGuard],
|
||||
data: {
|
||||
permission: 'Service Charge'
|
||||
},
|
||||
resolve: {
|
||||
voucher: IncentiveResolver
|
||||
},
|
||||
runGuardsAndResolvers: 'always'
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule.forChild(incentiveRoutes)
|
||||
|
||||
],
|
||||
exports: [
|
||||
RouterModule
|
||||
],
|
||||
providers: [
|
||||
IncentiveResolver
|
||||
]
|
||||
})
|
||||
export class IncentiveRoutingModule {
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
.right {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.selected {
|
||||
background: #fff3cd
|
||||
}
|
||||
|
||||
.unposted {
|
||||
background: #f8d7da
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.img-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.img-container .overlay {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.img-container:hover > .overlay {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 60px;
|
||||
top: 0;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<mat-card>
|
||||
<mat-card-title-group>
|
||||
<mat-card-title>Incentives</mat-card-title>
|
||||
</mat-card-title-group>
|
||||
<mat-card-content>
|
||||
<form [formGroup]="form" fxLayout="column">
|
||||
<div fxLayout="row" fxLayout.lt-md="column" fxLayoutGap="20px" fxLayoutGap.lt-md="0px">
|
||||
<mat-form-field fxFlex>
|
||||
<input matInput [matDatepicker]="date" (focus)="date.open()" placeholder="Date" formControlName="date"
|
||||
autocomplete="off">
|
||||
<mat-datepicker-toggle matSuffix [for]="date"></mat-datepicker-toggle>
|
||||
<mat-datepicker #date></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div fxLayout="row" fxLayout.lt-md="column" fxLayoutGap="20px" fxLayoutGap.lt-md="0px">
|
||||
Total Service Charge: <strong>{{voucher.incentive | number:'1.2-2'}}</strong>
|
||||
Total Points: <strong>{{totalPoints() | number:'1.2-2'}}</strong>
|
||||
Point Value: <strong>{{pointValue() | number:'1.2-2'}}</strong>
|
||||
</div>
|
||||
<mat-table #table [dataSource]="dataSource" formArrayName="incentives">
|
||||
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{row.name}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Designation Column -->
|
||||
<ng-container matColumnDef="designation">
|
||||
<mat-header-cell *matHeaderCellDef>Designation</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{row.designation}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Department Column -->
|
||||
<ng-container matColumnDef="department">
|
||||
<mat-header-cell *matHeaderCellDef>Department</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{row.department}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- DaysWorked Column -->
|
||||
<ng-container matColumnDef="daysWorked">
|
||||
<mat-header-cell *matHeaderCellDef class="right">DaysWorked</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="right">{{row.daysWorked | number:'1.2-2'}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Points Column -->
|
||||
<ng-container matColumnDef="points">
|
||||
<mat-header-cell *matHeaderCellDef class="center" fxFlex>Service Points</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row; let i = index" class="center" [formGroupName]="i" fxFlex>
|
||||
<mat-form-field>
|
||||
<button matPrefix (click)="less(row, i)">
|
||||
<mat-icon>remove</mat-icon>
|
||||
</button>
|
||||
<input matInput formControlName="points" autocomplete="off">
|
||||
<button matSuffix color="warn" (click)="more(row, i)">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Amount Column -->
|
||||
<ng-container matColumnDef="amount">
|
||||
<mat-header-cell *matHeaderCellDef class="right">Amount</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="right">{{amount(row) | currency:'INR'}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
||||
</mat-table>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-raised-button color="primary" (click)="save()" [disabled]="!canSave()">
|
||||
{{(voucher.id) ? 'Update' : 'Save'}}
|
||||
</button>
|
||||
<button mat-raised-button (click)="post()" *ngIf="voucher.id"
|
||||
[disabled]="voucher.posted || auth.user.perms.indexOf('Post Vouchers') === -1">
|
||||
{{(voucher.posted) ? 'Posted' : 'Post'}}
|
||||
</button>
|
||||
<button mat-raised-button color="warn" (click)="confirmDelete()" *ngIf="voucher.id" [disabled]="!canSave()">
|
||||
Delete
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
<mat-card-subtitle *ngIf="voucher.id">
|
||||
Created on <strong>{{voucher.creationDate | localTime}}</strong> and
|
||||
Last Edited on <strong>{{voucher.lastEditDate | localTime}}</strong>
|
||||
by <strong>{{voucher.user.name}}</strong>. {{(voucher.poster) ? 'Posted by ' + voucher.poster : ''}}
|
||||
</mat-card-subtitle>
|
||||
</mat-card>
|
||||
@@ -0,0 +1,25 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {IncentiveComponent} from './incentive.component';
|
||||
|
||||
describe('IncentiveComponent', () => {
|
||||
let component: IncentiveComponent;
|
||||
let fixture: ComponentFixture<IncentiveComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [IncentiveComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(IncentiveComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,192 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {FormArray, FormBuilder, FormGroup} from '@angular/forms';
|
||||
import {MatDialog} from '@angular/material';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import {IncentiveDataSource} from './incentive-datasource';
|
||||
import {Account} from '../account/account';
|
||||
import {VoucherService} from '../journal/voucher.service';
|
||||
import {Incentive, Voucher} from '../journal/voucher';
|
||||
import * as moment from 'moment';
|
||||
import {AuthService} from '../auth/auth.service';
|
||||
import {ConfirmDialogComponent} from '../shared/confirm-dialog/confirm-dialog.component';
|
||||
import {ToasterService} from '../core/toaster.service';
|
||||
import {map} from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-incentive',
|
||||
templateUrl: './incentive.component.html',
|
||||
styleUrls: ['./incentive.component.css']
|
||||
})
|
||||
export class IncentiveComponent implements OnInit {
|
||||
public incentiveObservable = new BehaviorSubject<Incentive[]>([]);
|
||||
dataSource: IncentiveDataSource;
|
||||
form: FormGroup;
|
||||
voucher: Voucher;
|
||||
account: Account;
|
||||
accBal: any;
|
||||
|
||||
displayedColumns = ['name', 'designation', 'department', 'daysWorked', 'points', 'amount'];
|
||||
|
||||
accounts: Observable<Account[]>;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private fb: FormBuilder,
|
||||
private dialog: MatDialog,
|
||||
private toaster: ToasterService,
|
||||
private auth: AuthService,
|
||||
private ser: VoucherService
|
||||
) {
|
||||
this.createForm();
|
||||
this.listenToDateChange();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.route.data
|
||||
.subscribe((data: { voucher: Voucher }) => {
|
||||
this.voucher = data.voucher;
|
||||
this.form.get('date').setValue(moment(this.voucher.date, 'DD-MMM-YYYY').toDate());
|
||||
this.form.setControl('incentives', this.fb.array(
|
||||
this.voucher.incentives.map(
|
||||
x => this.fb.group({
|
||||
points: x.points
|
||||
})
|
||||
)
|
||||
));
|
||||
this.dataSource = new IncentiveDataSource(this.incentiveObservable);
|
||||
this.incentiveObservable.next(this.voucher.incentives);
|
||||
});
|
||||
}
|
||||
|
||||
createForm() {
|
||||
this.form = this.fb.group({
|
||||
date: '',
|
||||
incentives: this.fb.array([])
|
||||
});
|
||||
}
|
||||
|
||||
listenToDateChange(): void {
|
||||
this.form.get('date').valueChanges.pipe(
|
||||
map(x => moment(x).format('DD-MMM-YYYY'))
|
||||
).subscribe(x => {
|
||||
if (x !== this.voucher.date && !this.voucher.id) {
|
||||
return this.ser.getIncentive(x)
|
||||
.subscribe((voucher: Voucher) => {
|
||||
this.voucher = voucher;
|
||||
this.form.get('date').setValue(moment(this.voucher.date, 'DD-MMM-YYYY').toDate());
|
||||
this.form.setControl('incentives', this.fb.array(
|
||||
this.voucher.incentives.map(
|
||||
i => this.fb.group({
|
||||
points: i.points
|
||||
})
|
||||
)
|
||||
));
|
||||
this.dataSource = new IncentiveDataSource(this.incentiveObservable);
|
||||
this.incentiveObservable.next(this.voucher.incentives);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
totalPoints() {
|
||||
return this.voucher.incentives.map((item) => {
|
||||
return item.daysWorked * item.points;
|
||||
}).reduce((sum, item) => {
|
||||
return sum + item;
|
||||
});
|
||||
}
|
||||
|
||||
pointValue() {
|
||||
return Math.round(this.voucher.incentive * 100 / this.totalPoints()) / 100;
|
||||
}
|
||||
|
||||
less(row: Incentive, i: number) {
|
||||
if (row.points >= 1) {
|
||||
row.points -= 1;
|
||||
this.form.get('incentives').get('' + i).setValue({'points': '' + row.points});
|
||||
}
|
||||
}
|
||||
|
||||
more(row: Incentive, i: number) {
|
||||
row.points += 1;
|
||||
this.form.get('incentives').get('' + i).setValue({'points': '' + row.points});
|
||||
}
|
||||
|
||||
amount(row) {
|
||||
return row.points * row.daysWorked * this.pointValue();
|
||||
}
|
||||
|
||||
canSave() {
|
||||
if (!this.voucher.id) {
|
||||
return true;
|
||||
} else if (this.voucher.posted && this.auth.user.perms.indexOf('Edit Posted Vouchers') !== -1) {
|
||||
return true;
|
||||
} else {
|
||||
return this.voucher.user.id === this.auth.user.id || this.auth.user.perms.indexOf('Edit Other User\'s Vouchers') !== -1;
|
||||
}
|
||||
}
|
||||
|
||||
post() {
|
||||
this.ser.post(this.voucher.id)
|
||||
.subscribe(
|
||||
(result) => {
|
||||
this.toaster.show('Success', 'Voucher Posted');
|
||||
},
|
||||
(error) => {
|
||||
this.toaster.show('Danger', error.error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
save() {
|
||||
this.ser.save(this.getVoucher())
|
||||
.subscribe(
|
||||
(result) => {
|
||||
this.toaster.show('Success', '');
|
||||
this.router.navigate(['/Incentive', result.id]);
|
||||
},
|
||||
(error) => {
|
||||
this.toaster.show('Danger', error.error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getVoucher(): Voucher {
|
||||
const formModel = this.form.value;
|
||||
this.voucher.date = moment(formModel.date).format('DD-MMM-YYYY');
|
||||
const array = this.form.get('attendances') as FormArray;
|
||||
this.voucher.incentives.forEach((item, index) => {
|
||||
item.points = array.controls[index].value.points;
|
||||
});
|
||||
this.voucher.narration = formModel.narration;
|
||||
return this.voucher;
|
||||
}
|
||||
|
||||
delete() {
|
||||
this.ser.delete(this.voucher.id)
|
||||
.subscribe(
|
||||
(result) => {
|
||||
this.toaster.show('Success', '');
|
||||
this.router.navigate(['/Incentive'], {replaceUrl: true});
|
||||
},
|
||||
(error) => {
|
||||
this.toaster.show('Danger', error.error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
confirmDelete(): void {
|
||||
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
||||
width: '250px',
|
||||
data: {title: 'Delete Voucher?', content: 'Are you sure? This cannot be undone.'}
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((result: boolean) => {
|
||||
if (result) {
|
||||
this.delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import {IncentiveModule} from './incentive.module';
|
||||
|
||||
describe('IncentiveModule', () => {
|
||||
let incentiveModule: IncentiveModule;
|
||||
|
||||
beforeEach(() => {
|
||||
incentiveModule = new IncentiveModule();
|
||||
});
|
||||
|
||||
it('should create an instance', () => {
|
||||
expect(incentiveModule).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {
|
||||
DateAdapter,
|
||||
MAT_DATE_FORMATS,
|
||||
MAT_DATE_LOCALE,
|
||||
MatAutocompleteModule,
|
||||
MatButtonModule,
|
||||
MatCardModule,
|
||||
MatCheckboxModule,
|
||||
MatDatepickerModule,
|
||||
MatDialogModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatNativeDateModule,
|
||||
MatPaginatorModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatSelectModule,
|
||||
MatSortModule,
|
||||
MatTableModule
|
||||
} from '@angular/material';
|
||||
import {SharedModule} from '../shared/shared.module';
|
||||
import {ReactiveFormsModule} from '@angular/forms';
|
||||
import {CdkTableModule} from '@angular/cdk/table';
|
||||
import {IncentiveRoutingModule} from './incentive-routing.module';
|
||||
import {IncentiveComponent} from './incentive.component';
|
||||
import {MomentDateAdapter} from '@angular/material-moment-adapter';
|
||||
import {A11yModule} from '@angular/cdk/a11y';
|
||||
import {FlexLayoutModule, FlexModule} from '@angular/flex-layout';
|
||||
|
||||
export const MY_FORMATS = {
|
||||
parse: {
|
||||
dateInput: 'DD-MMM-YYYY',
|
||||
},
|
||||
display: {
|
||||
dateInput: 'DD-MMM-YYYY',
|
||||
monthYearLabel: 'MMM YYYY',
|
||||
dateA11yLabel: 'DD-MMM-YYYY',
|
||||
monthYearA11yLabel: 'MMM YYYY',
|
||||
},
|
||||
};
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
A11yModule,
|
||||
CommonModule,
|
||||
CdkTableModule,
|
||||
FlexModule,
|
||||
FlexLayoutModule,
|
||||
MatAutocompleteModule,
|
||||
MatButtonModule,
|
||||
MatCardModule,
|
||||
MatCheckboxModule,
|
||||
MatDatepickerModule,
|
||||
MatDialogModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatNativeDateModule,
|
||||
MatPaginatorModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatSelectModule,
|
||||
MatSortModule,
|
||||
MatTableModule,
|
||||
ReactiveFormsModule,
|
||||
SharedModule,
|
||||
IncentiveRoutingModule
|
||||
],
|
||||
declarations: [
|
||||
IncentiveComponent
|
||||
],
|
||||
providers: [
|
||||
{provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE]},
|
||||
{provide: MAT_DATE_FORMATS, useValue: MY_FORMATS},
|
||||
]
|
||||
})
|
||||
export class IncentiveModule {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import {inject, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {VoucherService} from './incentive.service';
|
||||
|
||||
describe('IncentiveService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [VoucherService]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([VoucherService], (service: VoucherService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
Reference in New Issue
Block a user