Merge branch 'develop' of ssh.dev.azure.com:v3/hugendubel/ISA/ISA-Frontend into develop

This commit is contained in:
Lorenz Hilpert
2025-03-13 21:24:00 +01:00
8 changed files with 89 additions and 64 deletions

View File

@@ -1,11 +1,22 @@
import { argsToTemplate, type Meta, type StoryObj, moduleMetadata } from '@storybook/angular';
import { UiSearchBarClearComponent, UiSearchBarComponent } from '@isa/ui/search-bar';
import {
argsToTemplate,
type Meta,
type StoryObj,
moduleMetadata,
} from '@storybook/angular';
import {
UiSearchBarClearComponent,
UiSearchBarComponent,
} from '@isa/ui/search-bar';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { isaActionSearch } from '@isa/icons';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { IconButtonComponent } from '@isa/ui/buttons';
type Appearance = 'main' | 'results';
export interface UiSearchBarComponentInputs {
appearance: Appearance;
placeholder: string;
value: string;
resetValue: string;
@@ -36,20 +47,37 @@ const meta: Meta<UiSearchBarComponentInputs> = {
resetValue: {
control: 'text',
},
appearance: {
control: {
type: 'select',
options: ['main', 'results'] as Appearance[],
},
},
},
args: {
placeholder: 'Rechnungsnummer, E-Mail, Kundenkarte, Name...',
appearance: 'main',
},
render: (args) => {
let button = '';
if (args.appearance === 'main') {
button =
'<button type="submit" uiIconButton color="brand"><ng-icon name="isaActionSearch"></ng-icon></button>';
} else if (args.appearance === 'results') {
button =
'<button type="submit" uiIconButton prefix color="neutral"><ng-icon name="isaActionSearch"></ng-icon></button>';
}
return {
props: { ...args, control: new FormControl(args.value) },
template: `<ui-search-bar class="flex items-center shrink-0 h-[3.75rem] pr-2 pl-6" ${argsToTemplate(args, { exclude: ['placeholder', 'value', 'resetValue', 'appearance'] })}>
<input [formControl]="control" type="text" placeholder="${args.placeholder}" />
<ui-search-bar-clear [class.pr-4]="${args.appearance === 'results'}" value="${args.resetValue}"></ui-search-bar-clear>
${button}
</ui-search-bar>`,
};
},
render: (args) => ({
props: { ...args, control: new FormControl(args.value) },
template: `<ui-search-bar ${argsToTemplate(args, { exclude: ['placeholder', 'value', 'resetValue'] })}>
<input [formControl]="control" type="text" placeholder="${args.placeholder}" />
<ui-search-bar-clear value="${args.resetValue}"></ui-search-bar-clear>
<button type="submit" uiIconButton color="brand">
<ng-icon name="isaActionSearch"></ng-icon>
</button>
</ui-search-bar>`,
}),
};
export default meta;

View File

@@ -1,14 +1,8 @@
@if (isMobileDevice) {
<ui-icon-button (click)="onOpenOrderByDialog()">
<ng-icon name="isaActionSort"></ng-icon>
</ui-icon-button>
} @else {
<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>
}
<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>

View File

@@ -1,9 +1,7 @@
import { Platform } from '@angular/cdk/platform';
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { isaActionSort } from '@isa/icons';
import { IconButtonComponent, TextButtonComponent } from '@isa/ui/buttons';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { TextButtonComponent } from '@isa/ui/buttons';
import { ToolbarComponent } from '@isa/ui/toolbar';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
@Component({
selector: 'lib-return-order-by-list',
@@ -11,21 +9,10 @@ import { NgIconComponent, provideIcons } from '@ng-icons/core';
styleUrls: ['./return-order-by-list.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [
ToolbarComponent,
TextButtonComponent,
NgIconComponent,
IconButtonComponent,
],
providers: [provideIcons({ isaActionSort })],
imports: [ToolbarComponent, TextButtonComponent],
})
export class ReturnOrderByListComponent {
#platform = inject(Platform);
isMobileDevice = this.#platform.ANDROID || this.#platform.IOS;
onClick(label: string) {
console.log('Order By click -', label);
}
onOpenOrderByDialog() {
console.log('Open Order By dialog');
}
}

View File

@@ -1,22 +1,33 @@
<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"
[appearance]="'results'"
inputKey="qs"
(search)="onSearch()"
></filter-search-bar-input>
<span class="text-isa-neutral-900 isa-text-body-2-regular">
{{ entityHits() }} Einträge
</span>
</div>
<filter-search-bar-input
class="flex flex-row gap-4 h-12"
[appearance]="'results'"
inputKey="qs"
(search)="onSearch()"
></filter-search-bar-input>
<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>
@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">

View File

@@ -5,6 +5,7 @@ import {
effect,
inject,
QueryList,
signal,
untracked,
viewChildren,
} from '@angular/core';
@@ -24,6 +25,9 @@ import { 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';
type EmptyState = {
title: string;
@@ -44,11 +48,14 @@ type EmptyState = {
IconButtonComponent,
SearchBarInputComponent,
EmptyStateComponent,
NgIconComponent,
],
providers: [provideIcons({ isaActionSort })],
})
export class ResultsPageComponent {
#route = inject(ActivatedRoute);
#router = inject(Router);
#platform = inject(Platform);
private _processId = injectActivatedProcessId();
private _returnSearchStore = inject(ReturnSearchStore);
@@ -85,6 +92,9 @@ export class ResultsPageComponent {
listElements = viewChildren<QueryList<ReturnResultsItemListComponent>>('listElement');
isMobileDevice = signal(this.#platform.ANDROID || this.#platform.IOS);
toggleOrderByToolbar = signal(false);
searchEffectFn = () =>
effect(() => {
const processId = this._processId();

View File

@@ -9,7 +9,7 @@ import {
import { debounceTime, distinctUntilChanged, pipe, switchMap, tap } from 'rxjs';
import { ReturnSearchService } from './return-search.service';
import { tapResponse } from '@ngrx/operators';
import { inject, input } from '@angular/core';
import { inject } from '@angular/core';
import { ReceiptListItem, QueryTokenSchema, ListResponseArgs } from './schemas';
export enum ReturnSearchStatus {

View File

@@ -1,11 +1,5 @@
import { z } from 'zod';
export const FilterSchema = z.record(z.any());
export const InputSchema = z.object({
qs: z.string().optional(),
});
export const OrderBySchema = z.object({
by: z.string().optional(),
label: z.string().optional(),
@@ -14,8 +8,8 @@ export const OrderBySchema = z.object({
});
export const QueryTokenSchema = z.object({
filter: FilterSchema.default({}),
input: InputSchema.default({}),
filter: z.record(z.any()).default({}),
input: z.record(z.any()).default({}),
orderBy: z.array(OrderBySchema).default([]),
skip: z.number().default(0),
take: z.number().default(25),