diff --git a/overlord/.eslintrc.json b/overlord/.eslintrc.json deleted file mode 100644 index c50a03c7..00000000 --- a/overlord/.eslintrc.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "root": true, - "ignorePatterns": [ - "projects/**/*" - ], - "overrides": [ - { - "files": [ - "*.ts" - ], - "parserOptions": { - "project": ["tsconfig.(app|spec).json"] - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@angular-eslint/recommended", - "plugin:@angular-eslint/template/process-inline-templates", - "prettier" - ], - "plugins": [ - "import", - "unused-imports", - "deprecation" - ], - "rules": { - "@angular-eslint/directive-selector": [ - "error", - { - "type": "attribute", - "prefix": "app", - "style": "camelCase" - } - ], - "@angular-eslint/component-selector": [ - "error", - { - "type": "element", - "prefix": "app", - "style": "kebab-case" - } - ], - "@typescript-eslint/lines-between-class-members": [ - "error", - "always", - { - "exceptAfterSingleLine": true - } - ], - "import/order": [ - "error", - { - "alphabetize": { - "order": "asc", - "caseInsensitive": true - }, - "newlines-between": "always" - } - ], - "@typescript-eslint/no-explicit-any": [ - "error" - ], - "unused-imports/no-unused-imports": "error", - "@typescript-eslint/no-empty-function": "off", - "deprecation/deprecation": "warn" - } - }, - { - "files": [ - "*.html" - ], - "extends": [ - "plugin:@angular-eslint/template/recommended" - ], - "rules": {} - } - ] -} diff --git a/overlord/angular.json b/overlord/angular.json index 1a8a9e7f..c85f42b2 100644 --- a/overlord/angular.json +++ b/overlord/angular.json @@ -24,16 +24,13 @@ "base": "../frontend" }, "index": "src/index.html", - "polyfills": [ - "zone.js" - ], "tsConfig": "tsconfig.app.json", "assets": [ "src/favicon.ico", "src/assets" ], "styles": [ - "@angular/material/prebuilt-themes/indigo-pink.css", + "@angular/material/prebuilt-themes/azure-blue.css", "src/styles.css" ], "scripts": [], @@ -88,10 +85,6 @@ "test": { "builder": "@angular-devkit/build-angular:karma", "options": { - "polyfills": [ - "zone.js", - "zone.js/testing" - ], "tsConfig": "tsconfig.spec.json", "inlineStyleLanguage": "scss", "assets": [ diff --git a/overlord/eslint.config.js b/overlord/eslint.config.js new file mode 100644 index 00000000..c3b61637 --- /dev/null +++ b/overlord/eslint.config.js @@ -0,0 +1,74 @@ +// @ts-check +const eslint = require("@eslint/js"); +const tseslint = require("typescript-eslint"); +const angular = require("angular-eslint"); +const eslintConfigPrettier = require("eslint-config-prettier"); +// const plugin_import = require("eslint-plugin-import"); +const unusedImports = require("eslint-plugin-unused-imports"); +// const deprecation = require("eslint-plugin-deprecation"); + +module.exports = tseslint.config( + { + files: ["**/*.ts"], + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommended, + ...tseslint.configs.stylistic, + ...angular.configs.tsRecommended, + eslintConfigPrettier + ], + processor: angular.processInlineTemplates, + plugins: { + unusedImports: unusedImports, + }, + rules: { + "@angular-eslint/directive-selector": [ + "error", + { + type: "attribute", + prefix: "app", + style: "camelCase", + }, + ], + "@angular-eslint/component-selector": [ + "error", + { + type: "element", + prefix: "app", + style: "kebab-case", + }, + ], + "@/lines-between-class-members": [ + "error", + "always", + { + "exceptAfterSingleLine": true + } + ], + // "import/order": [ + // "error", + // { + // "alphabetize": { + // "order": "asc", + // "caseInsensitive": true + // }, + // "newlines-between": "always" + // } + // ], + "@typescript-eslint/no-explicit-any": [ + "error" + ], + "unusedImports/no-unused-imports": "error", + "@typescript-eslint/no-empty-function": "off", + // "deprecation/deprecation": "warn" +}, + }, + { + files: ["**/*.html"], + extends: [ + ...angular.configs.templateRecommended, + // ...angular.configs.templateAccessibility, + ], + rules: {}, + } +); diff --git a/overlord/package.json b/overlord/package.json index a0b1c3f0..58bd8470 100644 --- a/overlord/package.json +++ b/overlord/package.json @@ -28,36 +28,24 @@ "@ngx-loading-bar/core": "^6.0.2", "@ngx-loading-bar/http-client": "^6.0.2", "@ngx-loading-bar/router": "^6.0.2", - "@types/mousetrap": "1.6.15", - "angular2-hotkeys": "^16.0.1", - "mathjs": "^12.4.2", + "mathjs": "^13.0.2", "moment": "^2.30.1", "rxjs": "~7.8.0", - "tslib": "^2.6.0", - "zone.js": "~0.14.4" + "tslib": "^2.6.0" }, "devDependencies": { "@angular-devkit/build-angular": "^18.0.2", - "@angular-eslint/builder": "^18.0.1", - "@angular-eslint/eslint-plugin": "^18.0.1", - "@angular-eslint/eslint-plugin-template": "^18.0.1", - "@angular-eslint/schematics": "^18.0.1", - "@angular-eslint/template-parser": "^18.0.1", "@angular/cli": "^18.0.2", "@angular/compiler-cli": "^18.0.1", "@angular/language-service": "^18.0.1", "@types/jasmine": "~5.1.4", - "@typescript-eslint/eslint-plugin": "^7.11.0", - "@typescript-eslint/parser": "^7.11.0", + "angular-eslint": "^18.0.1", "autoprefixer": "^10.4.19", - "eslint": "^8.57.0", + "eslint": "^9.6.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-deprecation": "^2.0.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-unused-imports": "^3.2.0", + "eslint-plugin-unused-imports": "^4.0.0", "husky": "^9.0.11", "jasmine-core": "~5.1.0", - "jasmine-spec-reporter": "7.0.0", "karma": "^6.4.2", "karma-chrome-launcher": "~3.2.0", "karma-coverage": "~2.2.1", @@ -68,7 +56,12 @@ "prettier": "^3.2.5", "standard-version": "^9.5.0", "tailwindcss": "^3.4.3", - "typescript": "~5.4.5" + "typescript": "~5.4.5", + "typescript-eslint": "8.0.0-alpha.54" + }, + "removedDevDepForEslint9": { + "eslint-plugin-deprecation": "^3.0.0", + "eslint-plugin-import": "^2.29.1" }, "husky": { "hooks": { diff --git a/overlord/src/app/account/account-detail/account-detail.component.ts b/overlord/src/app/account/account-detail/account-detail.component.ts index c727ee6f..7e9f7e53 100644 --- a/overlord/src/app/account/account-detail/account-detail.component.ts +++ b/overlord/src/app/account/account-detail/account-detail.component.ts @@ -22,7 +22,7 @@ import { Account } from '../../core/account'; import { AccountType } from '../../core/account-type'; import { AccountService } from '../../core/account.service'; import { CostCentre } from '../../core/cost-centre'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; @Component({ @@ -50,7 +50,7 @@ import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dial ], }) export class AccountDetailComponent implements OnInit, AfterViewInit { - @ViewChild('nameElement', { static: true }) nameElement?: ElementRef; + @ViewChild('nameElement', { static: true }) nameElement!: ElementRef; form: FormGroup<{ code: FormControl; name: FormControl; @@ -68,7 +68,7 @@ export class AccountDetailComponent implements OnInit, AfterViewInit { private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private toaster: ToasterService, + private snackBar: MatSnackBar, private ser: AccountService, ) { this.form = new FormGroup({ @@ -109,20 +109,18 @@ export class AccountDetailComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.nameElement) { - this.nameElement.nativeElement.focus(); - } + this.nameElement.nativeElement.focus(); }, 0); } save() { this.ser.saveOrUpdate(this.getItem()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/accounts'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -130,11 +128,11 @@ export class AccountDetailComponent implements OnInit, AfterViewInit { delete() { this.ser.delete(this.item.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/accounts'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/account/account-list/account-list.component.ts b/overlord/src/app/account/account-list/account-list.component.ts index 99782480..dbc91b13 100644 --- a/overlord/src/app/account/account-list/account-list.component.ts +++ b/overlord/src/app/account/account-list/account-list.component.ts @@ -63,9 +63,9 @@ import { AccountListDataSource } from './account-list-datasource'; ], }) export class AccountListComponent implements OnInit, AfterViewInit { - @ViewChild('filterElement', { static: true }) filterElement?: ElementRef; - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild('filterElement', { static: true }) filterElement!: ElementRef; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; dataSource: AccountListDataSource; filter: Observable; form: FormGroup<{ @@ -85,7 +85,7 @@ export class AccountListComponent implements OnInit, AfterViewInit { // Listen to Filter Change this.filter = this.form.controls.filter.valueChanges.pipe(debounceTime(150), distinctUntilChanged()); - this.dataSource = new AccountListDataSource(this.list, this.filter); + this.dataSource = new AccountListDataSource(this.list, this.filter, this.paginator, this.sort); } ngOnInit() { @@ -100,9 +100,7 @@ export class AccountListComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.filterElement) { - this.filterElement.nativeElement.focus(); - } + this.filterElement.nativeElement.focus(); }, 0); } } diff --git a/overlord/src/app/app.config.ts b/overlord/src/app/app.config.ts index 32b40bdf..0686137c 100644 --- a/overlord/src/app/app.config.ts +++ b/overlord/src/app/app.config.ts @@ -1,6 +1,11 @@ import { LayoutModule } from '@angular/cdk/layout'; import { provideHttpClient, withInterceptors } from '@angular/common/http'; -import { LOCALE_ID, importProvidersFrom, ApplicationConfig } from '@angular/core'; +import { + LOCALE_ID, + importProvidersFrom, + ApplicationConfig, + provideExperimentalZonelessChangeDetection, +} from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -13,7 +18,7 @@ import { MatMenuModule } from '@angular/material/menu'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSidenavModule } from '@angular/material/sidenav'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MAT_SNACK_BAR_DEFAULT_OPTIONS, MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { MatToolbarModule } from '@angular/material/toolbar'; @@ -21,7 +26,6 @@ import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { BrowserModule } from '@angular/platform-browser'; import { provideAnimations } from '@angular/platform-browser/animations'; import { provideRouter, withRouterConfig } from '@angular/router'; -import { HotkeyModule } from 'angular2-hotkeys'; import { dateFormat } from './app.environment'; import { routes } from './app.routes'; @@ -34,7 +38,6 @@ export const appConfig: ApplicationConfig = { providers: [ importProvidersFrom( BrowserModule, - HotkeyModule.forRoot(), LayoutModule, MatButtonModule, MatCardModule, @@ -64,5 +67,7 @@ export const appConfig: ApplicationConfig = { ), { provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] }, { provide: MAT_DATE_FORMATS, useValue: dateFormat }, + { provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, useValue: { duration: 3000 } }, + provideExperimentalZonelessChangeDetection(), ], }; diff --git a/overlord/src/app/app.environment.ts b/overlord/src/app/app.environment.ts index 63ff5658..b658b127 100644 --- a/overlord/src/app/app.environment.ts +++ b/overlord/src/app/app.environment.ts @@ -1,5 +1,4 @@ export const environment = { - // eslint-disable-next-line @typescript-eslint/naming-convention ACCESS_TOKEN_REFRESH_MINUTES: 10, // refresh token 10 minutes before expiry version: '13.0.0', title: 'HnG / TGB', diff --git a/overlord/src/app/attendance/attendance.component.html b/overlord/src/app/attendance/attendance.component.html index ba41fd24..a5a8a442 100644 --- a/overlord/src/app/attendance/attendance.component.html +++ b/overlord/src/app/attendance/attendance.component.html @@ -7,7 +7,14 @@
Date - + diff --git a/overlord/src/app/attendance/attendance.component.ts b/overlord/src/app/attendance/attendance.component.ts index bc1a91b7..0c389a0a 100644 --- a/overlord/src/app/attendance/attendance.component.ts +++ b/overlord/src/app/attendance/attendance.component.ts @@ -27,13 +27,16 @@ import moment from 'moment'; import { BehaviorSubject } from 'rxjs'; import { AuthService } from '../auth/auth.service'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { Attendance } from './attendance'; import { AttendanceDataSource } from './attendance-datasource'; import { AttendanceItem } from './attendance-item'; import { AttendanceType } from './attendance-type'; import { AttendanceService } from './attendance.service'; +import { ViewChild } from '@angular/core'; +import { ElementRef } from '@angular/core'; +import { HostListener } from '@angular/core'; @Component({ selector: 'app-attendance', @@ -73,6 +76,7 @@ import { AttendanceService } from './attendance.service'; ], }) export class AttendanceComponent implements OnInit { + @ViewChild('dateElement', { static: false }) dateElement!: ElementRef; public attendanceObservable = new BehaviorSubject([]); dataSource: AttendanceDataSource = new AttendanceDataSource(this.attendanceObservable); form: FormGroup<{ @@ -89,11 +93,18 @@ export class AttendanceComponent implements OnInit { displayedColumns = ['code', 'name', 'designation', 'department', 'status', 'prints']; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.dateElement.nativeElement.focus(); + this.dateElement.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private toaster: ToasterService, + private snackBar: MatSnackBar, private auth: AuthService, private ser: AttendanceService, ) { @@ -138,10 +149,10 @@ export class AttendanceComponent implements OnInit { save() { this.ser.save(this.getAttendance()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/auth/auth-guard.service.ts b/overlord/src/app/auth/auth-guard.service.ts index c098faec..63af9b28 100644 --- a/overlord/src/app/auth/auth-guard.service.ts +++ b/overlord/src/app/auth/auth-guard.service.ts @@ -2,7 +2,7 @@ import { inject } from '@angular/core'; import { CanActivateFn } from '@angular/router'; import { Router } from '@angular/router'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { AuthService } from './auth.service'; @@ -18,7 +18,7 @@ export const authGuard: CanActivateFn = (route, state) => { return false; } if (permission !== undefined && user.perms.indexOf(permission) === -1) { - inject(ToasterService).show('Danger', 'You do not have the permission to access this area.'); + inject(MatSnackBar).open('You do not have the permission to access this area.', 'Danger'); return false; } // logged in so return true diff --git a/overlord/src/app/auth/login/login.component.ts b/overlord/src/app/auth/login/login.component.ts index 81496046..224c3699 100644 --- a/overlord/src/app/auth/login/login.component.ts +++ b/overlord/src/app/auth/login/login.component.ts @@ -8,7 +8,7 @@ import { MatIcon } from '@angular/material/icon'; import { MatInput } from '@angular/material/input'; import { ActivatedRoute, Router } from '@angular/router'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { CookieService } from '../../shared/cookie.service'; import { AuthService } from '../auth.service'; @@ -34,7 +34,7 @@ import { AuthService } from '../auth.service'; ], }) export class LoginComponent implements OnInit, AfterViewInit { - @ViewChild('nameElement', { static: true }) nameElement?: ElementRef; + @ViewChild('nameElement', { static: true }) nameElement!: ElementRef; form: FormGroup<{ username: FormControl; password: FormControl; @@ -50,7 +50,7 @@ export class LoginComponent implements OnInit, AfterViewInit { private route: ActivatedRoute, private auth: AuthService, private router: Router, - private toaster: ToasterService, + private snackBar: MatSnackBar, private cs: CookieService, ) { this.hide = true; @@ -68,9 +68,7 @@ export class LoginComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.nameElement) { - this.nameElement.nativeElement.focus(); - } + this.nameElement.nativeElement.focus(); }, 0); } @@ -91,7 +89,7 @@ export class LoginComponent implements OnInit, AfterViewInit { this.showOtp = true; this.clientId = this.cs.getCookie('client_id'); } - this.toaster.show('Danger', error.error.details); + this.snackBar.open(error.error.details, 'Danger'); }, }); } diff --git a/overlord/src/app/auth/logout/logout.component.ts b/overlord/src/app/auth/logout/logout.component.ts index 1d36fb13..4f166f7c 100644 --- a/overlord/src/app/auth/logout/logout.component.ts +++ b/overlord/src/app/auth/logout/logout.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { AuthService } from '../auth.service'; @Component({ @@ -13,7 +13,7 @@ export class LogoutComponent implements OnInit { constructor( private auth: AuthService, private router: Router, - private toaster: ToasterService, + private snackBar: MatSnackBar, ) {} ngOnInit() { diff --git a/overlord/src/app/balance-sheet/balance-sheet.component.html b/overlord/src/app/balance-sheet/balance-sheet.component.html index ee412e37..72c8a5c6 100644 --- a/overlord/src/app/balance-sheet/balance-sheet.component.html +++ b/overlord/src/app/balance-sheet/balance-sheet.component.html @@ -7,13 +7,7 @@
Date - + diff --git a/overlord/src/app/balance-sheet/balance-sheet.component.ts b/overlord/src/app/balance-sheet/balance-sheet.component.ts index 878cd178..ce816232 100644 --- a/overlord/src/app/balance-sheet/balance-sheet.component.ts +++ b/overlord/src/app/balance-sheet/balance-sheet.component.ts @@ -1,5 +1,5 @@ import { CurrencyPipe } from '@angular/common'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardHeader, MatCardTitle, MatCardContent } from '@angular/material/card'; @@ -73,10 +73,11 @@ import { BalanceSheetDataSource } from './balance-sheet-datasource'; ], }) export class BalanceSheetComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; + @ViewChild('dateElement', { static: true }) dateElement!: ElementRef; info: BalanceSheet = new BalanceSheet(); - dataSource: BalanceSheetDataSource = new BalanceSheetDataSource(this.info.body); + dataSource: BalanceSheetDataSource = new BalanceSheetDataSource(this.info.body, this.paginator, this.sort); form: FormGroup<{ date: FormControl; }>; @@ -84,6 +85,13 @@ export class BalanceSheetComponent implements OnInit { /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['group', 'name', 'subAmount', 'total']; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.dateElement.nativeElement.focus(); + this.dateElement.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, diff --git a/overlord/src/app/cash-flow/cash-flow.component.html b/overlord/src/app/cash-flow/cash-flow.component.html index 20b6a9a8..a7fb87e9 100644 --- a/overlord/src/app/cash-flow/cash-flow.component.html +++ b/overlord/src/app/cash-flow/cash-flow.component.html @@ -9,8 +9,8 @@ Start Date @@ -19,13 +19,7 @@ Finish Date - + diff --git a/overlord/src/app/cash-flow/cash-flow.component.ts b/overlord/src/app/cash-flow/cash-flow.component.ts index eeb11473..74c0f4d0 100644 --- a/overlord/src/app/cash-flow/cash-flow.component.ts +++ b/overlord/src/app/cash-flow/cash-flow.component.ts @@ -1,5 +1,5 @@ import { CurrencyPipe } from '@angular/common'; -import { Component, OnInit } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardHeader, MatCardTitle, MatCardContent } from '@angular/material/card'; @@ -64,6 +64,7 @@ import { CashFlowDataSource } from './cash-flow-datasource'; ], }) export class CashFlowComponent implements OnInit { + @ViewChild('startDateElement', { static: true }) startDate!: ElementRef; info: CashFlow = new CashFlow(); dataSource: CashFlowDataSource = new CashFlowDataSource(CashFlow.Data(this.info)); form: FormGroup<{ @@ -74,6 +75,13 @@ export class CashFlowComponent implements OnInit { /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['name', 'amount']; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.startDate.nativeElement.focus(); + this.startDate.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, diff --git a/overlord/src/app/client/client-detail/client-detail.component.ts b/overlord/src/app/client/client-detail/client-detail.component.ts index 0abf5d0d..c3c5c7ae 100644 --- a/overlord/src/app/client/client-detail/client-detail.component.ts +++ b/overlord/src/app/client/client-detail/client-detail.component.ts @@ -8,7 +8,7 @@ import { MatFormField, MatLabel } from '@angular/material/form-field'; import { MatInput } from '@angular/material/input'; import { ActivatedRoute, Router } from '@angular/router'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { Client } from '../client'; import { ClientService } from '../client.service'; @@ -33,7 +33,7 @@ import { ClientService } from '../client.service'; ], }) export class ClientDetailComponent implements OnInit, AfterViewInit { - @ViewChild('nameElement', { static: true }) nameElement?: ElementRef; + @ViewChild('nameElement', { static: true }) nameElement!: ElementRef; form: FormGroup<{ code: FormControl; name: FormControl; @@ -47,7 +47,7 @@ export class ClientDetailComponent implements OnInit, AfterViewInit { private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private toaster: ToasterService, + private snackBar: MatSnackBar, private ser: ClientService, ) { this.form = new FormGroup({ @@ -78,20 +78,18 @@ export class ClientDetailComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.nameElement) { - this.nameElement.nativeElement.focus(); - } + this.nameElement.nativeElement.focus(); }, 0); } save() { this.ser.update(this.getItem()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/clients'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -99,11 +97,11 @@ export class ClientDetailComponent implements OnInit, AfterViewInit { delete() { this.ser.delete(this.item.id).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/clients'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/client/client-list/client-list.component.ts b/overlord/src/app/client/client-list/client-list.component.ts index 8b18f29b..27319191 100644 --- a/overlord/src/app/client/client-list/client-list.component.ts +++ b/overlord/src/app/client/client-list/client-list.component.ts @@ -49,10 +49,10 @@ import { ClientListDataSource } from './client-list-datasource'; ], }) export class ClientListComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; list: Client[] = []; - dataSource: ClientListDataSource = new ClientListDataSource(this.list); + dataSource: ClientListDataSource = new ClientListDataSource(this.list, this.paginator, this.sort); /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['code', 'name', 'enabled', 'otp', 'created', 'last']; diff --git a/overlord/src/app/closing-stock/closing-stock.component.html b/overlord/src/app/closing-stock/closing-stock.component.html index bd98009b..17841055 100644 --- a/overlord/src/app/closing-stock/closing-stock.component.html +++ b/overlord/src/app/closing-stock/closing-stock.component.html @@ -24,13 +24,7 @@ Date - + diff --git a/overlord/src/app/closing-stock/closing-stock.component.ts b/overlord/src/app/closing-stock/closing-stock.component.ts index fae8b4c7..d0cb268f 100644 --- a/overlord/src/app/closing-stock/closing-stock.component.ts +++ b/overlord/src/app/closing-stock/closing-stock.component.ts @@ -1,5 +1,5 @@ import { DecimalPipe, CurrencyPipe } from '@angular/common'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormGroup, FormArray, FormControl, ReactiveFormsModule } from '@angular/forms'; import { MatIconButton, MatButton } from '@angular/material/button'; import { @@ -36,7 +36,7 @@ import moment from 'moment'; import { AuthService } from '../auth/auth.service'; import { CostCentre } from '../core/cost-centre'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { User } from '../core/user'; import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component'; import { ToCsvService } from '../shared/to-csv.service'; @@ -89,10 +89,11 @@ import { ClosingStockService } from './closing-stock.service'; ], }) export class ClosingStockComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; + @ViewChild('dateElement', { static: true }) dateElement!: ElementRef; info: ClosingStock = new ClosingStock(); - dataSource: ClosingStockDataSource = new ClosingStockDataSource(this.info.items); + dataSource: ClosingStockDataSource = new ClosingStockDataSource(this.info.items, this.paginator, this.sort); form: FormGroup<{ date: FormControl; costCentre: FormControl; @@ -109,12 +110,19 @@ export class ClosingStockComponent implements OnInit { costCentres: CostCentre[]; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.dateElement.nativeElement.focus(); + this.dateElement.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, private toCsv: ToCsvService, private dialog: MatDialog, - private toaster: ToasterService, + private snackBar: MatSnackBar, public auth: AuthService, private ser: ClosingStockService, ) { @@ -162,10 +170,10 @@ export class ClosingStockComponent implements OnInit { save() { this.ser.save(this.getClosingStock()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -243,10 +251,10 @@ export class ClosingStockComponent implements OnInit { post() { this.ser.post(this.info.date, this.info.costCentre.id as string).subscribe({ next: () => { - this.toaster.show('Success', 'Voucher Posted'); + this.snackBar.open('Voucher Posted', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -254,11 +262,11 @@ export class ClosingStockComponent implements OnInit { delete() { this.ser.delete(this.info.date, this.info.costCentre.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigate(['/closing-stock'], { replaceUrl: true }); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/core/auth.interceptor.ts b/overlord/src/app/core/auth.interceptor.ts index b965adf4..3fb8a63e 100644 --- a/overlord/src/app/core/auth.interceptor.ts +++ b/overlord/src/app/core/auth.interceptor.ts @@ -8,7 +8,7 @@ import { catchError } from 'rxjs/operators'; import { AuthService } from '../auth/auth.service'; import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component'; -import { ToasterService } from './toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; export const authInterceptor: HttpInterceptorFn = (req, next) => { return next(req).pipe( @@ -31,7 +31,7 @@ export const authInterceptor: HttpInterceptorFn = (req, next) => { } // auto logout if 401 response returned from api inject(AuthService).logout(); - inject(ToasterService).show('Danger', 'User has been logged out'); + inject(MatSnackBar).open('User has been logged out', 'Danger'); const dialogRef = inject(MatDialog).open(ConfirmDialogComponent, { width: '250px', data: { diff --git a/overlord/src/app/core/toaster.service.spec.ts b/overlord/src/app/core/toaster.service.spec.ts deleted file mode 100644 index 97314644..00000000 --- a/overlord/src/app/core/toaster.service.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { inject, TestBed } from '@angular/core/testing'; - -import { ToasterService } from './toaster.service'; - -describe('ToasterService', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ToasterService], - }); - }); - - it('should be created', inject([ToasterService], (service: ToasterService) => { - expect(service).toBeTruthy(); - })); -}); diff --git a/overlord/src/app/core/toaster.service.ts b/overlord/src/app/core/toaster.service.ts deleted file mode 100644 index dc62a7ff..00000000 --- a/overlord/src/app/core/toaster.service.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Injectable } from '@angular/core'; -import { MatSnackBar } from '@angular/material/snack-bar'; - -@Injectable({ - providedIn: 'root', -}) -export class ToasterService { - constructor(private snackBar: MatSnackBar) {} - - show(type: string, msg: string) { - this.snackBar.open(msg, type, { - duration: 3000, - }); - } -} diff --git a/overlord/src/app/cost-centre/cost-centre-detail/cost-centre-detail.component.ts b/overlord/src/app/cost-centre/cost-centre-detail/cost-centre-detail.component.ts index 7cbfc77c..6234e004 100644 --- a/overlord/src/app/cost-centre/cost-centre-detail/cost-centre-detail.component.ts +++ b/overlord/src/app/cost-centre/cost-centre-detail/cost-centre-detail.component.ts @@ -7,7 +7,7 @@ import { MatInput } from '@angular/material/input'; import { ActivatedRoute, Router } from '@angular/router'; import { CostCentre } from '../../core/cost-centre'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { CostCentreService } from '../cost-centre.service'; @Component({ @@ -29,7 +29,7 @@ import { CostCentreService } from '../cost-centre.service'; ], }) export class CostCentreDetailComponent implements OnInit, AfterViewInit { - @ViewChild('nameElement', { static: true }) nameElement?: ElementRef; + @ViewChild('nameElement', { static: true }) nameElement!: ElementRef; form: FormGroup<{ name: FormControl; }>; @@ -39,7 +39,7 @@ export class CostCentreDetailComponent implements OnInit, AfterViewInit { constructor( private route: ActivatedRoute, private router: Router, - private toaster: ToasterService, + private snackBar: MatSnackBar, private ser: CostCentreService, ) { this.form = new FormGroup({ @@ -64,20 +64,18 @@ export class CostCentreDetailComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.nameElement) { - this.nameElement.nativeElement.focus(); - } + this.nameElement.nativeElement.focus(); }, 0); } save() { this.ser.saveOrUpdate(this.getItem()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/cost-centres'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/cost-centre/cost-centre-list/cost-centre-list.component.ts b/overlord/src/app/cost-centre/cost-centre-list/cost-centre-list.component.ts index 5a44e927..f3da9559 100644 --- a/overlord/src/app/cost-centre/cost-centre-list/cost-centre-list.component.ts +++ b/overlord/src/app/cost-centre/cost-centre-list/cost-centre-list.component.ts @@ -52,10 +52,10 @@ import { CostCentreListDataSource } from './cost-centre-list-datasource'; ], }) export class CostCentreListComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; list: CostCentre[] = []; - dataSource: CostCentreListDataSource = new CostCentreListDataSource(this.list); + dataSource: CostCentreListDataSource = new CostCentreListDataSource(this.list, this.paginator, this.sort); /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['name', 'isFixture']; diff --git a/overlord/src/app/daybook/daybook.component.html b/overlord/src/app/daybook/daybook.component.html index 6de10961..8e7699f2 100644 --- a/overlord/src/app/daybook/daybook.component.html +++ b/overlord/src/app/daybook/daybook.component.html @@ -9,8 +9,8 @@ Start Date @@ -19,13 +19,7 @@ Finish Date - + diff --git a/overlord/src/app/daybook/daybook.component.ts b/overlord/src/app/daybook/daybook.component.ts index 3dc0495d..5ddca427 100644 --- a/overlord/src/app/daybook/daybook.component.ts +++ b/overlord/src/app/daybook/daybook.component.ts @@ -1,5 +1,5 @@ import { CurrencyPipe } from '@angular/common'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardHeader, MatCardTitle, MatCardContent } from '@angular/material/card'; @@ -63,10 +63,11 @@ import { DaybookDataSource } from './daybook-datasource'; ], }) export class DaybookComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; + @ViewChild('startDateElement', { static: true }) startDate!: ElementRef; info: Daybook = new Daybook(); - dataSource: DaybookDataSource = new DaybookDataSource(this.info.body); + dataSource: DaybookDataSource = new DaybookDataSource(this.info.body, this.paginator, this.sort); form: FormGroup<{ startDate: FormControl; finishDate: FormControl; @@ -76,6 +77,13 @@ export class DaybookComponent implements OnInit { /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['date', 'type', 'narration', 'debitText', 'debitAmount', 'creditText', 'creditAmount']; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.startDate.nativeElement.focus(); + this.startDate.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, diff --git a/overlord/src/app/employee-attendance/employee-attendance.component.html b/overlord/src/app/employee-attendance/employee-attendance.component.html index 51ba2f7f..6630d637 100644 --- a/overlord/src/app/employee-attendance/employee-attendance.component.html +++ b/overlord/src/app/employee-attendance/employee-attendance.component.html @@ -9,8 +9,8 @@ Start Date @@ -19,13 +19,7 @@ Finish Date - + diff --git a/overlord/src/app/employee-attendance/employee-attendance.component.ts b/overlord/src/app/employee-attendance/employee-attendance.component.ts index 26d5b4fc..ca435382 100644 --- a/overlord/src/app/employee-attendance/employee-attendance.component.ts +++ b/overlord/src/app/employee-attendance/employee-attendance.component.ts @@ -1,5 +1,5 @@ import { AsyncPipe } from '@angular/common'; -import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormArray, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete'; import { MatButton } from '@angular/material/button'; @@ -31,7 +31,7 @@ import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; import { AttendanceType } from '../attendance/attendance-type'; import { AuthService } from '../auth/auth.service'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { Employee } from '../employee/employee'; import { EmployeeService } from '../employee/employee.service'; @@ -81,7 +81,8 @@ import { EmployeeAttendanceService } from './employee-attendance.service'; ], }) export class EmployeeAttendanceComponent implements OnInit, AfterViewInit { - @ViewChild('employeeElement', { static: true }) employeeElement?: ElementRef; + @ViewChild('employeeElement', { static: true }) employeeElement!: ElementRef; + @ViewChild('startDateElement', { static: true }) startDate!: ElementRef; public employeeAttendanceObservable = new BehaviorSubject([]); dataSource: EmployeeAttendanceDataSource = new EmployeeAttendanceDataSource(this.employeeAttendanceObservable); @@ -103,11 +104,18 @@ export class EmployeeAttendanceComponent implements OnInit, AfterViewInit { employees: Observable; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.startDate.nativeElement.focus(); + this.startDate.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private toaster: ToasterService, + private snackBar: MatSnackBar, private auth: AuthService, private ser: EmployeeAttendanceService, private employeeSer: EmployeeService, @@ -150,9 +158,7 @@ export class EmployeeAttendanceComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.employeeElement) { - this.employeeElement.nativeElement.focus(); - } + this.employeeElement.nativeElement.focus(); }, 0); } @@ -188,10 +194,10 @@ export class EmployeeAttendanceComponent implements OnInit, AfterViewInit { save() { this.ser.save(this.getEmployeeAttendance()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/employee-benefits/employee-benefits.component.html b/overlord/src/app/employee-benefits/employee-benefits.component.html index b90f268e..ddac1615 100644 --- a/overlord/src/app/employee-benefits/employee-benefits.component.html +++ b/overlord/src/app/employee-benefits/employee-benefits.component.html @@ -7,7 +7,14 @@
Date - + diff --git a/overlord/src/app/employee-benefits/employee-benefits.component.ts b/overlord/src/app/employee-benefits/employee-benefits.component.ts index bc76789e..ceee0c2f 100644 --- a/overlord/src/app/employee-benefits/employee-benefits.component.ts +++ b/overlord/src/app/employee-benefits/employee-benefits.component.ts @@ -37,7 +37,7 @@ import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; import { AuthService } from '../auth/auth.service'; import { AccountBalance } from '../core/account-balance'; import { EmployeeBenefit } from '../core/employee-benefit'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { User } from '../core/user'; import { Voucher } from '../core/voucher'; import { VoucherService } from '../core/voucher.service'; @@ -47,6 +47,7 @@ import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog. import { LocalTimePipe } from '../shared/local-time.pipe'; import { EmployeeBenefitsDataSource } from './employee-benefits-datasource'; +import { HostListener } from '@angular/core'; @Component({ selector: 'app-employee-benefits', @@ -91,7 +92,8 @@ import { EmployeeBenefitsDataSource } from './employee-benefits-datasource'; ], }) export class EmployeeBenefitsComponent implements OnInit, AfterViewInit { - @ViewChild('employeeElement', { static: true }) employeeElement?: ElementRef; + @ViewChild('employeeElement', { static: true }) employeeElement!: ElementRef; + @ViewChild('dateElement', { static: true }) dateElement!: ElementRef; public benefitsObservable = new BehaviorSubject([]); dataSource: EmployeeBenefitsDataSource = new EmployeeBenefitsDataSource(this.benefitsObservable); form: FormGroup<{ @@ -121,11 +123,18 @@ export class EmployeeBenefitsComponent implements OnInit, AfterViewInit { employees: Observable; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.dateElement.nativeElement.focus(); + this.dateElement.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private toaster: ToasterService, + private snackBar: MatSnackBar, public auth: AuthService, private ser: VoucherService, private employeeSer: EmployeeService, @@ -174,9 +183,7 @@ export class EmployeeBenefitsComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.employeeElement) { - this.employeeElement.nativeElement.focus(); - } + this.employeeElement.nativeElement.focus(); }, 0); } @@ -205,7 +212,7 @@ export class EmployeeBenefitsComponent implements OnInit, AfterViewInit { addRow() { const oldFiltered = this.voucher.employeeBenefits.filter((x) => x.employee.id === (this.employee as Employee).id); if (oldFiltered.length) { - this.toaster.show('Danger', 'Employee has already been added'); + this.snackBar.open('Employee has already been added', 'Danger'); return; } @@ -239,9 +246,7 @@ export class EmployeeBenefitsComponent implements OnInit, AfterViewInit { this.form.controls.addRow.reset(); this.employee = null; setTimeout(() => { - if (this.employeeElement) { - this.employeeElement.nativeElement.focus(); - } + this.employeeElement.nativeElement.focus(); }, 0); } @@ -264,10 +269,10 @@ export class EmployeeBenefitsComponent implements OnInit, AfterViewInit { this.ser.post(this.voucher.id as string).subscribe({ next: (result) => { this.loadVoucher(result); - this.toaster.show('Success', 'Voucher Posted'); + this.snackBar.open('Voucher Posted', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -276,7 +281,7 @@ export class EmployeeBenefitsComponent implements OnInit, AfterViewInit { const voucher: Voucher = this.getVoucher(); this.ser.saveOrUpdate(voucher).subscribe({ next: (result) => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); if (voucher.id === result.id) { this.loadVoucher(result); } else { @@ -284,7 +289,7 @@ export class EmployeeBenefitsComponent implements OnInit, AfterViewInit { } }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -298,11 +303,11 @@ export class EmployeeBenefitsComponent implements OnInit, AfterViewInit { delete() { this.ser.delete(this.voucher.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigate(['/employee-benefits'], { replaceUrl: true }); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/employee-functions/employee-functions.component.html b/overlord/src/app/employee-functions/employee-functions.component.html index 58756fa2..e5623caa 100644 --- a/overlord/src/app/employee-functions/employee-functions.component.html +++ b/overlord/src/app/employee-functions/employee-functions.component.html @@ -6,13 +6,7 @@
Date - + Start Date - + Finish Date - + diff --git a/overlord/src/app/employee-functions/employee-functions.component.ts b/overlord/src/app/employee-functions/employee-functions.component.ts index 683e877b..177dca8c 100644 --- a/overlord/src/app/employee-functions/employee-functions.component.ts +++ b/overlord/src/app/employee-functions/employee-functions.component.ts @@ -9,7 +9,7 @@ import { MatTabGroup, MatTab } from '@angular/material/tabs'; import moment from 'moment'; import { Moment } from 'moment'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { EmployeeFunctionsService } from './employee-functions.service'; @@ -48,7 +48,7 @@ export class EmployeeFunctionsComponent { fingerprintFile: File | null = null; constructor( - private toaster: ToasterService, + private snackBar: MatSnackBar, private ser: EmployeeFunctionsService, ) { const startDate = moment().date(1); @@ -80,15 +80,15 @@ export class EmployeeFunctionsComponent { creditSalary() { const date = (this.creditSalaryForm.value.date ?? moment()).format('DD-MMM-YYYY'); if (!date) { - this.toaster.show('Danger', 'Please choose a valid date.'); + this.snackBar.open('Please choose a valid date.', 'Danger'); return; } this.ser.creditSalary(date).subscribe({ next: () => { - this.toaster.show('Success', 'Salaries Credited'); + this.snackBar.open('Salaries Credited', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -97,7 +97,7 @@ export class EmployeeFunctionsComponent { const startDate = (this.attendanceRecordForm.value.startDate ?? moment()).format('DD-MMM-YYYY'); const finishDate = (this.attendanceRecordForm.value.finishDate ?? moment()).format('DD-MMM-YYYY'); if (!startDate || !finishDate) { - // this.toaster.show('Danger', 'Please choose a start and finish date.'); + // this.snackBar.open('Please choose a start and finish date.', 'Danger'); return ''; } return `/attendance-report?s=${startDate}&f=${finishDate}`; @@ -107,7 +107,7 @@ export class EmployeeFunctionsComponent { const startDate = (this.attendanceRecordForm.value.startDate ?? moment()).format('DD-MMM-YYYY'); const finishDate = (this.attendanceRecordForm.value.finishDate ?? moment()).format('DD-MMM-YYYY'); if (!startDate || !finishDate) { - // this.toaster.show('Danger', 'Please choose a start and finish date.'); + // this.snackBar.open('Please choose a start and finish date.', 'Danger'); return ''; } return `/fingerprint-report?s=${startDate}&f=${finishDate}`; @@ -119,15 +119,15 @@ export class EmployeeFunctionsComponent { uploadFingerprints() { if (!this.fingerprintFile) { - this.toaster.show('Danger', 'Please choose a file first!'); + this.snackBar.open('Please choose a file first!', 'Danger'); return; } this.ser.uploadFingerprints(this.fingerprintFile).subscribe({ next: () => { - this.toaster.show('Success', 'Fingerprints uploaded'); + this.snackBar.open('Fingerprints uploaded', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/employee/employee-detail/employee-detail.component.html b/overlord/src/app/employee/employee-detail/employee-detail.component.html index 6cadce48..77a32f5e 100644 --- a/overlord/src/app/employee/employee-detail/employee-detail.component.html +++ b/overlord/src/app/employee/employee-detail/employee-detail.component.html @@ -56,26 +56,14 @@
Joining Date - + @if (!item.isActive) { Leaving Date - + diff --git a/overlord/src/app/employee/employee-detail/employee-detail.component.ts b/overlord/src/app/employee/employee-detail/employee-detail.component.ts index e380587d..1606cce7 100644 --- a/overlord/src/app/employee/employee-detail/employee-detail.component.ts +++ b/overlord/src/app/employee/employee-detail/employee-detail.component.ts @@ -21,7 +21,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import moment from 'moment'; import { CostCentre } from '../../core/cost-centre'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { Employee } from '../employee'; import { EmployeeService } from '../employee.service'; @@ -54,7 +54,7 @@ import { EmployeeService } from '../employee.service'; ], }) export class EmployeeDetailComponent implements OnInit, AfterViewInit { - @ViewChild('nameElement', { static: true }) nameElement?: ElementRef; + @ViewChild('nameElement', { static: true }) nameElement!: ElementRef; form: FormGroup<{ code: FormControl; name: FormControl; @@ -74,7 +74,7 @@ export class EmployeeDetailComponent implements OnInit, AfterViewInit { private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private toaster: ToasterService, + private snackBar: MatSnackBar, private ser: EmployeeService, ) { this.form = new FormGroup({ @@ -120,20 +120,18 @@ export class EmployeeDetailComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.nameElement) { - this.nameElement.nativeElement.focus(); - } + this.nameElement.nativeElement.focus(); }, 0); } save() { this.ser.saveOrUpdate(this.getItem()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/employees'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -141,11 +139,11 @@ export class EmployeeDetailComponent implements OnInit, AfterViewInit { delete() { this.ser.delete(this.item.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/employees'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/employee/employee-list/employee-list.component.ts b/overlord/src/app/employee/employee-list/employee-list.component.ts index b3796da8..11ffd5f0 100644 --- a/overlord/src/app/employee/employee-list/employee-list.component.ts +++ b/overlord/src/app/employee/employee-list/employee-list.component.ts @@ -67,9 +67,9 @@ import { EmployeeListDataSource } from './employee-list-datasource'; ], }) export class EmployeeListComponent implements OnInit, AfterViewInit { - @ViewChild('filterElement', { static: true }) filterElement?: ElementRef; - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild('filterElement', { static: true }) filterElement!: ElementRef; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; dataSource: EmployeeListDataSource; filter: Observable; form: FormGroup<{ @@ -89,7 +89,7 @@ export class EmployeeListComponent implements OnInit, AfterViewInit { filter: new FormControl('', { nonNullable: true }), }); this.filter = this.form.controls.filter.valueChanges.pipe(debounceTime(150), distinctUntilChanged()); - this.dataSource = new EmployeeListDataSource(this.list, this.filter); + this.dataSource = new EmployeeListDataSource(this.list, this.filter, this.paginator, this.sort); } ngOnInit() { @@ -103,9 +103,7 @@ export class EmployeeListComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.filterElement) { - this.filterElement.nativeElement.focus(); - } + this.filterElement.nativeElement.focus(); }, 0); } diff --git a/overlord/src/app/entries/entries.component.html b/overlord/src/app/entries/entries.component.html index 8b9c3085..35c9f92c 100644 --- a/overlord/src/app/entries/entries.component.html +++ b/overlord/src/app/entries/entries.component.html @@ -7,7 +7,13 @@
Create / Last Edit Date From - + diff --git a/overlord/src/app/entries/entries.component.ts b/overlord/src/app/entries/entries.component.ts index 017a72b2..ecd3ff3c 100644 --- a/overlord/src/app/entries/entries.component.ts +++ b/overlord/src/app/entries/entries.component.ts @@ -1,5 +1,5 @@ import { CurrencyPipe } from '@angular/common'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardHeader, MatCardTitle, MatCardContent } from '@angular/material/card'; @@ -73,10 +73,11 @@ import { Report } from './report'; ], }) export class EntriesComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; + @ViewChild('startDateElement', { static: true }) startDate!: ElementRef; info: Report = new Report(); - dataSource: EntriesDatasource = new EntriesDatasource(this.info.report, 0); + dataSource: EntriesDatasource; form: FormGroup<{ startDate: FormControl; finishDate: FormControl; @@ -88,6 +89,14 @@ export class EntriesComponent implements OnInit { posted: boolean | null = null; issue = false; + + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.startDate.nativeElement.focus(); + this.startDate.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, @@ -98,6 +107,7 @@ export class EntriesComponent implements OnInit { posted: new FormControl(null), issue: new FormControl(false, { nonNullable: true }), }); + this.dataSource = new EntriesDatasource(this.info.report, 0, this.paginator, this.sort); } ngOnInit() { @@ -141,7 +151,7 @@ export class EntriesComponent implements OnInit { sortDirection, ); }); - this.sort?.sortChange.subscribe({ + this.sort.sortChange.subscribe({ next: () => this.router.navigate([], { relativeTo: this.route, @@ -149,11 +159,11 @@ export class EntriesComponent implements OnInit { queryParamsHandling: 'merge', }), }); - this.paginator?.page.subscribe({ + this.paginator.page.subscribe({ next: () => this.router.navigate([], { relativeTo: this.route, - queryParams: { ps: this.paginator?.pageSize, pi: this.paginator?.pageIndex }, + queryParams: { ps: this.paginator.pageSize, pi: this.paginator.pageIndex }, queryParamsHandling: 'merge', }), }); diff --git a/overlord/src/app/incentive/incentive.component.html b/overlord/src/app/incentive/incentive.component.html index 5878c41e..759940b8 100644 --- a/overlord/src/app/incentive/incentive.component.html +++ b/overlord/src/app/incentive/incentive.component.html @@ -7,7 +7,14 @@
Date - + diff --git a/overlord/src/app/incentive/incentive.component.ts b/overlord/src/app/incentive/incentive.component.ts index 45308785..114b5a00 100644 --- a/overlord/src/app/incentive/incentive.component.ts +++ b/overlord/src/app/incentive/incentive.component.ts @@ -36,7 +36,7 @@ import { AuthService } from '../auth/auth.service'; import { Account } from '../core/account'; import { AccountBalance } from '../core/account-balance'; import { Incentive } from '../core/incentive'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { User } from '../core/user'; import { Voucher } from '../core/voucher'; import { VoucherService } from '../core/voucher.service'; @@ -44,6 +44,9 @@ import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog. import { LocalTimePipe } from '../shared/local-time.pipe'; import { IncentiveDataSource } from './incentive-datasource'; +import { ViewChild } from '@angular/core'; +import { ElementRef } from '@angular/core'; +import { HostListener } from '@angular/core'; @Component({ selector: 'app-incentive', @@ -85,6 +88,7 @@ import { IncentiveDataSource } from './incentive-datasource'; ], }) export class IncentiveComponent implements OnInit { + @ViewChild('dateElement', { static: true }) dateElement!: ElementRef; public incentiveObservable = new BehaviorSubject([]); dataSource: IncentiveDataSource = new IncentiveDataSource(this.incentiveObservable); form: FormGroup<{ @@ -102,11 +106,18 @@ export class IncentiveComponent implements OnInit { displayedColumns = ['name', 'designation', 'department', 'daysWorked', 'points', 'amount']; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.dateElement.nativeElement.focus(); + this.dateElement.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private toaster: ToasterService, + private snackBar: MatSnackBar, public auth: AuthService, private ser: VoucherService, ) { @@ -193,10 +204,10 @@ export class IncentiveComponent implements OnInit { this.ser.post(this.voucher.id as string).subscribe({ next: (result) => { this.loadVoucher(result); - this.toaster.show('Success', 'Voucher Posted'); + this.snackBar.open('Voucher Posted', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -212,7 +223,7 @@ export class IncentiveComponent implements OnInit { } }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -231,11 +242,11 @@ export class IncentiveComponent implements OnInit { delete() { this.ser.delete(this.voucher.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigate(['/incentive'], { replaceUrl: true }); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/issue/issue.component.html b/overlord/src/app/issue/issue.component.html index 9e4987e4..dde4bce0 100644 --- a/overlord/src/app/issue/issue.component.html +++ b/overlord/src/app/issue/issue.component.html @@ -7,14 +7,7 @@
Date - + diff --git a/overlord/src/app/issue/issue.component.ts b/overlord/src/app/issue/issue.component.ts index 4366b2ea..5a993108 100644 --- a/overlord/src/app/issue/issue.component.ts +++ b/overlord/src/app/issue/issue.component.ts @@ -1,5 +1,5 @@ import { AsyncPipe, DecimalPipe, CurrencyPipe } from '@angular/common'; -import { AfterViewInit, Component, ElementRef, OnInit, OnDestroy, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, OnInit, ViewChild, HostListener } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete'; import { MatButton, MatIconButton } from '@angular/material/button'; @@ -32,7 +32,6 @@ import { MatRow, } from '@angular/material/table'; import { ActivatedRoute, Router } from '@angular/router'; -import { Hotkey, HotkeysService } from 'angular2-hotkeys'; import { round } from 'mathjs'; import moment from 'moment'; import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; @@ -44,7 +43,7 @@ import { BatchService } from '../core/batch.service'; import { CostCentre } from '../core/cost-centre'; import { Inventory } from '../core/inventory'; import { IssueGridItem } from '../core/issue-grid-item'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { User } from '../core/user'; import { Voucher } from '../core/voucher'; import { VoucherService } from '../core/voucher.service'; @@ -102,9 +101,25 @@ import { IssueGridService } from './issue-grid.service'; LocalTimePipe, ], }) -export class IssueComponent implements OnInit, AfterViewInit, OnDestroy { - @ViewChild('batchElement', { static: true }) batchElement?: ElementRef; - @ViewChild('dateElement', { static: true }) dateElement?: ElementRef; +export class IssueComponent implements OnInit, AfterViewInit { + @ViewChild('batchElement', { static: true }) batchElement!: ElementRef; + @ViewChild('dateElement', { static: true }) dateElement!: ElementRef; + + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.dateElement.nativeElement.focus(); + this.dateElement.nativeElement.select(); + } + + @HostListener('window:keydown.control.s', ['$event']) + saveListner(event: KeyboardEvent) { + event.preventDefault(); + if (this.canSave()) { + this.save(); + } + } + public inventoryObservable = new BehaviorSubject([]); public gridObservable = new BehaviorSubject([]); dataSource: IssueDataSource = new IssueDataSource(this.inventoryObservable); @@ -134,8 +149,7 @@ export class IssueComponent implements OnInit, AfterViewInit, OnDestroy { private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private hotkeys: HotkeysService, - private toaster: ToasterService, + private snackBar: MatSnackBar, private auth: AuthService, private math: MathService, private ser: VoucherService, @@ -177,42 +191,12 @@ export class IssueComponent implements OnInit, AfterViewInit, OnDestroy { this.costCentres = data.costCentres; this.loadVoucher(data.voucher); }); - this.hotkeys.add( - new Hotkey( - 'f2', - (): boolean => { - setTimeout(() => { - if (this.dateElement) { - this.dateElement.nativeElement.focus(); - } - }, 0); - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); - this.hotkeys.add( - new Hotkey( - 'ctrl+s', - (): boolean => { - if (this.canSave()) { - this.save(); - } - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); } ngAfterViewInit() { this.focusBatch(); } - ngOnDestroy() { - this.hotkeys.reset(); - } - loadVoucher(voucher: Voucher) { this.voucher = voucher; this.form.setValue({ @@ -232,9 +216,7 @@ export class IssueComponent implements OnInit, AfterViewInit, OnDestroy { focusBatch() { setTimeout(() => { - if (this.batchElement) { - this.batchElement.nativeElement.focus(); - } + this.batchElement.nativeElement.focus(); }, 0); } @@ -251,13 +233,13 @@ export class IssueComponent implements OnInit, AfterViewInit, OnDestroy { const old = this.voucher.inventories.find((x) => x.batch.id === (this.batch as Batch).id); if (old !== undefined) { if (isConsumption && old.quantity + quantity > this.batch.quantityRemaining) { - this.toaster.show('Danger', 'Quantity issued cannot be more than quantity available'); + this.snackBar.open('Quantity issued cannot be more than quantity available', 'Danger'); return; } old.quantity += quantity; } else { if (isConsumption && quantity > this.batch.quantityRemaining) { - this.toaster.show('Danger', 'Quantity issued cannot be more than quantity available'); + this.snackBar.open('Quantity issued cannot be more than quantity available', 'Danger'); return; } this.voucher.inventories.push( @@ -279,9 +261,7 @@ export class IssueComponent implements OnInit, AfterViewInit, OnDestroy { this.form.controls.addRow.reset(); this.batch = null; setTimeout(() => { - if (this.batchElement) { - this.batchElement.nativeElement.focus(); - } + this.batchElement.nativeElement.focus(); }, 0); } @@ -335,7 +315,7 @@ export class IssueComponent implements OnInit, AfterViewInit, OnDestroy { const voucher: Voucher = this.getVoucher(); this.ser.saveOrUpdate(voucher).subscribe({ next: (result) => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); if (voucher.id === result.id) { this.loadVoucher(result); } else { @@ -343,7 +323,7 @@ export class IssueComponent implements OnInit, AfterViewInit, OnDestroy { } }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -364,11 +344,11 @@ export class IssueComponent implements OnInit, AfterViewInit, OnDestroy { delete() { this.ser.delete(this.voucher.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigate(['/issue'], { replaceUrl: true }); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/journal/journal.component.html b/overlord/src/app/journal/journal.component.html index ee02ee2c..ff28a63e 100644 --- a/overlord/src/app/journal/journal.component.html +++ b/overlord/src/app/journal/journal.component.html @@ -17,14 +17,7 @@
Date - + diff --git a/overlord/src/app/journal/journal.component.ts b/overlord/src/app/journal/journal.component.ts index 75449844..1c305d32 100644 --- a/overlord/src/app/journal/journal.component.ts +++ b/overlord/src/app/journal/journal.component.ts @@ -1,6 +1,6 @@ import { COMMA, ENTER } from '@angular/cdk/keycodes'; import { AsyncPipe, CurrencyPipe } from '@angular/common'; -import { AfterViewInit, Component, ElementRef, OnInit, OnDestroy, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, OnInit, ViewChild, HostListener } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete'; import { MatButton, MatIconButton } from '@angular/material/button'; @@ -34,7 +34,6 @@ import { MatRow, } from '@angular/material/table'; import { ActivatedRoute, Router } from '@angular/router'; -import { Hotkey, HotkeysService } from 'angular2-hotkeys'; import { round } from 'mathjs'; import moment from 'moment'; import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; @@ -48,7 +47,7 @@ import { DbFile } from '../core/db-file'; import { Journal } from '../core/journal'; import { Tag } from '../core/tag'; import { TagService } from '../core/tag.service'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { User } from '../core/user'; import { Voucher } from '../core/voucher'; import { VoucherService } from '../core/voucher.service'; @@ -112,10 +111,34 @@ import { JournalDialogComponent } from './journal-dialog.component'; LocalTimePipe, ], }) -export class JournalComponent implements OnInit, AfterViewInit, OnDestroy { - @ViewChild('accountElement', { static: true }) accountElement?: ElementRef; - @ViewChild('dateElement', { static: true }) dateElement?: ElementRef; +export class JournalComponent implements OnInit, AfterViewInit { + @ViewChild('accountElement', { static: true }) accountElement!: ElementRef; + @ViewChild('dateElement', { static: false }) dateElement!: ElementRef; @ViewChild('tagInput') tagInput?: ElementRef; + + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.dateElement.nativeElement.focus(); + this.dateElement.nativeElement.select(); + } + + @HostListener('window:keydown.control.s', ['$event']) + saveListner(event: KeyboardEvent) { + event.preventDefault(); + if (this.canSave()) { + this.save(); + } + } + + @HostListener('window:keydown.control.p', ['$event']) + postListner(event: KeyboardEvent) { + event.preventDefault(); + if (this.voucher.id && !this.voucher.posted && this.auth.allowed('post-vouchers')) { + this.post(); + } + } + separatorKeysCodes: number[] = [ENTER, COMMA]; public journalObservable = new BehaviorSubject([]); dataSource: JournalDataSource = new JournalDataSource(this.journalObservable); @@ -143,8 +166,7 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy { private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private hotkeys: HotkeysService, - private toaster: ToasterService, + private snackBar: MatSnackBar, public auth: AuthService, private math: MathService, public image: ImageService, @@ -184,54 +206,12 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy { this.loadVoucher(data.voucher); }); - this.hotkeys.add( - new Hotkey( - 'f2', - (): boolean => { - setTimeout(() => { - if (this.dateElement) { - this.dateElement.nativeElement.focus(); - } - }, 0); - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); - this.hotkeys.add( - new Hotkey( - 'ctrl+s', - (): boolean => { - if (this.canSave()) { - this.save(); - } - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); - this.hotkeys.add( - new Hotkey( - 'ctrl+p', - (): boolean => { - if (this.voucher.id && !this.voucher.posted && this.auth.allowed('post-vouchers')) { - this.post(); - } - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); } ngAfterViewInit() { this.focusAccount(); } - ngOnDestroy() { - this.hotkeys.reset(); - } - loadVoucher(voucher: Voucher) { this.voucher = voucher; this.form.setValue({ @@ -250,9 +230,7 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy { focusAccount() { setTimeout(() => { - if (this.accountElement) { - this.accountElement.nativeElement.focus(); - } + this.accountElement.nativeElement.focus(); }, 0); } @@ -295,9 +273,7 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy { this.account = null; this.accBal = null; setTimeout(() => { - if (this.accountElement) { - this.accountElement.nativeElement.focus(); - } + this.accountElement.nativeElement.focus(); }, 0); } @@ -350,10 +326,10 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy { this.ser.post(this.voucher.id as string).subscribe({ next: (result) => { this.loadVoucher(result); - this.toaster.show('Success', 'Voucher Posted'); + this.snackBar.open('Voucher Posted', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -362,7 +338,7 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy { const voucher: Voucher = this.getVoucher(); this.ser.saveOrUpdate(voucher).subscribe({ next: (result) => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); if (voucher.id === result.id) { this.loadVoucher(result); } else { @@ -370,7 +346,7 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy { } }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -385,11 +361,11 @@ export class JournalComponent implements OnInit, AfterViewInit, OnDestroy { delete() { this.ser.delete(this.voucher.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigate(['/journal'], { replaceUrl: true }); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/ledger/ledger.component.html b/overlord/src/app/ledger/ledger.component.html index bc64268f..c615eb48 100644 --- a/overlord/src/app/ledger/ledger.component.html +++ b/overlord/src/app/ledger/ledger.component.html @@ -16,8 +16,8 @@ Start Date @@ -26,13 +26,7 @@ Finish Date - + @@ -109,7 +103,7 @@ - Running + Running {{ row.running | currency: 'INR' | accounting }} {{ running | currency: 'INR' | accounting }} diff --git a/overlord/src/app/ledger/ledger.component.ts b/overlord/src/app/ledger/ledger.component.ts index 6a55217f..3ae33734 100644 --- a/overlord/src/app/ledger/ledger.component.ts +++ b/overlord/src/app/ledger/ledger.component.ts @@ -92,9 +92,10 @@ import { LedgerService } from './ledger.service'; ], }) export class LedgerComponent implements OnInit, AfterViewInit { - @ViewChild('accountElement', { static: true }) accountElement?: ElementRef; - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild('accountElement', { static: true }) accountElement!: ElementRef; + @ViewChild('startDateElement', { static: true }) startDate!: ElementRef; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; info: Ledger = new Ledger(); dataSource: LedgerDataSource = new LedgerDataSource(this.info.body); form: FormGroup<{ @@ -112,6 +113,13 @@ export class LedgerComponent implements OnInit, AfterViewInit { accounts: Observable; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.startDate.nativeElement.focus(); + this.startDate.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, diff --git a/overlord/src/app/net-transactions/net-transactions.component.html b/overlord/src/app/net-transactions/net-transactions.component.html index e123233d..12e6ce51 100644 --- a/overlord/src/app/net-transactions/net-transactions.component.html +++ b/overlord/src/app/net-transactions/net-transactions.component.html @@ -9,8 +9,8 @@ Start Date @@ -19,13 +19,7 @@ Finish Date - + diff --git a/overlord/src/app/net-transactions/net-transactions.component.ts b/overlord/src/app/net-transactions/net-transactions.component.ts index 8be94de9..cece2ac5 100644 --- a/overlord/src/app/net-transactions/net-transactions.component.ts +++ b/overlord/src/app/net-transactions/net-transactions.component.ts @@ -1,5 +1,5 @@ import { CurrencyPipe } from '@angular/common'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardHeader, MatCardTitle, MatCardContent } from '@angular/material/card'; @@ -65,10 +65,11 @@ import { NetTransactionsDataSource } from './net-transactions-datasource'; ], }) export class NetTransactionsComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; + @ViewChild('startDateElement', { static: true }) startDate!: ElementRef; info: NetTransactions = new NetTransactions(); - dataSource: NetTransactionsDataSource = new NetTransactionsDataSource(this.info.body); + dataSource: NetTransactionsDataSource = new NetTransactionsDataSource(this.info.body, this.paginator, this.sort); form: FormGroup<{ startDate: FormControl; finishDate: FormControl; @@ -78,6 +79,13 @@ export class NetTransactionsComponent implements OnInit { /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['type', 'name', 'debit', 'credit']; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.startDate.nativeElement.focus(); + this.startDate.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, diff --git a/overlord/src/app/payment/payment.component.html b/overlord/src/app/payment/payment.component.html index 7fc8cdb3..c6329d83 100644 --- a/overlord/src/app/payment/payment.component.html +++ b/overlord/src/app/payment/payment.component.html @@ -17,14 +17,7 @@
Date - + diff --git a/overlord/src/app/payment/payment.component.ts b/overlord/src/app/payment/payment.component.ts index c202f002..700d8a92 100644 --- a/overlord/src/app/payment/payment.component.ts +++ b/overlord/src/app/payment/payment.component.ts @@ -1,6 +1,6 @@ import { COMMA, ENTER } from '@angular/cdk/keycodes'; import { AsyncPipe, CurrencyPipe } from '@angular/common'; -import { AfterViewInit, Component, ElementRef, OnInit, OnDestroy, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, OnInit, ViewChild, HostListener } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete'; import { MatButton, MatIconButton } from '@angular/material/button'; @@ -34,7 +34,6 @@ import { MatRow, } from '@angular/material/table'; import { ActivatedRoute, Router } from '@angular/router'; -import { Hotkey, HotkeysService } from 'angular2-hotkeys'; import { round } from 'mathjs'; import moment from 'moment'; import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; @@ -48,7 +47,7 @@ import { DbFile } from '../core/db-file'; import { Journal } from '../core/journal'; import { Tag } from '../core/tag'; import { TagService } from '../core/tag.service'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { User } from '../core/user'; import { Voucher } from '../core/voucher'; import { VoucherService } from '../core/voucher.service'; @@ -112,10 +111,34 @@ import { PaymentDialogComponent } from './payment-dialog.component'; LocalTimePipe, ], }) -export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy { - @ViewChild('accountElement', { static: true }) accountElement?: ElementRef; - @ViewChild('dateElement', { static: true }) dateElement?: ElementRef; +export class PaymentComponent implements OnInit, AfterViewInit { + @ViewChild('accountElement', { static: true }) accountElement!: ElementRef; + @ViewChild('dateElement', { static: true }) dateElement!: ElementRef; @ViewChild('tagInput') tagInput?: ElementRef; + + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.dateElement.nativeElement.focus(); + this.dateElement.nativeElement.select(); + } + + @HostListener('window:keydown.control.s', ['$event']) + saveListner(event: KeyboardEvent) { + event.preventDefault(); + if (this.canSave()) { + this.save(); + } + } + + @HostListener('window:keydown.control.p', ['$event']) + postListner(event: KeyboardEvent) { + event.preventDefault(); + if (this.voucher.id && !this.voucher.posted && this.auth.allowed('post-vouchers')) { + this.post(); + } + } + separatorKeysCodes: number[] = [ENTER, COMMA]; public journalObservable = new BehaviorSubject([]); dataSource: PaymentDataSource = new PaymentDataSource(this.journalObservable); @@ -146,8 +169,7 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy { private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private hotkeys: HotkeysService, - private toaster: ToasterService, + private snackBar: MatSnackBar, public auth: AuthService, private math: MathService, public image: ImageService, @@ -197,54 +219,12 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy { this.paymentAccounts = data.paymentAccounts; this.loadVoucher(data.voucher); }); - this.hotkeys.add( - new Hotkey( - 'f2', - (): boolean => { - setTimeout(() => { - if (this.dateElement) { - this.dateElement.nativeElement.focus(); - } - }, 0); - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); - this.hotkeys.add( - new Hotkey( - 'ctrl+s', - (): boolean => { - if (this.canSave()) { - this.save(); - } - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); - this.hotkeys.add( - new Hotkey( - 'ctrl+p', - (): boolean => { - if (this.voucher.id && !this.voucher.posted && this.auth.allowed('post-vouchers')) { - this.post(); - } - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); } ngAfterViewInit() { this.focusAccount(); } - ngOnDestroy() { - this.hotkeys.reset(); - } - loadVoucher(voucher: Voucher) { this.voucher = voucher; [this.paymentJournal] = this.voucher.journals.filter((x) => x.debit === -1); @@ -265,9 +245,7 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy { focusAccount() { setTimeout(() => { - if (this.accountElement) { - this.accountElement.nativeElement.focus(); - } + this.accountElement.nativeElement.focus(); }, 0); } @@ -303,9 +281,7 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy { this.account = null; this.accBal = null; setTimeout(() => { - if (this.accountElement) { - this.accountElement.nativeElement.focus(); - } + this.accountElement.nativeElement.focus(); }, 0); } @@ -360,10 +336,10 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy { this.ser.post(this.voucher.id as string).subscribe({ next: (result) => { this.loadVoucher(result); - this.toaster.show('Success', 'Voucher Posted'); + this.snackBar.open('Voucher Posted', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -372,7 +348,7 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy { const voucher: Voucher = this.getVoucher(); this.ser.saveOrUpdate(voucher).subscribe({ next: (result) => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); if (voucher.id === result.id) { this.loadVoucher(result); } else { @@ -380,7 +356,7 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy { } }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -396,11 +372,11 @@ export class PaymentComponent implements OnInit, AfterViewInit, OnDestroy { delete() { this.ser.delete(this.voucher.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigate(['/payment'], { replaceUrl: true }); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/period/period-detail/period-detail.component.html b/overlord/src/app/period/period-detail/period-detail.component.html index 81e9c1d9..f32e15bc 100644 --- a/overlord/src/app/period/period-detail/period-detail.component.html +++ b/overlord/src/app/period/period-detail/period-detail.component.html @@ -7,7 +7,13 @@
Valid From - + diff --git a/overlord/src/app/period/period-detail/period-detail.component.ts b/overlord/src/app/period/period-detail/period-detail.component.ts index 5790891c..425ac8b7 100644 --- a/overlord/src/app/period/period-detail/period-detail.component.ts +++ b/overlord/src/app/period/period-detail/period-detail.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardHeader, MatCardTitle, MatCardContent, MatCardActions } from '@angular/material/card'; @@ -9,7 +9,7 @@ import { MatInput } from '@angular/material/input'; import { ActivatedRoute, Router } from '@angular/router'; import moment from 'moment'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { Period } from '../period'; import { PeriodService } from '../period.service'; @@ -37,6 +37,7 @@ import { PeriodService } from '../period.service'; ], }) export class PeriodDetailComponent implements OnInit { + @ViewChild('startDateElement', { static: true }) startDate!: ElementRef; form: FormGroup<{ validFrom: FormControl; validTill: FormControl; @@ -44,11 +45,18 @@ export class PeriodDetailComponent implements OnInit { item: Period = new Period(); + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.startDate.nativeElement.focus(); + this.startDate.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private toaster: ToasterService, + private snackBar: MatSnackBar, private ser: PeriodService, ) { this.form = new FormGroup({ @@ -76,11 +84,11 @@ export class PeriodDetailComponent implements OnInit { save() { this.ser.saveOrUpdate(this.getItem()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/periods'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -88,11 +96,11 @@ export class PeriodDetailComponent implements OnInit { delete() { this.ser.delete(this.item.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/periods'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/product-group/product-group-detail/product-group-detail.component.ts b/overlord/src/app/product-group/product-group-detail/product-group-detail.component.ts index 41088172..dfa031b3 100644 --- a/overlord/src/app/product-group/product-group-detail/product-group-detail.component.ts +++ b/overlord/src/app/product-group/product-group-detail/product-group-detail.component.ts @@ -8,7 +8,7 @@ import { MatInput } from '@angular/material/input'; import { ActivatedRoute, Router } from '@angular/router'; import { ProductGroup } from '../../core/product-group'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ProductGroupService } from '../product-group.service'; @Component({ @@ -31,7 +31,7 @@ import { ProductGroupService } from '../product-group.service'; ], }) export class ProductGroupDetailComponent implements OnInit, AfterViewInit { - @ViewChild('nameElement', { static: true }) nameElement?: ElementRef; + @ViewChild('nameElement', { static: true }) nameElement!: ElementRef; form: FormGroup<{ name: FormControl; nutritional: FormControl; @@ -43,7 +43,7 @@ export class ProductGroupDetailComponent implements OnInit, AfterViewInit { constructor( private route: ActivatedRoute, private router: Router, - private toaster: ToasterService, + private snackBar: MatSnackBar, private ser: ProductGroupService, ) { this.form = new FormGroup({ @@ -72,20 +72,18 @@ export class ProductGroupDetailComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.nameElement) { - this.nameElement.nativeElement.focus(); - } + this.nameElement.nativeElement.focus(); }, 0); } save() { this.ser.saveOrUpdate(this.getItem()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/product-groups'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/product-group/product-group-list/product-group-list.component.ts b/overlord/src/app/product-group/product-group-list/product-group-list.component.ts index cc3fb5e2..c8be799e 100644 --- a/overlord/src/app/product-group/product-group-list/product-group-list.component.ts +++ b/overlord/src/app/product-group/product-group-list/product-group-list.component.ts @@ -52,10 +52,10 @@ import { ProductGroupListDataSource } from './product-group-list-datasource'; ], }) export class ProductGroupListComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; list: ProductGroup[] = []; - dataSource: ProductGroupListDataSource = new ProductGroupListDataSource(this.list); + dataSource: ProductGroupListDataSource = new ProductGroupListDataSource(this.list, this.paginator, this.sort); /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['name', 'isFixture']; diff --git a/overlord/src/app/product-ledger/product-ledger.component.html b/overlord/src/app/product-ledger/product-ledger.component.html index d7692c8f..1ded04c6 100644 --- a/overlord/src/app/product-ledger/product-ledger.component.html +++ b/overlord/src/app/product-ledger/product-ledger.component.html @@ -16,8 +16,8 @@ Start Date @@ -26,13 +26,7 @@ Finish Date - + diff --git a/overlord/src/app/product-ledger/product-ledger.component.ts b/overlord/src/app/product-ledger/product-ledger.component.ts index e2d3857a..e93fe2a8 100644 --- a/overlord/src/app/product-ledger/product-ledger.component.ts +++ b/overlord/src/app/product-ledger/product-ledger.component.ts @@ -1,5 +1,5 @@ import { AsyncPipe, DecimalPipe, CurrencyPipe } from '@angular/common'; -import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete'; import { MatIconButton, MatButton } from '@angular/material/button'; @@ -90,11 +90,12 @@ import { ProductLedgerService } from './product-ledger.service'; ], }) export class ProductLedgerComponent implements OnInit, AfterViewInit { - @ViewChild('productElement', { static: true }) productElement?: ElementRef; - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild('productElement', { static: true }) productElement!: ElementRef; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; + @ViewChild('startDateElement', { static: true }) startDate!: ElementRef; info: ProductLedger = new ProductLedger(); - dataSource: ProductLedgerDataSource = new ProductLedgerDataSource(this.info.body); + dataSource: ProductLedgerDataSource = new ProductLedgerDataSource(this.info.body, this.paginator, this.sort); form: FormGroup<{ startDate: FormControl; finishDate: FormControl; @@ -124,6 +125,13 @@ export class ProductLedgerComponent implements OnInit, AfterViewInit { products: Observable; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.startDate.nativeElement.focus(); + this.startDate.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, @@ -161,9 +169,7 @@ export class ProductLedgerComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.productElement) { - this.productElement.nativeElement.focus(); - } + this.productElement.nativeElement.focus(); }, 0); } diff --git a/overlord/src/app/product/product-detail/product-detail.component.ts b/overlord/src/app/product/product-detail/product-detail.component.ts index 9155eae2..9bafbafc 100644 --- a/overlord/src/app/product/product-detail/product-detail.component.ts +++ b/overlord/src/app/product/product-detail/product-detail.component.ts @@ -27,7 +27,7 @@ import { BehaviorSubject } from 'rxjs'; import { Product, StockKeepingUnit } from '../../core/product'; import { ProductGroup } from '../../core/product-group'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { ProductService } from '../product.service'; @@ -69,7 +69,7 @@ import { ProductDetailDialogComponent } from './product-detail-dialog.component' ], }) export class ProductDetailComponent implements OnInit, AfterViewInit { - @ViewChild('nameElement', { static: true }) nameElement?: ElementRef; + @ViewChild('nameElement', { static: true }) nameElement!: ElementRef; form: FormGroup<{ code: FormControl; name: FormControl; @@ -115,7 +115,7 @@ export class ProductDetailComponent implements OnInit, AfterViewInit { private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private toaster: ToasterService, + private snackBar: MatSnackBar, private ser: ProductService, ) { this.form = new FormGroup({ @@ -182,7 +182,7 @@ export class ProductDetailComponent implements OnInit, AfterViewInit { isPurchased: this.item.isPurchased, isSold: this.item.isSold, isActive: this.item.isActive, - productGroup: this.item.productGroup ? this.item.productGroup.id ?? '' : '', + productGroup: this.item.productGroup ? (this.item.productGroup.id ?? '') : '', allergen: this.item.allergen ?? '', protein: this.item.protein, @@ -204,9 +204,7 @@ export class ProductDetailComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.nameElement) { - this.nameElement.nativeElement.focus(); - } + this.nameElement.nativeElement.focus(); }, 0); } @@ -221,22 +219,22 @@ export class ProductDetailComponent implements OnInit, AfterViewInit { } const fraction = Number(formValue.fraction); if (fraction < 1) { - this.toaster.show('Danger', 'Fraction has to be >= 1'); + this.snackBar.open('Fraction has to be >= 1', 'Danger'); return; } const productYield = Number(formValue.productYield); if (productYield < 0 || productYield > 1) { - this.toaster.show('Danger', 'Product Yield has to be > 0 and <= 1'); + this.snackBar.open('Product Yield has to be > 0 and <= 1', 'Danger'); return; } const costPrice = Number(formValue.costPrice); if (costPrice < 0) { - this.toaster.show('Danger', 'Price has to be >= 0'); + this.snackBar.open('Price has to be >= 0', 'Danger'); return; } const salePrice = Number(formValue.salePrice); if (salePrice < 0) { - this.toaster.show('Danger', 'Sale Price has to be >= 0'); + this.snackBar.open('Sale Price has to be >= 0', 'Danger'); return; } this.item.skus.push( @@ -285,11 +283,11 @@ export class ProductDetailComponent implements OnInit, AfterViewInit { save() { this.ser.saveOrUpdate(this.getItem()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/products'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -297,11 +295,11 @@ export class ProductDetailComponent implements OnInit, AfterViewInit { delete() { this.ser.delete(this.item.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/products'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/product/product-list/product-list.component.ts b/overlord/src/app/product/product-list/product-list.component.ts index 97af15c7..f8436bb4 100644 --- a/overlord/src/app/product/product-list/product-list.component.ts +++ b/overlord/src/app/product/product-list/product-list.component.ts @@ -66,9 +66,9 @@ import { ProductListDataSource } from './product-list-datasource'; ], }) export class ProductListComponent implements OnInit, AfterViewInit { - @ViewChild('filterElement', { static: true }) filterElement?: ElementRef; - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild('filterElement', { static: true }) filterElement!: ElementRef; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; list: Product[] = []; filter: Observable; dataSource: ProductListDataSource; @@ -90,7 +90,7 @@ export class ProductListComponent implements OnInit, AfterViewInit { filter: new FormControl('', { nonNullable: true }), }); this.filter = this.form.controls.filter.valueChanges.pipe(debounceTime(150), distinctUntilChanged()); - this.dataSource = new ProductListDataSource(this.list, this.filter); + this.dataSource = new ProductListDataSource(this.list, this.filter, this.paginator, this.sort); } get showExtended(): boolean { @@ -117,9 +117,7 @@ export class ProductListComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.filterElement) { - this.filterElement.nativeElement.focus(); - } + this.filterElement.nativeElement.focus(); }, 0); } diff --git a/overlord/src/app/profit-loss/profit-loss.component.html b/overlord/src/app/profit-loss/profit-loss.component.html index 587226bb..ea22360a 100644 --- a/overlord/src/app/profit-loss/profit-loss.component.html +++ b/overlord/src/app/profit-loss/profit-loss.component.html @@ -9,8 +9,8 @@ Start Date @@ -19,13 +19,7 @@ Finish Date - + diff --git a/overlord/src/app/profit-loss/profit-loss.component.ts b/overlord/src/app/profit-loss/profit-loss.component.ts index b3a27542..5a59a8af 100644 --- a/overlord/src/app/profit-loss/profit-loss.component.ts +++ b/overlord/src/app/profit-loss/profit-loss.component.ts @@ -1,5 +1,5 @@ import { CurrencyPipe } from '@angular/common'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardHeader, MatCardTitle, MatCardContent } from '@angular/material/card'; @@ -70,10 +70,11 @@ import { ProfitLossDataSource } from './profit-loss-datasource'; ], }) export class ProfitLossComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; + @ViewChild('startDateElement', { static: true }) startDate!: ElementRef; info: ProfitLoss = new ProfitLoss(); - dataSource: ProfitLossDataSource = new ProfitLossDataSource(this.info.body); + dataSource: ProfitLossDataSource = new ProfitLossDataSource(this.info.body, this.paginator, this.sort); form: FormGroup<{ startDate: FormControl; finishDate: FormControl; @@ -83,6 +84,13 @@ export class ProfitLossComponent implements OnInit { /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['group', 'name', 'amount', 'total']; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.startDate.nativeElement.focus(); + this.startDate.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, diff --git a/overlord/src/app/purchase-entries/purchase-entries.component.html b/overlord/src/app/purchase-entries/purchase-entries.component.html index a7c240f1..4db2ac47 100644 --- a/overlord/src/app/purchase-entries/purchase-entries.component.html +++ b/overlord/src/app/purchase-entries/purchase-entries.component.html @@ -9,8 +9,8 @@ Start Date @@ -19,13 +19,7 @@ Finish Date - + diff --git a/overlord/src/app/purchase-entries/purchase-entries.component.ts b/overlord/src/app/purchase-entries/purchase-entries.component.ts index c218f1f5..78360ba7 100644 --- a/overlord/src/app/purchase-entries/purchase-entries.component.ts +++ b/overlord/src/app/purchase-entries/purchase-entries.component.ts @@ -1,5 +1,5 @@ import { DecimalPipe, PercentPipe, CurrencyPipe } from '@angular/common'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardHeader, MatCardTitle, MatCardContent } from '@angular/material/card'; @@ -65,10 +65,11 @@ import { PurchaseEntriesDataSource } from './purchase-entries-datasource'; ], }) export class PurchaseEntriesComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; + @ViewChild('startDateElement', { static: true }) startDate!: ElementRef; info: PurchaseEntries = new PurchaseEntries(); - dataSource: PurchaseEntriesDataSource = new PurchaseEntriesDataSource(this.info.body); + dataSource: PurchaseEntriesDataSource = new PurchaseEntriesDataSource(this.info.body, this.paginator, this.sort); form: FormGroup<{ startDate: FormControl; finishDate: FormControl; @@ -78,6 +79,13 @@ export class PurchaseEntriesComponent implements OnInit { /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['date', 'supplier', 'product', 'quantity', 'rate', 'tax', 'discount', 'amount']; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.startDate.nativeElement.focus(); + this.startDate.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, diff --git a/overlord/src/app/purchase-return/purchase-return.component.html b/overlord/src/app/purchase-return/purchase-return.component.html index be34d8c7..0d780f06 100644 --- a/overlord/src/app/purchase-return/purchase-return.component.html +++ b/overlord/src/app/purchase-return/purchase-return.component.html @@ -17,14 +17,7 @@
Date - + diff --git a/overlord/src/app/purchase-return/purchase-return.component.ts b/overlord/src/app/purchase-return/purchase-return.component.ts index 5badb636..c74b194f 100644 --- a/overlord/src/app/purchase-return/purchase-return.component.ts +++ b/overlord/src/app/purchase-return/purchase-return.component.ts @@ -1,6 +1,6 @@ import { COMMA, ENTER } from '@angular/cdk/keycodes'; import { AsyncPipe, DecimalPipe, PercentPipe, CurrencyPipe } from '@angular/common'; -import { AfterViewInit, Component, ElementRef, OnInit, OnDestroy, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, OnInit, ViewChild, HostListener } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete'; import { MatButton, MatIconButton } from '@angular/material/button'; @@ -33,7 +33,6 @@ import { MatRow, } from '@angular/material/table'; import { ActivatedRoute, Router } from '@angular/router'; -import { Hotkey, HotkeysService } from 'angular2-hotkeys'; import { round } from 'mathjs'; import moment from 'moment'; import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; @@ -49,7 +48,7 @@ import { DbFile } from '../core/db-file'; import { Inventory } from '../core/inventory'; import { Tag } from '../core/tag'; import { TagService } from '../core/tag.service'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { User } from '../core/user'; import { Voucher } from '../core/voucher'; import { VoucherService } from '../core/voucher.service'; @@ -114,11 +113,35 @@ import { PurchaseReturnDialogComponent } from './purchase-return-dialog.componen LocalTimePipe, ], }) -export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy { - @ViewChild('accountElement', { static: true }) accountElement?: ElementRef; - @ViewChild('batchElement', { static: true }) batchElement?: ElementRef; - @ViewChild('dateElement', { static: true }) dateElement?: ElementRef; +export class PurchaseReturnComponent implements OnInit, AfterViewInit { + @ViewChild('accountElement', { static: true }) accountElement!: ElementRef; + @ViewChild('batchElement', { static: true }) batchElement!: ElementRef; + @ViewChild('dateElement', { static: true }) dateElement!: ElementRef; @ViewChild('tagInput') tagInput?: ElementRef; + + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.dateElement.nativeElement.focus(); + this.dateElement.nativeElement.select(); + } + + @HostListener('window:keydown.control.s', ['$event']) + saveListner(event: KeyboardEvent) { + event.preventDefault(); + if (this.canSave()) { + this.save(); + } + } + + @HostListener('window:keydown.control.p', ['$event']) + postListner(event: KeyboardEvent) { + event.preventDefault(); + if (this.voucher.id && !this.voucher.posted && this.auth.allowed('post-vouchers')) { + this.post(); + } + } + separatorKeysCodes: number[] = [ENTER, COMMA]; public inventoryObservable = new BehaviorSubject([]); dataSource: PurchaseReturnDataSource = new PurchaseReturnDataSource(this.inventoryObservable); @@ -148,8 +171,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private hotkeys: HotkeysService, - private toaster: ToasterService, + private snackBar: MatSnackBar, public auth: AuthService, private math: MathService, public image: ImageService, @@ -199,54 +221,12 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy this.loadVoucher(data.voucher); }); - this.hotkeys.add( - new Hotkey( - 'f2', - (): boolean => { - setTimeout(() => { - if (this.dateElement) { - this.dateElement.nativeElement.focus(); - } - }, 0); - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); - this.hotkeys.add( - new Hotkey( - 'ctrl+s', - (): boolean => { - if (this.canSave()) { - this.save(); - } - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); - this.hotkeys.add( - new Hotkey( - 'ctrl+p', - (): boolean => { - if (this.voucher.id && !this.voucher.posted && this.auth.allowed('post-vouchers')) { - this.post(); - } - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); } ngAfterViewInit() { this.focusAccount(); } - ngOnDestroy() { - this.hotkeys.reset(); - } - loadVoucher(voucher: Voucher) { this.voucher = voucher; this.form.setValue({ @@ -266,9 +246,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy focusAccount() { setTimeout(() => { - if (this.accountElement) { - this.accountElement.nativeElement.focus(); - } + this.accountElement.nativeElement.focus(); }, 0); } @@ -283,7 +261,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy } const old = this.voucher.inventories.find((x) => x.batch.id === (this.batch as Batch).id); if (old !== undefined) { - this.toaster.show('Danger', 'Product already added'); + this.snackBar.open('Product already added', 'Danger'); return; } this.voucher.inventories.push( @@ -304,9 +282,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy this.form.controls.addRow.reset(); this.batch = null; setTimeout(() => { - if (this.batchElement) { - this.batchElement.nativeElement.focus(); - } + this.batchElement.nativeElement.focus(); }, 0); } @@ -361,10 +337,10 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy this.ser.post(this.voucher.id as string).subscribe({ next: (result) => { this.loadVoucher(result); - this.toaster.show('Success', 'Voucher Posted'); + this.snackBar.open('Voucher Posted', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -373,7 +349,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy const voucher: Voucher = this.getVoucher(); this.ser.saveOrUpdate(voucher).subscribe({ next: (result) => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); if (voucher.id === result.id) { this.loadVoucher(result); } else { @@ -381,7 +357,7 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy } }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -399,11 +375,11 @@ export class PurchaseReturnComponent implements OnInit, AfterViewInit, OnDestroy delete() { this.ser.delete(this.voucher.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigate(['/purchase-return'], { replaceUrl: true }); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/purchase/purchase.component.html b/overlord/src/app/purchase/purchase.component.html index b254988e..677e25a8 100644 --- a/overlord/src/app/purchase/purchase.component.html +++ b/overlord/src/app/purchase/purchase.component.html @@ -17,14 +17,7 @@
Date - + diff --git a/overlord/src/app/purchase/purchase.component.ts b/overlord/src/app/purchase/purchase.component.ts index 0a63a8d0..5cfd2e13 100644 --- a/overlord/src/app/purchase/purchase.component.ts +++ b/overlord/src/app/purchase/purchase.component.ts @@ -1,6 +1,6 @@ import { COMMA, ENTER } from '@angular/cdk/keycodes'; import { AsyncPipe, DecimalPipe, PercentPipe, CurrencyPipe } from '@angular/common'; -import { AfterViewInit, Component, ElementRef, OnInit, OnDestroy, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, OnInit, ViewChild, HostListener } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete'; import { MatButton, MatIconButton } from '@angular/material/button'; @@ -33,7 +33,6 @@ import { MatRow, } from '@angular/material/table'; import { ActivatedRoute, Router } from '@angular/router'; -import { Hotkey, HotkeysService } from 'angular2-hotkeys'; import { round } from 'mathjs'; import moment from 'moment'; import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; @@ -50,7 +49,7 @@ import { Product } from '../core/product'; import { ProductSku } from '../core/product-sku'; import { Tag } from '../core/tag'; import { TagService } from '../core/tag.service'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { User } from '../core/user'; import { Voucher } from '../core/voucher'; import { VoucherService } from '../core/voucher.service'; @@ -116,11 +115,35 @@ import { PurchaseDialogComponent } from './purchase-dialog.component'; LocalTimePipe, ], }) -export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy { - @ViewChild('accountElement', { static: true }) accountElement?: ElementRef; - @ViewChild('productElement', { static: true }) productElement?: ElementRef; - @ViewChild('dateElement', { static: true }) dateElement?: ElementRef; +export class PurchaseComponent implements OnInit, AfterViewInit { + @ViewChild('accountElement', { static: true }) accountElement!: ElementRef; + @ViewChild('productElement', { static: true }) productElement!: ElementRef; + @ViewChild('dateElement', { static: true }) dateElement!: ElementRef; @ViewChild('tagInput') tagInput?: ElementRef; + + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.dateElement.nativeElement.focus(); + this.dateElement.nativeElement.select(); + } + + @HostListener('window:keydown.control.s', ['$event']) + saveListner(event: KeyboardEvent) { + event.preventDefault(); + if (this.canSave()) { + this.save(); + } + } + + @HostListener('window:keydown.control.p', ['$event']) + postListner(event: KeyboardEvent) { + event.preventDefault(); + if (this.voucher.id && !this.voucher.posted && this.auth.allowed('post-vouchers')) { + this.post(); + } + } + separatorKeysCodes: number[] = [ENTER, COMMA]; public inventoryObservable = new BehaviorSubject([]); dataSource: PurchaseDataSource = new PurchaseDataSource(this.inventoryObservable); @@ -153,8 +176,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy { private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private hotkeys: HotkeysService, - private toaster: ToasterService, + private snackBar: MatSnackBar, public auth: AuthService, private math: MathService, public image: ImageService, @@ -214,54 +236,12 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy { this.loadVoucher(data.voucher); }); - this.hotkeys.add( - new Hotkey( - 'f2', - (): boolean => { - setTimeout(() => { - if (this.dateElement) { - this.dateElement.nativeElement.focus(); - } - }, 0); - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); - this.hotkeys.add( - new Hotkey( - 'ctrl+s', - (): boolean => { - if (this.canSave()) { - this.save(); - } - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); - this.hotkeys.add( - new Hotkey( - 'ctrl+p', - (): boolean => { - if (this.voucher.id && !this.voucher.posted && this.auth.allowed('post-vouchers')) { - this.post(); - } - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); } ngAfterViewInit() { this.focusAccount(); } - ngOnDestroy() { - this.hotkeys.reset(); - } - loadVoucher(voucher: Voucher) { this.voucher = voucher; this.form.setValue({ @@ -284,9 +264,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy { focusAccount() { setTimeout(() => { - if (this.accountElement) { - this.accountElement.nativeElement.focus(); - } + this.accountElement.nativeElement.focus(); }, 0); } @@ -309,7 +287,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy { } const oldFiltered = this.voucher.inventories.filter((x) => x.batch?.sku.id === (this.product as ProductSku).id); if (oldFiltered.length) { - this.toaster.show('Danger', 'Product already added'); + this.snackBar.open('Product already added', 'Danger'); return; } this.voucher.inventories.push( @@ -333,9 +311,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy { this.form.controls.addRow.controls.tax.enable(); this.form.controls.addRow.controls.discount.enable(); setTimeout(() => { - if (this.productElement) { - this.productElement.nativeElement.focus(); - } + this.productElement.nativeElement.focus(); }, 0); } @@ -387,10 +363,10 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy { this.ser.post(this.voucher.id as string).subscribe({ next: (result) => { this.loadVoucher(result); - this.toaster.show('Success', 'Voucher Posted'); + this.snackBar.open('Voucher Posted', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -399,7 +375,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy { const voucher: Voucher = this.getVoucher(); this.ser.saveOrUpdate(voucher).subscribe({ next: (result) => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); if (voucher.id === result.id) { this.loadVoucher(result); } else { @@ -407,7 +383,7 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy { } }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -425,11 +401,11 @@ export class PurchaseComponent implements OnInit, AfterViewInit, OnDestroy { delete() { this.ser.delete(this.voucher.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigate(['/purchase'], { replaceUrl: true }); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/purchases/purchases.component.html b/overlord/src/app/purchases/purchases.component.html index 32e1094f..3101d50e 100644 --- a/overlord/src/app/purchases/purchases.component.html +++ b/overlord/src/app/purchases/purchases.component.html @@ -9,8 +9,8 @@ Start Date @@ -19,13 +19,7 @@ Finish Date - + diff --git a/overlord/src/app/purchases/purchases.component.ts b/overlord/src/app/purchases/purchases.component.ts index 566247b5..b050be27 100644 --- a/overlord/src/app/purchases/purchases.component.ts +++ b/overlord/src/app/purchases/purchases.component.ts @@ -1,5 +1,5 @@ import { DecimalPipe, CurrencyPipe } from '@angular/common'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardHeader, MatCardTitle, MatCardContent } from '@angular/material/card'; @@ -73,10 +73,11 @@ import { PurchasesItem } from './purchases-item'; ], }) export class PurchasesComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; + @ViewChild('startDateElement', { static: true }) startDate!: ElementRef; info: Purchases = new Purchases(); - dataSource: PurchasesDataSource = new PurchasesDataSource(this.info.body); + dataSource: PurchasesDataSource = new PurchasesDataSource(this.info.body, this.paginator, this.sort); form: FormGroup<{ startDate: FormControl; finishDate: FormControl; @@ -86,6 +87,13 @@ export class PurchasesComponent implements OnInit { /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['product', 'quantity', 'rate', 'amount']; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.startDate.nativeElement.focus(); + this.startDate.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, diff --git a/overlord/src/app/rate-contract/rate-contract-detail/rate-contract-detail.component.ts b/overlord/src/app/rate-contract/rate-contract-detail/rate-contract-detail.component.ts index 1927936b..e93b919b 100644 --- a/overlord/src/app/rate-contract/rate-contract-detail/rate-contract-detail.component.ts +++ b/overlord/src/app/rate-contract/rate-contract-detail/rate-contract-detail.component.ts @@ -32,7 +32,7 @@ import { Account } from '../../core/account'; import { AccountService } from '../../core/account.service'; import { Product } from '../../core/product'; import { ProductSku } from '../../core/product-sku'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ProductService } from '../../product/product.service'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { MathService } from '../../shared/math.service'; @@ -41,6 +41,7 @@ import { RateContractItem } from '../rate-contract-item'; import { RateContractService } from '../rate-contract.service'; import { RateContractDetailDatasource } from './rate-contract-detail-datasource'; +import { HostListener } from '@angular/core'; @Component({ selector: 'app-rate-contract-detail', @@ -83,8 +84,9 @@ import { RateContractDetailDatasource } from './rate-contract-detail-datasource' ], }) export class RateContractDetailComponent implements OnInit, AfterViewInit { - @ViewChild('accountElement', { static: true }) accountElement?: ElementRef; - @ViewChild('productElement', { static: true }) productElement?: ElementRef; + @ViewChild('accountElement', { static: true }) accountElement!: ElementRef; + @ViewChild('productElement', { static: true }) productElement!: ElementRef; + @ViewChild('dateElement', { static: true }) dateElement!: ElementRef; public itemsObservable = new BehaviorSubject([]); dataSource: RateContractDetailDatasource = new RateContractDetailDatasource(this.itemsObservable); form: FormGroup<{ @@ -109,10 +111,17 @@ export class RateContractDetailComponent implements OnInit, AfterViewInit { accounts: Observable; products: Observable; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.dateElement.nativeElement.focus(); + this.dateElement.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, - private toaster: ToasterService, + private snackBar: MatSnackBar, private dialog: MatDialog, private math: MathService, private ser: RateContractService, @@ -169,9 +178,7 @@ export class RateContractDetailComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.accountElement) { - this.accountElement.nativeElement.focus(); - } + this.accountElement.nativeElement.focus(); }, 0); } @@ -186,7 +193,7 @@ export class RateContractDetailComponent implements OnInit, AfterViewInit { } const oldFiltered = this.item.items.filter((x) => x.sku.id === (this.product as Product).id); if (oldFiltered.length) { - this.toaster.show('Danger', 'Product already added'); + this.snackBar.open('Product already added', 'Danger'); return; } this.item.items.push( @@ -203,9 +210,7 @@ export class RateContractDetailComponent implements OnInit, AfterViewInit { this.form.controls.addRow.reset(); this.product = null; setTimeout(() => { - if (this.productElement) { - this.productElement.nativeElement.focus(); - } + this.productElement.nativeElement.focus(); }, 0); } @@ -233,11 +238,11 @@ export class RateContractDetailComponent implements OnInit, AfterViewInit { save() { this.ser.saveOrUpdate(this.getItem()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/rate-contracts'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -245,11 +250,11 @@ export class RateContractDetailComponent implements OnInit, AfterViewInit { delete() { this.ser.delete(this.item.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/rate-contracts'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/rate-contract/rate-contract-list/rate-contract-list.component.ts b/overlord/src/app/rate-contract/rate-contract-list/rate-contract-list.component.ts index 49c13741..4af28a2c 100644 --- a/overlord/src/app/rate-contract/rate-contract-list/rate-contract-list.component.ts +++ b/overlord/src/app/rate-contract/rate-contract-list/rate-contract-list.component.ts @@ -54,10 +54,10 @@ import { RateContractListDatasource } from './rate-contract-list-datasource'; ], }) export class RateContractListComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; list: RateContract[] = []; - dataSource: RateContractListDatasource = new RateContractListDatasource(this.list); + dataSource: RateContractListDatasource = new RateContractListDatasource(this.list, this.paginator, this.sort); /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['vendor', 'validity', 'products']; diff --git a/overlord/src/app/raw-material-cost/raw-material-cost.component.html b/overlord/src/app/raw-material-cost/raw-material-cost.component.html index 12532a60..a6337b79 100644 --- a/overlord/src/app/raw-material-cost/raw-material-cost.component.html +++ b/overlord/src/app/raw-material-cost/raw-material-cost.component.html @@ -16,8 +16,8 @@ Start Date @@ -26,13 +26,7 @@ Finish Date - + diff --git a/overlord/src/app/raw-material-cost/raw-material-cost.component.ts b/overlord/src/app/raw-material-cost/raw-material-cost.component.ts index bf408b40..e111aa91 100644 --- a/overlord/src/app/raw-material-cost/raw-material-cost.component.ts +++ b/overlord/src/app/raw-material-cost/raw-material-cost.component.ts @@ -1,5 +1,5 @@ import { DecimalPipe, PercentPipe, CurrencyPipe } from '@angular/common'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatIconButton, MatButton } from '@angular/material/button'; import { MatCard, MatCardHeader, MatCardTitleGroup, MatCardTitle, MatCardContent } from '@angular/material/card'; @@ -79,10 +79,11 @@ import { RawMaterialCostDataSource } from './raw-material-cost-datasource'; ], }) export class RawMaterialCostComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; + @ViewChild('startDateElement', { static: true }) startDate!: ElementRef; info: RawMaterialCost = new RawMaterialCost(); - dataSource: RawMaterialCostDataSource = new RawMaterialCostDataSource(this.info.body); + dataSource: RawMaterialCostDataSource = new RawMaterialCostDataSource(this.info.body, this.paginator, this.sort); form: FormGroup<{ startDate: FormControl; finishDate: FormControl; @@ -99,6 +100,13 @@ export class RawMaterialCostComponent implements OnInit { columnsNoId = ['name', 'issue', 'sale', 'rmc']; columnsId = ['name', 'group', 'quantity', 'net', 'gross']; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.startDate.nativeElement.focus(); + this.startDate.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, @@ -144,7 +152,7 @@ export class RawMaterialCostComponent implements OnInit { } exportCsv() { - let headers: { [display: string]: string }; + let headers: Record; if (this.info.id) { headers = { Name: 'name', Group: 'group', Quantity: 'quantity', Net: 'net', Gross: 'gross' }; } else { diff --git a/overlord/src/app/receipt/receipt.component.html b/overlord/src/app/receipt/receipt.component.html index 1aaf1fdf..708bae84 100644 --- a/overlord/src/app/receipt/receipt.component.html +++ b/overlord/src/app/receipt/receipt.component.html @@ -17,14 +17,7 @@
Date - + diff --git a/overlord/src/app/receipt/receipt.component.ts b/overlord/src/app/receipt/receipt.component.ts index d3055cee..c7ecf10e 100644 --- a/overlord/src/app/receipt/receipt.component.ts +++ b/overlord/src/app/receipt/receipt.component.ts @@ -1,6 +1,6 @@ import { COMMA, ENTER } from '@angular/cdk/keycodes'; import { AsyncPipe, CurrencyPipe } from '@angular/common'; -import { AfterViewInit, Component, ElementRef, OnInit, OnDestroy, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, OnInit, ViewChild, HostListener } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete'; import { MatButton, MatIconButton } from '@angular/material/button'; @@ -34,7 +34,6 @@ import { MatRow, } from '@angular/material/table'; import { ActivatedRoute, Router } from '@angular/router'; -import { Hotkey, HotkeysService } from 'angular2-hotkeys'; import { round } from 'mathjs'; import moment from 'moment'; import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; @@ -48,7 +47,7 @@ import { DbFile } from '../core/db-file'; import { Journal } from '../core/journal'; import { Tag } from '../core/tag'; import { TagService } from '../core/tag.service'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { User } from '../core/user'; import { Voucher } from '../core/voucher'; import { VoucherService } from '../core/voucher.service'; @@ -112,10 +111,34 @@ import { ReceiptDialogComponent } from './receipt-dialog.component'; LocalTimePipe, ], }) -export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy { - @ViewChild('accountElement', { static: true }) accountElement?: ElementRef; - @ViewChild('dateElement', { static: true }) dateElement?: ElementRef; +export class ReceiptComponent implements OnInit, AfterViewInit { + @ViewChild('accountElement', { static: true }) accountElement!: ElementRef; + @ViewChild('dateElement', { static: true }) dateElement!: ElementRef; @ViewChild('tagInput') tagInput?: ElementRef; + + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.dateElement.nativeElement.focus(); + this.dateElement.nativeElement.select(); + } + + @HostListener('window:keydown.control.s', ['$event']) + saveListner(event: KeyboardEvent) { + event.preventDefault(); + if (this.canSave()) { + this.save(); + } + } + + @HostListener('window:keydown.control.p', ['$event']) + postListner(event: KeyboardEvent) { + event.preventDefault(); + if (this.voucher.id && !this.voucher.posted && this.auth.allowed('post-vouchers')) { + this.post(); + } + } + separatorKeysCodes: number[] = [ENTER, COMMA]; public journalObservable = new BehaviorSubject([]); dataSource: ReceiptDataSource = new ReceiptDataSource(this.journalObservable); @@ -146,8 +169,7 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy { private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private hotkeys: HotkeysService, - private toaster: ToasterService, + private snackBar: MatSnackBar, public auth: AuthService, private math: MathService, public image: ImageService, @@ -196,54 +218,12 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy { this.receiptAccounts = data.receiptAccounts; this.loadVoucher(data.voucher); }); - this.hotkeys.add( - new Hotkey( - 'f2', - (): boolean => { - setTimeout(() => { - if (this.dateElement) { - this.dateElement.nativeElement.focus(); - } - }, 0); - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); - this.hotkeys.add( - new Hotkey( - 'ctrl+s', - (): boolean => { - if (this.canSave()) { - this.save(); - } - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); - this.hotkeys.add( - new Hotkey( - 'ctrl+p', - (): boolean => { - if (this.voucher.id && !this.voucher.posted && this.auth.allowed('post-vouchers')) { - this.post(); - } - return false; // Prevent bubbling - }, - ['INPUT', 'SELECT', 'TEXTAREA'], - ), - ); } ngAfterViewInit() { this.focusAccount(); } - ngOnDestroy() { - this.hotkeys.reset(); - } - loadVoucher(voucher: Voucher) { this.voucher = voucher; [this.receiptJournal] = this.voucher.journals.filter((x) => x.debit === 1); @@ -264,9 +244,7 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy { focusAccount() { setTimeout(() => { - if (this.accountElement) { - this.accountElement.nativeElement.focus(); - } + this.accountElement.nativeElement.focus(); }, 0); } @@ -302,9 +280,7 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy { this.account = null; this.accBal = null; setTimeout(() => { - if (this.accountElement) { - this.accountElement.nativeElement.focus(); - } + this.accountElement.nativeElement.focus(); }, 0); } @@ -359,10 +335,10 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy { this.ser.post(this.voucher.id as string).subscribe({ next: (result) => { this.loadVoucher(result); - this.toaster.show('Success', 'Voucher Posted'); + this.snackBar.open('Voucher Posted', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -371,7 +347,7 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy { const voucher: Voucher = this.getVoucher(); this.ser.saveOrUpdate(voucher).subscribe({ next: (result) => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); if (voucher.id === result.id) { this.loadVoucher(result); } else { @@ -379,7 +355,7 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy { } }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -395,11 +371,11 @@ export class ReceiptComponent implements OnInit, AfterViewInit, OnDestroy { delete() { this.ser.delete(this.voucher.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigate(['/receipt'], { replaceUrl: true }); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.html b/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.html index 641d328c..4b4e07f2 100644 --- a/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.html +++ b/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.html @@ -12,14 +12,7 @@ Date - + diff --git a/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.ts b/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.ts index 776f3c63..f55631ed 100644 --- a/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.ts +++ b/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.ts @@ -10,7 +10,7 @@ import { MatInput } from '@angular/material/input'; import { ActivatedRoute, Router } from '@angular/router'; import moment from 'moment'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { RecipeTemplate } from '../recipe-template'; import { RecipeTemplateService } from '../recipe-template.service'; @@ -39,7 +39,7 @@ import { RecipeTemplateService } from '../recipe-template.service'; ], }) export class RecipeTemplateDetailComponent implements OnInit, AfterViewInit { - @ViewChild('nameElement', { static: true }) nameElement?: ElementRef; + @ViewChild('nameElement', { static: true }) nameElement!: ElementRef; form: FormGroup<{ name: FormControl; date: FormControl; @@ -53,7 +53,7 @@ export class RecipeTemplateDetailComponent implements OnInit, AfterViewInit { private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private toaster: ToasterService, + private snackBar: MatSnackBar, private ser: RecipeTemplateService, ) { this.form = new FormGroup({ @@ -84,20 +84,18 @@ export class RecipeTemplateDetailComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.nameElement) { - this.nameElement.nativeElement.focus(); - } + this.nameElement.nativeElement.focus(); }, 0); } save() { this.ser.saveOrUpdate(this.getItem()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/recipe-templates'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -105,11 +103,11 @@ export class RecipeTemplateDetailComponent implements OnInit, AfterViewInit { delete() { this.ser.delete(this.item.id).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/recipe-templates'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.ts b/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.ts index 6f472fb5..2d849f40 100644 --- a/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.ts +++ b/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.ts @@ -52,10 +52,10 @@ import { RecipeTemplateListDataSource } from './recipe-template-list-datasource' ], }) export class RecipeTemplateListComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; list: RecipeTemplate[] = []; - dataSource: RecipeTemplateListDataSource = new RecipeTemplateListDataSource(this.list); + dataSource: RecipeTemplateListDataSource = new RecipeTemplateListDataSource(this.list, this.paginator, this.sort); /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['name', 'selected', 'date']; diff --git a/overlord/src/app/recipe/recipe-detail/recipe-detail.component.html b/overlord/src/app/recipe/recipe-detail/recipe-detail.component.html index 1a8df8f6..9b5e0453 100644 --- a/overlord/src/app/recipe/recipe-detail/recipe-detail.component.html +++ b/overlord/src/app/recipe/recipe-detail/recipe-detail.component.html @@ -7,14 +7,7 @@
Date - + diff --git a/overlord/src/app/recipe/recipe-detail/recipe-detail.component.ts b/overlord/src/app/recipe/recipe-detail/recipe-detail.component.ts index 98bc0d68..9d3ec66d 100644 --- a/overlord/src/app/recipe/recipe-detail/recipe-detail.component.ts +++ b/overlord/src/app/recipe/recipe-detail/recipe-detail.component.ts @@ -31,7 +31,7 @@ import { Period } from 'src/app/period/period'; import { Product } from '../../core/product'; import { ProductSku } from '../../core/product-sku'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ProductService } from '../../product/product.service'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { MathService } from '../../shared/math.service'; @@ -82,8 +82,8 @@ import { RecipeDetailDatasource } from './recipe-detail-datasource'; ], }) export class RecipeDetailComponent implements OnInit, AfterViewInit { - @ViewChild('productElement', { static: true }) productElement?: ElementRef; - @ViewChild('ingredientElement', { static: true }) ingredientElement?: ElementRef; + @ViewChild('productElement', { static: true }) productElement!: ElementRef; + @ViewChild('ingredientElement', { static: true }) ingredientElement!: ElementRef; public itemsObservable = new BehaviorSubject([]); dataSource: RecipeDetailDatasource = new RecipeDetailDatasource(this.itemsObservable); form: FormGroup<{ @@ -114,7 +114,7 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private toaster: ToasterService, + private snackBar: MatSnackBar, private math: MathService, private ser: RecipeService, private productSer: ProductService, @@ -182,9 +182,7 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.productElement) { - this.productElement.nativeElement.focus(); - } + this.productElement.nativeElement.focus(); }, 0); } @@ -218,7 +216,7 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { } const oldFiltered = this.item.items.filter((x) => x.product.id === (this.ingredient as ProductSku).id); if (oldFiltered.length) { - this.toaster.show('Danger', 'Product already added'); + this.snackBar.open('Product already added', 'Danger'); return; } this.item.items.push( @@ -236,9 +234,7 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { this.form.controls.addRow.reset(); this.ingredient = null; setTimeout(() => { - if (this.ingredientElement) { - this.ingredientElement.nativeElement.focus(); - } + this.ingredientElement.nativeElement.focus(); }, 0); } @@ -254,11 +250,11 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { save() { this.ser.saveOrUpdate(this.getItem()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/recipes'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -266,11 +262,11 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { delete() { this.ser.delete(this.item.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigate(['/recipes']); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/recipe/recipe-list/recipe-list.component.ts b/overlord/src/app/recipe/recipe-list/recipe-list.component.ts index 2e2f7d6b..84440ef3 100644 --- a/overlord/src/app/recipe/recipe-list/recipe-list.component.ts +++ b/overlord/src/app/recipe/recipe-list/recipe-list.component.ts @@ -65,8 +65,8 @@ import { RecipeListDatasource } from './recipe-list-datasource'; ], }) export class RecipeListComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; form: FormGroup<{ period: FormControl; productGroup: FormControl; @@ -75,10 +75,16 @@ export class RecipeListComponent implements OnInit { productGroups: ProductGroup[] = []; periods: Period[] = []; periodFilter: BehaviorSubject = new BehaviorSubject(new Period()); - productGroupFilter: BehaviorSubject = new BehaviorSubject(''); + productGroupFilter = new BehaviorSubject(''); list: Recipe[] = []; data: BehaviorSubject = new BehaviorSubject([]); - dataSource: RecipeListDatasource = new RecipeListDatasource(this.productGroupFilter, this.data); + dataSource: RecipeListDatasource = new RecipeListDatasource( + this.productGroupFilter, + this.data, + this.paginator, + this.sort, + ); + period: Period = new Period(); /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ diff --git a/overlord/src/app/role/role-detail/role-detail.component.ts b/overlord/src/app/role/role-detail/role-detail.component.ts index 633cf6a1..b9fadfd6 100644 --- a/overlord/src/app/role/role-detail/role-detail.component.ts +++ b/overlord/src/app/role/role-detail/role-detail.component.ts @@ -9,7 +9,7 @@ import { MatFormField, MatLabel } from '@angular/material/form-field'; import { MatInput } from '@angular/material/input'; import { ActivatedRoute, Router } from '@angular/router'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { Role } from '../role'; import { RoleService } from '../role.service'; @@ -35,7 +35,7 @@ import { RoleService } from '../role.service'; ], }) export class RoleDetailComponent implements OnInit, AfterViewInit { - @ViewChild('nameElement', { static: true }) nameElement?: ElementRef; + @ViewChild('nameElement', { static: true }) nameElement!: ElementRef; form: FormGroup<{ name: FormControl; permissions: FormArray< @@ -50,7 +50,7 @@ export class RoleDetailComponent implements OnInit, AfterViewInit { constructor( private route: ActivatedRoute, private router: Router, - private toaster: ToasterService, + private snackBar: MatSnackBar, private dialog: MatDialog, private ser: RoleService, ) { @@ -79,20 +79,18 @@ export class RoleDetailComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.nameElement) { - this.nameElement.nativeElement.focus(); - } + this.nameElement.nativeElement.focus(); }, 0); } save() { this.ser.saveOrUpdate(this.getItem()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/roles'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -100,11 +98,11 @@ export class RoleDetailComponent implements OnInit, AfterViewInit { delete() { this.ser.delete(this.item.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/roles'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/role/role-list/role-list.component.ts b/overlord/src/app/role/role-list/role-list.component.ts index 97371cf5..bbcf77bf 100644 --- a/overlord/src/app/role/role-list/role-list.component.ts +++ b/overlord/src/app/role/role-list/role-list.component.ts @@ -52,10 +52,10 @@ import { RoleListDatasource } from './role-list-datasource'; ], }) export class RoleListComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; list: Role[] = []; - dataSource: RoleListDatasource = new RoleListDatasource(this.list); + dataSource: RoleListDatasource = new RoleListDatasource(this.list, this.paginator, this.sort); /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['name', 'permissions']; diff --git a/overlord/src/app/settings/settings.component.ts b/overlord/src/app/settings/settings.component.ts index fa2b5ffe..a281dddc 100644 --- a/overlord/src/app/settings/settings.component.ts +++ b/overlord/src/app/settings/settings.component.ts @@ -29,7 +29,7 @@ import { BehaviorSubject } from 'rxjs'; import { environment } from '../app.environment'; import { AuthService } from '../auth/auth.service'; import { AccountType } from '../core/account-type'; -import { ToasterService } from '../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component'; import { LockDataSource } from './lock-datasource'; @@ -115,7 +115,7 @@ export class SettingsComponent implements OnInit { private route: ActivatedRoute, private router: Router, private dialog: MatDialog, - private toaster: ToasterService, + private snackBar: MatSnackBar, public auth: AuthService, private ser: SettingsService, ) { @@ -243,10 +243,10 @@ export class SettingsComponent implements OnInit { this.ser.setLockInformation(item).subscribe({ next: (result) => { this.showLockInformation(result); - this.toaster.show('Success', 'Lock information Updated'); + this.snackBar.open('Lock information Updated', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -277,10 +277,10 @@ export class SettingsComponent implements OnInit { rebaseData(rebaseDate: string) { this.ser.rebaseDatabase(rebaseDate).subscribe({ next: () => { - this.toaster.show('Success', 'Data has been rebased!'); + this.snackBar.open('Data has been rebased!', 'Success'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/shared/cookie.service.ts b/overlord/src/app/shared/cookie.service.ts index f007fe28..04b9d67a 100644 --- a/overlord/src/app/shared/cookie.service.ts +++ b/overlord/src/app/shared/cookie.service.ts @@ -5,7 +5,7 @@ import { Injectable } from '@angular/core'; }) export class CookieService { public getCookie(name: string) { - const ca: Array = document.cookie.split(';'); + const ca: string[] = document.cookie.split(';'); const caLen: number = ca.length; const cookieName = `${name}=`; let c: string; diff --git a/overlord/src/app/shared/to-csv-type.ts b/overlord/src/app/shared/to-csv-type.ts index 87d1981c..d7f027bc 100644 --- a/overlord/src/app/shared/to-csv-type.ts +++ b/overlord/src/app/shared/to-csv-type.ts @@ -1,3 +1 @@ -export interface ToCsvType { - [column: string]: string | number | boolean | null; -} +export type ToCsvType = Record; diff --git a/overlord/src/app/shared/to-csv.service.ts b/overlord/src/app/shared/to-csv.service.ts index 3eb8da4d..f2b4d2c3 100644 --- a/overlord/src/app/shared/to-csv.service.ts +++ b/overlord/src/app/shared/to-csv.service.ts @@ -6,7 +6,7 @@ import { ToCsvType } from './to-csv-type'; providedIn: 'root', }) export class ToCsvService { - toCsv(headers: { [display: string]: string }, data: unknown[]): string { + toCsv(headers: Record, data: unknown[]): string { const header = Object.keys(headers); const replacer = (key: string, value: string | number | null) => (value === null ? '' : value); const csv = data.map((row) => diff --git a/overlord/src/app/stock-movement/stock-movement.component.html b/overlord/src/app/stock-movement/stock-movement.component.html index f8ba0fe6..b96ab3f7 100644 --- a/overlord/src/app/stock-movement/stock-movement.component.html +++ b/overlord/src/app/stock-movement/stock-movement.component.html @@ -9,8 +9,8 @@ Start Date @@ -19,13 +19,7 @@ Finish Date - + diff --git a/overlord/src/app/stock-movement/stock-movement.component.ts b/overlord/src/app/stock-movement/stock-movement.component.ts index 8afa7c5d..8691e9d5 100644 --- a/overlord/src/app/stock-movement/stock-movement.component.ts +++ b/overlord/src/app/stock-movement/stock-movement.component.ts @@ -1,5 +1,5 @@ import { DecimalPipe } from '@angular/common'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardHeader, MatCardTitle, MatCardContent } from '@angular/material/card'; @@ -63,10 +63,11 @@ import { StockMovementDataSource } from './stock-movement-datasource'; ], }) export class StockMovementComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; + @ViewChild('startDateElement', { static: true }) startDate!: ElementRef; info: StockMovement = new StockMovement(); - dataSource: StockMovementDataSource = new StockMovementDataSource(this.info.body); + dataSource: StockMovementDataSource = new StockMovementDataSource(this.info.body, this.paginator, this.sort); form: FormGroup<{ startDate: FormControl; finishDate: FormControl; @@ -76,6 +77,13 @@ export class StockMovementComponent implements OnInit { /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['group', 'name', 'opening', 'purchase', 'issue', 'closing']; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.startDate.nativeElement.focus(); + this.startDate.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, diff --git a/overlord/src/app/trial-balance/trial-balance.component.html b/overlord/src/app/trial-balance/trial-balance.component.html index 335b0d4f..cc006b49 100644 --- a/overlord/src/app/trial-balance/trial-balance.component.html +++ b/overlord/src/app/trial-balance/trial-balance.component.html @@ -7,13 +7,7 @@
Date - + diff --git a/overlord/src/app/trial-balance/trial-balance.component.ts b/overlord/src/app/trial-balance/trial-balance.component.ts index e763de05..9f006424 100644 --- a/overlord/src/app/trial-balance/trial-balance.component.ts +++ b/overlord/src/app/trial-balance/trial-balance.component.ts @@ -1,5 +1,5 @@ import { CurrencyPipe } from '@angular/common'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { MatCard, MatCardHeader, MatCardTitle, MatCardContent } from '@angular/material/card'; @@ -65,10 +65,11 @@ import { TrialBalanceDataSource } from './trial-balance-datasource'; ], }) export class TrialBalanceComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; - @ViewChild(MatSort, { static: true }) sort?: MatSort; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; + @ViewChild('dateElement', { static: true }) dateElement!: ElementRef; info: TrialBalance = new TrialBalance(); - dataSource: TrialBalanceDataSource = new TrialBalanceDataSource(this.info.body); + dataSource: TrialBalanceDataSource = new TrialBalanceDataSource(this.info.body, this.paginator, this.sort); form: FormGroup<{ date: FormControl; }>; @@ -76,6 +77,13 @@ export class TrialBalanceComponent implements OnInit { /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['type', 'name', 'debit', 'credit']; + @HostListener('window:keydown.f2', ['$event']) + focusDate(event: KeyboardEvent) { + event.preventDefault(); + this.dateElement.nativeElement.focus(); + this.dateElement.nativeElement.select(); + } + constructor( private route: ActivatedRoute, private router: Router, diff --git a/overlord/src/app/user/user-detail/user-detail.component.ts b/overlord/src/app/user/user-detail/user-detail.component.ts index 06898825..817d2a4b 100644 --- a/overlord/src/app/user/user-detail/user-detail.component.ts +++ b/overlord/src/app/user/user-detail/user-detail.component.ts @@ -10,7 +10,7 @@ import { MatIcon } from '@angular/material/icon'; import { MatInput } from '@angular/material/input'; import { ActivatedRoute, Router } from '@angular/router'; -import { ToasterService } from '../../core/toaster.service'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { User } from '../../core/user'; import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; import { UserService } from '../user.service'; @@ -38,7 +38,7 @@ import { UserService } from '../user.service'; ], }) export class UserDetailComponent implements OnInit, AfterViewInit { - @ViewChild('nameElement', { static: true }) nameElement?: ElementRef; + @ViewChild('nameElement', { static: true }) nameElement!: ElementRef; form: FormGroup<{ name: FormControl; password: FormControl; @@ -56,7 +56,7 @@ export class UserDetailComponent implements OnInit, AfterViewInit { constructor( private route: ActivatedRoute, private router: Router, - private toaster: ToasterService, + private snackBar: MatSnackBar, private dialog: MatDialog, private ser: UserService, ) { @@ -94,9 +94,7 @@ export class UserDetailComponent implements OnInit, AfterViewInit { ngAfterViewInit() { setTimeout(() => { - if (this.nameElement) { - this.nameElement.nativeElement.focus(); - } + this.nameElement.nativeElement.focus(); }, 0); } @@ -104,21 +102,21 @@ export class UserDetailComponent implements OnInit, AfterViewInit { if (this.route.snapshot.paramMap.get('id') === 'me') { this.ser.updateMe(this.getItem()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } else { this.ser.saveOrUpdate(this.getItem()).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/users'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } @@ -127,11 +125,11 @@ export class UserDetailComponent implements OnInit, AfterViewInit { delete() { this.ser.delete(this.item.id as string).subscribe({ next: () => { - this.toaster.show('Success', ''); + this.snackBar.open('', 'Success'); this.router.navigateByUrl('/users'); }, error: (error) => { - this.toaster.show('Danger', error); + this.snackBar.open(error, 'Danger'); }, }); } diff --git a/overlord/src/app/user/user-list/user-list.component.ts b/overlord/src/app/user/user-list/user-list.component.ts index b25a9992..25ff38bc 100644 --- a/overlord/src/app/user/user-list/user-list.component.ts +++ b/overlord/src/app/user/user-list/user-list.component.ts @@ -54,8 +54,8 @@ import { UserListDataSource } from './user-list-datasource'; ], }) export class UserListComponent implements OnInit { - @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator = undefined; - @ViewChild(MatSort, { static: true }) sort?: MatSort = undefined; + @ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator; + @ViewChild(MatSort, { static: true }) sort!: MatSort; list: User[] = []; dataSource: UserListDataSource = new UserListDataSource(this.list); /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ diff --git a/overlord/src/main.ts b/overlord/src/main.ts index 6278520e..df46ffce 100644 --- a/overlord/src/main.ts +++ b/overlord/src/main.ts @@ -3,6 +3,11 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; import { appConfig } from './app/app.config'; +import enIn from '@angular/common/locales/en-IN'; +import {registerLocaleData} from '@angular/common'; //function to register it. + +registerLocaleData(enIn); + bootstrapApplication(AppComponent, appConfig) .catch((err) => console.error(err));