feat: add schemas and utility functions for return processing; update styles and component structure

This commit is contained in:
Lorenz Hilpert
2025-03-12 16:13:47 +01:00
parent abce5f43e2
commit 5bba1dff8f
80 changed files with 1187 additions and 157 deletions

View File

@@ -1,87 +1,87 @@
{
"title": "ISA - Local",
"silentRefresh": {
"interval": 300000
},
"debug": true,
"dev-scanner": true,
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
"@core/auth": {
"issuer": "https://sso-test.paragon-data.de",
"clientId": "isa-client",
"responseType": "code",
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi isa-wws-webapi",
"showDebugInformation": true
},
"@core/logger": {
"logLevel": "debug"
},
"@swagger/isa": {
"rootUrl": "https://isa-test.paragon-data.net/isa/v1"
},
"@swagger/cat": {
"rootUrl": "https://isa-test.paragon-data.net/catsearch/v6"
},
"@swagger/av": {
"rootUrl": "https://isa-test.paragon-data.net/ava/v6"
},
"@swagger/checkout": {
"rootUrl": "https://isa-test.paragon-data.net/checkout/v6"
},
"@swagger/crm": {
"rootUrl": "https://isa-test.paragon-data.net/crm/v6"
},
"@swagger/oms": {
"rootUrl": "https://isa-test.paragon-data.net/oms/v6"
},
"@swagger/print": {
"rootUrl": "https://isa-test.paragon-data.net/print/v1"
},
"@swagger/eis": {
"rootUrl": "https://filialinformationsystem-test.paragon-systems.de/eiswebapi/v1"
},
"@swagger/remi": {
"rootUrl": "https://isa-test.paragon-data.net/inv/v6"
},
"@swagger/wws": {
"rootUrl": "https://isa-test.paragon-data.net/wws/v1"
},
"@domain/checkout": {
"olaExpiration": "5m"
},
"hubs": {
"notifications": {
"url": "https://isa-test.paragon-data.net/isa/v1/rt",
"enableAutomaticReconnect": false,
"httpOptions": {
"transport": 1,
"logMessageContent": true,
"skipNegotiation": true
}
}
},
"process": {
"ids": {
"goodsOut": 1000,
"goodsIn": 2000,
"taskCalendar": 3000,
"remission": 4000,
"packageInspection": 5000,
"assortment": 6000,
"pickupShelf": 7000
}
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "Ae8F2Wx2RMq5Lvn7UUAlWzVFZTt2+ubMAF8XtDpmPlNkBeG/LWs1M7AbgDW0LQqYLnszClEENaEHS56/6Ts2vrJ1Ux03CXUjK3jUvZpF5OchXR1CpnmpepJ6WxPCd7LMVHUGG1BbwPLDTFjP3y8uT0caTSmmGrYQWAs4CZcEF+ZBabP0z7vfm+hCZF/ebj9qqCJZcW8nH/n19hohshllzYBjFXjh87P2lIh1s6yZS3OaQWWXo/o0AKdxx7T6CVyR0/G5zq6uYJWf6rs3euUBEhpzOZHbHZK86Lvy2AVBEyVkkcttlDW1J2fA4l1W1JV/Xibz8AQV6kG482EpGF42KEoK48paZgX3e1AQsqUtmqzw294dcP4zMVstnw5/WrwKKi/5E/nOOJT2txYP1ZufIjPrwNFsqTlv7xCQlHjMzFGYwT816yD5qLRLbwOtjrkUPXNZLZ06T4upvWwJDmm8XgdeoDqMjHdcO4lwji1bl9EiIYJ/2qnsk9yZ2FqSaHzn4cbiL0f5u2HFlNAP0GUujGRlthGhHi6o4dFU+WAxKsFMKVt+SfoQUazNKHFVQgiAklTIZxIc/HUVzRvOLMxf+wFDerraBtcqGJg+g/5mrWYqeDBGhCBHtKiYf6244IJ4afzNTiH1/30SJcRzXwbEa3A7q1fJTx9/nLTOfVPrJKBQs7f/OQs2dA7LDCel8mzXdbjvsNQaeU5+iCIAq6zbTNKy1xT8wwj+VZrQmtNJs+qeznD+u29nCM24h8xCmRpvNPo4/Mww/lrTNrrNwLBSn1pMIwsH7yS9hH0v0oNAM3A6bVtk1D9qEkbyw+xZa+MZGpMP0D0CdcsqHalPcm5r/Ik="
},
"gender": {
"0": "Keine Anrede",
"1": "Enby",
"2": "Herr",
"4": "Frau"
},
"@shared/icon": "/assets/icons.json"
}
"title": "ISA - Local",
"silentRefresh": {
"interval": 300000
},
"debug": true,
"dev-scanner": true,
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
"@core/auth": {
"issuer": "https://sso-test.paragon-data.de",
"clientId": "isa-client",
"responseType": "code",
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi isa-wws-webapi",
"showDebugInformation": true
},
"@core/logger": {
"logLevel": "debug"
},
"@swagger/isa": {
"rootUrl": "https://isa-test.paragon-data.net/isa/v1"
},
"@swagger/cat": {
"rootUrl": "https://isa-test.paragon-data.net/catsearch/v6"
},
"@swagger/av": {
"rootUrl": "https://isa-test.paragon-data.net/ava/v6"
},
"@swagger/checkout": {
"rootUrl": "https://isa-test.paragon-data.net/checkout/v6"
},
"@swagger/crm": {
"rootUrl": "https://isa-test.paragon-data.net/crm/v6"
},
"@swagger/oms": {
"rootUrl": "https://isa-test.paragon-data.net/oms/v6"
},
"@swagger/print": {
"rootUrl": "https://isa-test.paragon-data.net/print/v1"
},
"@swagger/eis": {
"rootUrl": "https://filialinformationsystem-test.paragon-systems.de/eiswebapi/v1"
},
"@swagger/remi": {
"rootUrl": "https://isa-test.paragon-data.net/inv/v6"
},
"@swagger/wws": {
"rootUrl": "https://isa-test.paragon-data.net/wws/v1"
},
"@domain/checkout": {
"olaExpiration": "5m"
},
"hubs": {
"notifications": {
"url": "https://isa-test.paragon-data.net/isa/v1/rt",
"enableAutomaticReconnect": false,
"httpOptions": {
"transport": 1,
"logMessageContent": true,
"skipNegotiation": true
}
}
},
"process": {
"ids": {
"goodsOut": 1000,
"goodsIn": 2000,
"taskCalendar": 3000,
"remission": 4000,
"packageInspection": 5000,
"assortment": 6000,
"pickupShelf": 7000
}
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "Ae8F2Wx2RMq5Lvn7UUAlWzVFZTt2+ubMAF8XtDpmPlNkBeG/LWs1M7AbgDW0LQqYLnszClEENaEHS56/6Ts2vrJ1Ux03CXUjK3jUvZpF5OchXR1CpnmpepJ6WxPCd7LMVHUGG1BbwPLDTFjP3y8uT0caTSmmGrYQWAs4CZcEF+ZBabP0z7vfm+hCZF/ebj9qqCJZcW8nH/n19hohshllzYBjFXjh87P2lIh1s6yZS3OaQWWXo/o0AKdxx7T6CVyR0/G5zq6uYJWf6rs3euUBEhpzOZHbHZK86Lvy2AVBEyVkkcttlDW1J2fA4l1W1JV/Xibz8AQV6kG482EpGF42KEoK48paZgX3e1AQsqUtmqzw294dcP4zMVstnw5/WrwKKi/5E/nOOJT2txYP1ZufIjPrwNFsqTlv7xCQlHjMzFGYwT816yD5qLRLbwOtjrkUPXNZLZ06T4upvWwJDmm8XgdeoDqMjHdcO4lwji1bl9EiIYJ/2qnsk9yZ2FqSaHzn4cbiL0f5u2HFlNAP0GUujGRlthGhHi6o4dFU+WAxKsFMKVt+SfoQUazNKHFVQgiAklTIZxIc/HUVzRvOLMxf+wFDerraBtcqGJg+g/5mrWYqeDBGhCBHtKiYf6244IJ4afzNTiH1/30SJcRzXwbEa3A7q1fJTx9/nLTOfVPrJKBQs7f/OQs2dA7LDCel8mzXdbjvsNQaeU5+iCIAq6zbTNKy1xT8wwj+VZrQmtNJs+qeznD+u29nCM24h8xCmRpvNPo4/Mww/lrTNrrNwLBSn1pMIwsH7yS9hH0v0oNAM3A6bVtk1D9qEkbyw+xZa+MZGpMP0D0CdcsqHalPcm5r/Ik="
},
"gender": {
"0": "Keine Anrede",
"1": "Enby",
"2": "Herr",
"4": "Frau"
},
"@shared/icon": "/assets/icons.json"
}

View File

@@ -31,8 +31,8 @@ const meta: Meta<InfoButtonComponentInputs> = {
render: (args) => ({
props: args,
template: `<ui-info-button ${argsToTemplate(args, { exclude: ['disabled', 'icon'] })} ${args.disabled ? 'disabled' : ''}>
<span>${args.label}</span>
<ng-icon name="${args.icon}"></ng-icon>
<span uiInfoButtonLabel>${args.label}</span>
<ng-icon name="${args.icon}" uiInfoButtonIcon></ng-icon>
</ui-info-button>`,
}),
};

View File

@@ -0,0 +1,66 @@
import { type Meta, type StoryObj, moduleMetadata } from '@storybook/angular';
import { ItemRowComponent } from '@isa/ui/item-rows';
import { ProductImageDirective, provideProductImageUrl } from '@isa/shared/product-image';
import { isaArtikelTaschenbuch, isaActionCheck } from '@isa/icons';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { CheckboxComponent } from '@isa/ui/input-controls';
const meta: Meta<ItemRowComponent> = {
component: ItemRowComponent,
title: 'ui/item-rows/ItemRow',
decorators: [
moduleMetadata({
imports: [ProductImageDirective, NgIconComponent, CheckboxComponent],
providers: [
provideProductImageUrl('https://produktbilder-test.paragon-data.net'),
provideIcons({ isaArtikelTaschenbuch, isaActionCheck }),
],
}),
],
render: (args) => ({
props: args,
template: `
<ui-item-row>
<div>
<a href="#">
<img sharedProductImage ean="9783742327529" imageWidth="100" imageHeight="100" />
</a>
</div>
<div class="text-isa-neutral-900 flex flex-col gap-2">
<h4 class="isa-text-body-2-bold">Hefter, Martina</h4>
<h3 class="isa-text-subtitle-1-bold">Hey guten Morgen, wie geht es dir?</h3>
<div class="flex flex-row justify-start items-center gap-2">
<span class="isa-text-body-2-bold text-isa-secondary-900">12,99 €</span>
<span class="isa-text-body-2-regular text-isa-neutral-600" >inkl. 7% MwSt</span>
<ng-icon name="isaActionCheck" class="text-isa-accent-blue"></ng-icon>
</div>
</div>
<div class="text-isa-neutral-900 flex flex-col gap-2">
<div class="flex flex-row gap-2 items-center justify-start">
<ng-icon name="isaArtikelTaschenbuch"></ng-icon>
<span class="isa-text-body-2-bold truncate">Taschenbuch (Kartoniert, Paper lorem ipsum sum)</span>
</div>
<div>
Droemer/Knaur; lorem dolor sum et ent ... | 987654345678
</div>
<div>
27. März 2024
</div>
</div>
<div>
<ui-checkbox>
<input type="checkbox" />
</ui-checkbox>
</div>
</ui-item-row>
`,
}),
};
export default meta;
type Story = StoryObj<ItemRowComponent>;
export const Default: Story = {
args: {},
};

View File

@@ -0,0 +1,7 @@
# common-result
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test common-result` to execute the unit tests.

View File

@@ -0,0 +1,34 @@
import nx from '@nx/eslint-plugin';
import baseConfig from '../../../eslint.config.mjs';
export default [
...baseConfig,
...nx.configs['flat/angular'],
...nx.configs['flat/angular-template'],
{
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'lib',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'lib',
style: 'kebab-case',
},
],
},
},
{
files: ['**/*.html'],
// Override or add rules here
rules: {},
},
];

View File

@@ -0,0 +1,21 @@
export default {
displayName: 'common-result',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../coverage/libs/common/result',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
};

View File

@@ -0,0 +1,20 @@
{
"name": "common-result",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/common/result/src",
"prefix": "lib",
"projectType": "library",
"tags": [],
"targets": {
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/common/result/jest.config.ts"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}

View File

@@ -0,0 +1,3 @@
export * from './lib/response-args.schema';
export * from './lib/entity-container.schema';
export * from './lib/result';

View File

@@ -0,0 +1,15 @@
import { z } from 'zod';
const EntityContainerSchema = z.object({
id: z.number(),
uId: z.string(),
displayLabel: z.string(),
enabled: z.boolean(),
selected: z.boolean(),
});
export function entityContainerSchema<T>(data: z.ZodType<T>) {
return EntityContainerSchema.extend({
data: data,
});
}

View File

@@ -0,0 +1,25 @@
import { z } from 'zod';
export const ResponseArgsSchema = z.object({
error: z.boolean(),
invalidProperties: z.record(z.string()),
message: z.string().optional(),
});
export function ResponseArgs<T>(resultSchema: z.ZodType<T>) {
return ResponseArgsSchema.extend({
result: resultSchema,
});
}
const ListResponseArgsSchema = ResponseArgsSchema.extend({
skip: z.number(),
take: z.number(),
hits: z.number(),
});
export function ListResponseArgs<T>(resultSchema: z.ZodType<T>) {
return ListResponseArgsSchema.extend({
result: resultSchema.array(),
});
}

View File

@@ -0,0 +1,14 @@
export const ResultStatus = {
Idle: 'idle',
Pending: 'pending',
Success: 'success',
Error: 'error',
} as const;
export type ResultStatus = (typeof ResultStatus)[keyof typeof ResultStatus];
export interface Result<T> {
status: ResultStatus;
data: T;
error?: unknown;
}

View File

@@ -0,0 +1,6 @@
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
setupZoneTestEnv({
errorOnUnknownElements: true,
errorOnUnknownProperties: true,
});

View File

@@ -0,0 +1,28 @@
{
"compilerOptions": {
"target": "es2022",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"extends": "../../../tsconfig.base.json",
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

View File

@@ -0,0 +1,17 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": []
},
"exclude": [
"src/**/*.spec.ts",
"src/test-setup.ts",
"jest.config.ts",
"src/**/*.test.ts"
],
"include": ["src/**/*.ts"]
}

View File

@@ -0,0 +1,16 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"module": "commonjs",
"target": "es2016",
"types": ["jest", "node"]
},
"files": ["src/test-setup.ts"],
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

View File

@@ -1,3 +1,7 @@
export * from './lib/return-details-data/return-details-data.component';
export * from './lib/return-details-header/return-details-header.component';
export * from './lib/return-details-order-group/return-details-order-group.component';
export * from './lib/return-details-order-group-item/return-details-order-group-item.component';
export * from './lib/return-results-list/';
export * from './lib/return-results-item-list/';
export * from './lib/return-order-by-list/';

View File

@@ -0,0 +1,20 @@
<ui-item-row-data>
<ui-item-row-data-row>
<ui-item-row-data-label>Email:</ui-item-row-data-label>
<ui-item-row-data-value>
<span class="isa-text-body-2-bold">{{ communicationDetails().email }}</span>
</ui-item-row-data-value>
</ui-item-row-data-row>
<ui-item-row-data-row>
<ui-item-row-data-label>Belegart:</ui-item-row-data-label>
<ui-item-row-data-value>
<span class="isa-text-body-2-bold">{{
receipt().receiptType | omsReceiptTypeTranslation
}}</span>
</ui-item-row-data-value>
</ui-item-row-data-row>
</ui-item-row-data>
<!-- <button class="mt-3" uiTextButton type="button" color="strong" size="small">
<ng-icon name="isaActionPlus" size="1rem"></ng-icon>
<span>Bestelldetails anzeigen</span>
</button> -->

View File

@@ -0,0 +1,3 @@
ui-item-row-data-label {
width: 14.5rem;
}

View File

@@ -0,0 +1,26 @@
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
import { Buyer, Receipt } from '@feature/return/services';
import { ReceiptTypeTranslationPipe } from '@isa/oms/utils/translation';
import { ItemRowDataImports } from '@isa/ui/item-rows';
@Component({
selector: 'lib-return-details-data',
templateUrl: './return-details-data.component.html',
styleUrls: ['./return-details-data.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [ItemRowDataImports, ReceiptTypeTranslationPipe],
})
export class ReturnDetailsDataComponent {
receipt = input.required<Receipt>();
buyer = computed<Buyer>(() => {
const receipt = this.receipt();
return receipt.buyer;
});
communicationDetails = computed(() => {
const buyer = this.buyer();
return buyer.communicationDetails;
});
}

View File

@@ -0,0 +1,7 @@
<div class="return-details-header--header">
<button uiInfoButton>
<ng-icon uiInfoButtonLabel name="isaNavigationKunden"></ng-icon>
<ng-icon uiInfoButtonIcon name="isaActionChevronDown"></ng-icon>
</button>
<h3 class="isa-text-subtitle-1-regular">{{ name() }}</h3>
</div>

View File

@@ -0,0 +1,19 @@
:host {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 1.5rem;
align-self: stretch;
}
.return-details-header--header {
display: flex;
padding-top: 1rem;
align-items: flex-start;
gap: 1rem;
align-self: stretch;
}
ui-item-row-data-label {
width: 14.5rem;
}

View File

@@ -0,0 +1,26 @@
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
import { isaActionChevronDown, isaNavigationKunden } from '@isa/icons';
import { InfoButtonComponent } from '@isa/ui/buttons';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { Buyer } from '@feature/return/services';
@Component({
selector: 'lib-return-details-header',
templateUrl: './return-details-header.component.html',
styleUrls: ['./return-details-header.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [InfoButtonComponent, NgIconComponent],
providers: [provideIcons({ isaNavigationKunden, isaActionChevronDown })],
})
export class ReturnDetailsHeaderComponent {
buyer = input.required<Buyer>();
name = computed<string>(() => {
const buyer = this.buyer();
const { firstName, lastName } = buyer;
return `${firstName} ${lastName}`;
});
}

View File

@@ -0,0 +1,28 @@
<ui-item-row-data>
<ui-item-row-data-row>
<ui-item-row-data-label>Belegdatum:</ui-item-row-data-label>
<ui-item-row-data-value> </ui-item-row-data-value>
</ui-item-row-data-row>
<ui-item-row-data-row>
<ui-item-row-data-label>Belegart:</ui-item-row-data-label>
<ui-item-row-data-value>
{{ receipt().receiptType | omsReceiptTypeTranslation }}
</ui-item-row-data-value>
</ui-item-row-data-row>
<ui-item-row-data-row>
<ui-item-row-data-label>Vorgangs-ID:</ui-item-row-data-label>
<ui-item-row-data-value> </ui-item-row-data-value>
</ui-item-row-data-row>
<ui-item-row-data-row>
<ui-item-row-data-label>Bestelldatum:</ui-item-row-data-label>
<ui-item-row-data-value> </ui-item-row-data-value>
</ui-item-row-data-row>
<ui-item-row-data-row>
<ui-item-row-data-label>Anschrift:</ui-item-row-data-label>
<ui-item-row-data-value> </ui-item-row-data-value>
</ui-item-row-data-row>
<ui-item-row-data-row>
<ui-item-row-data-label>Lieferanschrift:</ui-item-row-data-label>
<ui-item-row-data-value> </ui-item-row-data-value>
</ui-item-row-data-row>
</ui-item-row-data>

View File

@@ -0,0 +1,16 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { Receipt } from '@feature/return/services';
import { ReceiptTypeTranslationPipe } from '@isa/oms/utils/translation';
import { ItemRowDataImports } from '@isa/ui/item-rows';
@Component({
selector: 'lib-return-details-order-group-data',
templateUrl: './return-details-order-group-data.component.html',
styleUrls: ['./return-details-order-group-data.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [ItemRowDataImports, ReceiptTypeTranslationPipe],
})
export class ReturnDetailsOrderGroupDataComponent {
receipt = input.required<Receipt>();
}

View File

@@ -0,0 +1,30 @@
<ui-item-row>
<div>
<a href="#">
<img sharedProductImage ean="9783742327529" [imageWidth]="100" [imageHeight]="100" alt="" />
</a>
</div>
<div class="text-isa-neutral-900 flex flex-col gap-2">
<h4 class="isa-text-body-2-bold">Hefter, Martina</h4>
<h3 class="isa-text-subtitle-1-bold">Hey guten Morgen, wie geht es dir?</h3>
<div class="flex flex-row justify-start items-center gap-2">
<span class="isa-text-body-2-bold text-isa-secondary-900">12,99 €</span>
<span class="isa-text-body-2-regular text-isa-neutral-600">inkl. 7% MwSt</span>
</div>
</div>
<div class="text-isa-neutral-900 flex flex-col gap-2">
<div class="flex flex-row gap-2 items-center justify-start">
<ng-icon name="isaArtikelTaschenbuch"></ng-icon>
<span class="isa-text-body-2-bold truncate"
>Taschenbuch (Kartoniert, Paper lorem ipsum sum)</span
>
</div>
<div>Droemer/Knaur; lorem dolor sum et ent ... | 987654345678</div>
<div>27. März 2024</div>
</div>
<div>
<ui-checkbox>
<input type="checkbox" />
</ui-checkbox>
</div>
</ui-item-row>

View File

@@ -0,0 +1,20 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { ReceiptItem } from '@feature/return/services';
import { isaArtikelTaschenbuch } from '@isa/icons';
import { ProductImageDirective } from '@isa/shared/product-image';
import { CheckboxComponent } from '@isa/ui/input-controls';
import { ItemRowComponent } from '@isa/ui/item-rows';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
@Component({
selector: 'lib-return-details-order-group-item',
templateUrl: './return-details-order-group-item.component.html',
styleUrls: ['./return-details-order-group-item.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [ItemRowComponent, ProductImageDirective, CheckboxComponent, NgIconComponent],
providers: [provideIcons({ isaArtikelTaschenbuch })],
})
export class ReturnDetailsOrderGroupItemComponent {
item = input.required<ReceiptItem>();
}

View File

@@ -0,0 +1,5 @@
<ui-toolbar size="small" class="justify-self-stretch">
<div class="isa-text-body-2-bold">{{ items().length }} Artikel</div>
<div class="flex-grow"></div>
<button type="button" uiTextButton color="strong" size="small">Alles auswählen</button>
</ui-toolbar>

View File

@@ -0,0 +1,6 @@
:host {
display: flex;
flex-direction: column;
align-items: stretch;
width: 100%;
}

View File

@@ -0,0 +1,16 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { ReceiptItem } from '@feature/return/services';
import { TextButtonComponent } from '@isa/ui/buttons';
import { ToolbarComponent } from '@isa/ui/toolbar';
@Component({
selector: 'lib-return-details-order-group',
templateUrl: './return-details-order-group.component.html',
styleUrls: ['./return-details-order-group.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [ToolbarComponent, TextButtonComponent],
})
export class ReturnDetailsOrderGroupComponent {
items = input.required<ReceiptItem[]>();
}

View File

@@ -1,3 +0,0 @@
:host {
@apply flex flex-col gap-4 w-full justify-start items-center overflow-y-scroll;
}

View File

@@ -1 +1,10 @@
<h1>Details page works! {{ itemId() }}</h1>
@let receipt = receiptResult().data;
@if (receipt) {
<lib-return-details-header [buyer]="receipt.buyer"></lib-return-details-header>
<lib-return-details-data [receipt]="receipt"></lib-return-details-data>
<lib-return-details-order-group [items]="receipt.items"></lib-return-details-order-group>
@for (item of receipt.items; track item.id) {
<lib-return-details-order-group-item [item]="item"></lib-return-details-order-group-item>
}
}

View File

@@ -0,0 +1,11 @@
:host {
display: flex;
padding: 1rem 1.5rem;
flex-direction: column;
align-items: flex-start;
gap: 1.5rem;
align-self: stretch;
border-radius: 1rem;
background: #fff;
}

View File

@@ -4,37 +4,69 @@ import {
computed,
effect,
inject,
} from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { toSignal } from "@angular/core/rxjs-interop";
untracked,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { toSignal } from '@angular/core/rxjs-interop';
import {
ReturnDetailsDataComponent,
ReturnDetailsHeaderComponent,
ReturnDetailsOrderGroupComponent,
ReturnDetailsOrderGroupItemComponent,
} from '@feature/return/containers';
import { z } from 'zod';
import { ReceiptItem, ReturnDetailsStore } from '@feature/return/services';
import { Dictionary, groupBy } from 'lodash';
@Component({
selector: "lib-return-details-page",
templateUrl: "./details-page.component.html",
styleUrls: ["./details-page.component.css"],
selector: 'lib-return-details-page',
templateUrl: './details-page.component.html',
styleUrls: ['./details-page.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [
ReturnDetailsHeaderComponent,
ReturnDetailsDataComponent,
ReturnDetailsOrderGroupComponent,
ReturnDetailsOrderGroupItemComponent,
],
})
export class DetailsPageComponent {
private _params = toSignal(inject(ActivatedRoute).params);
itemId = computed(() => {
const params = this._params();
#returnDetailsStore = inject(ReturnDetailsStore);
itemId = computed<number>(() => {
const params = this._params();
if (params) {
return params["itemId"];
return z.coerce.number().parse(params['receiptId']);
}
throw new Error('No receiptId found in route params');
});
receiptResult = computed(() => {
const itemId = this.itemId();
return this.#returnDetailsStore.entityMap()[itemId];
});
itemsGroupedByOrder = computed<Dictionary<ReceiptItem[]>>(() => {
const receipt = this.receiptResult().data;
if (!receipt) {
return {};
}
return groupBy(
receipt.items.flatMap((i) => i.data),
'orderNumber',
);
});
constructor() {
effect(() => {
const itemId = this.itemId();
if (!itemId) {
console.log("No itemId");
return;
if (itemId) {
untracked(() => this.#returnDetailsStore.fetch({ receiptId: itemId }));
}
console.log(itemId);
});
}
}

View File

@@ -18,10 +18,8 @@
<lib-return-results-list>
@for (item of items(); track item.id) {
@defer (on viewport) {
<a [routerLink]="['../', 'details', item.id]" class="w-full">
<lib-return-results-item-list
[item]="item"
></lib-return-results-item-list>
<a [routerLink]="['../', 'receipt', item.id]" class="w-full">
<lib-return-results-item-list [item]="item"></lib-return-results-item-list>
</a>
} @placeholder {
<ui-icon-button [pending]="true" [color]="'tertiary'"></ui-icon-button>

View File

@@ -1,20 +1,19 @@
import { Routes } from "@angular/router";
import { ReturnPagesComponent } from "./return-pages.component";
import { querySettingsResolverFn } from "./resolvers/query-settings.resolver-fn";
import { MainPageComponent } from "./main/main-page.component";
import { ResultsPageComponent } from "./results/results-page.component";
import { DetailsPageComponent } from "./details/details-page.component";
import { Routes } from '@angular/router';
import { ReturnPagesComponent } from './return-pages.component';
import { querySettingsResolverFn } from './resolvers/query-settings.resolver-fn';
import { MainPageComponent } from './main/main-page.component';
import { ResultsPageComponent } from './results/results-page.component';
import { DetailsPageComponent } from './details/details-page.component';
export const routes: Routes = [
{
path: "",
path: '',
component: ReturnPagesComponent,
resolve: { querySettings: querySettingsResolverFn },
children: [
{ path: "", component: MainPageComponent },
{ path: "results", component: ResultsPageComponent },
{ path: "details", component: DetailsPageComponent },
{ path: "details/:itemId", component: DetailsPageComponent },
{ path: '', component: MainPageComponent },
{ path: 'results', component: ResultsPageComponent },
{ path: 'receipt/:receiptId', component: DetailsPageComponent },
],
},
];

View File

@@ -1,3 +1,5 @@
export * from "./lib/return-search.service";
export * from "./lib/return-search.store";
export * from "./lib/schemas";
export * from './lib/return-search.service';
export * from './lib/return-search.store';
export * from './lib/return-details.service';
export * from './lib/return-details.store';
export * from './lib/schemas';

View File

@@ -0,0 +1,37 @@
import { inject, Injectable } from '@angular/core';
import {
FetchReturnDetails,
FetchReturnDetailsSchema,
} from './schemas/fetch-return-details.schema';
import { map, Observable, throwError } from 'rxjs';
import { ReceiptService } from '@generated/swagger/oms-api';
import { safeParse } from '@isa/utils/z-safe-parse';
import {
ResponseArgsOfReceipt,
ResponseArgsOfReceiptSchema,
} from './schemas/response-args-of-receipt.schema';
@Injectable({ providedIn: 'root' })
export class ReturnDetailsService {
#receiptService = inject(ReceiptService);
fetchReturnDetails(params: FetchReturnDetails): Observable<ResponseArgsOfReceipt> {
try {
const parsed = FetchReturnDetailsSchema.parse(params);
return this.#receiptService
.ReceiptGetReceipt({ receiptId: parsed.receiptId, eagerLoading: 2 })
.pipe(
map((res) => {
if (res.error || !res.result) {
throw new Error('Failed to fetch return details');
}
return safeParse(ResponseArgsOfReceiptSchema, res);
}),
);
} catch (error) {
return throwError(() => error);
}
}
}

View File

@@ -0,0 +1,67 @@
import { patchState, signalStore, type, withMethods } from '@ngrx/signals';
import { addEntity, entityConfig, updateEntity, withEntities } from '@ngrx/signals/entities';
import { Result, ResultStatus } from '@isa/common/result';
import { Receipt } from './schemas';
import { rxMethod } from '@ngrx/signals/rxjs-interop';
import { pipe, switchMap, tap } from 'rxjs';
import { inject } from '@angular/core';
import { ReturnDetailsService } from './return-details.service';
import { tapResponse } from '@ngrx/operators';
export type ReturnResult = Result<Receipt | undefined> & { id: number };
const initialEntity: Omit<ReturnResult, 'id'> = {
data: undefined,
status: ResultStatus.Idle,
};
const config = entityConfig({
entity: type<ReturnResult>(),
selectId: (entity) => entity.id,
});
export const ReturnDetailsStore = signalStore(
{ providedIn: 'root' },
withEntities(config),
withMethods((store) => ({
_beforeFetch(receiptId: number) {
let entity: ReturnResult | undefined = store.entityMap()[receiptId];
if (!entity) {
entity = { ...initialEntity, id: receiptId, status: ResultStatus.Pending };
}
patchState(store, addEntity(entity, config));
return entity;
},
_fetchSuccess(receiptId: number, data: Receipt) {
patchState(
store,
updateEntity({ id: receiptId, changes: { data, status: ResultStatus.Success } }, config),
);
},
_fetchError(receiptId: number, error: unknown) {
patchState(
store,
updateEntity({ id: receiptId, changes: { error, status: ResultStatus.Error } }, config),
);
},
})),
withMethods((store, returnDetailsService = inject(ReturnDetailsService)) => ({
fetch: rxMethod<{ receiptId: number }>(
pipe(
tap(({ receiptId }) => store._beforeFetch(receiptId)),
switchMap(({ receiptId }) =>
returnDetailsService.fetchReturnDetails({ receiptId }).pipe(
tapResponse({
next(value) {
store._fetchSuccess(receiptId, value.result);
},
error(error) {
store._fetchError(receiptId, error);
},
}),
),
),
),
),
})),
);

View File

@@ -0,0 +1,26 @@
import { z } from 'zod';
import { GeoLocationSchema } from './geo-location.schema';
export const AddressSchema = z
.object({
apartment: z.string().optional().describe('Apartment'),
careOf: z.string().optional().describe('c/o, zu Händen von'),
city: z.string().optional().describe('Ort'),
country: z
.string()
.regex(/^[A-Z]{3}$/)
.optional()
.describe('Land (ISO3166 A 3)'),
district: z.string().optional().describe('Stadtteil, District'),
geoLocation: GeoLocationSchema.optional().describe('Orts-Koordinaten'),
info: z.string().optional().describe('Adresszusatz'),
po: z.string().optional().describe('Postfach'),
region: z.string().optional().describe('Region'),
state: z.string().optional().describe('Bundesland, Bundesstaat, Kanton, ...'),
street: z.string().optional().describe('Straße'),
streetNumber: z.string().optional().describe('Hausnummer'),
zipCode: z.string().optional().describe('Postleitzahl'),
})
.describe('AddressDTO');
export type Address = z.infer<typeof AddressSchema>;

View File

@@ -0,0 +1,11 @@
import { z } from 'zod';
export enum AddressType {
NotSet = 0,
Billing = 1,
Shipping = 2,
Buyer = 4,
PackageStation = 8,
}
export const AddressTypeSchema = z.nativeEnum(AddressType);

View File

@@ -0,0 +1,10 @@
import { z } from 'zod';
import { CommunicationDetailsSchema } from './communication-details.schemas';
export const BuyerSchema = z.object({
firstName: z.string(),
lastName: z.string(),
communicationDetails: CommunicationDetailsSchema,
});
export type Buyer = z.infer<typeof BuyerSchema>;

View File

@@ -0,0 +1,9 @@
import { z } from 'zod';
export const CommunicationDetailsSchema = z.object({
email: z.string().email(),
mobile: z.string().optional(),
phone: z.string().optional(),
});
export type CommunicationDetails = z.infer<typeof CommunicationDetailsSchema>;

View File

@@ -0,0 +1,7 @@
import { z } from 'zod';
export const FetchReturnDetailsSchema = z.object({
receiptId: z.number(),
});
export type FetchReturnDetails = z.infer<typeof FetchReturnDetailsSchema>;

View File

@@ -0,0 +1,3 @@
import { z } from 'zod';
export const GeoLocationSchema = z.object({});

View File

@@ -1,3 +1,21 @@
export * from "./receipt-list-item.schema";
export * from "./response-args.schema";
export * from "./query-token.schema";
export * from './address.schema';
export * from './address.type.schema';
export * from './buyer.schema';
export * from './communication-details.schemas';
export * from './fetch-return-details.schema';
export * from './geo-location.schema';
export * from './label.schema';
export * from './order.schema';
export * from './payer-2.schema';
export * from './payment-info.schema';
export * from './payment.schema';
export * from './query-token.schema';
export * from './receipt-delivery-type.schema';
export * from './receipt-item.schema';
export * from './receipt-list-item.schema';
export * from './receipt-type.schema';
export * from './receipt.schema';
export * from './response-args-of-receipt.schema';
export * from './response-args.schema';
export * from './shipping-address-2.schema';
export * from './shipping-type.schema';

View File

@@ -0,0 +1,3 @@
import { z } from 'zod';
export const LabelSchema = z.object({});

View File

@@ -0,0 +1,3 @@
import { z } from 'zod';
export const OrderSchema = z.object({});

View File

@@ -0,0 +1,3 @@
import { z } from 'zod';
export const Payer2Schema = z.object({});

View File

@@ -0,0 +1,3 @@
import { z } from 'zod';
export const PaymentInfoSchema = z.object({});

View File

@@ -0,0 +1,3 @@
import { z } from 'zod';
export const Payment = z.object({});

View File

@@ -0,0 +1,7 @@
import { z } from 'zod';
export enum ReceiptDeliveryType {
NotSet = 0,
}
export const ReceiptDeliveryTypeSchema = z.nativeEnum(ReceiptDeliveryType);

View File

@@ -0,0 +1,7 @@
import { z } from 'zod';
export const ReceiptItemSchema = z.object({
id: z.number(),
});
export type ReceiptItem = z.infer<typeof ReceiptItemSchema>;

View File

@@ -1,5 +1,7 @@
import { z } from "zod";
import { ResponseArgsSchema } from "./response-args.schema";
import { z } from 'zod';
import { ResponseArgsSchema } from './response-args.schema';
import { CommunicationDetailsSchema } from './communication-details.schemas';
import { AddressSchema } from './address.schema';
// PersonNamesDTO
export const PersonSchema = z.object({
@@ -9,30 +11,13 @@ export const PersonSchema = z.object({
title: z.string().optional(),
});
// AddressDTO
export const AddressSchema = z.object({
city: z.string(),
country: z.string(),
info: z.string().optional(),
street: z.string(),
streetNumber: z.string(),
zipCode: z.string(),
});
// OrganisationDTO
export const OrganisationSchema = z.object({
name: z.string(),
});
// CommunicationDetailsDTO
export const CommunicationDetailsSchema = z.object({
email: z.string().optional(),
mobile: z.string().optional(),
phone: z.string().optional(),
});
// AddresseeDTO
export const BillingSchema = z.object({
export const AddresseeSchema = z.object({
person: PersonSchema,
address: AddressSchema.optional(),
organisation: OrganisationSchema.optional(),
@@ -46,7 +31,7 @@ export const ReceiptListItemSchema = z.object({
orderNumber: z.string(),
receiptNumber: z.string(),
printedDate: z.string(),
billing: BillingSchema,
billing: AddresseeSchema,
});
// ListResponseArgsOfReceiptListItemDTO

View File

@@ -0,0 +1,19 @@
import { z } from 'zod';
export enum ReceiptType {
NotSet = 0,
ShippingNote = 1,
CreditNote = 2,
CollectiveShippingNote = 4,
CollectiveCreditNote = 8,
BonusCardCollectiveShippingNote = 16,
BonusCardCollectiveCreditNote = 32,
PaymentReceipt = 64,
Invoice = 128,
CollectiveInvoice = 256,
ProformaInvoice = 512,
CashReceipt = 1024,
ReturnReceipt = 2048,
}
export const ReceiptTypeSchema = z.nativeEnum(ReceiptType);

View File

@@ -0,0 +1,14 @@
import { z } from 'zod';
import { BuyerSchema } from './buyer.schema';
import { entityContainerSchema } from '@isa/common/result';
import { ReceiptItemSchema } from './receipt-item.schema';
import { ReceiptTypeSchema } from './receipt-type.schema';
export const ReceiptSchema = z.object({
id: z.number(),
receiptType: ReceiptTypeSchema,
buyer: BuyerSchema,
items: entityContainerSchema(ReceiptItemSchema).array(),
});
export type Receipt = z.infer<typeof ReceiptSchema>;

View File

@@ -0,0 +1,7 @@
import { ResponseArgs } from '@isa/common/result';
import { ReceiptSchema } from './receipt.schema';
import { z } from 'zod';
export const ResponseArgsOfReceiptSchema = ResponseArgs(ReceiptSchema);
export type ResponseArgsOfReceipt = z.infer<typeof ResponseArgsOfReceiptSchema>;

View File

@@ -0,0 +1,3 @@
import { z } from 'zod';
export const ShippingAddress2Schema = z.object({});

View File

@@ -0,0 +1,7 @@
export enum ShippingType {
NotSet = 0,
PostCard = 1,
Letter = 2,
LargeLetter = 4,
BookRate = 8,
}

View File

@@ -0,0 +1,7 @@
# oms-utils-translation
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test oms-utils-translation` to execute the unit tests.

View File

@@ -0,0 +1,34 @@
import nx from '@nx/eslint-plugin';
import baseConfig from '../../../../eslint.config.mjs';
export default [
...baseConfig,
...nx.configs['flat/angular'],
...nx.configs['flat/angular-template'],
{
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'oms',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'oms',
style: 'kebab-case',
},
],
},
},
{
files: ['**/*.html'],
// Override or add rules here
rules: {},
},
];

View File

@@ -0,0 +1,21 @@
export default {
displayName: 'oms-utils-translation',
preset: '../../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../../coverage/libs/oms/utils/translation',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
};

View File

@@ -0,0 +1,20 @@
{
"name": "oms-utils-translation",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/oms/utils/translation/src",
"prefix": "lib",
"projectType": "library",
"tags": [],
"targets": {
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/oms/utils/translation/jest.config.ts"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}

View File

@@ -0,0 +1 @@
export * from './lib/receipt-type';

View File

@@ -0,0 +1,2 @@
export * from './receipt-type-translation.pipe';
export * from './receipt-type-translation.service';

View File

@@ -0,0 +1,14 @@
import { inject, Pipe, PipeTransform } from '@angular/core';
import { ReceiptType } from '@feature/return/services';
import { ReceiptTypeTranslationService } from './receipt-type-translation.service';
@Pipe({
name: 'omsReceiptTypeTranslation',
})
export class ReceiptTypeTranslationPipe implements PipeTransform {
readonly translationService = inject(ReceiptTypeTranslationService);
transform(value: ReceiptType = ReceiptType.NotSet): string {
return this.translationService.translate(value);
}
}

View File

@@ -0,0 +1,48 @@
import { inject, Injectable, InjectionToken, Provider } from '@angular/core';
import { ReceiptType } from '@feature/return/services';
const receiptTypeTranslations = {
[ReceiptType.NotSet]: 'Nicht gesetzt',
[ReceiptType.ShippingNote]: 'Lieferschein',
[ReceiptType.CreditNote]: 'Gutschrift',
[ReceiptType.CollectiveShippingNote]: 'Sammellieferschein',
[ReceiptType.CollectiveCreditNote]: 'Sammelgutschrift',
[ReceiptType.BonusCardCollectiveShippingNote]: 'Bonuskarte Sammellieferschein',
[ReceiptType.BonusCardCollectiveCreditNote]: 'Bonuskarte Sammelgutschrift',
[ReceiptType.PaymentReceipt]: 'Zahlungsbeleg',
[ReceiptType.Invoice]: 'Rechnung',
[ReceiptType.CollectiveInvoice]: 'Sammelrechnung',
[ReceiptType.ProformaInvoice]: 'Proforma-Rechnung',
[ReceiptType.CashReceipt]: 'Kassenbeleg',
[ReceiptType.ReturnReceipt]: 'Retourenbeleg',
};
export type ReceiptTypeTranslation = typeof receiptTypeTranslations;
export const RECEIPT_TYPE_TRANSLATION = new InjectionToken<ReceiptTypeTranslation>(
'RECEIPT_TYPE_TRANSLATION',
{
factory() {
return receiptTypeTranslations;
},
},
);
@Injectable({ providedIn: 'root' })
export class ReceiptTypeTranslationService {
#translation = inject(RECEIPT_TYPE_TRANSLATION);
translate(type: ReceiptType): string {
return this.#translation[type] ?? ReceiptType[type];
}
}
export function provideReceiptTypeTranslation(translation: ReceiptTypeTranslation): Provider[] {
return [
{
provide: RECEIPT_TYPE_TRANSLATION,
useValue: translation,
},
ReceiptTypeTranslationService,
];
}

View File

@@ -0,0 +1,6 @@
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
setupZoneTestEnv({
errorOnUnknownElements: true,
errorOnUnknownProperties: true,
});

View File

@@ -0,0 +1,28 @@
{
"compilerOptions": {
"target": "es2022",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"extends": "../../../../tsconfig.base.json",
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

View File

@@ -0,0 +1,17 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": []
},
"exclude": [
"src/**/*.spec.ts",
"src/test-setup.ts",
"jest.config.ts",
"src/**/*.test.ts"
],
"include": ["src/**/*.ts"]
}

View File

@@ -0,0 +1,16 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"module": "commonjs",
"target": "es2016",
"types": ["jest", "node"]
},
"files": ["src/test-setup.ts"],
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

View File

@@ -1,6 +1,6 @@
<ng-content></ng-content>
<ng-content select="[uiInfoButtonLabel]"></ng-content>
@if (pending()) {
<ng-icon class="animate-spin" name="isaLoading"></ng-icon>
} @else {
<ng-content select="ng-icon"></ng-content>
<ng-content select="ng-icon[uiInfoButtonIcon]"></ng-content>
}

View File

@@ -12,18 +12,26 @@
justify-content: center;
align-items: center;
@apply rounded-full bg-isa-neutral-300 size-12;
@apply rounded-full size-12;
}
ng-icon[uiInfoButtonLabel] {
@apply -mx-3;
}
ng-icon[uiInfoButtonIcon] {
@apply bg-isa-neutral-300;
}
&:hover {
ng-icon {
ng-icon[uiInfoButtonIcon] {
@apply bg-isa-neutral-400;
}
}
&:active,
&.active {
ng-icon {
ng-icon[uiInfoButtonIcon] {
@apply text-isa-white bg-isa-neutral-500;
}
}

View File

@@ -4,6 +4,7 @@
justify-content: center;
align-items: center;
text-align: center;
gap: 0.5rem;
font-style: normal;
font-weight: 700;
@@ -48,16 +49,19 @@
}
.ui-text-button__small {
padding: 0.375rem 0.75rem;
font-size: 0.75rem;
line-height: 1rem; /* 133.333% */
}
.ui-text-button__medium {
padding: 0.5rem 1.25rem;
font-size: 0.875rem;
line-height: 1.25rem; /* 142.857% */
}
.ui-text-button__large {
padding: 0.75rem 1.5rem;
font-size: 1rem;
line-height: 1.5rem; /* 150% */
}

View File

@@ -1,2 +1,3 @@
export * from './lib/client-row/client-row.component';
export * from './lib/item-row/item-row.component';
export * from './lib/item-row-data/item-row-data.component';

View File

@@ -0,0 +1 @@
<ng-content></ng-content>

View File

@@ -1,2 +1,3 @@
.ui-item-row {
@apply grid grid-cols-[3.5rem,repeat(2,1fr),auto] gap-4;
}

View File

@@ -1,9 +1,10 @@
.ui-toolbar {
display: flex;
height: 3.25rem;
padding: 1rem 1.5rem;
padding-left: 1.5rem;
padding-right: 1.5rem;
justify-content: flex-start;
align-items: flex-start;
align-items: center;
gap: 2.5rem;
@apply rounded-full bg-isa-neutral-400;

View File

@@ -44,10 +44,12 @@
],
"@generated/swagger/wws-api": ["generated/swagger/wws-api/src/index.ts"],
"@hub/*": ["apps/isa-app/src/hub/*/index.ts"],
"@isa/common/result": ["libs/common/result/src/index.ts"],
"@isa/core/config": ["libs/core/config/src/index.ts"],
"@isa/core/process": ["libs/core/process/src/index.ts"],
"@isa/core/scanner": ["libs/core/scanner/src/index.ts"],
"@isa/icons": ["libs/icons/src/index.ts"],
"@isa/oms/utils/translation": ["libs/oms/utils/translation/src/index.ts"],
"@isa/shared/filter": ["libs/shared/filter/src/index.ts"],
"@isa/shared/product-image": ["libs/shared/product-image/src/index.ts"],
"@isa/ui/buttons": ["libs/ui/buttons/src/index.ts"],
@@ -55,8 +57,8 @@
"@isa/ui/input-controls": ["libs/ui/input-controls/src/index.ts"],
"@isa/ui/item-rows": ["libs/ui/item-rows/src/index.ts"],
"@isa/ui/search-bar": ["libs/ui/search-bar/src/index.ts"],
"@isa/utils/z-safe-parse": ["libs/utils/z-safe-parse/src/index.ts"],
"@isa/ui/toolbar": ["libs/ui/toolbar/src/index.ts"],
"@isa/utils/z-safe-parse": ["libs/utils/z-safe-parse/src/index.ts"],
"@modal/*": ["apps/isa-app/src/modal/*/index.ts"],
"@page/*": ["apps/isa-app/src/page/*/index.ts"],
"@shared/*": ["apps/isa-app/src/shared/*/index.ts"],