mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
feat(shared-task-list), feat(return-search-main), feat(return-review): create shared task list component and refactor return views
Implement new shared task list component to replace duplicate task list functionality across the application. Update return review and search views to use the new shared component. - Create new @isa/oms/shared/task-list library - Extract task list functionality from return review component - Add task list to return search main view - Handle task completion actions through the shared component - Fix typo in return review success message Ref: #4942, #4972, #4974
This commit is contained in:
@@ -17,3 +17,4 @@ export * from './return-process-question';
|
||||
export * from './return-process-status';
|
||||
export * from './return-process';
|
||||
export * from './shipping-type';
|
||||
export * from './task-action-type';
|
||||
|
||||
4
libs/oms/data-access/src/lib/models/task-action-type.ts
Normal file
4
libs/oms/data-access/src/lib/models/task-action-type.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface TaskActionType {
|
||||
type: 'complete' | 'damaged' | 'resell' | 'print';
|
||||
taskId: number;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<h2 class="isa-text-subtitle-1-regular">Die Rückgabe war erfolgreich!</h2>
|
||||
|
||||
<button
|
||||
data-what="button"
|
||||
data-which="print-receipt"
|
||||
class="self-start"
|
||||
(click)="printReceipt.emit()"
|
||||
uiInfoButton
|
||||
>
|
||||
<span uiInfoButtonLabel>Rückgabe Bestätigung erneut drucken</span>
|
||||
<ng-icon name="isaActionPrinter" uiInfoButtonIcon></ng-icon>
|
||||
</button>
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply w-full flex flex-col gap-6 desktop:gap-0 desktop:flex-row desktop:justify-between desktop:items-center border-b border-solid border-isa-neutral-300 pb-6;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ChangeDetectionStrategy, Component, output } from '@angular/core';
|
||||
import { isaActionPrinter } from '@isa/icons';
|
||||
import { InfoButtonComponent } from '@isa/ui/buttons';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
|
||||
@Component({
|
||||
selector: 'oms-feature-return-review-header',
|
||||
templateUrl: './return-review-header.component.html',
|
||||
styleUrl: './return-review-header.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [InfoButtonComponent, NgIconComponent],
|
||||
providers: [provideIcons({ isaActionPrinter })],
|
||||
})
|
||||
export class ReturnReviewHeaderComponent {
|
||||
printReceipt = output<void>();
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
<oms-shared-return-product-info
|
||||
class="self-start"
|
||||
[product]="product()"
|
||||
data-what="component"
|
||||
data-which="return-product-info"
|
||||
></oms-shared-return-product-info>
|
||||
<div data-what="review-list" data-which="processing-comment" class="self-start">
|
||||
{{ processingComment() }}
|
||||
</div>
|
||||
@if (!item()?.completed) {
|
||||
<button
|
||||
type="button"
|
||||
uiButton
|
||||
color="secondary"
|
||||
(click)="markAsDone.emit(item().id)"
|
||||
data-what="button"
|
||||
data-which="mark-as-done"
|
||||
>
|
||||
Als erledigt markieren
|
||||
</button>
|
||||
} @else {
|
||||
Abgeschlossen
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
:host {
|
||||
@apply w-full grid grid-cols-[1fr,1fr,auto] p-6 text-isa-secondary-900 items-center;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
input,
|
||||
output,
|
||||
computed,
|
||||
} from '@angular/core';
|
||||
import { isaActionCheck } from '@isa/icons';
|
||||
import { provideIcons } from '@ng-icons/core';
|
||||
import { ReturnProductInfoComponent } from '@isa/oms/shared/product-info';
|
||||
import { Product, ReceiptItemTaskListItem } from '@isa/oms/data-access';
|
||||
import { ButtonComponent } from '@isa/ui/buttons';
|
||||
|
||||
@Component({
|
||||
selector: 'oms-feature-return-review-item',
|
||||
templateUrl: './return-review-item.component.html',
|
||||
styleUrl: './return-review-item.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [ReturnProductInfoComponent, ButtonComponent],
|
||||
providers: [provideIcons({ isaActionCheck })],
|
||||
})
|
||||
export class ReturnReviewItemComponent {
|
||||
item = input.required<ReceiptItemTaskListItem>();
|
||||
markAsDone = output<number>();
|
||||
|
||||
product = computed(() => {
|
||||
const item = this.item();
|
||||
const product = item.product;
|
||||
|
||||
if (product) {
|
||||
return product as Product;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
processingComment = computed(() => {
|
||||
const item = this.item();
|
||||
const processingComment = item.processingComment;
|
||||
|
||||
if (processingComment) {
|
||||
return processingComment;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
@@ -1,22 +1,7 @@
|
||||
<h2 class="isa-text-subtitle-1-regular">Die Rückgabe ware erfolgreich!</h2>
|
||||
<oms-feature-return-review-header
|
||||
(printReceipt)="printReceipt()"
|
||||
></oms-feature-return-review-header>
|
||||
|
||||
<div class="flex flex-col gap-4 w-full items-center justify-center">
|
||||
@for (item of taskListItems(); track item.id) {
|
||||
@defer (on viewport) {
|
||||
<oms-feature-return-review-item
|
||||
[item]="item"
|
||||
(markAsDone)="completeTask($event)"
|
||||
></oms-feature-return-review-item>
|
||||
} @placeholder {
|
||||
<!-- TODO: Den Spinner durch Skeleton Loader Kacheln ersetzen -->
|
||||
<div class="h-[7.75rem] w-full flex items-center justify-center">
|
||||
<ui-icon-button
|
||||
[pending]="true"
|
||||
[color]="'tertiary'"
|
||||
data-what="load-spinner"
|
||||
data-which="item-placeholder"
|
||||
></ui-icon-button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<oms-shared-return-task-list
|
||||
[appearance]="'review'"
|
||||
></oms-shared-return-task-list>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
:host {
|
||||
@apply flex flex-col gap-4 w-full justify-start items-center;
|
||||
@apply flex flex-col w-full justify-start mt-6 p-6 bg-white rounded-2xl;
|
||||
}
|
||||
|
||||
@@ -1,74 +1,34 @@
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
effect,
|
||||
inject,
|
||||
untracked,
|
||||
} from '@angular/core';
|
||||
import { ReturnReviewService, ReturnReviewStore } from '@isa/oms/data-access';
|
||||
import { logger, provideLoggerContext } from '@isa/core/logging';
|
||||
ReturnPrintReceiptsService,
|
||||
ReturnProcessStore,
|
||||
} from '@isa/oms/data-access';
|
||||
import { injectActivatedProcessId } from '@isa/core/process';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
import { ReturnReviewItemComponent } from './return-review-item/return-review-item.component';
|
||||
import { ReturnTaskListComponent } from '@isa/oms/shared/task-list';
|
||||
import { ReturnReviewHeaderComponent } from './return-review-header/return-review-header.component';
|
||||
|
||||
@Component({
|
||||
selector: 'oms-feature-return-review',
|
||||
templateUrl: './return-review.component.html',
|
||||
styleUrl: './return-review.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [IconButtonComponent, ReturnReviewItemComponent],
|
||||
providers: [provideLoggerContext({ component: 'ReturnReviewComponent' })],
|
||||
standalone: true,
|
||||
imports: [ReturnTaskListComponent, ReturnReviewHeaderComponent],
|
||||
})
|
||||
export class ReturnReviewComponent {
|
||||
#returnReviewService = inject(ReturnReviewService);
|
||||
#returnReviewStore = inject(ReturnReviewStore);
|
||||
#logger = logger();
|
||||
|
||||
#printReceiptsService = inject(ReturnPrintReceiptsService);
|
||||
#returnProcessStore = inject(ReturnProcessStore);
|
||||
processId = injectActivatedProcessId();
|
||||
|
||||
taskListItems = computed(() => {
|
||||
async printReceipt() {
|
||||
const processId = this.processId();
|
||||
if (!processId) {
|
||||
return [];
|
||||
}
|
||||
return this.#returnReviewStore.entityMap()[processId].data ?? [];
|
||||
});
|
||||
if (processId) {
|
||||
const receiptId =
|
||||
this.#returnProcessStore.entityMap()[processId].receiptId;
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
const processId = this.processId();
|
||||
if (processId) {
|
||||
untracked(() =>
|
||||
this.#returnReviewStore.fetchTaskListItems({ processId }),
|
||||
);
|
||||
if (receiptId) {
|
||||
await this.#printReceiptsService.printReturns([receiptId]);
|
||||
}
|
||||
});
|
||||
|
||||
effect(() => {
|
||||
const taskListItems = this.taskListItems();
|
||||
console.log(taskListItems);
|
||||
});
|
||||
}
|
||||
|
||||
async completeTask(taskId: number) {
|
||||
try {
|
||||
const processId = this.processId();
|
||||
const result = await firstValueFrom(
|
||||
this.#returnReviewService.completeTask(taskId),
|
||||
);
|
||||
|
||||
if (result && processId) {
|
||||
this.#returnReviewStore.updateTaskListItem({
|
||||
processId,
|
||||
taskListItem: result,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.#logger.error('Error completing task', error, {
|
||||
function: 'completeTask',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,43 @@
|
||||
<div class="flex flex-col items-center justify-center gap-4">
|
||||
<h1 class="isa-text-subtitle-1-regular">Rückgabe starten</h1>
|
||||
<p class="isa-text-body-1-regular text-center">
|
||||
Scannen Sie den QR-Code auf der Rechnung oder suchen Sie den Beleg
|
||||
<br />
|
||||
via Rechnungsnummer, E-Mail-Adresse oder Kundennamen
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<filter-search-bar-input
|
||||
class="mt-[1.88rem] mb-[3.12rem]"
|
||||
inputKey="qs"
|
||||
(triggerSearch)="onSearch()"
|
||||
></filter-search-bar-input>
|
||||
|
||||
@if (entityPending()) {
|
||||
<div class="h-12 w-full flex items-center justify-center mb-12">
|
||||
<ui-icon-button
|
||||
name=""
|
||||
[pending]="true"
|
||||
[color]="'tertiary'"
|
||||
></ui-icon-button>
|
||||
<div class="flex flex-col pt-12 items-center">
|
||||
<div class="flex flex-col items-center justify-center gap-4">
|
||||
<h1 class="isa-text-subtitle-1-regular">Rückgabe starten</h1>
|
||||
<p class="isa-text-body-1-regular text-center">
|
||||
Scannen Sie den QR-Code auf der Rechnung oder suchen Sie den Beleg
|
||||
<br />
|
||||
via Rechnungsnummer, E-Mail-Adresse oder Kundennamen
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex flex-row gap-2">
|
||||
@for (filterInput of filterInputs(); track filterInput.key) {
|
||||
<filter-input-menu-button
|
||||
[filterInput]="filterInput"
|
||||
(applied)="onSearch()"
|
||||
>
|
||||
</filter-input-menu-button>
|
||||
<filter-search-bar-input
|
||||
class="mt-[1.88rem] mb-[3.12rem]"
|
||||
inputKey="qs"
|
||||
(triggerSearch)="onSearch()"
|
||||
></filter-search-bar-input>
|
||||
|
||||
@if (entityPending()) {
|
||||
<div class="h-12 w-full flex items-center justify-center mb-12">
|
||||
<ui-icon-button
|
||||
name=""
|
||||
[pending]="true"
|
||||
[color]="'tertiary'"
|
||||
></ui-icon-button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex flex-row gap-2">
|
||||
@for (filterInput of filterInputs(); track filterInput.key) {
|
||||
<filter-input-menu-button
|
||||
[filterInput]="filterInput"
|
||||
(applied)="onSearch()"
|
||||
>
|
||||
</filter-input-menu-button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center gap-6 w-[24.25rem] justify-self-center">
|
||||
<span class="isa-text-subtitle-2-bold self-start">OFFENE AUFGABEN</span>
|
||||
<oms-shared-return-task-list
|
||||
[appearance]="'main'"
|
||||
></oms-shared-return-task-list>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
:host {
|
||||
@apply flex flex-col pt-12 items-center;
|
||||
@apply w-full grid gap-16 grid-flow-row justify-center desktop:grid-cols-[1fr,auto] desktop:gap-6;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
ReturnSearchStatus,
|
||||
ReturnSearchStore,
|
||||
} from '@isa/oms/data-access';
|
||||
import { ReturnTaskListComponent } from '@isa/oms/shared/task-list';
|
||||
import {
|
||||
FilterService,
|
||||
SearchBarInputComponent,
|
||||
@@ -28,6 +29,7 @@ import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
SearchBarInputComponent,
|
||||
IconButtonComponent,
|
||||
FilterInputMenuButtonComponent,
|
||||
ReturnTaskListComponent,
|
||||
],
|
||||
})
|
||||
export class ReturnSearchMainComponent {
|
||||
|
||||
7
libs/oms/shared/task-list/README.md
Normal file
7
libs/oms/shared/task-list/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# return-task-list
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test return-task-list` to execute the unit tests.
|
||||
34
libs/oms/shared/task-list/eslint.config.mjs
Normal file
34
libs/oms/shared/task-list/eslint.config.mjs
Normal file
@@ -0,0 +1,34 @@
|
||||
import nx from '@nx/eslint-plugin';
|
||||
import baseConfig from '../../../../eslint.config.mjs';
|
||||
|
||||
export default [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'omsShared',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'oms-shared',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
21
libs/oms/shared/task-list/jest.config.ts
Normal file
21
libs/oms/shared/task-list/jest.config.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export default {
|
||||
displayName: 'return-task-list',
|
||||
preset: '../../../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
coverageDirectory: '../../../../coverage/libs/oms/shared/task-list',
|
||||
transform: {
|
||||
'^.+\\.(ts|mjs|js|html)$': [
|
||||
'jest-preset-angular',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
},
|
||||
],
|
||||
},
|
||||
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||
'jest-preset-angular/build/serializers/html-comment',
|
||||
],
|
||||
};
|
||||
20
libs/oms/shared/task-list/project.json
Normal file
20
libs/oms/shared/task-list/project.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "return-task-list",
|
||||
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/oms/shared/task-list/src",
|
||||
"prefix": "oms-shared",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "libs/oms/shared/task-list/jest.config.ts"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint"
|
||||
}
|
||||
}
|
||||
}
|
||||
1
libs/oms/shared/task-list/src/index.ts
Normal file
1
libs/oms/shared/task-list/src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './lib/return-task-list/return-task-list.component';
|
||||
@@ -0,0 +1,38 @@
|
||||
<oms-shared-return-product-info
|
||||
[product]="product()"
|
||||
data-what="component"
|
||||
data-which="return-product-info"
|
||||
></oms-shared-return-product-info>
|
||||
|
||||
<div class="p-4 flex flex-col gap-4 rounded-lg bg-isa-secondary-100">
|
||||
<div
|
||||
data-what="review-list"
|
||||
data-which="processing-comment"
|
||||
class="isa-text-body-2-bold"
|
||||
>
|
||||
{{ processingComment() }}
|
||||
</div>
|
||||
@if (!item()?.completed) {
|
||||
<button
|
||||
class="flex items-center gap-2 self-end"
|
||||
type="button"
|
||||
uiButton
|
||||
color="primary"
|
||||
(click)="onActionClick({ type: 'complete' })"
|
||||
data-what="button"
|
||||
data-which="complete"
|
||||
>
|
||||
<ng-icon name="isaActionCheck" uiButtonIcon></ng-icon>
|
||||
Als erledigt markieren
|
||||
</button>
|
||||
} @else {
|
||||
<span
|
||||
class="flex items-center gap-2 text-isa-accent-green isa-text-body-2-bold self-end"
|
||||
data-what="info"
|
||||
data-which="completed"
|
||||
>
|
||||
<ng-icon name="isaActionCheck" uiButtonIcon></ng-icon>
|
||||
Abgeschlossen
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
.oms-shared-return-task-list-item {
|
||||
@apply w-full;
|
||||
}
|
||||
|
||||
.oms-shared-return-task-list-item__review {
|
||||
@apply grid grid-cols-[1fr,1fr] desktop:grid-cols-[1fr,1fr,minmax(20rem,auto)] gap-6 py-6 text-isa-secondary-900 items-center border-b border-solid border-isa-neutral-300 last:pb-0 last:border-none;
|
||||
}
|
||||
|
||||
.oms-shared-return-task-list-item__main {
|
||||
@apply bg-white rounded-2xl p-6 flex flex-col gap-6;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
input,
|
||||
output,
|
||||
ViewEncapsulation,
|
||||
} from '@angular/core';
|
||||
import { isaActionCheck } from '@isa/icons';
|
||||
import {
|
||||
Product,
|
||||
ReceiptItemTaskListItem,
|
||||
TaskActionType,
|
||||
} from '@isa/oms/data-access';
|
||||
import { ReturnProductInfoComponent } from '@isa/oms/shared/product-info';
|
||||
import { ButtonComponent } from '@isa/ui/buttons';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
|
||||
@Component({
|
||||
selector: 'oms-shared-return-task-list-item',
|
||||
templateUrl: './return-task-list-item.component.html',
|
||||
styleUrl: './return-task-list-item.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [ReturnProductInfoComponent, ButtonComponent, NgIconComponent],
|
||||
providers: [provideIcons({ isaActionCheck })],
|
||||
host: {
|
||||
'[class]': "['oms-shared-return-task-list-item', appearanceClass()]",
|
||||
},
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class ReturnTaskListItemComponent {
|
||||
appearance = input<'main' | 'review'>('main');
|
||||
item = input.required<ReceiptItemTaskListItem>();
|
||||
action = output<TaskActionType>();
|
||||
|
||||
appearanceClass = computed(
|
||||
() => `oms-shared-return-task-list-item__${this.appearance()}`,
|
||||
);
|
||||
|
||||
product = computed(() => {
|
||||
const item = this.item();
|
||||
const product = item.product;
|
||||
|
||||
if (product) {
|
||||
return product as Product;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
processingComment = computed(() => {
|
||||
const item = this.item();
|
||||
const processingComment = item.processingComment;
|
||||
|
||||
if (processingComment) {
|
||||
return processingComment;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
onActionClick(type: Omit<TaskActionType, 'taskId'>) {
|
||||
const taskId = this.item().id;
|
||||
const actionType = { ...type, taskId };
|
||||
this.action.emit(actionType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
@let taskList = taskListItems();
|
||||
@if (taskList?.length !== 0) {
|
||||
<div
|
||||
class="flex flex-col w-full items-center justify-center"
|
||||
[class.list-gap]="appearance() === 'main'"
|
||||
>
|
||||
@for (item of taskList; track item.id) {
|
||||
@defer (on viewport) {
|
||||
<oms-shared-return-task-list-item
|
||||
[appearance]="appearance()"
|
||||
[item]="item"
|
||||
(action)="handleAction($event)"
|
||||
></oms-shared-return-task-list-item>
|
||||
} @placeholder {
|
||||
<!-- TODO: Den Spinner durch Skeleton Loader Kacheln ersetzen -->
|
||||
<div class="h-[11rem] w-full flex items-center justify-center">
|
||||
<ui-icon-button
|
||||
[pending]="true"
|
||||
[color]="'tertiary'"
|
||||
data-what="load-spinner"
|
||||
data-which="item-placeholder"
|
||||
></ui-icon-button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.list-gap {
|
||||
@apply gap-6;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ReturnTaskListComponent } from './return-task-list.component';
|
||||
|
||||
describe('ReturnTaskListComponent', () => {
|
||||
let component: ReturnTaskListComponent;
|
||||
let fixture: ComponentFixture<ReturnTaskListComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ReturnTaskListComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ReturnTaskListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
effect,
|
||||
inject,
|
||||
input,
|
||||
untracked,
|
||||
ViewEncapsulation,
|
||||
} from '@angular/core';
|
||||
import { ReturnTaskListItemComponent } from './return-task-list-item/return-task-list-item.component';
|
||||
import {
|
||||
ReturnReviewService,
|
||||
ReturnReviewStore,
|
||||
TaskActionType,
|
||||
} from '@isa/oms/data-access';
|
||||
import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { injectActivatedProcessId } from '@isa/core/process';
|
||||
import { logger, provideLoggerContext } from '@isa/core/logging';
|
||||
|
||||
@Component({
|
||||
selector: 'oms-shared-return-task-list',
|
||||
templateUrl: './return-task-list.component.html',
|
||||
styleUrl: './return-task-list.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [ReturnTaskListItemComponent, IconButtonComponent],
|
||||
providers: [provideLoggerContext({ component: 'ReturnTaskListComponent' })],
|
||||
host: {
|
||||
'[class]': "['oms-shared-return-task-list', appearanceClass()]",
|
||||
},
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class ReturnTaskListComponent {
|
||||
appearance = input<'main' | 'review'>('main');
|
||||
#returnReviewService = inject(ReturnReviewService);
|
||||
#returnReviewStore = inject(ReturnReviewStore);
|
||||
#logger = logger();
|
||||
|
||||
processId = injectActivatedProcessId();
|
||||
|
||||
appearanceClass = computed(
|
||||
() => `oms-shared-return-task-list__${this.appearance()}`,
|
||||
);
|
||||
|
||||
taskListItems = computed(() => {
|
||||
const processId = this.processId();
|
||||
if (!processId) {
|
||||
return [];
|
||||
}
|
||||
return this.#returnReviewStore.entityMap()[processId].data ?? [];
|
||||
});
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
const processId = this.processId();
|
||||
if (processId) {
|
||||
untracked(() =>
|
||||
this.#returnReviewStore.fetchTaskListItems({ processId }),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
effect(() => {
|
||||
const taskListItems = this.taskListItems();
|
||||
console.log(taskListItems);
|
||||
});
|
||||
}
|
||||
|
||||
async handleAction(action: TaskActionType) {
|
||||
switch (action.type) {
|
||||
case 'complete':
|
||||
await this.completeTask(action.taskId);
|
||||
break;
|
||||
// TODO: Implement other action types
|
||||
}
|
||||
}
|
||||
|
||||
async completeTask(taskId: number) {
|
||||
try {
|
||||
const processId = this.processId();
|
||||
const result = await firstValueFrom(
|
||||
this.#returnReviewService.completeTask(taskId),
|
||||
);
|
||||
|
||||
if (result && processId) {
|
||||
this.#returnReviewStore.updateTaskListItem({
|
||||
processId,
|
||||
taskListItem: result,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.#logger.error('Error completing task', error, {
|
||||
function: 'completeTask',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
6
libs/oms/shared/task-list/src/test-setup.ts
Normal file
6
libs/oms/shared/task-list/src/test-setup.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
|
||||
|
||||
setupZoneTestEnv({
|
||||
errorOnUnknownElements: true,
|
||||
errorOnUnknownProperties: true,
|
||||
});
|
||||
28
libs/oms/shared/task-list/tsconfig.json
Normal file
28
libs/oms/shared/task-list/tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
17
libs/oms/shared/task-list/tsconfig.lib.json
Normal file
17
libs/oms/shared/task-list/tsconfig.lib.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/test-setup.ts",
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts"
|
||||
],
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
16
libs/oms/shared/task-list/tsconfig.spec.json
Normal file
16
libs/oms/shared/task-list/tsconfig.spec.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"target": "es2016",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"files": ["src/test-setup.ts"],
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -67,6 +67,7 @@
|
||||
"@isa/oms/shared/product-info": [
|
||||
"libs/oms/shared/product-info/src/index.ts"
|
||||
],
|
||||
"@isa/oms/shared/task-list": ["libs/oms/shared/task-list/src/index.ts"],
|
||||
"@isa/oms/utils/translation": ["libs/oms/utils/translation/src/index.ts"],
|
||||
"@isa/shared/filter": ["libs/shared/filter/src/index.ts"],
|
||||
"@isa/shared/product-image": ["libs/shared/product-image/src/index.ts"],
|
||||
|
||||
Reference in New Issue
Block a user