mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
- feat(remission-list): Added Tooltip and Static Toolbar - Merge branch 'develop' into feature/5236-Remission-Abteilungsremission-Offene-Punkte - feat(remission-list, shared-filter, ui-input-controls): enhance department filtering and UI improvements - Merge branch 'develop' into feature/5236-Remission-Abteilungsremission-Offene-Punkte - Merge branch 'develop' into feature/5236-Remission-Abteilungsremission-Offene-Punkte - feat(remission-list, remission-data-access): add department capacity display functionality #5236 #4771 Abteilungsremission
This commit is contained in:
committed by
Lorenz Hilpert
parent
baf4a0dfbc
commit
0da9800ca0
@@ -0,0 +1,207 @@
|
||||
import {
|
||||
calculateCapacity,
|
||||
calculateMaxCapacity,
|
||||
} from './calc-capacity.helper';
|
||||
|
||||
describe('calculateCapacity', () => {
|
||||
it('should return capacityValue2 when it is smaller than capacityValue3', () => {
|
||||
// Arrange
|
||||
const input = { capacityValue2: 5, capacityValue3: 10 };
|
||||
|
||||
// Act
|
||||
const result = calculateCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(5);
|
||||
});
|
||||
|
||||
it('should return capacityValue3 when it is smaller than capacityValue2', () => {
|
||||
// Arrange
|
||||
const input = { capacityValue2: 15, capacityValue3: 8 };
|
||||
|
||||
// Act
|
||||
const result = calculateCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(8);
|
||||
});
|
||||
|
||||
it('should return capacityValue3 when both values are equal', () => {
|
||||
// Arrange
|
||||
const input = { capacityValue2: 10, capacityValue3: 10 };
|
||||
|
||||
// Act
|
||||
const result = calculateCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(10);
|
||||
});
|
||||
|
||||
it('should handle zero values correctly', () => {
|
||||
// Arrange
|
||||
const input = { capacityValue2: 0, capacityValue3: 5 };
|
||||
|
||||
// Act
|
||||
const result = calculateCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle negative values correctly', () => {
|
||||
// Arrange
|
||||
const input = { capacityValue2: -3, capacityValue3: 2 };
|
||||
|
||||
// Act
|
||||
const result = calculateCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(-3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateMaxCapacity', () => {
|
||||
it('should return capacityValue2 when capacityValue4 is greater than capacityValue2', () => {
|
||||
// Arrange
|
||||
const input = {
|
||||
capacityValue2: 10,
|
||||
capacityValue3: 15,
|
||||
capacityValue4: 20,
|
||||
comparer: 5,
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = calculateMaxCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(10);
|
||||
});
|
||||
|
||||
it('should return capacityValue4 when it is positive and less than capacityValue2', () => {
|
||||
// Arrange
|
||||
const input = {
|
||||
capacityValue2: 10,
|
||||
capacityValue3: 15,
|
||||
capacityValue4: 8,
|
||||
comparer: 5,
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = calculateMaxCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(8);
|
||||
});
|
||||
|
||||
it('should return capacityValue2 when capacityValue4 is zero and capacityValue3 is greater than capacityValue2', () => {
|
||||
// Arrange
|
||||
const input = {
|
||||
capacityValue2: 10,
|
||||
capacityValue3: 15,
|
||||
capacityValue4: 0,
|
||||
comparer: 5,
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = calculateMaxCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(10);
|
||||
});
|
||||
|
||||
it('should return capacityValue3 when capacityValue4 is zero and capacityValue3 is positive and less than capacityValue2', () => {
|
||||
// Arrange
|
||||
const input = {
|
||||
capacityValue2: 10,
|
||||
capacityValue3: 8,
|
||||
capacityValue4: 0,
|
||||
comparer: 5,
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = calculateMaxCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(8);
|
||||
});
|
||||
|
||||
it('should return comparer when it is greater than calculated max capacity', () => {
|
||||
// Arrange
|
||||
const input = {
|
||||
capacityValue2: 5,
|
||||
capacityValue3: 3,
|
||||
capacityValue4: 2,
|
||||
comparer: 10,
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = calculateMaxCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(10);
|
||||
});
|
||||
|
||||
it('should handle undefined capacityValue4 with default value 0', () => {
|
||||
// Arrange
|
||||
const input = {
|
||||
capacityValue2: 10,
|
||||
capacityValue3: 15,
|
||||
capacityValue4: undefined,
|
||||
comparer: 5,
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = calculateMaxCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(10);
|
||||
});
|
||||
|
||||
it('should handle all zero capacity values', () => {
|
||||
// Arrange
|
||||
const input = {
|
||||
capacityValue2: 0,
|
||||
capacityValue3: 0,
|
||||
capacityValue4: 0,
|
||||
comparer: 3,
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = calculateMaxCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(3);
|
||||
});
|
||||
|
||||
it('should handle negative capacity values', () => {
|
||||
// Arrange
|
||||
const input = {
|
||||
capacityValue2: -5,
|
||||
capacityValue3: -3,
|
||||
capacityValue4: -2,
|
||||
comparer: 1,
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = calculateMaxCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(1);
|
||||
});
|
||||
|
||||
it('should use default values for optional parameters when not provided', () => {
|
||||
// Arrange
|
||||
const input = {
|
||||
capacityValue2: 10,
|
||||
capacityValue3: 8,
|
||||
capacityValue4: undefined,
|
||||
comparer: 5,
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = calculateMaxCapacity(input);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(8);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Calculates the capacity based on the provided capacity values.
|
||||
* It returns the minimum of the two capacity values.
|
||||
* @param {Object} params - The parameters for the calculation
|
||||
* @param {number} params.capacityValue2 - The second capacity value
|
||||
* @param {number} params.capacityValue3 - The third capacity value
|
||||
* @return {number} The calculated capacity
|
||||
*/
|
||||
export const calculateCapacity = ({
|
||||
capacityValue2,
|
||||
capacityValue3,
|
||||
}: {
|
||||
capacityValue2: number;
|
||||
capacityValue3: number;
|
||||
}): number => {
|
||||
return capacityValue3 > capacityValue2 ? capacityValue2 : capacityValue3;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates the maximum capacity based on the provided capacity values.
|
||||
* It compares the values and returns the maximum capacity that is greater than or equal to the comparer
|
||||
* or the maximum of the capacity values.
|
||||
* @param {Object} params - The parameters for the calculation
|
||||
* @param {number} params.capacityValue2 - The second capacity value
|
||||
* @param {number} params.capacityValue3 - The third capacity value
|
||||
* @param {number} params.capacityValue4 - The fourth capacity value (optional)
|
||||
* @param {number} params.comparer - The value to compare against
|
||||
* @return {number} The maximum capacity calculated
|
||||
*/
|
||||
export const calculateMaxCapacity = ({
|
||||
capacityValue2 = 0,
|
||||
capacityValue3 = 0,
|
||||
capacityValue4 = 0,
|
||||
comparer,
|
||||
}: {
|
||||
capacityValue2: number;
|
||||
capacityValue3: number;
|
||||
capacityValue4: number | undefined;
|
||||
comparer: number;
|
||||
}): number => {
|
||||
let maxCapacity = 0;
|
||||
|
||||
if (capacityValue4 < capacityValue2) {
|
||||
if (capacityValue4 > 0) {
|
||||
maxCapacity = capacityValue4;
|
||||
} else if (capacityValue3 > capacityValue2) {
|
||||
maxCapacity = capacityValue2;
|
||||
} else if (capacityValue3 > 0) {
|
||||
maxCapacity = capacityValue3;
|
||||
}
|
||||
} else {
|
||||
maxCapacity = capacityValue2;
|
||||
}
|
||||
|
||||
return Math.max(comparer, maxCapacity);
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './calc-available-stock.helper';
|
||||
export * from './calc-stock-to-remit.helper';
|
||||
export * from './calc-target-stock.helper';
|
||||
export * from './calc-capacity.helper';
|
||||
|
||||
@@ -14,3 +14,4 @@ export * from './stock';
|
||||
export * from './supplier';
|
||||
export * from './receipt-return-tuple';
|
||||
export * from './receipt-return-suggestion-tuple';
|
||||
export * from './value-tuple-sting-and-integer';
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { ValueTupleOfStringAndIntegerAndIntegerAndNullableIntegerAndString } from '@generated/swagger/inventory-api';
|
||||
|
||||
export interface ValueTupleOfStringAndInteger
|
||||
extends ValueTupleOfStringAndIntegerAndIntegerAndNullableIntegerAndString {
|
||||
item1?: string;
|
||||
item2: number;
|
||||
item3: number;
|
||||
item4?: number;
|
||||
item5?: string;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const FetchRequiredCapacitySchema = z.object({
|
||||
departments: z.array(z.string()),
|
||||
supplierId: z.number(),
|
||||
stockId: z.number(),
|
||||
});
|
||||
|
||||
export type FetchRequiredCapacity = z.infer<typeof FetchRequiredCapacitySchema>;
|
||||
@@ -8,3 +8,4 @@ export * from './fetch-remission-return-receipt.schema';
|
||||
export * from './fetch-remission-return-receipts.schema';
|
||||
export * from './fetch-stock-in-stock.schema';
|
||||
export * from './query-token.schema';
|
||||
export * from './fetch-required-capacity.schema';
|
||||
|
||||
@@ -5,11 +5,14 @@ import {
|
||||
RemissionListTypeKey,
|
||||
ReturnItem,
|
||||
ReturnSuggestion,
|
||||
ValueTupleOfStringAndInteger,
|
||||
} from '../models';
|
||||
import { RemiService } from '@generated/swagger/inventory-api';
|
||||
import {
|
||||
FetchQuerySettings,
|
||||
FetchQuerySettingsSchema,
|
||||
FetchRequiredCapacity,
|
||||
FetchRequiredCapacitySchema,
|
||||
RemissionQueryTokenInput,
|
||||
RemissionQueryTokenSchema,
|
||||
} from '../schemas';
|
||||
@@ -78,6 +81,58 @@ export class RemissionSearchService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the required capacity for remission based on departments and supplier ID.
|
||||
* Validates input parameters using FetchRequiredCapacitySchema.
|
||||
*
|
||||
* @async
|
||||
* @param {FetchRequiredCapacity} params - Parameters for fetching required capacity
|
||||
* @param {string[]} params.departments - List of department names
|
||||
* @param {number} params.supplierId - ID of the supplier
|
||||
* @param {number} params.stockId - ID of the stock
|
||||
* @returns {Promise<ValueTupleOfStringAndInteger[]>} Required capacity data as an array of key-value pairs
|
||||
* @throws {Error} When the API request fails or returns an error
|
||||
* @throws {z.ZodError} When parameter validation fails
|
||||
*
|
||||
* @example
|
||||
* const capacity = await service.fetchRequiredCapacity({
|
||||
* departments: ['Department1', 'Department2'],
|
||||
* supplierId: 123,
|
||||
* stockId: 456
|
||||
* });
|
||||
*/
|
||||
async fetchRequiredCapacity(
|
||||
params: FetchRequiredCapacity,
|
||||
): Promise<ValueTupleOfStringAndInteger[]> {
|
||||
this.#logger.debug('Fetching required capacity', () => ({ params }));
|
||||
const parsed = FetchRequiredCapacitySchema.parse(params);
|
||||
|
||||
this.#logger.info('Fetching required capacity from API', () => ({
|
||||
stockId: parsed.stockId,
|
||||
departments: parsed.departments,
|
||||
supplierId: parsed.supplierId,
|
||||
}));
|
||||
|
||||
const req$ = this.#remiService.RemiGetRequiredCapacities({
|
||||
stockId: parsed.stockId,
|
||||
payload: {
|
||||
departments: parsed.departments,
|
||||
supplierId: parsed.supplierId,
|
||||
},
|
||||
});
|
||||
|
||||
const res = await firstValueFrom(req$);
|
||||
|
||||
if (res.error) {
|
||||
const error = new ResponseArgsError(res);
|
||||
this.#logger.error('Failed to fetch required capacity', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
this.#logger.debug('Successfully fetched required capacity');
|
||||
return (res?.result ?? []) as ValueTupleOfStringAndInteger[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches query settings for mandatory remission articles.
|
||||
* Validates input parameters using FetchQuerySettingsSchema.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<filter-input-menu-button
|
||||
[filterInput]="filterDepartmentInput()"
|
||||
[label]="selectedDepartments()"
|
||||
[commitOnClose]="true"
|
||||
>
|
||||
</filter-input-menu-button>
|
||||
|
||||
@if (displayCapacityValues()) {
|
||||
<ui-toolbar class="ui-toolbar-rounded">
|
||||
<span class="isa-text-body-2-regular"
|
||||
><span class="isa-text-body-2-bold"
|
||||
>{{ leistung() }}/{{ maxLeistung() }}</span
|
||||
>
|
||||
Leistung</span
|
||||
>
|
||||
<span class="isa-text-body-2-regular"
|
||||
><span class="isa-text-body-2-bold"
|
||||
>{{ stapel() }}/{{ maxStapel() }}</span
|
||||
>
|
||||
Stapel</span
|
||||
>
|
||||
<button
|
||||
class="w-6 h-6 flex items-center justify-center text-isa-accent-blue"
|
||||
uiTooltip
|
||||
[title]="'Stapel/Leistungsplätze'"
|
||||
[content]="''"
|
||||
[triggerOn]="['click', 'hover']"
|
||||
>
|
||||
<ng-icon size="1.5rem" name="isaOtherInfo"></ng-icon>
|
||||
</button>
|
||||
</ui-toolbar>
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
:host {
|
||||
@apply flex flex-row gap-4 items-center max-h-12;
|
||||
}
|
||||
|
||||
.ui-toolbar-rounded {
|
||||
@apply rounded-2xl;
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { isaOtherInfo } from '@isa/icons';
|
||||
import {
|
||||
calculateCapacity,
|
||||
calculateMaxCapacity,
|
||||
} from '@isa/remission/data-access';
|
||||
import {
|
||||
FilterInputMenuButtonComponent,
|
||||
FilterService,
|
||||
InputType,
|
||||
} from '@isa/shared/filter';
|
||||
import { ToolbarComponent } from '@isa/ui/toolbar';
|
||||
import { TooltipDirective } from '@isa/ui/tooltip';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
import { createRemissionCapacityResource } from '../resources';
|
||||
|
||||
@Component({
|
||||
selector: 'remi-feature-remission-list-department-elements',
|
||||
templateUrl: './remission-list-department-elements.component.html',
|
||||
styleUrl: './remission-list-department-elements.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [provideIcons({ isaOtherInfo })],
|
||||
imports: [
|
||||
FilterInputMenuButtonComponent,
|
||||
ToolbarComponent,
|
||||
TooltipDirective,
|
||||
NgIconComponent,
|
||||
],
|
||||
})
|
||||
export class RemissionListDepartmentElementsComponent {
|
||||
/**
|
||||
* FilterService instance for managing filter state and queries.
|
||||
* @private
|
||||
*/
|
||||
#filterService = inject(FilterService);
|
||||
|
||||
/**
|
||||
* Filter input for departments, used to filter remission items by department.
|
||||
*/
|
||||
filterDepartmentInput = computed(() => {
|
||||
const inputs = this.#filterService
|
||||
.inputs()
|
||||
.filter((input) => input.group === 'filter');
|
||||
return inputs?.find((input) => input.key === 'abteilungen');
|
||||
});
|
||||
|
||||
/**
|
||||
* Computed signal for the selected departments from the filter input.
|
||||
* If the input type is Checkbox and has selected values, it returns a comma-separated string.
|
||||
* Otherwise, it returns undefined.
|
||||
*/
|
||||
selectedDepartments = computed(() => {
|
||||
const input = this.filterDepartmentInput();
|
||||
if (input?.type === InputType.Checkbox && input?.selected?.length > 0) {
|
||||
return input?.selected?.filter((selected) => !!selected).join(', ');
|
||||
}
|
||||
return;
|
||||
});
|
||||
|
||||
/**
|
||||
* Resource signal for fetching remission capacity based on selected departments.
|
||||
* Updates when the selected departments change.
|
||||
* @returns Remission capacity resource state.
|
||||
*/
|
||||
capacityResource = createRemissionCapacityResource(() => {
|
||||
return {
|
||||
departments: this.selectedDepartments()
|
||||
?.split(',')
|
||||
.map((d) => d.trim()),
|
||||
};
|
||||
});
|
||||
|
||||
capacityResourceValue = computed(() => this.capacityResource.value());
|
||||
|
||||
displayCapacityValues = computed(() => {
|
||||
const value = this.capacityResourceValue();
|
||||
return !!value && value?.length > 0;
|
||||
});
|
||||
|
||||
leistungValues = computed(() => {
|
||||
const value = this.capacityResourceValue();
|
||||
return value?.find((cap) => cap.item1 === 'Leistung');
|
||||
});
|
||||
|
||||
stapelValues = computed(() => {
|
||||
const value = this.capacityResourceValue();
|
||||
return value?.find((cap) => cap.item1 === 'Stapel');
|
||||
});
|
||||
|
||||
leistung = computed(() => {
|
||||
const values = this.leistungValues();
|
||||
return values
|
||||
? calculateCapacity({
|
||||
capacityValue2: values.item2,
|
||||
capacityValue3: values.item3,
|
||||
})
|
||||
: 0;
|
||||
});
|
||||
|
||||
maxLeistung = computed(() => {
|
||||
const values = this.leistungValues();
|
||||
return values
|
||||
? calculateMaxCapacity({
|
||||
capacityValue2: values.item2,
|
||||
capacityValue3: values.item3,
|
||||
capacityValue4: values.item4,
|
||||
comparer: this.leistung(),
|
||||
})
|
||||
: 0;
|
||||
});
|
||||
|
||||
stapel = computed(() => {
|
||||
const values = this.stapelValues();
|
||||
return values
|
||||
? calculateCapacity({
|
||||
capacityValue2: values.item2,
|
||||
capacityValue3: values.item3,
|
||||
})
|
||||
: 0;
|
||||
});
|
||||
|
||||
maxStapel = computed(() => {
|
||||
const values = this.stapelValues();
|
||||
return values
|
||||
? calculateMaxCapacity({
|
||||
capacityValue2: values.item2,
|
||||
capacityValue3: values.item3,
|
||||
capacityValue4: values.item4,
|
||||
comparer: this.stapel(),
|
||||
})
|
||||
: 0;
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<ui-dropdown
|
||||
class="remi-feature-remission-list-select__dropdown"
|
||||
[value]="selectedRemissionListType()"
|
||||
[label]="selectedRemissionListTypeLabel()"
|
||||
[appearance]="DropdownAppearance.Grey"
|
||||
(valueChange)="changeRemissionType($event)"
|
||||
data-which="remission-list-select-dropdown"
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import {
|
||||
RemissionListType,
|
||||
@@ -30,8 +35,6 @@ export class RemissionListSelectComponent {
|
||||
selectedRemissionListType = injectRemissionListType();
|
||||
|
||||
async changeRemissionType(remissionTypeValue: RemissionListType | undefined) {
|
||||
console.log(remissionTypeValue, remissionListTypeRouteMapping);
|
||||
|
||||
if (
|
||||
!remissionTypeValue ||
|
||||
remissionTypeValue === RemissionListType.Koerperlos
|
||||
@@ -45,4 +48,18 @@ export class RemissionListSelectComponent {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
selectedRemissionListTypeLabel = computed(() => {
|
||||
const type = this.selectedRemissionListType();
|
||||
|
||||
if (type === RemissionListType.Pflicht) {
|
||||
return 'Pflicht';
|
||||
}
|
||||
|
||||
if (type === RemissionListType.Abteilung) {
|
||||
return 'Abteilungen';
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,7 +6,12 @@
|
||||
<remi-feature-remission-return-card></remi-feature-remission-return-card>
|
||||
}
|
||||
|
||||
<remi-feature-remission-list-select></remi-feature-remission-list-select>
|
||||
<div class="flex flex-row gap-4 items-center max-h-12">
|
||||
<remi-feature-remission-list-select></remi-feature-remission-list-select>
|
||||
@if (isDepartment()) {
|
||||
<remi-feature-remission-list-department-elements></remi-feature-remission-list-department-elements>
|
||||
}
|
||||
</div>
|
||||
|
||||
<filter-controls-panel (triggerSearch)="search($event)"></filter-controls-panel>
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ import { RemissionListType } from '@isa/remission/data-access';
|
||||
import { RemissionReturnCardComponent } from './remission-return-card/remission-return-card.component';
|
||||
import { logger } from '@isa/core/logging';
|
||||
import { RemissionProcessedHintComponent } from './remission-processed-hint/remission-processed-hint.component';
|
||||
import { RemissionListDepartmentElementsComponent } from './remission-list-department-elements/remission-list-department-elements.component';
|
||||
|
||||
function querySettingsFactory() {
|
||||
return inject(ActivatedRoute).snapshot.data['querySettings'];
|
||||
@@ -85,6 +86,7 @@ function querySettingsFactory() {
|
||||
RemissionListItemComponent,
|
||||
IconButtonComponent,
|
||||
StatefulButtonComponent,
|
||||
RemissionListDepartmentElementsComponent,
|
||||
RemissionProcessedHintComponent,
|
||||
],
|
||||
host: {
|
||||
@@ -138,6 +140,14 @@ export class RemissionListComponent {
|
||||
*/
|
||||
selectedRemissionListType = injectRemissionListType();
|
||||
|
||||
/**
|
||||
* Computed signal indicating whether the remission list type is 'Abteilung'.
|
||||
* @returns True if the selected type is Abteilung, false otherwise.
|
||||
*/
|
||||
isDepartment = computed(() => {
|
||||
return this.selectedRemissionListType() === RemissionListType.Abteilung;
|
||||
});
|
||||
|
||||
/**
|
||||
* Resource signal for fetching the remission list based on current filters.
|
||||
* @returns Remission list resource state.
|
||||
@@ -255,7 +265,7 @@ export class RemissionListComponent {
|
||||
|
||||
/**
|
||||
* Commits the current filter state and triggers a new search.
|
||||
*
|
||||
*
|
||||
* @param trigger - The type of search trigger that initiated this search.
|
||||
* Used to track user interaction patterns and optimize search behavior.
|
||||
*/
|
||||
@@ -306,7 +316,7 @@ export class RemissionListComponent {
|
||||
* Computed signal that determines if the current search was triggered by user interaction.
|
||||
* Returns true for user-initiated actions (input, filter changes, sort changes, scanning)
|
||||
* and false for automatic/system-initiated searches (reload, initial load).
|
||||
*
|
||||
*
|
||||
* @returns True if search was user-initiated, false for system-initiated searches
|
||||
*/
|
||||
searchTriggeredByUser = computed(() => {
|
||||
@@ -336,13 +346,11 @@ export class RemissionListComponent {
|
||||
if (!this.searchTriggeredByUser()) {
|
||||
return;
|
||||
}
|
||||
const isDepartment =
|
||||
this.selectedRemissionListType() === RemissionListType.Abteilung;
|
||||
|
||||
this.searchItemToRemitDialog({
|
||||
data: {
|
||||
searchTerm: this.#filterService.query()?.input['qs'] || '',
|
||||
isDepartment,
|
||||
isDepartment: this.isDepartment(),
|
||||
},
|
||||
}).closed.subscribe(async (result) => {
|
||||
if (result) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './remission-list.resource';
|
||||
export * from './remission-instock.resource';
|
||||
export * from './remission-product-group.resource';
|
||||
export * from './remission-capacity.resource';
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { inject, resource } from '@angular/core';
|
||||
import {
|
||||
RemissionSearchService,
|
||||
RemissionStockService,
|
||||
RemissionSupplierService,
|
||||
} from '@isa/remission/data-access';
|
||||
|
||||
/**
|
||||
* Resource for fetching remission capacity based on departments.
|
||||
* This resource will fetch the required capacity for the given departments
|
||||
* using the RemissionSearchService and RemissionStockService.
|
||||
*
|
||||
* @param {Function} params - Function that returns an object with departments
|
||||
* @returns {Resource} A resource that can be used to fetch remission capacity
|
||||
*/
|
||||
export const createRemissionCapacityResource = (
|
||||
params: () => {
|
||||
departments?: string[];
|
||||
},
|
||||
) => {
|
||||
const remissionSearchService = inject(RemissionSearchService);
|
||||
const remissionStockService = inject(RemissionStockService);
|
||||
const remissionSupplierService = inject(RemissionSupplierService);
|
||||
return resource({
|
||||
params,
|
||||
loader: async ({ abortSignal, params }) => {
|
||||
if (!params.departments || params.departments.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const assignedStock = await remissionStockService.fetchAssignedStock();
|
||||
|
||||
if (!assignedStock || !assignedStock.id) {
|
||||
throw new Error('No current stock available');
|
||||
}
|
||||
|
||||
const suppliers =
|
||||
await remissionSupplierService.fetchSuppliers(abortSignal);
|
||||
|
||||
const firstSupplier = suppliers[0]; // Es gibt aktuell nur Blank als Supplier
|
||||
|
||||
if (!firstSupplier || !firstSupplier.id) {
|
||||
throw new Error('No Supplier available');
|
||||
}
|
||||
|
||||
return await remissionSearchService.fetchRequiredCapacity({
|
||||
departments: params.departments,
|
||||
supplierId: firstSupplier.id,
|
||||
stockId: assignedStock.id,
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -11,7 +11,7 @@
|
||||
#trigger="cdkOverlayOrigin"
|
||||
>
|
||||
<span class="filter-input-button__filter-button-label">{{
|
||||
input.label
|
||||
label() ?? input.label
|
||||
}}</span>
|
||||
<ng-icon
|
||||
class="filter-input-button__filter-button-icon"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@apply flex flex-row gap-2 items-center justify-center px-6 h-12 bg-isa-neutral-400 rounded-[3.125rem] border border-solid border-transparent;
|
||||
|
||||
.filter-input-button__filter-button-label {
|
||||
@apply text-isa-neutral-600 isa-text-body-2-bold;
|
||||
@apply text-isa-neutral-600 isa-text-body-2-bold overflow-hidden text-ellipsis whitespace-nowrap max-w-[9rem];
|
||||
}
|
||||
|
||||
.filter-input-button__filter-button-icon {
|
||||
|
||||
@@ -86,6 +86,12 @@ export class FilterInputMenuButtonComponent {
|
||||
return this.#filter.isDefaultFilterInput(input);
|
||||
});
|
||||
|
||||
/**
|
||||
* The label for the input menu button.
|
||||
* If not provided, it defaults to the label of the filter input.
|
||||
*/
|
||||
label = input<string | undefined>(undefined);
|
||||
|
||||
/**
|
||||
* Subscribes to the `applied` event to automatically close the menu.
|
||||
*/
|
||||
|
||||
@@ -182,7 +182,7 @@ export class DropdownButtonComponent<T>
|
||||
return this.label() ?? this.value();
|
||||
}
|
||||
|
||||
return selectedOption.getLabel();
|
||||
return this.label() ?? selectedOption.getLabel();
|
||||
});
|
||||
|
||||
constructor() {
|
||||
|
||||
Reference in New Issue
Block a user