Merged PR 1913: feat(remission): refactor return receipt details and extract shared actions

feat(remission): refactor return receipt details and extract shared actions

Refactor remission return receipt details to use return-based data flow
instead of individual receipt fetching. Extract reusable action components
for better code organization and consistency.

- Remove deprecated fetchRemissionReturnReceipt method and schema
- Add helper functions for extracting data from return objects
- Replace receipt-specific components with return-based equivalents
- Create shared return-receipt-actions library with reusable components
- Update components to use modern Angular patterns (signals, computed)
- Improve data flow consistency across remission features
- Add comprehensive test coverage for new components
- Update eager loading support in fetch return functionality

The new architecture provides better data consistency and reduces
code duplication by centralizing receipt actions and data extraction
logic into reusable components.

Refs: #5242, #5138, #5232, #5241
This commit is contained in:
Nino Righi
2025-08-12 13:32:57 +00:00
committed by Andreas Schickinger
parent 2e012a124a
commit 99e8e7cfe0
77 changed files with 3294 additions and 3039 deletions

View File

@@ -1,17 +1,24 @@
<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)="triggerSearch.emit($event)"
data-what="search-input"
></filter-search-bar-input>
<div
class="w-full flex flex-row justify-between items-start"
[class.empty-filter-input]="!hasFilter() && !hasInput()"
>
@if (hasInput()) {
<filter-search-bar-input
class="flex flex-row gap-4 h-12"
[appearance]="'results'"
[inputKey]="inputKey()"
(triggerSearch)="triggerSearch.emit($event)"
data-what="search-input"
></filter-search-bar-input>
}
<div class="flex flex-row gap-4 items-center">
<filter-filter-menu-button
(applied)="triggerSearch.emit('filter')"
[rollbackOnClose]="true"
></filter-filter-menu-button>
@if (hasFilter()) {
<filter-filter-menu-button
(applied)="triggerSearch.emit('filter')"
[rollbackOnClose]="true"
></filter-filter-menu-button>
}
@if (mobileBreakpoint()) {
<ui-icon-button
@@ -23,7 +30,9 @@
></ui-icon-button>
} @else {
<filter-order-by-toolbar
[class.empty-filter-input-width]="!hasFilter() && !hasInput()"
(toggled)="triggerSearch.emit('order-by')"
data-what="sort-toolbar"
></filter-order-by-toolbar>
}
</div>

View File

@@ -1,3 +1,11 @@
.filter-controls-panel {
@apply flex flex-col gap-4;
@apply w-full flex flex-col gap-4;
}
.empty-filter-input {
@apply justify-end;
}
.empty-filter-input-width {
@apply w-[44.375rem];
}

View File

@@ -1,32 +1,36 @@
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
linkedSignal,
output,
signal,
ViewEncapsulation,
} from '@angular/core';
import { SearchBarInputComponent } from '../inputs';
import { FilterMenuButtonComponent } from '../menus/filter-menu';
import { provideIcons } from '@ng-icons/core';
import { isaActionFilter, isaActionSort } from '@isa/icons';
import { IconButtonComponent } from '@isa/ui/buttons';
import { OrderByToolbarComponent } from '../order-by';
import { Breakpoint, breakpoint } from '@isa/ui/layout';
import { SearchTrigger } from '../types';
import { InputType, SearchTrigger } from '../types';
import { FilterService, TextFilterInput } from '../core';
import { SearchBarInputComponent } from '../inputs';
/**
* Filter controls panel component that provides a unified interface for search and filtering operations.
*
*
* This component combines search input, filter menu, and sorting controls into a responsive panel.
* It adapts its layout based on screen size, showing/hiding controls appropriately for mobile and desktop views.
*
*
* @example
* ```html
* <filter-controls-panel
* <filter-controls-panel
* (triggerSearch)="handleSearch($event)">
* </filter-controls-panel>
* ```
*
*
* Features:
* - Responsive design that adapts to mobile/desktop layouts
* - Integrated search bar with scanner support
@@ -53,11 +57,22 @@ import { SearchTrigger } from '../types';
providers: [provideIcons({ isaActionSort, isaActionFilter })],
})
export class FilterControlsPanelComponent {
/**
* Service for managing filter state and operations.
*/
#filterService = inject(FilterService);
/**
* The unique key identifier for this input in the filter system.
* @default 'qs'
*/
inputKey = signal('qs');
/**
* Output event that emits when any search action is triggered.
* Provides the specific SearchTrigger type to indicate how the search was initiated:
* - 'input': Text input or search button
* - 'filter': Filter menu changes
* - 'filter': Filter menu changes
* - 'order-by': Sort order changes
* - 'scan': Barcode scan
*/
@@ -75,4 +90,26 @@ export class FilterControlsPanelComponent {
* Linked to mobileBreakpoint to automatically adjust when screen size changes.
*/
showOrderByToolbarMobile = linkedSignal(() => !this.mobileBreakpoint());
/**
* Computed signal that determines if the search input is present in the filter inputs.
* This checks if there is a TextFilterInput with the specified inputKey.
* Used to conditionally render the search input in the template.
*/
hasInput = computed(() => {
const inputs = this.#filterService.inputs();
const input = inputs.find(
(input) => input.key === this.inputKey() && input.type === InputType.Text,
) as TextFilterInput;
return !!input;
});
/**
* Computed signal that checks if there are any active filters applied.
* This is determined by checking if there are any inputs of types other than Text.
*/
hasFilter = computed(() => {
const inputs = this.#filterService.inputs();
return inputs.some((input) => input.type !== InputType.Text);
});
}