Implement keyboard shortcuts and workflow changes

This commit is contained in:
2026-08-25 18:51:03 +05:30
parent 1b2ef5126e
commit fa2522e678
36 changed files with 1865 additions and 181 deletions
@@ -0,0 +1,129 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Component, inject } from '@angular/core';
import {
MAT_DIALOG_DATA,
MatDialogRef,
MatDialogTitle,
MatDialogContent,
MatDialogActions
} from '@angular/material/dialog';
import { KeyboardShortcutService, ShortcutItem } from '../../core/services/keyboard-shortcut.service';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { FormsModule } from '@angular/forms';
import { MatButton } from '@angular/material/button';
import { MatFormField, MatLabel, MatError } from '@angular/material/form-field';
import { MatInput } from '@angular/material/input';
import { take } from 'rxjs/operators';
@Component({
selector: 'mifosx-inline-create-shortcut-dialog',
templateUrl: './inline-create-shortcut-dialog.component.html',
styleUrls: ['../keyboard-shortcuts-dialog/keyboard-shortcuts-dialog.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
MatDialogTitle,
MatDialogContent,
MatDialogActions,
MatButton,
FormsModule,
MatFormField,
MatLabel,
MatInput,
MatError
]
})
export class InlineCreateShortcutDialogComponent {
dialogRef = inject<MatDialogRef<InlineCreateShortcutDialogComponent>>(MatDialogRef);
data = inject(MAT_DIALOG_DATA);
private keyboardShortcutService = inject(KeyboardShortcutService);
newShortcut: Partial<ShortcutItem> = {
title: this.data.pageName,
route: this.data.route,
action: 'navigate',
key: '',
ctrlKey: false,
shiftKey: false,
altKey: false,
custom: true,
id: 'custom_' + Date.now()
};
validationError: string | null = null;
formatShortcut(item: Partial<ShortcutItem>): string {
if (!item || (!item.key && !item.ctrlKey && !item.shiftKey && !item.altKey)) return '';
const parts = [];
if (item.ctrlKey) parts.push('Ctrl');
if (item.shiftKey) parts.push('Shift');
if (item.altKey) parts.push('Alt');
if (item.key) parts.push(item.key.toUpperCase());
return parts.join(' + ');
}
onKeyDown(event: KeyboardEvent) {
event.preventDefault();
event.stopPropagation();
// Ignore if only modifier keys are pressed
if ([
'Control',
'Shift',
'Alt',
'Meta'
].includes(event.key)) {
return;
}
this.newShortcut.ctrlKey = event.ctrlKey;
this.newShortcut.shiftKey = event.shiftKey;
this.newShortcut.altKey = event.altKey;
this.newShortcut.key = event.key.toLowerCase();
this.validationError = null;
}
save() {
if (!this.newShortcut.title || this.newShortcut.title.trim() === '') {
this.validationError = 'Shortcut Name is required';
return;
}
if (!this.newShortcut.key) {
this.validationError = 'Key combination is required';
return;
}
const keyStr = `${!!this.newShortcut.ctrlKey}-${!!this.newShortcut.shiftKey}-${!!this.newShortcut.altKey}-${this.newShortcut.key.toLowerCase()}`;
this.keyboardShortcutService.activeShortcuts$.pipe(take(1)).subscribe((shortcuts) => {
// Check for duplicates
const seen = new Set<string>();
for (const s of shortcuts) {
seen.add(`${!!s.ctrlKey}-${!!s.shiftKey}-${!!s.altKey}-${s.key.toLowerCase()}`);
}
if (seen.has(keyStr)) {
this.validationError = `Duplicate shortcut detected: ${this.formatShortcut(this.newShortcut)}`;
return;
}
// Add and save
if (this.newShortcut.route) {
this.newShortcut.route = this.keyboardShortcutService.normalizeRouteStr(this.newShortcut.route);
}
shortcuts.push(this.newShortcut as ShortcutItem);
this.keyboardShortcutService.updateShortcuts(shortcuts);
this.dialogRef.close(true);
});
}
cancel() {
this.dialogRef.close(false);
}
}