559 lines
16 KiB
TypeScript
Executable File
559 lines
16 KiB
TypeScript
Executable File
// import {
|
|
// Component,
|
|
// ElementRef,
|
|
// ViewChild,
|
|
// ChangeDetectorRef,
|
|
// OnInit,
|
|
// OnDestroy,
|
|
// } from '@angular/core';
|
|
// import {
|
|
// FormBuilder,
|
|
// FormGroup,
|
|
// Validators,
|
|
// ReactiveFormsModule,
|
|
// } from '@angular/forms';
|
|
// import { CommonModule } from '@angular/common';
|
|
// import { Router } from '@angular/router';
|
|
// import { BtcService } from '../service/btc.service';
|
|
// import { HttpClientModule } from '@angular/common/http';
|
|
// import { HttpResponse } from '@angular/common/http';
|
|
|
|
// @Component({
|
|
// selector: 'app-login',
|
|
// standalone: true,
|
|
// imports: [CommonModule, ReactiveFormsModule, HttpClientModule],
|
|
// templateUrl: './login.component.html',
|
|
// styleUrls: ['./login.component.css'],
|
|
// })
|
|
// export class LoginComponent implements OnInit, OnDestroy {
|
|
// loginForm: FormGroup;
|
|
// focusedField: 'email' | 'password' | null = null;
|
|
// loginError: string | null = null;
|
|
// passwordStatus: boolean = true;
|
|
// scanningEnabled: boolean = true;
|
|
|
|
// @ViewChild('emailInput', { static: true })
|
|
// emailInputRef!: ElementRef<HTMLInputElement>;
|
|
// @ViewChild('passwordInput', { static: true })
|
|
// passwordInputRef!: ElementRef<HTMLInputElement>;
|
|
|
|
// private barcodeBuffer = '';
|
|
// private scanStartedAt = 0;
|
|
// private readonly SCAN_GAP_MS = 100;
|
|
|
|
// constructor(
|
|
// private fb: FormBuilder,
|
|
// private cdRef: ChangeDetectorRef,
|
|
// private router: Router,
|
|
// private btcService: BtcService
|
|
// ) {
|
|
// this.loginForm = this.fb.group({
|
|
// email: ['', Validators.required],
|
|
// password: ['', [Validators.required, Validators.pattern(/^\d{6}$/)]],
|
|
// });
|
|
// }
|
|
|
|
// ngOnInit(): void {
|
|
// window.addEventListener('keydown', this.handleScan);
|
|
// }
|
|
|
|
// ngOnDestroy(): void {
|
|
// window.removeEventListener('keydown', this.handleScan);
|
|
// }
|
|
|
|
// toggleScan(): void {
|
|
// this.scanningEnabled = !this.scanningEnabled;
|
|
// }
|
|
|
|
// handleScan = (event: KeyboardEvent): void => {
|
|
// if (this.focusedField !== 'email') return;
|
|
|
|
// if (!this.scanningEnabled) {
|
|
// const ctrl = this.loginForm.get('email')!;
|
|
// let val = ctrl.value || '';
|
|
// if (event.key >= '0' && event.key <= '9') {
|
|
// if (val.length < 12) {
|
|
// val += event.key;
|
|
// ctrl.setValue(val);
|
|
// this.emailInputRef.nativeElement.value = val;
|
|
// }
|
|
// event.preventDefault();
|
|
// } else if (event.key === 'Backspace') {
|
|
// val = val.slice(0, -1);
|
|
// ctrl.setValue(val);
|
|
// this.emailInputRef.nativeElement.value = val;
|
|
// event.preventDefault();
|
|
// } else if (event.key === 'Delete') {
|
|
// ctrl.setValue('');
|
|
// this.emailInputRef.nativeElement.value = '';
|
|
// event.preventDefault();
|
|
// }
|
|
// return;
|
|
// }
|
|
|
|
// const now = Date.now();
|
|
// if (now - this.scanStartedAt > this.SCAN_GAP_MS) {
|
|
// this.barcodeBuffer = '';
|
|
// }
|
|
// this.scanStartedAt = now;
|
|
|
|
// if (event.key === 'Enter') {
|
|
// this.commitScan();
|
|
// return;
|
|
// }
|
|
|
|
// const ch = this.toPrintableChar(event);
|
|
// if (ch) this.barcodeBuffer += ch;
|
|
// };
|
|
|
|
// private toPrintableChar(ev: KeyboardEvent): string {
|
|
// if (ev.key.length === 1) return ev.key;
|
|
// if (ev.key === 'Process') {
|
|
// if (ev.keyCode >= 48 && ev.keyCode <= 57)
|
|
// return String.fromCharCode(ev.keyCode);
|
|
// if (ev.keyCode >= 65 && ev.keyCode <= 90)
|
|
// return String.fromCharCode(ev.keyCode);
|
|
// }
|
|
// return '';
|
|
// }
|
|
|
|
// private commitScan(): void {
|
|
// if (!this.barcodeBuffer) return;
|
|
// this.loginForm.get('email')!.setValue(this.barcodeBuffer);
|
|
// this.emailInputRef.nativeElement.value = this.barcodeBuffer;
|
|
// this.setFocus('password');
|
|
// setTimeout(() => this.passwordInputRef.nativeElement.focus(), 0);
|
|
// this.barcodeBuffer = '';
|
|
// }
|
|
|
|
// removeReadonly(input: HTMLInputElement): void {
|
|
// input.removeAttribute('readonly');
|
|
// setTimeout(() => input.setAttribute('readonly', 'true'), 100);
|
|
// }
|
|
|
|
// setFocus(field: 'email' | 'password'): void {
|
|
// this.focusedField = field;
|
|
// }
|
|
|
|
// formatUsernameDisplay(raw: string): string {
|
|
// return /^\d+$/.test(raw) ? raw.replace(/(\d{4})(?=\d)/g, '$1-') : raw;
|
|
// }
|
|
|
|
// onNumpadClick(value: string): void {
|
|
// if (this.focusedField === 'password') {
|
|
// const ctrl = this.loginForm.get('password')!;
|
|
// const current = ctrl.value || '';
|
|
// if (current.length >= 6) return;
|
|
|
|
// const newVal = current + value;
|
|
// ctrl.setValue(newVal);
|
|
// this.passwordInputRef.nativeElement.value = newVal;
|
|
|
|
// setTimeout(() => {
|
|
// this.passwordInputRef.nativeElement.focus();
|
|
// this.passwordInputRef.nativeElement.setSelectionRange(
|
|
// newVal.length,
|
|
// newVal.length
|
|
// );
|
|
// }, 0);
|
|
// this.cdRef.detectChanges();
|
|
// } else if (this.focusedField === 'email' && !this.scanningEnabled) {
|
|
// const ctrl = this.loginForm.get('email')!;
|
|
// let val = ctrl.value || '';
|
|
// if (val.length < 12) {
|
|
// val += value;
|
|
// ctrl.setValue(val);
|
|
// this.emailInputRef.nativeElement.value = val;
|
|
// }
|
|
// setTimeout(() => {
|
|
// this.emailInputRef.nativeElement.focus();
|
|
// this.emailInputRef.nativeElement.setSelectionRange(
|
|
// val.length,
|
|
// val.length
|
|
// );
|
|
// }, 0);
|
|
// this.cdRef.detectChanges();
|
|
// }
|
|
// }
|
|
|
|
// onBackspace(): void {
|
|
// if (this.focusedField === 'password') {
|
|
// const ctrl = this.loginForm.get('password')!;
|
|
// const current = ctrl.value || '';
|
|
// const newVal = current.slice(0, -1);
|
|
// ctrl.setValue(newVal);
|
|
// this.passwordInputRef.nativeElement.value = newVal;
|
|
|
|
// setTimeout(() => {
|
|
// this.passwordInputRef.nativeElement.focus();
|
|
// this.passwordInputRef.nativeElement.setSelectionRange(
|
|
// newVal.length,
|
|
// newVal.length
|
|
// );
|
|
// }, 0);
|
|
// this.cdRef.detectChanges();
|
|
// } else if (this.focusedField === 'email' && !this.scanningEnabled) {
|
|
// const ctrl = this.loginForm.get('email')!;
|
|
// let val = ctrl.value || '';
|
|
// val = val.slice(0, -1);
|
|
// ctrl.setValue(val);
|
|
// this.emailInputRef.nativeElement.value = val;
|
|
|
|
// setTimeout(() => {
|
|
// this.emailInputRef.nativeElement.focus();
|
|
// this.emailInputRef.nativeElement.setSelectionRange(
|
|
// val.length,
|
|
// val.length
|
|
// );
|
|
// }, 0);
|
|
// this.cdRef.detectChanges();
|
|
// }
|
|
// }
|
|
|
|
// resetUserPassMessage() {
|
|
// this.passwordStatus = true;
|
|
// }
|
|
|
|
// sharedWindow: Window | null = null; // Add this at the top of your class
|
|
|
|
|
|
// onSubmit(): void {
|
|
// if (this.loginForm.invalid) {
|
|
// this.loginForm.markAllAsTouched();
|
|
// return;
|
|
// }
|
|
|
|
// const { email, password } = this.loginForm.value;
|
|
|
|
// this.btcService.userLogin(email, password).subscribe({
|
|
// next: (response: HttpResponse<{ name: string; employeeId: string }>) => {
|
|
// const employee = response.body;
|
|
// this.passwordStatus = true;
|
|
|
|
// // Save user info
|
|
// localStorage.setItem('userName', employee?.name || 'Unknown User');
|
|
// localStorage.setItem('employeeId', employee?.employeeId || email);
|
|
|
|
// // ✅ Trigger shared display window via Electron
|
|
// (window as any).electronAPI?.openSecondScreen?.();
|
|
|
|
// // ✅ Navigate to home
|
|
// this.router.navigate(['/home']);
|
|
// },
|
|
// error: () => {
|
|
// this.loginError = 'Invalid login credentials';
|
|
// },
|
|
// });
|
|
// }
|
|
|
|
|
|
|
|
// shutdownSystem(): void {
|
|
// fetch('http://localhost:3000/shutdown', {
|
|
// method: 'POST',
|
|
// })
|
|
// .then((res) => {
|
|
// if (!res.ok) throw new Error('Shutdown failed');
|
|
// console.log('🛑 Shutdown triggered');
|
|
// })
|
|
// .catch((err) => {
|
|
// console.error('‼️ Failed to shutdown', err);
|
|
// });
|
|
// }
|
|
// }
|
|
|
|
|
|
import {
|
|
Component,
|
|
ElementRef,
|
|
ViewChild,
|
|
ChangeDetectorRef,
|
|
OnInit,
|
|
OnDestroy,
|
|
} from '@angular/core';
|
|
import {
|
|
FormBuilder,
|
|
FormGroup,
|
|
Validators,
|
|
ReactiveFormsModule,
|
|
} from '@angular/forms';
|
|
import { CommonModule } from '@angular/common';
|
|
import { Router } from '@angular/router';
|
|
import { BtcService } from '../service/btc.service';
|
|
import { HttpClientModule, HttpResponse } from '@angular/common/http';
|
|
|
|
@Component({
|
|
selector: 'app-login',
|
|
standalone: true,
|
|
imports: [CommonModule, ReactiveFormsModule, HttpClientModule],
|
|
templateUrl: './login.component.html',
|
|
styleUrls: ['./login.component.css'],
|
|
})
|
|
export class LoginComponent implements OnInit, OnDestroy {
|
|
loginForm: FormGroup;
|
|
focusedField: 'email' | 'password' | null = null;
|
|
loginError: string | null = null;
|
|
passwordStatus: boolean = true;
|
|
scanningEnabled: boolean = true;
|
|
|
|
@ViewChild('emailInput', { static: true })
|
|
emailInputRef!: ElementRef<HTMLInputElement>;
|
|
@ViewChild('passwordInput', { static: true })
|
|
passwordInputRef!: ElementRef<HTMLInputElement>;
|
|
|
|
private barcodeBuffer = '';
|
|
private scanStartedAt = 0;
|
|
private readonly SCAN_GAP_MS = 100;
|
|
|
|
constructor(
|
|
private fb: FormBuilder,
|
|
private cdRef: ChangeDetectorRef,
|
|
private router: Router,
|
|
private btcService: BtcService
|
|
) {
|
|
this.loginForm = this.fb.group({
|
|
email: ['', Validators.required],
|
|
password: ['', [Validators.required, Validators.pattern(/^\d{6}$/)]],
|
|
});
|
|
}
|
|
|
|
ngOnInit(): void {
|
|
window.addEventListener('keydown', this.handleScan);
|
|
}
|
|
|
|
ngOnDestroy(): void {
|
|
window.removeEventListener('keydown', this.handleScan);
|
|
}
|
|
|
|
toggleScan(): void {
|
|
this.scanningEnabled = !this.scanningEnabled;
|
|
}
|
|
|
|
handleScan = (event: KeyboardEvent): void => {
|
|
if (this.focusedField !== 'email') return;
|
|
|
|
if (!this.scanningEnabled) {
|
|
const ctrl = this.loginForm.get('email')!;
|
|
let val = ctrl.value || '';
|
|
if (event.key >= '0' && event.key <= '9') {
|
|
if (val.length < 12) {
|
|
val += event.key;
|
|
ctrl.setValue(val);
|
|
this.emailInputRef.nativeElement.value = val;
|
|
}
|
|
event.preventDefault();
|
|
} else if (event.key === 'Backspace') {
|
|
val = val.slice(0, -1);
|
|
ctrl.setValue(val);
|
|
this.emailInputRef.nativeElement.value = val;
|
|
event.preventDefault();
|
|
} else if (event.key === 'Delete') {
|
|
ctrl.setValue('');
|
|
this.emailInputRef.nativeElement.value = '';
|
|
event.preventDefault();
|
|
}
|
|
return;
|
|
}
|
|
|
|
const now = Date.now();
|
|
if (now - this.scanStartedAt > this.SCAN_GAP_MS) {
|
|
this.barcodeBuffer = '';
|
|
}
|
|
this.scanStartedAt = now;
|
|
|
|
if (event.key === 'Enter') {
|
|
this.commitScan();
|
|
return;
|
|
}
|
|
|
|
const ch = this.toPrintableChar(event);
|
|
if (ch) this.barcodeBuffer += ch;
|
|
};
|
|
|
|
private toPrintableChar(ev: KeyboardEvent): string {
|
|
if (ev.key.length === 1) return ev.key;
|
|
if (ev.key === 'Process') {
|
|
if (ev.keyCode >= 48 && ev.keyCode <= 57)
|
|
return String.fromCharCode(ev.keyCode);
|
|
if (ev.keyCode >= 65 && ev.keyCode <= 90)
|
|
return String.fromCharCode(ev.keyCode);
|
|
}
|
|
return '';
|
|
}
|
|
|
|
private commitScan(): void {
|
|
if (!this.barcodeBuffer) return;
|
|
this.loginForm.get('email')!.setValue(this.barcodeBuffer);
|
|
this.emailInputRef.nativeElement.value = this.barcodeBuffer;
|
|
this.setFocus('password');
|
|
setTimeout(() => this.passwordInputRef.nativeElement.focus(), 0);
|
|
this.barcodeBuffer = '';
|
|
}
|
|
|
|
removeReadonly(input: HTMLInputElement): void {
|
|
input.removeAttribute('readonly');
|
|
setTimeout(() => input.setAttribute('readonly', 'true'), 100);
|
|
}
|
|
|
|
setFocus(field: 'email' | 'password'): void {
|
|
this.focusedField = field;
|
|
}
|
|
|
|
formatUsernameDisplay(raw: string): string {
|
|
return /^\d+$/.test(raw) ? raw.replace(/(\d{4})(?=\d)/g, '$1-') : raw;
|
|
}
|
|
|
|
onNumpadClick(value: string): void {
|
|
if (this.focusedField === 'password') {
|
|
const ctrl = this.loginForm.get('password')!;
|
|
const current = ctrl.value || '';
|
|
if (current.length >= 6) return;
|
|
|
|
const newVal = current + value;
|
|
ctrl.setValue(newVal);
|
|
this.passwordInputRef.nativeElement.value = newVal;
|
|
|
|
setTimeout(() => {
|
|
this.passwordInputRef.nativeElement.focus();
|
|
this.passwordInputRef.nativeElement.setSelectionRange(
|
|
newVal.length,
|
|
newVal.length
|
|
);
|
|
}, 0);
|
|
this.cdRef.detectChanges();
|
|
} else if (this.focusedField === 'email' && !this.scanningEnabled) {
|
|
const ctrl = this.loginForm.get('email')!;
|
|
let val = ctrl.value || '';
|
|
if (val.length < 12) {
|
|
val += value;
|
|
ctrl.setValue(val);
|
|
this.emailInputRef.nativeElement.value = val;
|
|
}
|
|
setTimeout(() => {
|
|
this.emailInputRef.nativeElement.focus();
|
|
this.emailInputRef.nativeElement.setSelectionRange(
|
|
val.length,
|
|
val.length
|
|
);
|
|
}, 0);
|
|
this.cdRef.detectChanges();
|
|
}
|
|
}
|
|
|
|
onBackspace(): void {
|
|
if (this.focusedField === 'password') {
|
|
const ctrl = this.loginForm.get('password')!;
|
|
const current = ctrl.value || '';
|
|
const newVal = current.slice(0, -1);
|
|
ctrl.setValue(newVal);
|
|
this.passwordInputRef.nativeElement.value = newVal;
|
|
|
|
setTimeout(() => {
|
|
this.passwordInputRef.nativeElement.focus();
|
|
this.passwordInputRef.nativeElement.setSelectionRange(
|
|
newVal.length,
|
|
newVal.length
|
|
);
|
|
}, 0);
|
|
this.cdRef.detectChanges();
|
|
} else if (this.focusedField === 'email' && !this.scanningEnabled) {
|
|
const ctrl = this.loginForm.get('email')!;
|
|
let val = ctrl.value || '';
|
|
val = val.slice(0, -1);
|
|
ctrl.setValue(val);
|
|
this.emailInputRef.nativeElement.value = val;
|
|
|
|
setTimeout(() => {
|
|
this.emailInputRef.nativeElement.focus();
|
|
this.emailInputRef.nativeElement.setSelectionRange(
|
|
val.length,
|
|
val.length
|
|
);
|
|
}, 0);
|
|
this.cdRef.detectChanges();
|
|
}
|
|
}
|
|
|
|
resetUserPassMessage() {
|
|
this.passwordStatus = true;
|
|
}
|
|
|
|
|
|
onSubmit(): void {
|
|
if (this.loginForm.invalid) {
|
|
this.loginForm.markAllAsTouched();
|
|
return;
|
|
}
|
|
|
|
const { email, password } = this.loginForm.value;
|
|
|
|
this.btcService.userLogin(email, password).subscribe({
|
|
next: (response: HttpResponse<{ name: string; employeeId: string }>) => {
|
|
const employee = response.body;
|
|
this.passwordStatus = true;
|
|
|
|
const printData = {
|
|
name: employee?.name || 'Unknown User',
|
|
employeeId: employee?.employeeId || email,
|
|
action: 'login',
|
|
};
|
|
|
|
localStorage.setItem('userName', printData.name);
|
|
localStorage.setItem('employeeId', printData.employeeId);
|
|
|
|
// ✅ Re-enable print logic
|
|
this.printAndRedirect(printData);
|
|
},
|
|
error: () => {
|
|
this.loginError = 'Invalid login credentials';
|
|
},
|
|
});
|
|
}
|
|
|
|
|
|
|
|
private async printAndRedirect(printData: { name: string; employeeId: string; action: string }) {
|
|
try {
|
|
const res = await fetch('http://localhost:9100/print', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(printData),
|
|
});
|
|
|
|
if (!res.ok) throw new Error('Print failed');
|
|
|
|
console.log('🖨️ Print successful');
|
|
|
|
// ✅ Open second screen
|
|
(window as any).electronAPI?.openSecondScreen?.();
|
|
|
|
// ✅ Navigate to home screen
|
|
this.router.navigate(['/home']);
|
|
} catch (err) {
|
|
console.error('‼️ Print failed', err);
|
|
this.loginError = 'Login OK, but printing failed. Please check the printer.';
|
|
}
|
|
}
|
|
|
|
showConfirmModal : boolean = false;
|
|
|
|
confirmShutdown() : void{
|
|
this.showConfirmModal = false;
|
|
this.shutdownSystem();
|
|
|
|
}
|
|
|
|
shutdownSystem(): void {
|
|
fetch('http://localhost:3000/shutdown', {
|
|
method: 'POST',
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error('Shutdown failed');
|
|
console.log('🛑 Shutdown triggered');
|
|
})
|
|
.catch((err) => {
|
|
console.error('‼️ Failed to shutdown', err);
|
|
});
|
|
}
|
|
}
|