mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
feat(oms-data-acess), feat(return-review): implement return review functionality
Add new ReturnReview library with core components and service structure: - Create ReturnReviewService and ReturnReviewStore for task management - Extract PrintReceipts functionality into dedicated service - Implement review page components with task listing and completion - Add support for receipt item tasks data model - Update error handling with consistent error types - Add comprehensive JSDoc documentation throughout The implementation provides the foundation for the return review workflow, including task listing and completion functionality. Ref: #4942
This commit is contained in:
@@ -4,7 +4,3 @@ import { ProcessService } from './process.service';
|
||||
export function injectActivatedProcessId() {
|
||||
return inject(ProcessService).activatedProcessId;
|
||||
}
|
||||
|
||||
export function injectActivatedProcess() {
|
||||
return inject(ProcessService).activatedProcess;
|
||||
}
|
||||
|
||||
@@ -22,3 +22,6 @@ export * from './lib/return-process.service';
|
||||
export * from './lib/return-process.store';
|
||||
export * from './lib/return-search.service';
|
||||
export * from './lib/return-search.store';
|
||||
export * from './lib/return-print-receipts.service';
|
||||
export * from './lib/return-review.service';
|
||||
export * from './lib/return-review.store';
|
||||
|
||||
@@ -18,7 +18,7 @@ export class ReturnSearchError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export class ReturnSearchParseQueryTokenError extends ReturnSearchError {
|
||||
export class ReturnParseQueryTokenError extends ReturnSearchError {
|
||||
constructor(
|
||||
public queryToken: QueryTokenInput,
|
||||
messsage: string,
|
||||
|
||||
@@ -2,9 +2,11 @@ export * from './errors';
|
||||
export * from './models';
|
||||
export * from './return-details.service';
|
||||
export * from './return-details.store';
|
||||
export * from './return-process-questions';
|
||||
export * from './return-process.service';
|
||||
export * from './return-process.store';
|
||||
export * from './return-search.service';
|
||||
export * from './return-search.store';
|
||||
export * from './return-print-receipts.service';
|
||||
export * from './return-review.service';
|
||||
export * from './return-review.store';
|
||||
export * from './schemas';
|
||||
|
||||
@@ -4,6 +4,7 @@ export * from './eligible-for-return';
|
||||
export * from './gender';
|
||||
export * from './product';
|
||||
export * from './quantity';
|
||||
export * from './receipt-item-task-list-item';
|
||||
export * from './receipt-item';
|
||||
export * from './receipt-list-item';
|
||||
export * from './receipt-type';
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ReceiptItemTaskListItemDTO } from '@generated/swagger/oms-api';
|
||||
|
||||
export interface ReceiptItemTaskListItem extends ReceiptItemTaskListItemDTO {
|
||||
id: number;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { DomainPrinterService, Printer } from '@domain/printer';
|
||||
import { PrintModalComponent, PrintModalData } from '@modal/printer';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ReturnPrintReceiptsService {
|
||||
#printService = inject(DomainPrinterService);
|
||||
#environmentSerivce = inject(EnvironmentService);
|
||||
|
||||
// TODO: Refactor: CDK Dialog verwenden für neuen Printer Dialog
|
||||
#uiModal = inject(UiModalService);
|
||||
// TODO: Refactor: CDK Dialog verwenden für neuen Printer Dialog
|
||||
/**
|
||||
* Prints return receipts using the appropriate printer
|
||||
*
|
||||
* This method:
|
||||
* 1. Retrieves available label printers
|
||||
* 2. Determines if a printer is selected or if running on tablet mode
|
||||
* 3. Opens a print modal dialog if no printer is selected or on tablet
|
||||
* 4. Prints directly to the selected printer when available
|
||||
*
|
||||
* @param {number[]} receiptIds - Array of receipt IDs to print
|
||||
* @returns {Promise<number[]>} Promise resolving to the same array of receipt IDs
|
||||
* @throws {Error} When printing operations fail
|
||||
*/
|
||||
async printReturns(receiptIds: number[]) {
|
||||
const printerList = await firstValueFrom(
|
||||
this.#printService.getAvailableLabelPrinters(),
|
||||
);
|
||||
let printer: Printer | undefined = undefined;
|
||||
|
||||
if (Array.isArray(printerList)) {
|
||||
printer = printerList.find((printer) => printer.selected === true);
|
||||
}
|
||||
|
||||
if (!printer || this.#environmentSerivce.matchTablet()) {
|
||||
await this.#uiModal
|
||||
.open({
|
||||
content: PrintModalComponent,
|
||||
config: { showScrollbarY: false },
|
||||
data: {
|
||||
printImmediately: !this.#environmentSerivce.matchTablet(),
|
||||
printerType: 'Label',
|
||||
print: (printer) =>
|
||||
this.#printService
|
||||
.printReturnReceipt({
|
||||
printer: printer,
|
||||
receiptIds,
|
||||
})
|
||||
.toPromise(),
|
||||
} as PrintModalData,
|
||||
})
|
||||
.afterClosed$.toPromise();
|
||||
} else {
|
||||
await firstValueFrom(
|
||||
this.#printService.printReturnReceipt({
|
||||
printer: printer.key,
|
||||
receiptIds,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return receiptIds;
|
||||
}
|
||||
}
|
||||
@@ -30,10 +30,7 @@ import {
|
||||
ReturnReceiptValuesDTO,
|
||||
} from '@generated/swagger/oms-api';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { DomainPrinterService, Printer } from '@domain/printer';
|
||||
import { PrintModalComponent, PrintModalData } from '@modal/printer';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { ReturnPrintReceiptsService } from './return-print-receipts.service';
|
||||
|
||||
/**
|
||||
* Service responsible for managing the return process workflow.
|
||||
@@ -45,11 +42,7 @@ export class ReturnProcessService {
|
||||
#logger = logger();
|
||||
|
||||
#receiptService = inject(ReceiptService);
|
||||
#printService = inject(DomainPrinterService);
|
||||
|
||||
// TODO: Refactor: CDK Dialog verwenden für neuen Printer Dialog
|
||||
#environmentSerivce = inject(EnvironmentService);
|
||||
#uiModal = inject(UiModalService);
|
||||
#printReceiptsService = inject(ReturnPrintReceiptsService);
|
||||
|
||||
/**
|
||||
* Gets all available product categories that have defined question sets.
|
||||
@@ -222,6 +215,26 @@ export class ReturnProcessService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves consolidated return information from a return process.
|
||||
*
|
||||
* @param process - The return process to extract information from.
|
||||
* @returns The consolidated return information or undefined if unable to generate.
|
||||
* @throws {PropertyNullOrUndefinedError} If questions cannot be found.
|
||||
*/
|
||||
getReturnInfo(process: ReturnProcess): ReturnInfo | undefined {
|
||||
const questions = this.returnProcessQuestions(process);
|
||||
if (!questions) {
|
||||
throw new PropertyNullOrUndefinedError('questions');
|
||||
}
|
||||
const answers = process.answers || {};
|
||||
|
||||
return getReturnInfo({
|
||||
questions,
|
||||
answers,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the return process for a collection of return items.
|
||||
* This method validates all return processes are complete and creates return receipts.
|
||||
@@ -291,83 +304,8 @@ export class ReturnProcessService {
|
||||
|
||||
const receipts = response.result as Receipt[];
|
||||
const receiptIds = receipts.map((receipt) => receipt.id);
|
||||
|
||||
await this.printReturns(receiptIds);
|
||||
await this.#printReceiptsService.printReturns(receiptIds);
|
||||
|
||||
return receipts;
|
||||
}
|
||||
|
||||
// TODO: Refactor: CDK Dialog verwenden für neuen Printer Dialog
|
||||
|
||||
/**
|
||||
* Prints return receipts using the appropriate printer
|
||||
*
|
||||
* This method:
|
||||
* 1. Retrieves available label printers
|
||||
* 2. Determines if a printer is selected or if running on tablet mode
|
||||
* 3. Opens a print modal dialog if no printer is selected or on tablet
|
||||
* 4. Prints directly to the selected printer when available
|
||||
*
|
||||
* @param {number[]} receiptIds - Array of receipt IDs to print
|
||||
* @returns {Promise<number[]>} Promise resolving to the same array of receipt IDs
|
||||
* @throws {Error} When printing operations fail
|
||||
*/
|
||||
async printReturns(receiptIds: number[]) {
|
||||
const printerList = await firstValueFrom(
|
||||
this.#printService.getAvailableLabelPrinters(),
|
||||
);
|
||||
let printer: Printer | undefined = undefined;
|
||||
|
||||
if (Array.isArray(printerList)) {
|
||||
printer = printerList.find((printer) => printer.selected === true);
|
||||
}
|
||||
|
||||
if (!printer || this.#environmentSerivce.matchTablet()) {
|
||||
await this.#uiModal
|
||||
.open({
|
||||
content: PrintModalComponent,
|
||||
config: { showScrollbarY: false },
|
||||
data: {
|
||||
printImmediately: !this.#environmentSerivce.matchTablet(),
|
||||
printerType: 'Label',
|
||||
print: (printer) =>
|
||||
this.#printService
|
||||
.printReturnReceipt({
|
||||
printer: printer,
|
||||
receiptIds,
|
||||
})
|
||||
.toPromise(),
|
||||
} as PrintModalData,
|
||||
})
|
||||
.afterClosed$.toPromise();
|
||||
} else {
|
||||
await firstValueFrom(
|
||||
this.#printService.printReturnReceipt({
|
||||
printer: printer.key,
|
||||
receiptIds,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return receiptIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves consolidated return information from a return process.
|
||||
*
|
||||
* @param process - The return process to extract information from.
|
||||
* @returns The consolidated return information or undefined if unable to generate.
|
||||
* @throws {PropertyNullOrUndefinedError} If questions cannot be found.
|
||||
*/
|
||||
getReturnInfo(process: ReturnProcess): ReturnInfo | undefined {
|
||||
const questions = this.returnProcessQuestions(process);
|
||||
if (!questions) {
|
||||
throw new PropertyNullOrUndefinedError('questions');
|
||||
}
|
||||
const answers = process.answers || {};
|
||||
|
||||
return getReturnInfo({
|
||||
questions,
|
||||
answers,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
76
libs/oms/data-access/src/lib/return-review.service.ts
Normal file
76
libs/oms/data-access/src/lib/return-review.service.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { map, Observable, throwError } from 'rxjs';
|
||||
import { ReceiptItemTaskListItem } from './models';
|
||||
import { QueryTokenInput, QueryTokenSchema } from './schemas';
|
||||
import { ZodError } from 'zod';
|
||||
import { ReturnParseQueryTokenError } from './errors';
|
||||
import { ReceiptService } from '@generated/swagger/oms-api';
|
||||
|
||||
/**
|
||||
* Service responsible for managing return review operations.
|
||||
*
|
||||
* Provides functionality to query receipt item tasks and complete tasks
|
||||
* in the return review process.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ReturnReviewService {
|
||||
#receiptService = inject(ReceiptService);
|
||||
|
||||
/**
|
||||
* Queries receipt item tasks that are not yet completed.
|
||||
*
|
||||
* Constructs a query token with a filter for incomplete tasks,
|
||||
* validates it using Zod schema validation, and fetches the tasks
|
||||
* from the receipt service.
|
||||
*
|
||||
* @returns An Observable of receipt item task list items that match the query
|
||||
* @throws ReturnParseQueryTokenError when query token parsing fails
|
||||
*/
|
||||
queryReceiptItemTasks(): Observable<ReceiptItemTaskListItem[]> {
|
||||
let queryToken: QueryTokenInput = {
|
||||
filter: {
|
||||
completed: false,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
queryToken = QueryTokenSchema.parse(queryToken);
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
return throwError(
|
||||
() => new ReturnParseQueryTokenError(queryToken, error.message),
|
||||
);
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
return throwError(
|
||||
() => new ReturnParseQueryTokenError(queryToken, error.message),
|
||||
);
|
||||
}
|
||||
return throwError(
|
||||
() => new ReturnParseQueryTokenError(queryToken, 'Unknown error'),
|
||||
);
|
||||
}
|
||||
|
||||
return this.#receiptService.ReceiptQueryReceiptItemTasks(queryToken).pipe(
|
||||
map((res) => {
|
||||
// TODO: Response Error Handling
|
||||
return res.result as ReceiptItemTaskListItem[];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a receipt item task as completed.
|
||||
*
|
||||
* @param taskId - The unique identifier of the task to complete
|
||||
* @returns An Observable containing the updated receipt item task after completion
|
||||
*/
|
||||
completeTask(taskId: number) {
|
||||
return this.#receiptService.ReceiptReceiptItemTaskCompleted(taskId).pipe(
|
||||
map((res) => {
|
||||
// TODO: Response Error Handling
|
||||
return res.result as ReceiptItemTaskListItem;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
130
libs/oms/data-access/src/lib/return-review.store.ts
Normal file
130
libs/oms/data-access/src/lib/return-review.store.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { patchState, signalStore, withMethods } from '@ngrx/signals';
|
||||
import { withEntities, updateEntity, addEntity } from '@ngrx/signals/entities';
|
||||
import { inject } from '@angular/core';
|
||||
import { ReceiptItemTaskListItem } from './models';
|
||||
import { AsyncResult, AsyncResultStatus } from '@isa/common/data-access';
|
||||
import { rxMethod } from '@ngrx/signals/rxjs-interop';
|
||||
import { ReturnReviewService } from './return-review.service';
|
||||
import { pipe, switchMap, tap } from 'rxjs';
|
||||
import { tapResponse } from '@ngrx/operators';
|
||||
|
||||
/**
|
||||
* Represents the structure of a return review task list item in the store.
|
||||
* Extends the AsyncResult type with an ID field to uniquely identify each entity.
|
||||
*/
|
||||
export type ReturnReviewTaskListItems = AsyncResult<
|
||||
ReceiptItemTaskListItem[] | undefined
|
||||
> & {
|
||||
/** The unique identifier for the entity, matching the processId */
|
||||
id: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Default initial state for a new ReturnReviewTaskListItems entity.
|
||||
* Sets up the entity with undefined data and an Idle status.
|
||||
*/
|
||||
const initialEntity: Omit<ReturnReviewTaskListItems, 'id'> = {
|
||||
data: undefined,
|
||||
status: AsyncResultStatus.Idle,
|
||||
};
|
||||
|
||||
/**
|
||||
* Signal store for managing return review task list items.
|
||||
* Uses NgRx Signals with entity support to track the async state
|
||||
* of receipt item tasks for different processes.
|
||||
*/
|
||||
export const ReturnReviewStore = signalStore(
|
||||
{ providedIn: 'root' },
|
||||
withEntities<ReturnReviewTaskListItems>(),
|
||||
withMethods((store) => ({
|
||||
/**
|
||||
* Prepares the store for a data fetch operation for a specific process.
|
||||
* Creates a new entity if one doesn't exist, or updates an existing entity's status to Pending.
|
||||
*
|
||||
* @param id - The process ID to prepare for fetching
|
||||
*/
|
||||
beforeFetch(id: number) {
|
||||
let entity: ReturnReviewTaskListItems | undefined =
|
||||
store.entityMap()?.[id];
|
||||
if (!entity) {
|
||||
entity = {
|
||||
...initialEntity,
|
||||
id,
|
||||
status: AsyncResultStatus.Pending,
|
||||
};
|
||||
patchState(store, addEntity(entity));
|
||||
} else {
|
||||
patchState(
|
||||
store,
|
||||
updateEntity({
|
||||
id,
|
||||
changes: { status: AsyncResultStatus.Pending },
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the store with successfully fetched task list items.
|
||||
* Sets the entity status to Success and stores the retrieved data.
|
||||
*
|
||||
* @param id - The process ID to update
|
||||
* @param data - The fetched receipt item task list items
|
||||
*/
|
||||
fetchSuccess(id: number, data: ReceiptItemTaskListItem[]) {
|
||||
patchState(
|
||||
store,
|
||||
updateEntity({
|
||||
id,
|
||||
changes: { data, status: AsyncResultStatus.Success },
|
||||
}),
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the store with error information when fetching fails.
|
||||
* Sets the entity status to Error and stores the error for reference.
|
||||
*
|
||||
* @param id - The process ID that encountered an error
|
||||
* @param error - The error that occurred during the fetch operation
|
||||
*/
|
||||
fetchError(id: number, error: unknown) {
|
||||
patchState(
|
||||
store,
|
||||
updateEntity({
|
||||
id,
|
||||
changes: { error, status: AsyncResultStatus.Error },
|
||||
}),
|
||||
);
|
||||
},
|
||||
})),
|
||||
withMethods((store, returnReviewService = inject(ReturnReviewService)) => ({
|
||||
/**
|
||||
* Fetches task list items for a specific process ID.
|
||||
* This is an rxMethod that handles the entire fetch lifecycle:
|
||||
* - Updates store status to Pending before fetching
|
||||
* - Calls the ReturnReviewService to query receipt item tasks
|
||||
* - Updates the store with either success or error status based on the result
|
||||
*
|
||||
* @param params - Object containing the processId to fetch tasks for
|
||||
* @returns An Observable that completes when the fetch operation is done
|
||||
*/
|
||||
fetchTaskListItems: rxMethod<{ processId: number }>(
|
||||
pipe(
|
||||
tap(({ processId }) => store.beforeFetch(processId)),
|
||||
switchMap(({ processId }) =>
|
||||
returnReviewService.queryReceiptItemTasks().pipe(
|
||||
tapResponse({
|
||||
next(value) {
|
||||
store.fetchSuccess(processId, value);
|
||||
},
|
||||
error(error) {
|
||||
store.fetchError(processId, error);
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
})),
|
||||
);
|
||||
@@ -5,7 +5,7 @@ import { QueryTokenInput, QueryTokenSchema } from './schemas';
|
||||
import { ReceiptListItem } from './models';
|
||||
import { ListResponseArgs } from '@isa/common/data-access';
|
||||
import {
|
||||
ReturnSearchParseQueryTokenError,
|
||||
ReturnParseQueryTokenError,
|
||||
ReturnSearchSearchError,
|
||||
} from './errors/return-search.error';
|
||||
import { ZodError } from 'zod';
|
||||
@@ -36,7 +36,7 @@ export class ReturnSearchService {
|
||||
*
|
||||
* @param {QueryTokenInput} queryToken - The query token containing search parameters.
|
||||
* @returns {Observable<ListResponseArgs<ReceiptListItem>>} An observable containing the search results.
|
||||
* @throws {ReturnSearchParseQueryTokenError} If the query token is invalid.
|
||||
* @throws {ReturnParseQueryTokenError} If the query token is invalid.
|
||||
* @throws {ReturnSearchSearchError} If the search fails due to an API error.
|
||||
*/
|
||||
search(
|
||||
@@ -47,16 +47,16 @@ export class ReturnSearchService {
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
return throwError(
|
||||
() => new ReturnSearchParseQueryTokenError(queryToken, error.message),
|
||||
() => new ReturnParseQueryTokenError(queryToken, error.message),
|
||||
);
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
return throwError(
|
||||
() => new ReturnSearchParseQueryTokenError(queryToken, error.message),
|
||||
() => new ReturnParseQueryTokenError(queryToken, error.message),
|
||||
);
|
||||
}
|
||||
return throwError(
|
||||
() => new ReturnSearchParseQueryTokenError(queryToken, 'Unknown error'),
|
||||
() => new ReturnParseQueryTokenError(queryToken, 'Unknown error'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@ export const OrderBySchema = z.object({
|
||||
*/
|
||||
export const QueryTokenSchema = z.object({
|
||||
filter: z.record(z.any()).default({}), // Filter criteria as key-value pairs
|
||||
input: z.record(z.any()).default({}), // Input values for the query
|
||||
orderBy: z.array(OrderBySchema).default([]), // Sorting parameters
|
||||
skip: z.number().default(0), // Number of items to skip (for pagination)
|
||||
take: z.number().default(25), // Number of items to take (page size)
|
||||
input: z.record(z.any()).default({}).optional(), // Input values for the query
|
||||
orderBy: z.array(OrderBySchema).default([]).optional(), // Sorting parameters
|
||||
skip: z.number().default(0).optional(), // Number of items to skip (for pagination)
|
||||
take: z.number().default(25).optional(), // Number of items to take (page size)
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<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>
|
||||
<button
|
||||
type="button"
|
||||
uiButton
|
||||
color="secondary"
|
||||
(click)="markAsDone.emit(this.item().id)"
|
||||
data-what="button"
|
||||
data-which="mark-as-done"
|
||||
>
|
||||
Als erledigt markieren
|
||||
</button>
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply w-full grid grid-cols-[1fr,1fr,auto] p-6 text-isa-secondary-900 items-center;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
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 +1,22 @@
|
||||
<p>ReturnReview works!</p>
|
||||
<h2 class="isa-text-subtitle-1-regular">Die Rückgabe ware erfolgreich!</h2>
|
||||
|
||||
<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>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply flex flex-col gap-4 w-full justify-start items-center;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,71 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
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';
|
||||
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';
|
||||
|
||||
@Component({
|
||||
selector: 'oms-feature-return-review',
|
||||
imports: [CommonModule],
|
||||
templateUrl: './return-review.component.html',
|
||||
styleUrl: './return-review.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [IconButtonComponent, ReturnReviewItemComponent],
|
||||
providers: [provideLoggerContext({ component: 'ReturnReviewComponent' })],
|
||||
})
|
||||
export class ReturnReviewComponent {}
|
||||
export class ReturnReviewComponent {
|
||||
#returnReviewService = inject(ReturnReviewService);
|
||||
#returnReviewStore = inject(ReturnReviewStore);
|
||||
#logger = logger();
|
||||
|
||||
processId = injectActivatedProcessId();
|
||||
|
||||
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 completeTask(taskId: number) {
|
||||
try {
|
||||
const result = await firstValueFrom(
|
||||
this.#returnReviewService.completeTask(taskId),
|
||||
);
|
||||
|
||||
if (result) {
|
||||
// TODO: Update List
|
||||
// this.#returnReviewStore.updateTaskListItem(result);
|
||||
}
|
||||
} catch (error) {
|
||||
this.#logger.error('Error completing task', error, {
|
||||
function: 'completeTask',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user