mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-31 09:37:15 +01:00
Merged PR 2020: feat(confirmation-list-item-action-card): improve action card visibility logi...
feat(confirmation-list-item-action-card): improve action card visibility logic and add ordered state Enhance the action card display logic to show only when both feature flag and command/completion conditions are met. Add support for "Ordered" state to distinguish pending items from completed ones. Changes: - Add hasLoyaltyCollectCommand helper to check for LOYALTY_COLLECT_COMMAND - Update displayActionCard logic to require both Rücklage feature AND (loyalty collect command OR completion state) - Add ProcessingStatusState.Ordered to distinguish ordered vs completed items - Update isComplete to exclude ordered items from completion state - Move role-based visibility check to outer container level - Remove unused CSS class for completed state - Add comprehensive unit tests for new helpers The action card now correctly appears only for items that need user action, hiding for CallCenter role and for items without the required commands. Ref: #5459
This commit is contained in:
committed by
Lorenz Hilpert
parent
4a7b74a6c5
commit
6df02d9e86
@@ -0,0 +1,122 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { hasLoyaltyCollectCommand } from './has-loyalty-collect-command.helper';
|
||||
import { DisplayOrderItemSubset } from '@isa/oms/data-access';
|
||||
|
||||
describe('hasLoyaltyCollectCommand', () => {
|
||||
describe('when items is undefined', () => {
|
||||
it('should return false', () => {
|
||||
// Act
|
||||
const result = hasLoyaltyCollectCommand(undefined);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when items is empty array', () => {
|
||||
it('should return false', () => {
|
||||
// Arrange
|
||||
const items: DisplayOrderItemSubset[] = [];
|
||||
|
||||
// Act
|
||||
const result = hasLoyaltyCollectCommand(items);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when items have no actions', () => {
|
||||
it('should return false', () => {
|
||||
// Arrange
|
||||
const items: DisplayOrderItemSubset[] = [
|
||||
{
|
||||
id: 1,
|
||||
quantity: 1,
|
||||
} as DisplayOrderItemSubset,
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = hasLoyaltyCollectCommand(items);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when items have actions but no LOYALTY_COLLECT_COMMAND', () => {
|
||||
it('should return false', () => {
|
||||
// Arrange
|
||||
const items: any[] = [
|
||||
{
|
||||
id: 1,
|
||||
quantity: 1,
|
||||
actions: [
|
||||
{ command: 'SOME_OTHER_COMMAND', label: 'Other Action' },
|
||||
{ command: 'ANOTHER_COMMAND', label: 'Another Action' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = hasLoyaltyCollectCommand(items);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when items have LOYALTY_COLLECT_COMMAND action', () => {
|
||||
it('should return true', () => {
|
||||
// Arrange
|
||||
const items: any[] = [
|
||||
{
|
||||
id: 1,
|
||||
quantity: 1,
|
||||
actions: [
|
||||
{
|
||||
command: 'LOYALTY_COLLECT_COMMAND',
|
||||
label: 'Abschließen',
|
||||
selected: true,
|
||||
value: 'Abschließen',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = hasLoyaltyCollectCommand(items);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when items have multiple actions including LOYALTY_COLLECT_COMMAND', () => {
|
||||
it('should return true', () => {
|
||||
// Arrange
|
||||
const items: any[] = [
|
||||
{
|
||||
id: 1,
|
||||
quantity: 1,
|
||||
actions: [
|
||||
{ command: 'SOME_OTHER_COMMAND', label: 'Other Action' },
|
||||
{
|
||||
command: 'LOYALTY_COLLECT_COMMAND',
|
||||
label: 'Abschließen',
|
||||
selected: true,
|
||||
value: 'Abschließen',
|
||||
},
|
||||
{ command: 'ANOTHER_COMMAND', label: 'Another Action' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = hasLoyaltyCollectCommand(items);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { DisplayOrderItemSubset } from '@isa/oms/data-access';
|
||||
|
||||
/**
|
||||
* Checks if any of the subset items has a LOYALTY_COLLECT_COMMAND action
|
||||
* @param items - Array of DisplayOrderItemSubset to check
|
||||
* @returns true if at least one item has a LOYALTY_COLLECT_COMMAND action
|
||||
*/
|
||||
export const hasLoyaltyCollectCommand = (
|
||||
items?: DisplayOrderItemSubset[],
|
||||
): boolean => {
|
||||
const firstItem = items?.find((_) => true);
|
||||
return (
|
||||
firstItem?.actions?.some((action) =>
|
||||
action?.command?.includes('LOYALTY_COLLECT_COMMAND'),
|
||||
) ?? false
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from './get-order-type-feature.helper';
|
||||
export * from './has-order-type-feature.helper';
|
||||
export * from './has-loyalty-collect-command.helper';
|
||||
export * from './checkout-analysis.helpers';
|
||||
export * from './checkout-business-logic.helpers';
|
||||
export * from './checkout-data.helpers';
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.confirmation-list-item-done {
|
||||
@apply bg-transparent p-0;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
@if (displayActionCard()) {
|
||||
<div
|
||||
class="w-72 desktop-large:w-[24.5rem] justify-between h-full p-4 flex flex-col gap-4 rounded-lg bg-isa-secondary-100"
|
||||
[class.confirmation-list-item-done]="item().status !== 1"
|
||||
data-which="action-card"
|
||||
data-what="action-card"
|
||||
*ifNotRole="Role.CallCenter"
|
||||
>
|
||||
@if (!isComplete()) {
|
||||
<div
|
||||
@@ -16,7 +16,6 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
*ifNotRole="Role.CallCenter"
|
||||
class="flex flex-col gap-[0.62rem] desktop-large:gap-0 desktop-large:flex-row desktop-large:justify-between desktop-large:items-center"
|
||||
>
|
||||
<ui-dropdown
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
import {
|
||||
hasOrderTypeFeature,
|
||||
buildItemQuantityMap,
|
||||
hasLoyaltyCollectCommand,
|
||||
} from '@isa/checkout/data-access';
|
||||
import { IfRoleDirective, Role } from '@isa/core/auth';
|
||||
|
||||
@@ -92,11 +93,25 @@ export class ConfirmationListItemActionCardComponent {
|
||||
});
|
||||
|
||||
isComplete = computed(() => {
|
||||
return this.processingStatus() !== undefined;
|
||||
return (
|
||||
this.processingStatus() !== undefined &&
|
||||
this.processingStatus() !== ProcessingStatusState.Ordered
|
||||
);
|
||||
});
|
||||
|
||||
displayActionCard = computed(() =>
|
||||
hasOrderTypeFeature(this.item().features, ['Rücklage']),
|
||||
/**
|
||||
* #5459 - Determines whether the action card should be displayed for this order item.
|
||||
*
|
||||
* The action card is shown when ALL of the following conditions are met:
|
||||
* - The item MUST have the 'Rücklage' order type feature
|
||||
* - AND one of the following:
|
||||
* - The item has a loyalty collect command available (for collecting rewards)
|
||||
* - OR the item processing is complete (for displaying the completed state)
|
||||
*/
|
||||
displayActionCard = computed(
|
||||
() =>
|
||||
hasOrderTypeFeature(this.item().features, ['Rücklage']) &&
|
||||
(hasLoyaltyCollectCommand(this.item().subsetItems) || this.isComplete()),
|
||||
);
|
||||
|
||||
constructor() {
|
||||
|
||||
@@ -3,6 +3,22 @@ import { ProcessingStatusState } from '../../models';
|
||||
import { getProcessingStatusState } from './get-processing-status-state.helper';
|
||||
|
||||
describe('getProcessingStatusState', () => {
|
||||
describe('Ordered status', () => {
|
||||
it('should return Ordered when all items are Bestellt', () => {
|
||||
// Arrange
|
||||
const statuses = [
|
||||
OrderItemProcessingStatusValue.Bestellt,
|
||||
OrderItemProcessingStatusValue.Bestellt,
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = getProcessingStatusState(statuses);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(ProcessingStatusState.Ordered);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Cancelled status', () => {
|
||||
it('should return Cancelled when all items are cancelled', () => {
|
||||
// Arrange
|
||||
@@ -117,5 +133,19 @@ describe('getProcessingStatusState', () => {
|
||||
// Assert
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return undefined when items have mix of ordered and cancelled', () => {
|
||||
// Arrange
|
||||
const statuses = [
|
||||
OrderItemProcessingStatusValue.Bestellt,
|
||||
OrderItemProcessingStatusValue.Storniert, // 1024
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = getProcessingStatusState(statuses);
|
||||
|
||||
// Assert
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,6 +24,14 @@ export const getProcessingStatusState = (
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Check if all statuses are ordered
|
||||
const allOrdered = statuses.every(
|
||||
(status) => status === OrderItemProcessingStatusValue.Bestellt,
|
||||
);
|
||||
if (allOrdered) {
|
||||
return ProcessingStatusState.Ordered;
|
||||
}
|
||||
|
||||
// Check if all statuses are cancelled
|
||||
const allCancelled = statuses.every(
|
||||
(status) =>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* Processing status state types for order items
|
||||
*/
|
||||
export const ProcessingStatusState = {
|
||||
/** Item is still ordered and pending processing */
|
||||
Ordered: 'ordered',
|
||||
/** Item was cancelled by customer, merchant, or supplier */
|
||||
Cancelled: 'cancelled',
|
||||
/** Item was not found / not available */
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { EntitySchema, DateRangeSchema } from '@isa/common/data-access';
|
||||
import {
|
||||
EntitySchema,
|
||||
DateRangeSchema,
|
||||
KeyValueOfStringAndStringSchema,
|
||||
} from '@isa/common/data-access';
|
||||
import { z } from 'zod';
|
||||
import { OrderItemProcessingStatusValueSchema } from './order-item-processing-status-value.schema';
|
||||
|
||||
// Forward declaration for circular reference
|
||||
export const DisplayOrderItemSubsetSchema = z
|
||||
.object({
|
||||
actions: z
|
||||
.array(KeyValueOfStringAndStringSchema)
|
||||
.describe('Possible actions')
|
||||
.optional(),
|
||||
compartmentCode: z.string().describe('Compartment code').optional(),
|
||||
compartmentInfo: z.string().describe('Compartment information').optional(),
|
||||
compartmentStart: z.string().describe('Compartment start').optional(),
|
||||
|
||||
Reference in New Issue
Block a user