mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merge branch 'develop' of ssh.dev.azure.com:v3/hugendubel/ISA/ISA-Frontend into develop
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
import type { Preview } from '@storybook/angular';
|
||||
|
||||
const preview: Preview = {
|
||||
tags: ['autodocs'],
|
||||
};
|
||||
|
||||
export default preview;
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
export * from "./lib/return-results-list/";
|
||||
export * from './lib/return-results-list/';
|
||||
export * from './lib/return-results-item-list/';
|
||||
export * from './lib/return-order-by-list/';
|
||||
export * from './lib/return-results-filter-list/';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './return-order-by-list.component';
|
||||
@@ -0,0 +1,8 @@
|
||||
<ui-toolbar>
|
||||
<span class="text-isa-neutral-600 isa-text-body-1-regular">Sortieren</span>
|
||||
<div class="flex-grow"></div>
|
||||
<button uiTextButton>Belegdatum</button>
|
||||
<button uiTextButton>Email</button>
|
||||
<button uiTextButton>Name</button>
|
||||
<button uiTextButton>PLZ</button>
|
||||
</ui-toolbar>
|
||||
@@ -0,0 +1,12 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
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],
|
||||
})
|
||||
export class ReturnOrderByListComponent {}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './return-results-filter-list.component';
|
||||
@@ -0,0 +1,3 @@
|
||||
<ui-icon-button (click)="onFilter()">
|
||||
<ng-icon name="isaActionFilter"></ng-icon>
|
||||
</ui-icon-button>
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { isaActionFilter } from '@isa/icons';
|
||||
import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-return-results-filter-list',
|
||||
templateUrl: './return-results-filter-list.component.html',
|
||||
styleUrls: ['./return-results-filter-list.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [NgIconComponent, IconButtonComponent],
|
||||
providers: [provideIcons({ isaActionFilter })],
|
||||
})
|
||||
export class ReturnResultsFilterListComponent {
|
||||
onFilter() {
|
||||
console.log('Filter click - open overlay');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './return-results-item-list.component';
|
||||
@@ -4,4 +4,7 @@
|
||||
via Rechnungsnummer, E-Mail-Adresse oder Kundennamen
|
||||
</p>
|
||||
|
||||
<filter-search-bar-input inputKey="qs" (search)="onSearch()"></filter-search-bar-input>
|
||||
<filter-search-bar-input
|
||||
inputKey="qs"
|
||||
(search)="onSearch()"
|
||||
></filter-search-bar-input>
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
<div>Header Area</div>
|
||||
<div class="w-full flex flex-row justify-between items-start">
|
||||
<div class="flex flex-col gap-5 justify-start">
|
||||
<filter-search-bar-input
|
||||
class="flex flex-row gap-4 h-12"
|
||||
[scanButton]="true"
|
||||
inputKey="qs"
|
||||
(search)="onSearch()"
|
||||
></filter-search-bar-input>
|
||||
<span class="isa-text-body-2-regular"> {{ hits() }} Einträge </span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-4 items-center">
|
||||
<lib-return-results-filter-list></lib-return-results-filter-list>
|
||||
<lib-return-order-by-list></lib-return-order-by-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<lib-return-results-list>
|
||||
@for (item of items(); track item.id) {
|
||||
@defer (on viewport) {
|
||||
<a [routerLink]="['../', 'details', item.id]" [routerLink] class="w-full">
|
||||
<a [routerLink]="['../', 'details', item.id]" class="w-full">
|
||||
<lib-return-results-item-list
|
||||
[item]="item"
|
||||
></lib-return-results-item-list>
|
||||
</a>
|
||||
} @placeholder {
|
||||
<p>loading...</p>
|
||||
<ui-icon-button [pending]="true" [color]="'tertiary'"></ui-icon-button>
|
||||
}
|
||||
}
|
||||
</lib-return-results-list>
|
||||
|
||||
@@ -2,35 +2,109 @@ import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
effect,
|
||||
inject,
|
||||
} from "@angular/core";
|
||||
import { ReturnResultsListComponent } from "@feature/return/containers";
|
||||
import { injectActivatedProcessId } from "@isa/core/process";
|
||||
import { ReturnResultsItemListComponent } from "libs/feature/return/containers/src/lib/return-results-item-list/return-results-item-list.component";
|
||||
import { ReturnSearchStore } from "@feature/return/services";
|
||||
import { RouterLink } from "@angular/router";
|
||||
untracked,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
ReturnResultsListComponent,
|
||||
ReturnResultsItemListComponent,
|
||||
ReturnOrderByListComponent,
|
||||
ReturnResultsFilterListComponent,
|
||||
} from '@feature/return/containers';
|
||||
import { injectActivatedProcessId } from '@isa/core/process';
|
||||
import { ReturnSearchStore } from '@feature/return/services';
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
import { FilterService, SearchBarInputComponent } from '@isa/shared/filter';
|
||||
import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
@Component({
|
||||
selector: "lib-return-results-page",
|
||||
templateUrl: "./results-page.component.html",
|
||||
styleUrls: ["./results-page.component.css"],
|
||||
selector: 'lib-return-results-page',
|
||||
templateUrl: './results-page.component.html',
|
||||
styleUrls: ['./results-page.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [
|
||||
RouterLink,
|
||||
ReturnResultsListComponent,
|
||||
ReturnResultsItemListComponent,
|
||||
ReturnOrderByListComponent,
|
||||
ReturnResultsFilterListComponent,
|
||||
IconButtonComponent,
|
||||
SearchBarInputComponent,
|
||||
],
|
||||
})
|
||||
export class ResultsPageComponent {
|
||||
#route = inject(ActivatedRoute);
|
||||
#router = inject(Router);
|
||||
|
||||
private _processId = injectActivatedProcessId();
|
||||
private _returnSearchStore = inject(ReturnSearchStore);
|
||||
private _filterService = inject(FilterService);
|
||||
|
||||
items = computed(() => {
|
||||
private _resultEntity = computed(() => {
|
||||
const processId = this._processId();
|
||||
if (processId) {
|
||||
return this._returnSearchStore.getEntity(processId)?.items;
|
||||
return this._returnSearchStore.getEntity(processId);
|
||||
}
|
||||
return [];
|
||||
return undefined;
|
||||
});
|
||||
|
||||
items = computed(() => {
|
||||
return this._resultEntity()?.items ?? [];
|
||||
});
|
||||
|
||||
hits = computed(() => {
|
||||
return this._resultEntity()?.hits ?? 0;
|
||||
});
|
||||
|
||||
constructor() {
|
||||
this._initListSearchEffect();
|
||||
}
|
||||
|
||||
// Triggers search after page reload (via F5 f.e.)
|
||||
// TODO: Fix reload bug if click on navbar "Retoure"
|
||||
private _initListSearchEffect() {
|
||||
effect(() => {
|
||||
const processId = this._processId();
|
||||
|
||||
if (processId) {
|
||||
const entity = this._returnSearchStore.getEntity(processId);
|
||||
const queryParams = this._filterService.toParams();
|
||||
|
||||
if (entity || isEmpty(queryParams)) {
|
||||
return;
|
||||
}
|
||||
|
||||
untracked(async () => {
|
||||
// TODO: Navigieren und Params in der URL setzen auslagern
|
||||
await this.#router.navigate([], {
|
||||
queryParams,
|
||||
queryParamsHandling: 'merge',
|
||||
relativeTo: this.#route,
|
||||
});
|
||||
|
||||
this._returnSearchStore.search({ processId, params: queryParams });
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async onSearch() {
|
||||
const processId = this._processId();
|
||||
if (processId) {
|
||||
// TODO: Logik für Suche&Navigation in Main Page + Results Page auslagern da selbe Logik
|
||||
await this.#router.navigate([], {
|
||||
queryParams: this._filterService.toParams(),
|
||||
queryParamsHandling: 'merge',
|
||||
relativeTo: this.#route,
|
||||
});
|
||||
|
||||
this._returnSearchStore.search({
|
||||
processId,
|
||||
params: this._filterService.toParams(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import { patchState, signalStore, type, withMethods } from "@ngrx/signals";
|
||||
import { rxMethod } from "@ngrx/signals/rxjs-interop";
|
||||
import { patchState, signalStore, type, withMethods } from '@ngrx/signals';
|
||||
import { rxMethod } from '@ngrx/signals/rxjs-interop';
|
||||
import {
|
||||
addEntity,
|
||||
entityConfig,
|
||||
updateEntity,
|
||||
withEntities,
|
||||
} from "@ngrx/signals/entities";
|
||||
import { debounceTime, distinctUntilChanged, pipe, switchMap, tap } from "rxjs";
|
||||
import { ReturnSearchService } from "./return-search.service";
|
||||
import { tapResponse } from "@ngrx/operators";
|
||||
import { inject } from "@angular/core";
|
||||
import { ReceiptListItem, QueryTokenSchema, ListResponseArgs } from "./schemas";
|
||||
} from '@ngrx/signals/entities';
|
||||
import { debounceTime, distinctUntilChanged, pipe, switchMap, tap } from 'rxjs';
|
||||
import { ReturnSearchService } from './return-search.service';
|
||||
import { tapResponse } from '@ngrx/operators';
|
||||
import { inject } from '@angular/core';
|
||||
import { ReceiptListItem, QueryTokenSchema, ListResponseArgs } from './schemas';
|
||||
|
||||
enum ReturnSearchStatus {
|
||||
Idle = "idle",
|
||||
Pending = "pending",
|
||||
Success = "success",
|
||||
Error = "error",
|
||||
Idle = 'idle',
|
||||
Pending = 'pending',
|
||||
Success = 'success',
|
||||
Error = 'error',
|
||||
}
|
||||
|
||||
type ReturnSearchEntity = {
|
||||
@@ -34,7 +34,7 @@ const config = entityConfig({
|
||||
});
|
||||
|
||||
export const ReturnSearchStore = signalStore(
|
||||
{ providedIn: "root" },
|
||||
{ providedIn: 'root' },
|
||||
withEntities<ReturnSearchEntity>(config),
|
||||
withMethods((store) => ({
|
||||
getEntity(processId: number): ReturnSearchEntity | undefined {
|
||||
@@ -48,7 +48,14 @@ export const ReturnSearchStore = signalStore(
|
||||
patchState(
|
||||
store,
|
||||
updateEntity(
|
||||
{ id: processId, changes: { status: ReturnSearchStatus.Pending } },
|
||||
{
|
||||
id: processId,
|
||||
changes: {
|
||||
status: ReturnSearchStatus.Pending,
|
||||
items: [],
|
||||
hits: 0,
|
||||
},
|
||||
},
|
||||
config,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -10,11 +10,23 @@
|
||||
<button
|
||||
type="submit"
|
||||
uiIconButton
|
||||
color="brand"
|
||||
[color]="buttonColor()"
|
||||
[disabled]="control.invalid"
|
||||
(click)="onSearch()"
|
||||
>
|
||||
<ng-icon name="isaActionSearch"></ng-icon>
|
||||
</button>
|
||||
</ui-search-bar>
|
||||
|
||||
@if (scanButton()) {
|
||||
<button
|
||||
type="submit"
|
||||
uiIconButton
|
||||
[color]="scanButtonColor()"
|
||||
[disabled]="control.invalid"
|
||||
(click)="onSearch()"
|
||||
>
|
||||
<ng-icon name="isaActionScanner"></ng-icon>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import { ChangeDetectionStrategy, Component, computed, inject, input, output } from '@angular/core';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
inject,
|
||||
input,
|
||||
output,
|
||||
ViewEncapsulation,
|
||||
} from '@angular/core';
|
||||
import { UiSearchBarComponent } from '@isa/ui/search-bar';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
import { IconButtonColor, IconButtonComponent } from '@isa/ui/buttons';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
import { isaActionSearch } from '@isa/icons';
|
||||
import { isaActionSearch, isaActionScanner } from '@isa/icons';
|
||||
import { FilterService, TextFilterInput } from '../../core';
|
||||
import { InputType } from '../../types';
|
||||
|
||||
@@ -12,9 +20,16 @@ import { InputType } from '../../types';
|
||||
templateUrl: './search-bar-input.component.html',
|
||||
styleUrls: ['./search-bar-input.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
standalone: true,
|
||||
imports: [UiSearchBarComponent, IconButtonComponent, NgIconComponent, ReactiveFormsModule],
|
||||
providers: [provideIcons({ isaActionSearch })],
|
||||
imports: [
|
||||
UiSearchBarComponent,
|
||||
IconButtonComponent,
|
||||
NgIconComponent,
|
||||
ReactiveFormsModule,
|
||||
IconButtonComponent,
|
||||
],
|
||||
providers: [provideIcons({ isaActionSearch, isaActionScanner })],
|
||||
})
|
||||
export class SearchBarInputComponent {
|
||||
readonly filterService = inject(FilterService);
|
||||
@@ -22,6 +37,10 @@ export class SearchBarInputComponent {
|
||||
control = new FormControl();
|
||||
|
||||
inputKey = input.required<string>();
|
||||
buttonColor = input<IconButtonColor>('brand');
|
||||
|
||||
scanButton = input<boolean>(false);
|
||||
scanButtonColor = input<IconButtonColor>('primary');
|
||||
|
||||
input = computed<TextFilterInput>(() => {
|
||||
const inputs = this.filterService.inputs();
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
"fix:files:swagger": "node ./tools/fix-files.js generated/swagger",
|
||||
"prettier": "prettier --write .",
|
||||
"pretty-quick": "pretty-quick --staged",
|
||||
"prepare": "husky"
|
||||
"prepare": "husky",
|
||||
"storybook": "npx nx run isa-app:storybook"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
|
||||
Reference in New Issue
Block a user