mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-31 09:37:15 +01:00
Refactored return feature components and removed unused files.
- 🛠️ **Refactor**: Cleaned up return feature components and styles - 🗑️ **Chore**: Deleted obsolete files and updated exports - 📚 **Docs**: Added README for return details library
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
# feature-return-containers
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test feature-return-containers` to execute the unit tests.
|
||||
@@ -1,34 +0,0 @@
|
||||
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: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "feature-return-containers",
|
||||
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/feature/return/containers/src",
|
||||
"prefix": "lib",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "libs/feature/return/containers/jest.config.ts"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export * from './lib/return-details-data/return-details-data.component';
|
||||
export * from './lib/return-details-header/return-details-header.component';
|
||||
export * from './lib/return-details-order-group/return-details-order-group.component';
|
||||
export * from './lib/return-details-order-group-item/return-details-order-group-item.component';
|
||||
export * from './lib/return-details-order-group-data/return-details-order-group-data.component';
|
||||
export * from './lib/return-results-item-list/';
|
||||
export * from './lib/return-order-by-list/';
|
||||
@@ -1 +0,0 @@
|
||||
export * from './return-order-by-list.component';
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<ui-toolbar>
|
||||
<span class="text-isa-neutral-600 isa-text-body-2-regular">Sortieren</span>
|
||||
<div class="flex-grow"></div>
|
||||
<button (click)="onClick('Belegdatum')" uiTextButton>Belegdatum</button>
|
||||
<button (click)="onClick('Email')" uiTextButton>Email</button>
|
||||
<button (click)="onClick('Name')" uiTextButton>Name</button>
|
||||
<button (click)="onClick('PLZ')" uiTextButton>PLZ</button>
|
||||
</ui-toolbar>
|
||||
@@ -1,18 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
|
||||
import { TextButtonComponent } from '@isa/ui/buttons';
|
||||
import { ToolbarComponent } from '@isa/ui/toolbar';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-return-order-by-list',
|
||||
templateUrl: './return-order-by-list.component.html',
|
||||
styleUrls: ['./return-order-by-list.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [ToolbarComponent, TextButtonComponent],
|
||||
})
|
||||
export class ReturnOrderByListComponent {
|
||||
onClick(label: string) {
|
||||
console.log('Order By click -', label);
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export * from './return-results-item-list.component';
|
||||
@@ -1,37 +0,0 @@
|
||||
<ui-client-row>
|
||||
<ui-client-row-content>
|
||||
<h3 class="isa-text-subtitle-1-regular">{{ name() }}</h3>
|
||||
</ui-client-row-content>
|
||||
<ui-item-row-data>
|
||||
<ui-item-row-data-row>
|
||||
<ui-item-row-data-label>Belegdatum</ui-item-row-data-label>
|
||||
<ui-item-row-data-value>
|
||||
<span class="isa-text-body-2-bold">
|
||||
{{ receiptDate() | date: "dd.MM.yy" }}
|
||||
</span>
|
||||
</ui-item-row-data-value>
|
||||
</ui-item-row-data-row>
|
||||
<ui-item-row-data-row>
|
||||
<ui-item-row-data-label>Rechnugsnr.</ui-item-row-data-label>
|
||||
<ui-item-row-data-value>
|
||||
<span class="isa-text-body-2-bold"> {{ receiptNumber() }} </span>
|
||||
</ui-item-row-data-value>
|
||||
</ui-item-row-data-row>
|
||||
<ui-item-row-data-row>
|
||||
<ui-item-row-data-label>Vorgangs-ID</ui-item-row-data-label>
|
||||
<ui-item-row-data-value>
|
||||
<span class="isa-text-body-2-bold"> {{ orderNumber() }} </span>
|
||||
</ui-item-row-data-value>
|
||||
</ui-item-row-data-row>
|
||||
</ui-item-row-data>
|
||||
<ui-item-row-data>
|
||||
<ui-item-row-data-row>
|
||||
<ui-item-row-data-label>Email</ui-item-row-data-label>
|
||||
<ui-item-row-data-value>{{ email() }}</ui-item-row-data-value>
|
||||
</ui-item-row-data-row>
|
||||
<ui-item-row-data-row>
|
||||
<ui-item-row-data-label>Anschrift</ui-item-row-data-label>
|
||||
<ui-item-row-data-value>{{ address() }}</ui-item-row-data-value>
|
||||
</ui-item-row-data-row>
|
||||
</ui-item-row-data>
|
||||
</ui-client-row>
|
||||
@@ -1,46 +0,0 @@
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
|
||||
import { ReceiptListItem } from '@isa/oms/data-access';
|
||||
import { ClientRowImports, ItemRowDataImports } from '@isa/ui/item-rows';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-return-results-item-list',
|
||||
templateUrl: './return-results-item-list.component.html',
|
||||
styleUrls: ['./return-results-item-list.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [ClientRowImports, ItemRowDataImports, DatePipe],
|
||||
})
|
||||
export class ReturnResultsItemListComponent {
|
||||
item = input.required<ReceiptListItem>();
|
||||
|
||||
name = computed(() => {
|
||||
const firstName = this.item()?.billing?.person?.firstName;
|
||||
const lastName = this.item()?.billing?.person?.lastName;
|
||||
const buyerName = [lastName, firstName].filter((f) => !!f);
|
||||
const organisation = [this.item()?.billing?.organisation?.name].filter((f) => !!f);
|
||||
|
||||
return [organisation.join(), buyerName.join(' ')].filter((f) => !!f).join(' - ');
|
||||
});
|
||||
|
||||
receiptDate = computed(() => {
|
||||
return this.item()?.printedDate;
|
||||
});
|
||||
|
||||
receiptNumber = computed(() => {
|
||||
return this.item()?.receiptNumber;
|
||||
});
|
||||
|
||||
orderNumber = computed(() => {
|
||||
return this.item()?.orderNumber;
|
||||
});
|
||||
|
||||
email = computed(() => {
|
||||
return this.item()?.billing?.communicationDetails?.email;
|
||||
});
|
||||
|
||||
address = computed(() => {
|
||||
const address = this.item()?.billing?.address;
|
||||
return address ? [address.zipCode, address.city].join(' ') : '';
|
||||
});
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"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"]
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"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"]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
# feature-return-pages
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test feature-return-pages` to execute the unit tests.
|
||||
@@ -1,21 +0,0 @@
|
||||
export default {
|
||||
displayName: 'feature-return-pages',
|
||||
preset: '../../../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
coverageDirectory: '../../../../coverage/libs/feature/return/pages',
|
||||
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',
|
||||
],
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
:host {
|
||||
@apply flex flex-col pt-12 items-center;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<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="lib-return-main-page__loading-spinner">
|
||||
<ui-icon-button [pending]="true" [color]="'tertiary'"></ui-icon-button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex flex-row gap-4">
|
||||
@for (filterInput of filterInputs(); track filterInput.key) {
|
||||
<filter-input-menu-button [filterInput]="filterInput" (applied)="onSearch()">
|
||||
</filter-input-menu-button>
|
||||
}
|
||||
</div>
|
||||
@@ -1,63 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { injectActivatedProcessId } from '@isa/core/process';
|
||||
import { ReturnSearchStatus, ReturnSearchStore } from '@isa/oms/data-access';
|
||||
import {
|
||||
FilterService,
|
||||
SearchBarInputComponent,
|
||||
FilterInputMenuButtonComponent,
|
||||
} from '@isa/shared/filter';
|
||||
import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-return-main-page',
|
||||
templateUrl: './main-page.component.html',
|
||||
styleUrls: ['./main-page.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [SearchBarInputComponent, IconButtonComponent, FilterInputMenuButtonComponent],
|
||||
})
|
||||
export class MainPageComponent {
|
||||
#route = inject(ActivatedRoute);
|
||||
#router = inject(Router);
|
||||
|
||||
private _processId = injectActivatedProcessId();
|
||||
private _filterService = inject(FilterService);
|
||||
private _returnSearchStore = inject(ReturnSearchStore);
|
||||
|
||||
private _entity = computed(() => {
|
||||
const processId = this._processId();
|
||||
if (processId) {
|
||||
return this._returnSearchStore.getEntity(processId);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
entityPending = computed(() => {
|
||||
return this._entity()?.status === ReturnSearchStatus.Pending;
|
||||
});
|
||||
|
||||
filterInputs = computed(() =>
|
||||
this._filterService.inputs().filter((input) => input.group === 'filter'),
|
||||
);
|
||||
|
||||
// TODO: Suche als Provider in FilterService auslagern (+ Cancel Search, + Fetching Status)
|
||||
async onSearch() {
|
||||
const processId = this._processId();
|
||||
if (processId) {
|
||||
await this._updateQueryParams();
|
||||
this._returnSearchStore.search({
|
||||
processId,
|
||||
params: this._filterService.toParams(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _updateQueryParams() {
|
||||
await this.#router.navigate([], {
|
||||
queryParams: this._filterService.toParams(),
|
||||
relativeTo: this.#route,
|
||||
replaceUrl: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { inject } from '@angular/core';
|
||||
import { ResolveFn } from '@angular/router';
|
||||
import { QuerySettingsDTO } from '@generated/swagger/oms-api';
|
||||
import { ReturnSearchService } from '@isa/oms/data-access';
|
||||
|
||||
export const querySettingsResolverFn: ResolveFn<QuerySettingsDTO> = () =>
|
||||
inject(ReturnSearchService).querySettings();
|
||||
@@ -1,3 +0,0 @@
|
||||
:host {
|
||||
@apply flex flex-col gap-4 w-full justify-start items-center;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
<div class="w-full flex flex-row justify-between items-start">
|
||||
<filter-search-bar-input
|
||||
class="flex flex-row gap-4 h-12"
|
||||
[appearance]="'results'"
|
||||
inputKey="qs"
|
||||
(triggerSearch)="onSearch()"
|
||||
></filter-search-bar-input>
|
||||
|
||||
<div class="flex flex-row gap-4 items-center">
|
||||
<filter-filter-menu-button
|
||||
(applied)="onSearch()"
|
||||
[rollbackOnClose]="true"
|
||||
></filter-filter-menu-button>
|
||||
|
||||
@if (isMobileDevice()) {
|
||||
<ui-icon-button (click)="toggleOrderByToolbar.set(!toggleOrderByToolbar())">
|
||||
<ng-icon name="isaActionSort"></ng-icon>
|
||||
</ui-icon-button>
|
||||
} @else {
|
||||
<lib-return-order-by-list></lib-return-order-by-list>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (isMobileDevice() && toggleOrderByToolbar()) {
|
||||
<lib-return-order-by-list class="w-full"></lib-return-order-by-list>
|
||||
}
|
||||
|
||||
<span class="text-isa-neutral-900 isa-text-body-2-regular self-start">
|
||||
{{ entityHits() }} Einträge
|
||||
</span>
|
||||
|
||||
@let items = entityItems();
|
||||
@if (items.length > 0) {
|
||||
<div class="flex flex-col gap-4 w-full items-center justify-center">
|
||||
@for (item of items; track item.id) {
|
||||
@defer (on viewport) {
|
||||
<a [routerLink]="['../', 'receipt', item.id]" class="w-full">
|
||||
<lib-return-results-item-list #listElement [item]="item"></lib-return-results-item-list>
|
||||
</a>
|
||||
} @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'"></ui-icon-button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@if (entityStatus() === ReturnSearchStatus.Pending) {
|
||||
<div class="h-[7.75rem] w-full flex items-center justify-center">
|
||||
<ui-icon-button [pending]="true" [color]="'tertiary'"></ui-icon-button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
} @else if (items.length === 0 && entityStatus() === ReturnSearchStatus.Pending) {
|
||||
<div class="h-[7.75rem] w-full flex items-center justify-center">
|
||||
<ui-icon-button [pending]="true" [color]="'tertiary'"></ui-icon-button>
|
||||
</div>
|
||||
} @else if (entityStatus() !== ReturnSearchStatus.Idle) {
|
||||
<ui-empty-state [title]="emptyState().title" [description]="emptyState().description">
|
||||
</ui-empty-state>
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
effect,
|
||||
inject,
|
||||
QueryList,
|
||||
signal,
|
||||
untracked,
|
||||
viewChildren,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
ReturnResultsItemListComponent,
|
||||
ReturnOrderByListComponent,
|
||||
} from '@feature/return/containers';
|
||||
import { injectActivatedProcessId } from '@isa/core/process';
|
||||
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
import {
|
||||
FilterMenuButtonComponent,
|
||||
FilterService,
|
||||
SearchBarInputComponent,
|
||||
} from '@isa/shared/filter';
|
||||
import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
import { EmptyStateComponent } from '@isa/ui/empty-state';
|
||||
import { restoreScrollPosition } from '@isa/core/scroll-position';
|
||||
import { Platform } from '@angular/cdk/platform';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
import { isaActionSort } from '@isa/icons';
|
||||
import { ReturnSearchEntity, ReturnSearchStatus, ReturnSearchStore } from '@isa/oms/data-access';
|
||||
|
||||
type EmptyState = {
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'lib-return-results-page',
|
||||
templateUrl: './results-page.component.html',
|
||||
styleUrls: ['./results-page.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [
|
||||
RouterLink,
|
||||
ReturnResultsItemListComponent,
|
||||
ReturnOrderByListComponent,
|
||||
IconButtonComponent,
|
||||
SearchBarInputComponent,
|
||||
EmptyStateComponent,
|
||||
NgIconComponent,
|
||||
FilterMenuButtonComponent,
|
||||
],
|
||||
providers: [provideIcons({ isaActionSort })],
|
||||
})
|
||||
export class ResultsPageComponent {
|
||||
#route = inject(ActivatedRoute);
|
||||
#router = inject(Router);
|
||||
#platform = inject(Platform);
|
||||
|
||||
private _processId = injectActivatedProcessId();
|
||||
private _returnSearchStore = inject(ReturnSearchStore);
|
||||
private _filterService = inject(FilterService);
|
||||
|
||||
ReturnSearchStatus = ReturnSearchStatus;
|
||||
|
||||
filterInputs = computed(() =>
|
||||
this._filterService.inputs().filter((input) => input.group === 'filter'),
|
||||
);
|
||||
|
||||
private _entity = computed(() => {
|
||||
const processId = this._processId();
|
||||
if (processId) {
|
||||
return this._returnSearchStore.getEntity(processId);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
entityItems = computed(() => {
|
||||
return this._entity()?.items ?? [];
|
||||
});
|
||||
|
||||
entityHits = computed(() => {
|
||||
return this._entity()?.hits ?? 0;
|
||||
});
|
||||
|
||||
entityStatus = computed(() => {
|
||||
return this._entity()?.status ?? ReturnSearchStatus.Idle;
|
||||
});
|
||||
|
||||
emptyState = computed<EmptyState>(() => {
|
||||
return {
|
||||
title: 'Keine Suchergebnisse',
|
||||
description: 'Suchen Sie nach einer Rechnungsnummer oder Kundennamen.',
|
||||
};
|
||||
});
|
||||
|
||||
listElements = viewChildren<QueryList<ReturnResultsItemListComponent>>('listElement');
|
||||
|
||||
isMobileDevice = signal(this.#platform.ANDROID || this.#platform.IOS);
|
||||
toggleOrderByToolbar = signal(false);
|
||||
|
||||
searchEffectFn = () =>
|
||||
effect(() => {
|
||||
const processId = this._processId();
|
||||
const listLength = this.listElements().length;
|
||||
|
||||
untracked(async () => {
|
||||
if (processId) {
|
||||
const entity = this._entity();
|
||||
if (entity) {
|
||||
const isPending = this.entityStatus() === ReturnSearchStatus.Pending;
|
||||
// Trigger reload search if no search request is already pending and
|
||||
// 1. List scrolled to bottom
|
||||
// 2. After Process change AND no items in entity
|
||||
if (!isPending) {
|
||||
this._reload({ processId, entity, listLength });
|
||||
}
|
||||
} else {
|
||||
// Init Search after F5 / Refresh Page / No Entity Available
|
||||
await this._initSearch();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
constructor() {
|
||||
this.searchEffectFn();
|
||||
restoreScrollPosition();
|
||||
}
|
||||
|
||||
// TODO: Suche als Provider in FilterService auslagern (+ Cancel Search, + Fetching Status)
|
||||
async onSearch() {
|
||||
const processId = this._processId();
|
||||
if (processId) {
|
||||
await this._updateQueryParams();
|
||||
this._returnSearchStore.search({
|
||||
processId,
|
||||
params: this._filterService.toParams(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _updateQueryParams() {
|
||||
return await this.#router.navigate([], {
|
||||
queryParams: this._filterService.toParams(),
|
||||
relativeTo: this.#route,
|
||||
replaceUrl: true,
|
||||
});
|
||||
}
|
||||
|
||||
private async _reload({
|
||||
processId,
|
||||
entity,
|
||||
listLength,
|
||||
}: {
|
||||
processId: number;
|
||||
entity: ReturnSearchEntity;
|
||||
listLength: number;
|
||||
}) {
|
||||
const entityItemsLength = entity?.items?.length ?? 0;
|
||||
const hits = entity?.hits ?? 0;
|
||||
// Soll reloaden wenn man am Ende der Liste in der View angekommen ist und noch nicht alle Items insgesamt geladen wurden
|
||||
if (listLength === entityItemsLength && hits !== entityItemsLength) {
|
||||
await this._updateQueryParams();
|
||||
this._returnSearchStore.reload({
|
||||
processId,
|
||||
params: this._filterService.toParams(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _initSearch() {
|
||||
const entities = this._returnSearchStore.entities();
|
||||
// For routing away from the list this entities?.length === 0 check is necessary, otherwise the init search would trigger again
|
||||
if (entities?.length === 0) {
|
||||
await this.onSearch();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
:host {
|
||||
@apply flex flex-col gap-5 isa-desktop:gap-6 items-center overflow-x-hidden;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<router-outlet></router-outlet>
|
||||
@@ -1,80 +0,0 @@
|
||||
import { Component, effect, inject, untracked } from '@angular/core';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
|
||||
import { injectActivatedProcessId } from '@isa/core/process';
|
||||
import { ReturnSearchStore } from '@isa/oms/data-access';
|
||||
import { FilterService, provideQuerySettings } from '@isa/shared/filter';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-return-pages',
|
||||
imports: [RouterOutlet],
|
||||
templateUrl: './return-pages.component.html',
|
||||
styleUrl: './return-pages.component.css',
|
||||
providers: [provideQuerySettings(() => inject(ActivatedRoute).snapshot.data['querySettings'])],
|
||||
})
|
||||
export class ReturnPagesComponent {
|
||||
#route = inject(ActivatedRoute);
|
||||
#router = inject(Router);
|
||||
#filterService = inject(FilterService);
|
||||
#returnSearchStore = inject(ReturnSearchStore);
|
||||
#processId = injectActivatedProcessId();
|
||||
|
||||
#queryParams = toSignal(inject(ActivatedRoute).queryParams);
|
||||
|
||||
parseFilterParamsEffectFn = () =>
|
||||
effect(() => {
|
||||
const processId = this.#processId();
|
||||
untracked(() => {
|
||||
if (processId) {
|
||||
const params = this.#queryParams();
|
||||
const entity = this.#returnSearchStore.getEntity(processId);
|
||||
|
||||
// TODO: Caching der Entities einbauen - Aktuell kommt er sonst in den Filter Reset bei F5/Refresh Page
|
||||
if (!entity || !params) {
|
||||
this.#filterService.reset({ commit: true });
|
||||
return;
|
||||
}
|
||||
|
||||
this.#filterService.parseParams(params, { commit: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
searchResultsNavigationEffectFn = () =>
|
||||
effect(() => {
|
||||
// TODO: Wenn er hier beim Prozesswechsel nicht sofort navigieren soll, dann muss auf ReturnSearchStatus.Success geprüft werden und nach der Navigation auf ReturnSearchStatus.Idle gesetzt werden
|
||||
const processId = this.#processId();
|
||||
if (processId) {
|
||||
const entity = this.#returnSearchStore.getEntity(processId);
|
||||
if (entity && entity.status !== 'error') {
|
||||
untracked(async () => {
|
||||
const items = entity?.items;
|
||||
|
||||
if (items) {
|
||||
if (items?.length === 1) {
|
||||
return await this._navigateTo(['receipt', items[0].id.toString()]);
|
||||
}
|
||||
|
||||
if (items?.length >= 0) {
|
||||
return await this._navigateTo(['results']);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
constructor() {
|
||||
this.parseFilterParamsEffectFn();
|
||||
this.searchResultsNavigationEffectFn();
|
||||
}
|
||||
|
||||
private async _navigateTo(url: string[]) {
|
||||
return await this.#router.navigate(url, {
|
||||
queryParams: this.#filterService.toParams(),
|
||||
relativeTo: this.#route,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { ReturnPagesComponent } from './return-pages.component';
|
||||
import { querySettingsResolverFn } from './resolvers/query-settings.resolver-fn';
|
||||
import { MainPageComponent } from './main/main-page.component';
|
||||
import { ResultsPageComponent } from './results/results-page.component';
|
||||
import { DetailsPageComponent } from './details/details-page.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: ReturnPagesComponent,
|
||||
resolve: { querySettings: querySettingsResolverFn },
|
||||
children: [
|
||||
{ path: '', component: MainPageComponent },
|
||||
{
|
||||
path: 'results',
|
||||
component: ResultsPageComponent,
|
||||
data: { scrollPositionRestoration: true },
|
||||
},
|
||||
{
|
||||
path: 'receipt/:receiptId',
|
||||
component: DetailsPageComponent,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'process',
|
||||
loadChildren: () => import('@isa/oms/feature/return-process').then((feat) => feat.routes),
|
||||
},
|
||||
];
|
||||
@@ -1,6 +0,0 @@
|
||||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
|
||||
|
||||
setupZoneTestEnv({
|
||||
errorOnUnknownElements: true,
|
||||
errorOnUnknownProperties: true,
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
7
libs/oms/feature/return-details/README.md
Normal file
7
libs/oms/feature/return-details/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# oms-feature-return-details
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test oms-feature-return-details` to execute the unit tests.
|
||||
@@ -12,7 +12,7 @@ export default [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
prefix: 'omsFeature',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
@@ -20,7 +20,7 @@ export default [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
prefix: 'oms-feature',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
@@ -1,8 +1,8 @@
|
||||
export default {
|
||||
displayName: 'feature-return-containers',
|
||||
displayName: 'oms-feature-return-details',
|
||||
preset: '../../../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
coverageDirectory: '../../../../coverage/libs/feature/return/containers',
|
||||
coverageDirectory: '../../../../coverage/libs/oms/feature/return-details',
|
||||
transform: {
|
||||
'^.+\\.(ts|mjs|js|html)$': [
|
||||
'jest-preset-angular',
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "feature-return-pages",
|
||||
"name": "oms-feature-return-details",
|
||||
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/feature/return/pages/src",
|
||||
"prefix": "lib",
|
||||
"sourceRoot": "libs/oms/feature/return-details/src",
|
||||
"prefix": "oms-feature",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
@@ -10,7 +10,7 @@
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "libs/feature/return/pages/jest.config.ts"
|
||||
"jestConfig": "libs/oms/feature/return-details/jest.config.ts"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
@@ -12,7 +12,3 @@
|
||||
</ui-item-row-data-value>
|
||||
</ui-item-row-data-row>
|
||||
</ui-item-row-data>
|
||||
<!-- <button class="mt-3" uiTextButton type="button" color="strong" size="small">
|
||||
<ng-icon name="isaActionPlus" size="1rem"></ng-icon>
|
||||
<span>Bestelldetails anzeigen</span>
|
||||
</button> -->
|
||||
@@ -2,13 +2,11 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
|
||||
import { Buyer, Receipt } from '@isa/oms/data-access';
|
||||
import { ReceiptTypeTranslationPipe } from '@isa/oms/utils/translation';
|
||||
import { ItemRowDataImports } from '@isa/ui/item-rows';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-return-details-data',
|
||||
selector: 'oms-feature-return-details-data',
|
||||
templateUrl: './return-details-data.component.html',
|
||||
styleUrls: ['./return-details-data.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [ItemRowDataImports, ReceiptTypeTranslationPipe],
|
||||
})
|
||||
export class ReturnDetailsDataComponent {
|
||||
@@ -5,7 +5,7 @@ import { InfoButtonComponent } from '@isa/ui/buttons';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-return-details-header',
|
||||
selector: 'oms-feature-return-details-header',
|
||||
templateUrl: './return-details-header.component.html',
|
||||
styleUrls: ['./return-details-header.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
@@ -5,7 +5,7 @@ import { ReceiptTypeTranslationPipe } from '@isa/oms/utils/translation';
|
||||
import { ItemRowDataImports } from '@isa/ui/item-rows';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-return-details-order-group-data',
|
||||
selector: 'oms-feature-return-details-order-group-data',
|
||||
templateUrl: './return-details-order-group-data.component.html',
|
||||
styleUrls: ['./return-details-order-group-data.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
@@ -9,7 +9,7 @@ import { ItemRowComponent } from '@isa/ui/item-rows';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-return-details-order-group-item',
|
||||
selector: 'oms-feature-return-details-order-group-item',
|
||||
templateUrl: './return-details-order-group-item.component.html',
|
||||
styleUrls: ['./return-details-order-group-item.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ChangeDetectionStrategy, Component, computed, input, model, output } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, computed, input, model } from '@angular/core';
|
||||
import { ReceiptItem } from '@isa/oms/data-access';
|
||||
import { TextButtonComponent } from '@isa/ui/buttons';
|
||||
import { ToolbarComponent } from '@isa/ui/toolbar';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-return-details-order-group',
|
||||
selector: 'oms-feature-return-details-order-group',
|
||||
templateUrl: './return-details-order-group.component.html',
|
||||
styleUrls: ['./return-details-order-group.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
@@ -12,10 +12,12 @@
|
||||
</button>
|
||||
|
||||
@if (receipt) {
|
||||
<lib-return-details-header [buyer]="receipt.buyer"></lib-return-details-header>
|
||||
<oms-feature-return-details-header [buyer]="receipt.buyer"></oms-feature-return-details-header>
|
||||
|
||||
@if (showMore()) {
|
||||
<lib-return-details-order-group-data [receipt]="receipt"></lib-return-details-order-group-data>
|
||||
<oms-feature-return-details-order-group-data
|
||||
[receipt]="receipt"
|
||||
></oms-feature-return-details-order-group-data>
|
||||
<button
|
||||
class="-ml-3"
|
||||
uiTextButton
|
||||
@@ -28,7 +30,7 @@
|
||||
Weniger anzeigen
|
||||
</button>
|
||||
} @else {
|
||||
<lib-return-details-data [receipt]="receipt"></lib-return-details-data>
|
||||
<oms-feature-return-details-data [receipt]="receipt"></oms-feature-return-details-data>
|
||||
<button
|
||||
class="-ml-3"
|
||||
uiTextButton
|
||||
@@ -42,16 +44,16 @@
|
||||
</button>
|
||||
}
|
||||
<div></div>
|
||||
<lib-return-details-order-group
|
||||
<oms-feature-return-details-order-group
|
||||
[items]="receiptItems()"
|
||||
[(selectedItems)]="selectedItems"
|
||||
></lib-return-details-order-group>
|
||||
></oms-feature-return-details-order-group>
|
||||
@for (item of receipt.items; track item.id; let last = $last) {
|
||||
<lib-return-details-order-group-item
|
||||
<oms-feature-return-details-order-group-item
|
||||
class="border-b border-solid border-isa-neutral-300 last:border-none"
|
||||
[item]="item.data"
|
||||
(selectedChange)="selectItemById(item.id, $event)"
|
||||
[selected]="selectedItemIds().includes(item.id)"
|
||||
></lib-return-details-order-group-item>
|
||||
></oms-feature-return-details-order-group-item>
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,6 @@ import {
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import {
|
||||
ReturnDetailsDataComponent,
|
||||
ReturnDetailsHeaderComponent,
|
||||
ReturnDetailsOrderGroupComponent,
|
||||
ReturnDetailsOrderGroupDataComponent,
|
||||
ReturnDetailsOrderGroupItemComponent,
|
||||
} from '@feature/return/containers';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
@@ -23,13 +16,17 @@ import { isaActionPlus, isaActionMinus } from '@isa/icons';
|
||||
import { ButtonComponent, TextButtonComponent } from '@isa/ui/buttons';
|
||||
import { ReceiptItem, ReturnDetailsStore, ReturnProcessStore } from '@isa/oms/data-access';
|
||||
import { injectActivatedProcessId } from '@isa/core/process';
|
||||
import { ReturnDetailsDataComponent } from './return-details-data/return-details-data.component';
|
||||
import { ReturnDetailsHeaderComponent } from './return-details-header/return-details-header.component';
|
||||
import { ReturnDetailsOrderGroupComponent } from './return-details-order-group/return-details-order-group.component';
|
||||
import { ReturnDetailsOrderGroupItemComponent } from './return-details-order-group-item/return-details-order-group-item.component';
|
||||
import { ReturnDetailsOrderGroupDataComponent } from './return-details-order-group-data/return-details-order-group-data.component';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-return-details-page',
|
||||
templateUrl: './details-page.component.html',
|
||||
styleUrls: ['./details-page.component.scss'],
|
||||
selector: 'oms-feature-return-details',
|
||||
templateUrl: './return-details.component.html',
|
||||
styleUrls: ['./return-details.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgIconComponent,
|
||||
TextButtonComponent,
|
||||
@@ -42,7 +39,7 @@ import { injectActivatedProcessId } from '@isa/core/process';
|
||||
],
|
||||
providers: [provideIcons({ isaActionPlus, isaActionMinus })],
|
||||
})
|
||||
export class DetailsPageComponent {
|
||||
export class ReturnDetailsComponent {
|
||||
private processId = injectActivatedProcessId();
|
||||
|
||||
private _router = inject(Router);
|
||||
4
libs/oms/feature/return-details/src/lib/routes.ts
Normal file
4
libs/oms/feature/return-details/src/lib/routes.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { Routes } from '@angular/router';
|
||||
import { ReturnDetailsComponent } from './return-details.component';
|
||||
|
||||
export const routes: Routes = [{ path: ':receiptId', component: ReturnDetailsComponent }];
|
||||
@@ -7,6 +7,11 @@
|
||||
"inlineSources": true,
|
||||
"types": []
|
||||
},
|
||||
"exclude": ["src/**/*.spec.ts", "src/test-setup.ts", "jest.config.ts", "src/**/*.test.ts"],
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/test-setup.ts",
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts"
|
||||
],
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -7,5 +7,10 @@
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"files": ["src/test-setup.ts"],
|
||||
"include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -17,13 +17,13 @@
|
||||
<ng-icon name="isaActionSort"></ng-icon>
|
||||
</ui-icon-button>
|
||||
} @else {
|
||||
<lib-return-order-by-list></lib-return-order-by-list>
|
||||
<filter-order-by-toolbar></filter-order-by-toolbar>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (isMobileDevice() && toggleOrderByToolbar()) {
|
||||
<lib-return-order-by-list class="w-full"></lib-return-order-by-list>
|
||||
<filter-order-by-toolbar class="w-full"></filter-order-by-toolbar>
|
||||
}
|
||||
|
||||
<span class="text-isa-neutral-900 isa-text-body-2-regular self-start">
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
untracked,
|
||||
viewChildren,
|
||||
} from '@angular/core';
|
||||
import { ReturnOrderByListComponent } from '@feature/return/containers';
|
||||
import { injectActivatedProcessId } from '@isa/core/process';
|
||||
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
@@ -17,7 +16,9 @@ import {
|
||||
FilterMenuButtonComponent,
|
||||
FilterService,
|
||||
SearchBarInputComponent,
|
||||
OrderByToolbarComponent,
|
||||
} from '@isa/shared/filter';
|
||||
|
||||
import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
import { EmptyStateComponent } from '@isa/ui/empty-state';
|
||||
import { restoreScrollPosition } from '@isa/core/scroll-position';
|
||||
@@ -40,7 +41,7 @@ type EmptyState = {
|
||||
imports: [
|
||||
RouterLink,
|
||||
ReturnSearchResultItemComponent,
|
||||
ReturnOrderByListComponent,
|
||||
OrderByToolbarComponent,
|
||||
IconButtonComponent,
|
||||
SearchBarInputComponent,
|
||||
EmptyStateComponent,
|
||||
|
||||
@@ -14,6 +14,10 @@ export const routes: Routes = [
|
||||
{ path: 'receipts', component: ReturnSearchResultComponent },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'receipt',
|
||||
loadChildren: () => import('@isa/oms/feature/return-details').then((feat) => feat.routes),
|
||||
},
|
||||
{
|
||||
path: 'process',
|
||||
loadChildren: () => import('@isa/oms/feature/return-process').then((feat) => feat.routes),
|
||||
|
||||
@@ -4,3 +4,4 @@ export * from './lib/types';
|
||||
export * from './lib/actions';
|
||||
export * from './lib/menus/filter-menu';
|
||||
export * from './lib/menus/input-menu';
|
||||
export * from './lib/order-by';
|
||||
|
||||
@@ -25,6 +25,8 @@ export class FilterService {
|
||||
|
||||
inputs = this.#state.inputs;
|
||||
|
||||
orderBy = this.#state.orderBy;
|
||||
|
||||
setInputTextValue(key: string, value: string | undefined, options?: { commit: boolean }): void {
|
||||
const inputs = this.#state.inputs().map((input) => {
|
||||
if (input.key !== key) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
FilterGroup,
|
||||
FilterGroupSchema,
|
||||
FilterInput,
|
||||
OrderBySchema,
|
||||
TextFilterInput,
|
||||
TextFilterInputSchema,
|
||||
} from './schemas';
|
||||
@@ -18,6 +19,7 @@ export function mapToFilter(settings: QuerySettingsDTO): Filter {
|
||||
const filter: Filter = {
|
||||
groups: [],
|
||||
inputs: [],
|
||||
orderBy: [],
|
||||
};
|
||||
|
||||
const groups = [...settings.filter, ...settings.input];
|
||||
@@ -30,6 +32,20 @@ export function mapToFilter(settings: QuerySettingsDTO): Filter {
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.orderBy) {
|
||||
const bys = new Set<string>();
|
||||
for (const orderBy of settings.orderBy) {
|
||||
if (orderBy.by && bys.has(orderBy.by)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (orderBy.by) {
|
||||
filter.orderBy.push(OrderBySchema.parse(orderBy));
|
||||
bys.add(orderBy.by);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,10 +52,21 @@ export const FilterInputSchema = z.union([
|
||||
DateRangeFilterInputSchema,
|
||||
]);
|
||||
|
||||
export const OrderByDirectionSchema = z.enum(['asc', 'desc']);
|
||||
|
||||
export const OrderBySchema = z
|
||||
.object({
|
||||
by: z.string(),
|
||||
label: z.string(),
|
||||
dir: OrderByDirectionSchema.optional(),
|
||||
})
|
||||
.describe('OrderBy');
|
||||
|
||||
export const FilterSchema = z
|
||||
.object({
|
||||
groups: z.array(FilterGroupSchema),
|
||||
inputs: z.array(FilterInputSchema),
|
||||
orderBy: z.array(OrderBySchema),
|
||||
})
|
||||
.describe('Filter');
|
||||
|
||||
@@ -72,3 +83,7 @@ export type FilterInput = z.infer<typeof FilterInputSchema>;
|
||||
export type CheckboxFilterInputOption = z.infer<typeof CheckboxFilterInputOptionSchema>;
|
||||
|
||||
export type DateRangeFilterInput = z.infer<typeof DateRangeFilterInputSchema>;
|
||||
|
||||
export type OrderByDirection = z.infer<typeof OrderByDirectionSchema>;
|
||||
|
||||
export type OrderBy = z.infer<typeof OrderBySchema>;
|
||||
|
||||
1
libs/shared/filter/src/lib/order-by/index.ts
Normal file
1
libs/shared/filter/src/lib/order-by/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './order-by-toolbar.component';
|
||||
@@ -0,0 +1,7 @@
|
||||
<ui-toolbar>
|
||||
<span class="text-isa-neutral-600 isa-text-body-2-regular">Sortieren</span>
|
||||
<div class="flex-grow"></div>
|
||||
@for (orderBy of orderByOptions(); track orderBy.by) {
|
||||
<button uiTextButton>{{ orderBy.label }}</button>
|
||||
}
|
||||
</ui-toolbar>
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { TextButtonComponent } from '@isa/ui/buttons';
|
||||
import { ToolbarComponent } from '@isa/ui/toolbar';
|
||||
import { FilterService } from '../core';
|
||||
|
||||
@Component({
|
||||
selector: 'filter-order-by-toolbar',
|
||||
templateUrl: './order-by-toolbar.component.html',
|
||||
styleUrls: ['./order-by-toolbar.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [ToolbarComponent, TextButtonComponent],
|
||||
})
|
||||
export class OrderByToolbarComponent {
|
||||
#filter = inject(FilterService);
|
||||
|
||||
orderByOptions = this.#filter.orderBy;
|
||||
}
|
||||
@@ -18,10 +18,6 @@
|
||||
"@core/config": ["libs/core/config/src/index.ts"],
|
||||
"@domain/*": ["apps/isa-app/src/domain/*/index.ts"],
|
||||
"@external/*": ["apps/isa-app/src/external/*/index.ts"],
|
||||
"@feature/return/containers": [
|
||||
"libs/feature/return/containers/src/index.ts"
|
||||
],
|
||||
"@feature/return/pages": ["libs/feature/return/pages/src/index.ts"],
|
||||
"@generated/swagger/availability-api": [
|
||||
"generated/swagger/availability-api/src/index.ts"
|
||||
],
|
||||
@@ -53,6 +49,9 @@
|
||||
"@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"],
|
||||
"@isa/oms/feature/return-details": [
|
||||
"libs/oms/feature/return-details/src/index.ts"
|
||||
],
|
||||
"@isa/oms/feature/return-process": [
|
||||
"libs/oms/feature/return-process/src/index.ts"
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user