Merged PR 2057: feat(checkout): add branch selection to reward catalog

feat(checkout): add branch selection to reward catalog

- Add new select-branch-dropdown library with BranchDropdownComponent
  and SelectedBranchDropdownComponent for branch selection
- Extend DropdownButtonComponent with filter and option subcomponents
- Integrate branch selection into reward catalog page
- Add BranchesResource for fetching available branches
- Update CheckoutMetadataService with branch selection persistence
- Add comprehensive tests for dropdown components

Related work items: #5464
This commit is contained in:
Lorenz Hilpert
2025-11-27 10:38:52 +00:00
committed by Nino Righi
parent 4589146e31
commit 7950994d66
44 changed files with 3210 additions and 1580 deletions

View File

@@ -12,6 +12,7 @@ import {
} from '@isa/crm/data-access';
import { TabService } from '@isa/core/tabs';
import { BranchDTO } from '@generated/swagger/checkout-api';
import { CheckoutMetadataService } from '@isa/checkout/data-access';
/**
* Service for opening and managing the Purchase Options Modal.
@@ -39,6 +40,7 @@ export class PurchaseOptionsModalService {
#uiModal = inject(UiModalService);
#tabService = inject(TabService);
#crmTabMetadataService = inject(CrmTabMetadataService);
#checkoutMetadataService = inject(CheckoutMetadataService);
#customerFacade = inject(CustomerFacade);
/**
@@ -74,7 +76,10 @@ export class PurchaseOptionsModalService {
};
context.selectedCustomer = await this.#getSelectedCustomer(data);
context.selectedBranch = this.#getSelectedBranch(data.tabId);
context.selectedBranch = this.#getSelectedBranch(
data.tabId,
data.useRedemptionPoints,
);
return this.#uiModal.open<string, PurchaseOptionsModalContext>({
content: PurchaseOptionsModalComponent,
data: context,
@@ -95,7 +100,10 @@ export class PurchaseOptionsModalService {
return this.#customerFacade.fetchCustomer({ customerId });
}
#getSelectedBranch(tabId: number): BranchDTO | undefined {
#getSelectedBranch(
tabId: number,
useRedemptionPoints: boolean,
): BranchDTO | undefined {
const tab = untracked(() =>
this.#tabService.entities().find((t) => t.id === tabId),
);
@@ -104,6 +112,10 @@ export class PurchaseOptionsModalService {
return undefined;
}
if (useRedemptionPoints) {
return this.#checkoutMetadataService.getSelectedBranch(tabId);
}
const legacyProcessData = tab?.metadata?.process_data;
if (

View File

@@ -1,51 +1,106 @@
import {
DropdownAppearance,
DropdownButtonComponent,
DropdownOptionComponent,
} from '@isa/ui/input-controls';
import { type Meta, type StoryObj, argsToTemplate, moduleMetadata } from '@storybook/angular';
type DropdownInputProps = {
value: string;
label: string;
appearance: DropdownAppearance;
};
const meta: Meta<DropdownInputProps> = {
title: 'ui/input-controls/Dropdown',
decorators: [
moduleMetadata({
imports: [DropdownButtonComponent, DropdownOptionComponent],
}),
],
argTypes: {
value: { control: 'text' },
label: { control: 'text' },
appearance: {
control: 'select',
options: Object.values(DropdownAppearance),
},
},
render: (args) => ({
props: args,
template: `
<ui-dropdown ${argsToTemplate(args)}>
<ui-dropdown-option value="">Select an option</ui-dropdown-option>
<ui-dropdown-option value="1">Option 1</ui-dropdown-option>
<ui-dropdown-option value="2">Option 2</ui-dropdown-option>
<ui-dropdown-option value="3">Option 3</ui-dropdown-option>
</ui-dropdown>
`,
}),
};
export default meta;
type Story = StoryObj<DropdownInputProps>;
export const Default: Story = {
args: {
value: undefined,
label: 'Label',
},
};
import {
DropdownAppearance,
DropdownButtonComponent,
DropdownFilterComponent,
DropdownOptionComponent,
} from '@isa/ui/input-controls';
import {
type Meta,
type StoryObj,
argsToTemplate,
moduleMetadata,
} from '@storybook/angular';
type DropdownInputProps = {
value: string;
label: string;
appearance: DropdownAppearance;
};
const meta: Meta<DropdownInputProps> = {
title: 'ui/input-controls/Dropdown',
decorators: [
moduleMetadata({
imports: [
DropdownButtonComponent,
DropdownFilterComponent,
DropdownOptionComponent,
],
}),
],
argTypes: {
value: { control: 'text' },
label: { control: 'text' },
appearance: {
control: 'select',
options: Object.values(DropdownAppearance),
},
},
render: (args) => ({
props: args,
template: `
<ui-dropdown ${argsToTemplate(args)}>
<ui-dropdown-option value="">Select an option</ui-dropdown-option>
<ui-dropdown-option value="1">Option 1</ui-dropdown-option>
<ui-dropdown-option value="2">Option 2</ui-dropdown-option>
<ui-dropdown-option value="3">Option 3</ui-dropdown-option>
</ui-dropdown>
`,
}),
};
export default meta;
type Story = StoryObj<DropdownInputProps>;
export const Default: Story = {
args: {
value: undefined,
label: 'Label',
},
};
export const WithFilter: Story = {
args: {
value: undefined,
label: 'Select a country',
},
render: (args) => ({
props: args,
template: `
<ui-dropdown ${argsToTemplate(args)}>
<ui-dropdown-filter placeholder="Search countries..."></ui-dropdown-filter>
<ui-dropdown-option value="">Select a country</ui-dropdown-option>
<ui-dropdown-option value="de">Germany</ui-dropdown-option>
<ui-dropdown-option value="at">Austria</ui-dropdown-option>
<ui-dropdown-option value="ch">Switzerland</ui-dropdown-option>
<ui-dropdown-option value="fr">France</ui-dropdown-option>
<ui-dropdown-option value="it">Italy</ui-dropdown-option>
<ui-dropdown-option value="es">Spain</ui-dropdown-option>
<ui-dropdown-option value="nl">Netherlands</ui-dropdown-option>
<ui-dropdown-option value="be">Belgium</ui-dropdown-option>
<ui-dropdown-option value="pl">Poland</ui-dropdown-option>
<ui-dropdown-option value="cz">Czech Republic</ui-dropdown-option>
</ui-dropdown>
`,
}),
};
export const GreyAppearance: Story = {
args: {
value: undefined,
label: 'Filter',
appearance: DropdownAppearance.Grey,
},
};
export const Disabled: Story = {
render: () => ({
template: `
<ui-dropdown label="Disabled dropdown" [disabled]="true">
<ui-dropdown-option value="1">Option 1</ui-dropdown-option>
<ui-dropdown-option value="2">Option 2</ui-dropdown-option>
</ui-dropdown>
`,
}),
};