mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
feat(shared-scanner): Moved to shared/scanner
feat(common-data-access): takeUnitl operators for keydown Refs: #5062
This commit is contained in:
@@ -1 +1,2 @@
|
||||
export * from './take-until-aborted';
|
||||
export * from './take-unitl-keydown';
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { filter, fromEvent, Observable, takeUntil } from 'rxjs';
|
||||
|
||||
export const takeUntilKeydown =
|
||||
<T>(key: string) =>
|
||||
(source: Observable<T>): Observable<T> => {
|
||||
const keydownEvent$ = fromEvent<KeyboardEvent>(document, 'keydown').pipe(
|
||||
// Filter for the specific key
|
||||
filter((event) => event.key === key),
|
||||
);
|
||||
|
||||
return source.pipe(takeUntil(keydownEvent$));
|
||||
};
|
||||
|
||||
export const takeUntilKeydownEscape = <T>() => takeUntilKeydown<T>('Escape');
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Observable, fromEvent } from 'rxjs';
|
||||
import { Observable } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
/**
|
||||
@@ -19,7 +19,6 @@ export const fromAbortSignal = (signal: AbortSignal): Observable<void> => {
|
||||
return new Observable<void>((subscriber) => {
|
||||
const abortHandler = () => {
|
||||
subscriber.next();
|
||||
subscriber.complete();
|
||||
};
|
||||
|
||||
// Listen for the 'abort' event
|
||||
|
||||
@@ -18,7 +18,11 @@ import { ReturnSearchService } from '../services';
|
||||
import { tapResponse } from '@ngrx/operators';
|
||||
import { effect, inject } from '@angular/core';
|
||||
import { QueryTokenSchema } from '../schemas';
|
||||
import { Callback, ListResponseArgs } from '@isa/common/data-access';
|
||||
import {
|
||||
Callback,
|
||||
ListResponseArgs,
|
||||
takeUntilKeydownEscape,
|
||||
} from '@isa/common/data-access';
|
||||
import { ReceiptListItem } from '../models';
|
||||
import { Query } from '@isa/shared/filter';
|
||||
import { SessionStorageProvider, withStorage } from '@isa/core/storage';
|
||||
@@ -191,6 +195,26 @@ export const ReturnSearchStore = signalStore(
|
||||
),
|
||||
);
|
||||
},
|
||||
handleSearchCompleted(processId: number) {
|
||||
const entity = store.getEntity(processId);
|
||||
|
||||
if (entity?.status !== ReturnSearchStatus.Pending) {
|
||||
return;
|
||||
}
|
||||
|
||||
patchState(
|
||||
store,
|
||||
updateEntity(
|
||||
{
|
||||
id: processId, // Assuming we want to update the first entity
|
||||
changes: {
|
||||
status: ReturnSearchStatus.Idle,
|
||||
},
|
||||
},
|
||||
config,
|
||||
),
|
||||
);
|
||||
},
|
||||
})),
|
||||
withMethods((store, returnSearchService = inject(ReturnSearchService)) => ({
|
||||
/**
|
||||
@@ -219,6 +243,7 @@ export const ReturnSearchStore = signalStore(
|
||||
}),
|
||||
)
|
||||
.pipe(
|
||||
takeUntilKeydownEscape(),
|
||||
tapResponse(
|
||||
(response) => {
|
||||
store.handleSearchSuccess({ processId, response });
|
||||
@@ -228,6 +253,9 @@ export const ReturnSearchStore = signalStore(
|
||||
store.handleSearchError({ processId, error });
|
||||
cb?.({ error });
|
||||
},
|
||||
() => {
|
||||
store.handleSearchCompleted(processId);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -34,7 +34,7 @@ import { tapResponse } from '@ngrx/operators';
|
||||
import { ProductImageDirective } from '@isa/shared/product-image';
|
||||
import { provideIcons } from '@ng-icons/core';
|
||||
import { isaActionScanner } from '@isa/icons';
|
||||
import { ScannerButtonComponent } from '@isa/core/scanner';
|
||||
import { ScannerButtonComponent } from '@isa/shared/scanner';
|
||||
import { ProductRouterLinkDirective } from '@isa/shared/product-router-link';
|
||||
|
||||
const eanValidator: ValidatorFn = (
|
||||
|
||||
@@ -14,14 +14,13 @@
|
||||
></filter-filter-menu-button>
|
||||
|
||||
@if (mobileBreakpoint()) {
|
||||
<button
|
||||
uiIconButton
|
||||
<ui-icon-button
|
||||
type="button"
|
||||
(click)="showOrderByToolbarMobile.set(!showOrderByToolbarMobile())"
|
||||
[class.active]="showOrderByToolbarMobile()"
|
||||
data-what="sort-button-mobile"
|
||||
name="isaActionSort"
|
||||
></button>
|
||||
></ui-icon-button>
|
||||
} @else {
|
||||
<filter-order-by-toolbar (toggled)="search()"></filter-order-by-toolbar>
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
|
||||
import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
import { EmptyStateComponent } from '@isa/ui/empty-state';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
import { provideIcons } from '@ng-icons/core';
|
||||
import { isaActionSort, isaActionFilter } from '@isa/icons';
|
||||
import {
|
||||
ReceiptListItem,
|
||||
@@ -55,7 +55,6 @@ import { breakpoint } from '@isa/ui/layout';
|
||||
EmptyStateComponent,
|
||||
FilterMenuButtonComponent,
|
||||
InViewportDirective,
|
||||
NgIconComponent,
|
||||
],
|
||||
providers: [provideIcons({ isaActionSort, isaActionFilter })],
|
||||
})
|
||||
|
||||
@@ -20,7 +20,7 @@ import { isaActionSearch, isaActionScanner } from '@isa/icons';
|
||||
import { FilterService, TextFilterInput } from '../../core';
|
||||
import { InputType } from '../../types';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { ScannerButtonComponent } from '@isa/core/scanner';
|
||||
import { ScannerButtonComponent } from '@isa/shared/scanner';
|
||||
|
||||
@Component({
|
||||
selector: 'filter-search-bar-input',
|
||||
|
||||
@@ -28,6 +28,7 @@ The Scanner library provides barcode scanning capabilities for the ISA applicati
|
||||
A button component that integrates with the scanner service to trigger barcode scanning. It implements `OnDestroy` to properly clean up resources.
|
||||
|
||||
**Features:**
|
||||
|
||||
- Only appears when scanner is ready
|
||||
- Can be disabled through binding
|
||||
- Configurable size
|
||||
@@ -45,11 +46,12 @@ import { ScannerButtonComponent } from '@isa/core/scanner';
|
||||
<shared-scanner-button
|
||||
[disabled]="isDisabled"
|
||||
[size]="'large'"
|
||||
(scan)="onScan($event)">
|
||||
(scan)="onScan($event)"
|
||||
>
|
||||
</shared-scanner-button>
|
||||
`,
|
||||
imports: [ScannerButtonComponent],
|
||||
standalone: true
|
||||
standalone: true,
|
||||
})
|
||||
export class MyComponent {
|
||||
isDisabled = false;
|
||||
@@ -65,6 +67,7 @@ export class MyComponent {
|
||||
A structural directive (`*sharedScannerReady`) that conditionally renders its content based on the scanner's ready state. Similar to `*ngIf`, but specifically tied to scanner readiness.
|
||||
|
||||
**Features:**
|
||||
|
||||
- Only renders content when the scanner is ready
|
||||
- Supports an optional else template for when the scanner is not ready
|
||||
- Uses Angular's effect system for reactive updates
|
||||
@@ -94,7 +97,7 @@ import { ScannerReadyDirective } from '@isa/core/scanner';
|
||||
</ng-template>
|
||||
`,
|
||||
imports: [ScannerReadyDirective],
|
||||
standalone: true
|
||||
standalone: true,
|
||||
})
|
||||
export class MyComponent {
|
||||
// Component logic
|
||||
@@ -106,6 +109,7 @@ export class MyComponent {
|
||||
Internal component used by ScannerService to render the camera view and process barcode scanning.
|
||||
|
||||
**Features:**
|
||||
|
||||
- Integrates with Scandit SDK
|
||||
- Handles camera setup and barcode detection
|
||||
- Emits scanned values
|
||||
@@ -118,6 +122,7 @@ Internal component used by ScannerService to render the camera view and process
|
||||
Core service that provides barcode scanning functionality.
|
||||
|
||||
**Features:**
|
||||
|
||||
- Initializes and configures Scandit SDK
|
||||
- Checks platform compatibility
|
||||
- Manages scanner lifecycle
|
||||
@@ -136,7 +141,7 @@ import { Component, inject } from '@angular/core';
|
||||
<button (click)="scan()" [disabled]="!isReady()">Scan Barcode</button>
|
||||
<div *ngIf="result">Last Scan: {{ result }}</div>
|
||||
`,
|
||||
standalone: true
|
||||
standalone: true,
|
||||
})
|
||||
export class MyComponent {
|
||||
private scannerService = inject(ScannerService);
|
||||
@@ -169,10 +174,7 @@ The scanner module uses injection tokens for configuration:
|
||||
**Custom Configuration Example:**
|
||||
|
||||
```typescript
|
||||
import {
|
||||
SCANDIT_LICENSE,
|
||||
SCANDIT_LIBRARY_LOCATION
|
||||
} from '@isa/core/scanner';
|
||||
import { SCANDIT_LICENSE, SCANDIT_LIBRARY_LOCATION } from '@isa/core/scanner';
|
||||
import { Symbology } from 'scandit-web-datacapture-barcode';
|
||||
|
||||
@NgModule({
|
||||
@@ -180,16 +182,16 @@ import { Symbology } from 'scandit-web-datacapture-barcode';
|
||||
// Custom license key
|
||||
{
|
||||
provide: SCANDIT_LICENSE,
|
||||
useValue: 'YOUR-SCANDIT-LICENSE-KEY'
|
||||
useValue: 'YOUR-SCANDIT-LICENSE-KEY',
|
||||
},
|
||||
// Custom library location
|
||||
{
|
||||
provide: SCANDIT_LIBRARY_LOCATION,
|
||||
useValue: 'https://cdn.example.com/scandit/'
|
||||
}
|
||||
]
|
||||
useValue: 'https://cdn.example.com/scandit/',
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AppModule { }
|
||||
export class AppModule {}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
@@ -1,8 +1,8 @@
|
||||
export default {
|
||||
displayName: 'core-scanner',
|
||||
displayName: 'shared-scanner',
|
||||
preset: '../../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
coverageDirectory: '../../../coverage/libs/core/scanner',
|
||||
coverageDirectory: '../../../coverage/libs/shared/scanner',
|
||||
transform: {
|
||||
'^.+\\.(ts|mjs|js|html)$': [
|
||||
'jest-preset-angular',
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "core-scanner",
|
||||
"name": "shared-scanner",
|
||||
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/core/scanner/src",
|
||||
"sourceRoot": "libs/shared/scanner/src",
|
||||
"prefix": "shared",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
@@ -10,7 +10,7 @@
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "libs/core/scanner/jest.config.ts"
|
||||
"jestConfig": "libs/shared/scanner/jest.config.ts"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
@@ -174,14 +174,21 @@ export class ScannerService {
|
||||
libraryLocation: this.#libraryLocation,
|
||||
moduleLoaders: [barcodeCaptureLoader()],
|
||||
});
|
||||
|
||||
this.#status.set(ScannerStatus.Ready);
|
||||
this.#logger.info('Scanner ready');
|
||||
} catch (error) {
|
||||
if (error instanceof PlatformNotSupportedError) {
|
||||
this.#logger.warn(error.message, () => ({
|
||||
error,
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
this.#status.set(ScannerStatus.Error);
|
||||
this.#logger.error('Failed to configure Scandit', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
this.#status.set(ScannerStatus.Ready);
|
||||
this.#logger.info('Scanner ready');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,7 +46,6 @@
|
||||
"@isa/core/logging": ["libs/core/logging/src/index.ts"],
|
||||
"@isa/core/notifications": ["libs/core/notifications/src/index.ts"],
|
||||
"@isa/core/process": ["libs/core/process/src/index.ts"],
|
||||
"@isa/core/scanner": ["libs/core/scanner/src/index.ts"],
|
||||
"@isa/core/storage": ["libs/core/storage/src/index.ts"],
|
||||
"@isa/icons": ["libs/icons/src/index.ts"],
|
||||
"@isa/oms/data-access": ["libs/oms/data-access/src/index.ts"],
|
||||
@@ -75,6 +74,7 @@
|
||||
"@isa/shared/product-router-link": [
|
||||
"libs/shared/product-router-link/src/index.ts"
|
||||
],
|
||||
"@isa/shared/scanner": ["libs/shared/scanner/src/index.ts"],
|
||||
"@isa/ui/buttons": ["libs/ui/buttons/src/index.ts"],
|
||||
"@isa/ui/datepicker": ["libs/ui/datepicker/src/index.ts"],
|
||||
"@isa/ui/dialog": ["libs/ui/dialog/src/index.ts"],
|
||||
|
||||
Reference in New Issue
Block a user