Merge branch 'tracking' into nx-workspace-migration

This commit is contained in:
Lorenz Hilpert
2025-02-24 15:25:16 +01:00
77 changed files with 1318 additions and 473 deletions

2
.gitignore vendored
View File

@@ -1,5 +1,7 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
.matomo
# compiled output
/dist
/tmp

View File

@@ -3,8 +3,7 @@
/dist
/coverage
/helmvalues
/apps/swagger
/ng-swagger-gen
/generated
*.json
*.yml

View File

@@ -1,5 +1,34 @@
{
"singleQuote": true,
"printWidth": 140
}
"singleQuote": true,
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"trailingComma": "all",
"bracketSpacing": true,
"arrowParens": "always",
"htmlWhitespaceSensitivity": "ignore",
"endOfLine": "lf",
"overrides": [
{
"files": "*.html",
"options": {
"parser": "angular",
"printWidth": 140
}
},
{
"files": ["*.ts", "*.tsx"],
"options": {
"parser": "typescript"
}
},
{
"files": ["*.scss", "*.css"],
"options": {
"parser": "scss",
"singleQuote": false
}
}
]
}

View File

@@ -22,6 +22,7 @@ import { BranchSectionResolver, CustomerSectionResolver, ProcessIdResolver } fro
import { TokenLoginComponent, TokenLoginModule } from './token-login';
import { ProcessIdGuard } from './guards/process-id.guard';
import { ActivateProcessIdGuard, ActivateProcessIdWithConfigKeyGuard } from './guards/activate-process-id.guard';
import { MatomoRouteData } from 'ngx-matomo-client';
const routes: Routes = [
{
@@ -42,6 +43,11 @@ const routes: Routes = [
{
path: 'dashboard',
loadChildren: () => import('@page/dashboard').then((m) => m.DashboardModule),
data: {
matomo: {
title: 'Dashboard',
} as MatomoRouteData,
},
},
{
path: 'product',

View File

@@ -2,7 +2,6 @@ import { NgModule } from '@angular/core';
import { EffectsModule } from '@ngrx/effects';
import { ActionReducer, MetaReducer, StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { storeFreeze } from 'ngrx-store-freeze';
import packageInfo from 'packageJson';
import { environment } from '../environments/environment';
import { RootStateService } from './store/root-state.service';
@@ -22,7 +21,9 @@ export function storeInLocalStorage(reducer: ActionReducer<any>): ActionReducer<
};
}
export const metaReducers: MetaReducer<RootState>[] = !environment.production ? [storeFreeze, storeInLocalStorage] : [storeInLocalStorage];
export const metaReducers: MetaReducer<RootState>[] = !environment.production
? [storeInLocalStorage]
: [storeInLocalStorage];
@NgModule({
imports: [

View File

@@ -41,6 +41,8 @@ import { NgIconsModule } from '@ng-icons/core';
import { matClose, matWifi, matWifiOff } from '@ng-icons/material-icons/baseline';
import { NetworkStatusService } from './services/network-status.service';
import { firstValueFrom } from 'rxjs';
import { provideMatomo } from 'ngx-matomo-client';
import { withRouter, withRouteData } from 'ngx-matomo-client';
registerLocaleData(localeDe, localeDeExtra);
registerLocaleData(localeDe, 'de', localeDeExtra);
@@ -184,6 +186,7 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
},
{ provide: LOCALE_ID, useValue: 'de-DE' },
provideHttpClient(withInterceptorsFromDi()),
provideMatomo({ trackerUrl: 'https://matomo.paragon-data.net', siteId: '1' }, withRouter(), withRouteData()),
],
})
export class AppModule {}

View File

@@ -151,31 +151,31 @@ export class DomainTaskCalendarService {
*/
const processingStatusList: ProcessingStatusList = [];
if (!!(processingStatus & 1)) {
if (processingStatus & 1) {
processingStatusList.push('Approved');
}
if (!!(processingStatus & 2)) {
if (processingStatus & 2) {
processingStatusList.push('InProcess');
}
if (!!(processingStatus & 4)) {
if (processingStatus & 4) {
processingStatusList.push('Completed');
}
if (!!(processingStatus & 8)) {
if (processingStatus & 8) {
processingStatusList.push('Overdue');
}
if (!!(processingStatus & 16)) {
if (processingStatus & 16) {
processingStatusList.push('Archived');
}
if (!!(processingStatus & 32)) {
if (processingStatus & 32) {
processingStatusList.push('Removed');
}
if (!!(processingStatus & 64)) {
if (processingStatus & 64) {
processingStatusList.push('Uncompleted');
}
@@ -195,7 +195,7 @@ export class DomainTaskCalendarService {
}) {
const type = this.getInfoType({ hasTask, publicationDate, announcementDate });
if (type === ('Info' || 'PreInfo')) {
if (type === 'Info' || type === 'PreInfo') {
return '#8A949E';
}

View File

@@ -99,7 +99,7 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
// Logik nur beim Hinzufügen über Kaufoptionen, da über Ändern im Warenkorb die Info fehlt ob das jeweilige ShoppingCartItem ein Archivartikel ist oder nicht
const features = this.item?.features as KeyValueDTOOfStringAndString[];
if (!!features && Array.isArray(features)) {
const isArchive = !!features?.find((feature) => feature?.enabled === true && feature?.key === 'ARC') ?? false;
const isArchive = !!features?.find((feature) => feature?.enabled === true && feature?.key === 'ARC');
return isArchive ? !price?.value?.value || price?.vat === undefined : false;
}
return false;
@@ -110,7 +110,9 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
priceVat$ = this.price$.pipe(map((price) => price?.vat?.vatType));
canAddResult$ = this.item$.pipe(switchMap((item) => this._store.getCanAddResultForItemAndCurrentPurchaseOption$(item.id)));
canAddResult$ = this.item$.pipe(
switchMap((item) => this._store.getCanAddResultForItemAndCurrentPurchaseOption$(item.id)),
);
canEditPrice$ = this.item$.pipe(
switchMap((item) => combineLatest([this.canAddResult$, this._store.getCanEditPrice$(item.id)])),
@@ -173,10 +175,13 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
// Einstieg über Warenkorb - Da wir hier ein ShoppingCartItemDTO haben werden hier die Katalogdaten neu gefetched und eine Katalogavailability drangehängt
if (isShoppingCartItemDTO(this.item, this._store.type)) {
const catalogAvailabilities = this._store.availabilities?.filter((availability) => availability?.purchaseOption === 'catalog');
const catalogAvailabilities = this._store.availabilities?.filter(
(availability) => availability?.purchaseOption === 'catalog',
);
// #4813 Fix: Hier muss als Kriterium auf EAN statt itemId verglichen werden, denn ein ShoppingCartItemDTO (this.item) hat eine andere ItemId wie das ItemDTO (availability.itemId)
const firstDayOfSale = catalogAvailabilities?.find((availability) => this.item?.product?.ean === availability?.ean)?.data
?.firstDayOfSale;
const firstDayOfSale = catalogAvailabilities?.find(
(availability) => this.item?.product?.ean === availability?.ean,
)?.data?.firstDayOfSale;
return this.firstDayOfSaleBiggerThanToday(firstDayOfSale);
}
@@ -281,42 +286,46 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
}
});
const valueChangesSub = combineLatest([this.canEditPrice$, this.priceFormControl.valueChanges]).subscribe(([canEditPrice, value]) => {
if (!canEditPrice) {
return;
}
const valueChangesSub = combineLatest([this.canEditPrice$, this.priceFormControl.valueChanges]).subscribe(
([canEditPrice, value]) => {
if (!canEditPrice) {
return;
}
const price = this._store.getPrice(this.item.id);
const parsedPrice = this.parsePrice(value);
const price = this._store.getPrice(this.item.id);
const parsedPrice = this.parsePrice(value);
if (!parsedPrice) {
this._store.setPrice(this.item.id, null);
return;
}
if (!parsedPrice) {
this._store.setPrice(this.item.id, null);
return;
}
if (price[this.item.id] !== parsedPrice) {
this._store.setPrice(this.item.id, this.parsePrice(value));
}
});
if (price[this.item.id] !== parsedPrice) {
this._store.setPrice(this.item.id, this.parsePrice(value));
}
},
);
this._subscriptions.add(sub);
this._subscriptions.add(valueChangesSub);
}
initVatSubscription() {
const valueChangesSub = this.manualVatFormControl.valueChanges.pipe(withLatestFrom(this.vats$)).subscribe(([formVatType, vats]) => {
const price = this._store.getPrice(this.item.id);
const valueChangesSub = this.manualVatFormControl.valueChanges
.pipe(withLatestFrom(this.vats$))
.subscribe(([formVatType, vats]) => {
const price = this._store.getPrice(this.item.id);
const vat = vats.find((vat) => vat?.vatType === Number(formVatType));
const vat = vats.find((vat) => vat?.vatType === Number(formVatType));
if (!vat) {
this._store.setVat(this.item.id, null);
return;
}
if (!vat) {
this._store.setVat(this.item.id, null);
return;
}
if (price[this.item.id]?.vat?.vatType !== vat?.vatType) {
this._store.setVat(this.item.id, vat);
}
});
if (price[this.item.id]?.vat?.vatType !== vat?.vatType) {
this._store.setVat(this.item.id, vat);
}
});
this._subscriptions.add(valueChangesSub);
}

View File

@@ -53,14 +53,16 @@ export class PurchaseOptionsModalComponent implements OnInit, OnDestroy {
// Ticket #4074 analog zu Ticket #2244
// Ob Archivartikel kann nur über Kaufoptionen herausgefunden werden, nicht über Ändern im Warenkorb da am ShoppingCartItem das Archivartikel Feature fehlt
if (!!features && Array.isArray(features)) {
isArchive = !!features?.find((feature) => feature?.enabled === true && feature?.key === 'ARC') ?? false;
isArchive = !!features?.find((feature) => feature?.enabled === true && feature?.key === 'ARC');
}
return zip(
this.store
?.getPrice$(item?.id)
.pipe(
map((price) =>
isArchive ? !!price?.value?.value && price?.vat !== undefined && price?.vat?.value !== undefined : !!price?.value?.value,
isArchive
? !!price?.value?.value && price?.vat !== undefined && price?.vat?.value !== undefined
: !!price?.value?.value,
),
),
);
@@ -117,7 +119,9 @@ export class PurchaseOptionsModalComponent implements OnInit, OnDestroy {
itemTrackBy: TrackByFunction<Item> = (_, item) => item.id;
showOption(option: PurchaseOption): boolean {
return this._uiModalRef.data?.preSelectOption?.showOptionOnly ? this._uiModalRef.data?.preSelectOption?.option === option : true;
return this._uiModalRef.data?.preSelectOption?.showOptionOnly
? this._uiModalRef.data?.preSelectOption?.option === option
: true;
}
async save(action: string) {

View File

@@ -9,7 +9,7 @@
<ng-container *ngIf="isAvailable$ | async; else notAvailable">
Archivtitel. Wird nicht mehr gedruckt. Artikel ist bestellbar, weil lieferbar.
</ng-container>
<ng-template #notAvailable> Archivtitel. Wird nicht mehr gedruckt. Nicht bestellbar. </ng-template>
<ng-template #notAvailable>Archivtitel. Wird nicht mehr gedruckt. Nicht bestellbar.</ng-template>
</ui-tooltip>
</button>
</div>
@@ -20,9 +20,11 @@
>
<img src="/assets/images/bookmark_subscription.svg" alt="Fortsetzungsartikel Badge" />
</button>
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #subscribtionTooltip [closeable]="true"
>Artikel ist ein Fortsetzungsartikel,<br />
Artikel muss über eine Aboabteilung<br />
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #subscribtionTooltip [closeable]="true">
Artikel ist ein Fortsetzungsartikel,
<br />
Artikel muss über eine Aboabteilung
<br />
bestellt werden.
</ui-tooltip>
</div>
@@ -163,7 +165,7 @@
*ngIf="store.isTakeAwayAvailabilityAvailable$ | async"
class="w-[2.25rem] h-[2.25rem] bg-[#D8DFE5] rounded-[5px_5px_0px_5px] flex items-center justify-center"
>
<ui-icon class="mx-1" icon="shopping_bag" size="18px"> </ui-icon>
<ui-icon class="mx-1" icon="shopping_bag" size="18px"></ui-icon>
</div>
</ng-template>
@@ -209,7 +211,7 @@
*ngIf="showDeliveryB2BTruck$ | async"
class="w-[2.25rem] h-[2.25rem] bg-[#D8DFE5] rounded-[5px_5px_0px_5px] flex items-center justify-center ml-3"
>
<ui-icon class="-mb-[0.625rem] -mt-[0.625rem] mx-1" icon="truck_b2b" size="30px"> </ui-icon>
<ui-icon class="-mb-[0.625rem] -mt-[0.625rem] mx-1" icon="truck_b2b" size="30px"></ui-icon>
</div>
</ng-template>
@@ -312,14 +314,15 @@
</div>
<hr class="bg-[#E6EFF9] border-t-2 my-3" />
<div #description class="page-article-details__product-description flex flex-col flex-grow mb-6" *ngIf="item.texts?.length > 0">
<page-article-details-text class="block box-border" [text]="item.texts[0]"> </page-article-details-text>
<page-article-details-text class="block box-border" [text]="item.texts[0]"></page-article-details-text>
<div class="box-border">
<button
class="font-bold flex flex-row text-[#0556B4] items-center mt-2"
*ngIf="!showMore && item?.texts?.length > 1"
(click)="showMore = !showMore"
>
Mehr <ui-icon class="ml-2" size="15px" icon="arrow"></ui-icon>
Mehr
<ui-icon class="ml-2" size="15px" icon="arrow"></ui-icon>
</button>
</div>
@@ -330,7 +333,8 @@
</span>
<button class="font-bold flex flex-row text-[#0556B4] items-center mt-2" (click)="showMore = !showMore">
<ui-icon class="transform ml-0 mr-2 rotate-180" size="15px" icon="arrow"></ui-icon> Weniger
<ui-icon class="transform ml-0 mr-2 rotate-180" size="15px" icon="arrow"></ui-icon>
Weniger
</button>
<button class="page-article-details__scroll-top-cta" (click)="scrollTop(description)">
@@ -347,6 +351,9 @@
*ngIf="store.item$ | async; let item"
class="shadow-[#dce2e9_0px_-2px_18px_0px] mb-5 border-none outline-none flex items-center px-5 h-14 min-h-[3.5rem] bg-white w-full"
(click)="showRecommendations = true"
matomoClickCategory="prodict-details"
matomoClickAction="click"
matomoClickName="recommendations"
>
<span class="uppercase text-[#0556B4] font-bold text-p3">Empfehlungen</span>
<img class="absolute right-5 -top-[0.125rem] h-12" src="assets/images/recommendation_tag.png" alt="recommendation icon" />
@@ -362,6 +369,9 @@
*ngIf="!(store.isDownload$ | async)"
class="text-brand border-2 border-brand bg-white font-bold text-lg px-[1.375rem] py-4 rounded-full mr-[1.875rem]"
(click)="showAvailabilities()"
matomoClickCategory="prodict-details"
matomoClickAction="click"
matomoClickName="bestaende-in-anderen-filialen"
>
Bestände in anderen Filialen
</button>
@@ -369,6 +379,9 @@
class="text-white bg-brand border-brand font-bold text-lg px-[1.375rem] py-4 rounded-full border-none no-underline"
(click)="showPurchasingModal()"
[disabled]="!(isAvailable$ | async) || (fetchingAvailabilities$ | async) || (item?.features && (item?.features)[0]?.key === 'PFO')"
matomoClickCategory="prodict-details"
matomoClickAction="click"
matomoClickName="in-den-Warenkorb"
>
In den Warenkorb
</button>

View File

@@ -14,6 +14,7 @@ import { OrderDeadlinePipeModule } from '@shared/pipes/order-deadline';
import { IconModule } from '@shared/components/icon';
import { ArticleDetailsTextComponent } from './article-details-text/article-details-text.component';
import { IconBadgeComponent } from 'apps/isa-app/src/shared/components/icon/badge/icon-badge.component';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [
@@ -30,6 +31,7 @@ import { IconBadgeComponent } from 'apps/isa-app/src/shared/components/icon/badg
OrderDeadlinePipeModule,
ArticleDetailsTextComponent,
IconBadgeComponent,
MatomoModule,
],
exports: [ArticleDetailsComponent, ArticleRecommendationsComponent],
declarations: [ArticleDetailsComponent, ArticleRecommendationsComponent],

View File

@@ -36,6 +36,9 @@
<button
class="flex flex-row items-center outline-none border-none bg-white text-black text-p2 m-0 p-0"
(click)="setQueryHistory(filter, recentQuery.friendlyName)"
matomoClickCategory="search"
matomoClickAction="click"
matomoClickName="recent-search"
>
<shared-icon
class="flex w-8 h-8 justify-center items-center mr-3 rounded-full text-black bg-[#edeff0]"

View File

@@ -4,9 +4,10 @@ import { ArticleSearchMainComponent } from './search-main.component';
import { FilterModule } from '@shared/components/filter';
import { RouterModule } from '@angular/router';
import { IconComponent, IconModule } from '@shared/components/icon';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule, RouterModule, IconComponent, FilterModule, IconModule],
imports: [CommonModule, RouterModule, IconComponent, FilterModule, IconModule, MatomoModule],
exports: [ArticleSearchMainComponent],
declarations: [ArticleSearchMainComponent],
providers: [],

View File

@@ -59,7 +59,8 @@
</div>
<div class="page-search-result-item__item-misc desktop-small:text-p3">
{{ item?.product?.volume }} <span *ngIf="item?.product?.volume && item?.product?.publicationDate">|</span>
{{ item?.product?.volume }}
<span *ngIf="item?.product?.volume && item?.product?.publicationDate">|</span>
{{ publicationDate }}
</div>
@@ -75,9 +76,14 @@
*ngIf="selectable"
(click)="$event.stopPropagation()"
[ngModel]="selected"
(ngModelChange)="setSelected()"
(ngModelChange)="
setSelected();
tracker.trackEvent({ category: 'Trefferliste', action: 'select', name: item.product.name, value: selected ? 1 : 0 })
"
class="isa-select-bullet"
type="checkbox"
matomoTracker
#tracker="matomo"
/>
</div>
@@ -95,8 +101,9 @@
*ngIf="inStock$ | async; let stock"
[class.skeleton]="stock.inStock === undefined"
class="min-w-[0.75rem] text-right inline-block"
>{{ stock?.inStock }}</span
>
{{ stock?.inStock }}
</span>
</ng-container>
<ng-container *ngIf="!(isOrderBranch$ | async)">
<span class="min-w-[1rem] text-center inline-block">-</span>
@@ -115,7 +122,8 @@
<div class="hidden" [class.page-search-result-item__item-ssc-tooltip]="primaryOutletActive">
{{ ssc?.ssc }} - {{ ssc?.sscText }}
</div>
<strong>{{ ssc?.ssc }}</strong> - {{ ssc?.sscText }}
<strong>{{ ssc?.ssc }}</strong>
- {{ ssc?.sscText }}
</ng-container>
</div>
</div>

View File

@@ -42,8 +42,7 @@
*ngIf="filter$ | async; let filter"
[orderBy]="filter?.orderBy"
(selectedOrderByChange)="search({ filter, clear: true, orderBy: true }); updateBreadcrumbs()"
>
</shared-order-by-filter>
></shared-order-by-filter>
</div>
<ng-container *ngIf="primaryOutletActive$ | async; else sideOutlet">
@@ -74,6 +73,9 @@
*ngIf="(selectedItemIds$ | async)?.length > 0"
class="cta-cart cta-action-primary"
(click)="addToCart()"
matomoClickCategory="Trefferliste"
matomoClickAction="click"
matomoClickName="In den Warenkorb legen"
>
<ui-spinner [show]="loading$ | async">In den Warenkorb legen</ui-spinner>
</button>

View File

@@ -19,6 +19,7 @@ import { FilterAutocompleteProvider, FilterModule, OrderByFilterModule } from '@
import { FocusSearchboxEvent } from '../focus-searchbox.event';
import { ArticleSearchMainAutocompleteProvider } from '../providers';
import { IconComponent } from '@shared/components/icon';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [
@@ -35,6 +36,7 @@ import { IconComponent } from '@shared/components/icon';
UiTooltipModule,
FilterModule,
IconComponent,
MatomoModule,
],
exports: [ArticleSearchResultsComponent, SearchResultItemComponent],
declarations: [

View File

@@ -6,24 +6,45 @@ import { ArticleSearchFilterComponent } from './article-search/search-filter/sea
import { ArticleSearchMainComponent } from './article-search/search-main/search-main.component';
import { ArticleSearchResultsComponent } from './article-search/search-results/search-results.component';
import { PageCatalogComponent } from './page-catalog.component';
import { MatomoRouteData } from 'ngx-matomo-client';
const routes: Routes = [
{
path: '',
component: PageCatalogComponent,
data: {
matomo: {
title: 'Artikelsuche',
} as MatomoRouteData,
},
children: [
{
path: 'filter',
component: ArticleSearchFilterComponent,
data: {
matomo: {
title: 'Artikelsuche - Filter',
} as MatomoRouteData,
},
},
{
path: 'filter/:id',
component: ArticleSearchFilterComponent,
data: {
matomo: {
title: 'Artikelsuche - Filter',
} as MatomoRouteData,
},
},
{
path: 'search',
component: ArticleSearchComponent,
outlet: 'side',
data: {
matomo: {
title: 'Artikelsuche',
} as MatomoRouteData,
},
children: [
{
path: '',
@@ -34,29 +55,59 @@ const routes: Routes = [
{
path: 'results',
component: ArticleSearchResultsComponent,
data: {
matomo: {
title: 'Artikelsuche - Trefferliste',
} as MatomoRouteData,
},
},
{
path: 'results',
component: ArticleSearchResultsComponent,
outlet: 'side',
data: {
matomo: {
title: 'Artikelsuche - Trefferliste',
} as MatomoRouteData,
},
},
{
path: 'results/:id',
component: ArticleSearchResultsComponent,
outlet: 'side',
data: {
matomo: {
title: 'Artikelsuche - Artikeldetails',
} as MatomoRouteData,
},
},
{
path: 'results/:ean/ean',
component: ArticleSearchResultsComponent,
outlet: 'side',
data: {
matomo: {
title: 'Artikelsuche - Artikeldetails (EAN)',
} as MatomoRouteData,
},
},
{
path: 'details/:id',
component: ArticleDetailsComponent,
data: {
matomo: {
title: 'Artikelsuche - Artikeldetails (ID)',
} as MatomoRouteData,
},
},
{
path: 'details/:ean/ean',
component: ArticleDetailsComponent,
data: {
matomo: {
title: 'Artikelsuche - Artikeldetails (EAN)',
} as MatomoRouteData,
},
},
{
path: '',

View File

@@ -1,15 +1,23 @@
import { NgFor, AsyncPipe } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
import { UiCommonModule } from '@ui/common';
import { MatomoModule } from 'ngx-matomo-client';
import { BehaviorSubject, Subscription } from 'rxjs';
import { first, map } from 'rxjs/operators';
import { PickUpShelfDetailsTagsComponent } from '../../../pickup-shelf/shared/pickup-shelf-details-tags/pickup-shelf-details-tags.component';
@Component({
selector: 'page-customer-order-details-tags',
templateUrl: 'customer-order-details-tags.component.html',
styleUrls: ['customer-order-details-tags.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CustomerOrderDetailsTagsComponent), multi: true }],
standalone: false,
host: { class: 'page-pickup-shelf-details-tags' },
standalone: true,
imports: [NgFor, UiCommonModule, FormsModule, AsyncPipe, MatomoModule],
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => PickUpShelfDetailsTagsComponent), multi: true },
],
})
export class CustomerOrderDetailsTagsComponent implements OnInit, OnDestroy, ControlValueAccessor {
selected$ = new BehaviorSubject<string>('');
@@ -55,7 +63,9 @@ export class CustomerOrderDetailsTagsComponent implements OnInit, OnDestroy, Con
ngOnInit() {
this.subscription.add(
this.selected$.pipe(map((selected) => (this.defaultTags.includes(selected) ? '' : selected))).subscribe(this.inputValueSubject),
this.selected$
.pipe(map((selected) => (this.defaultTags.includes(selected) ? '' : selected)))
.subscribe(this.inputValueSubject),
);
}

View File

@@ -33,6 +33,7 @@ import { UiDropdownModule } from '@ui/dropdown';
TextFieldModule,
CustomerOrderPipesModule,
ProductImageModule,
CustomerOrderDetailsTagsComponent,
],
exports: [
CustomerOrderDetailsComponent,
@@ -40,12 +41,7 @@ import { UiDropdownModule } from '@ui/dropdown';
CustomerOrderDetailsHeaderComponent,
CustomerOrderDetailsTagsComponent,
],
declarations: [
CustomerOrderDetailsComponent,
CustomerOrderDetailsItemComponent,
CustomerOrderDetailsHeaderComponent,
CustomerOrderDetailsTagsComponent,
],
declarations: [CustomerOrderDetailsComponent, CustomerOrderDetailsItemComponent, CustomerOrderDetailsHeaderComponent],
providers: [CustomerOrderDetailsStore],
})
export class CustomerOrderDetailsModule {}

View File

@@ -2,7 +2,8 @@
<div class="cleanup-headline">
<h1 class="cleanup-title">Abholfachbereinigung</h1>
<p class="cleanup-paragraph">
Bitte nehmen Sie die<br />
Bitte nehmen Sie die
<br />
Artikel auf die Fläche.
</p>
</div>
@@ -28,14 +29,18 @@
*ngFor="let item of compartmentCodeGroup.items; let firstItem = first"
[item]="item"
[showCompartmentCode]="firstItem"
(selectedChange)="setSelectedItem(item, $event)"
(selectedChange)="
setSelectedItem(item, $event);
tracker.trackEvent({ category: 'goods-in-cleanup', action: 'select', name: 'order-item', value: $event ? 1 : 0 })
"
[selectable]="item | goodsInCleanupSelectable: selectionRules : selectedItems"
[selected]="item | goodsInCleanupSelected: selectedOrderItemSubsetIds"
(click)="navigateToDetails(item)"
[showSupplier]="true"
[quantityEditable]="item.overallQuantity > 1 && (selectedOrderItemSubsetIds$ | async)?.includes(item.orderItemSubsetId)"
>
</shared-goods-in-out-order-group-item>
matomoTracker
#tracker="matomo"
></shared-goods-in-out-order-group-item>
<div class="divider" *ngIf="!lastCompartmentCode"></div>
</ng-container>
<div class="divider" *ngIf="!lastProcessingStatus"></div>
@@ -47,7 +52,8 @@
<ng-template #emptyMessage>
<div class="empty-message">
Es sind im Moment keine Artikel vorhanden, die Sie in<br />
Es sind im Moment keine Artikel vorhanden, die Sie in
<br />
den Filialbestand übernehmen können.
</div>
</ng-template>
@@ -61,6 +67,9 @@
[class.cta-action-primary]="action.selected"
[class.cta-action-secondary]="!action.selected"
(click)="handleAction(action)"
matomoClickCategory="goods-in-cleanup"
matomoClickAction="click"
[matomoClickName]="action.label"
>
<ui-spinner [show]="(changeActionLoader$ | async) || (loading$ | async)">{{ action.label }}</ui-spinner>
</button>

View File

@@ -8,9 +8,18 @@ import { UiSpinnerModule } from '@ui/spinner';
import { GoodsInCleanupListComponent } from './goods-in-cleanup-list.component';
import { GoodsInCleanupSelectablePipe } from './goods-in-cleanup-selectable.pipe';
import { GoodsInCleanupSelectedPipe } from './goods-in-cleanup-selected.pipe';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule, UiCommonModule, GoodsInOutOrderGroupModule, UiSpinnerModule, UiScrollContainerModule, RouterModule],
imports: [
CommonModule,
UiCommonModule,
GoodsInOutOrderGroupModule,
UiSpinnerModule,
UiScrollContainerModule,
RouterModule,
MatomoModule,
],
exports: [GoodsInCleanupListComponent],
declarations: [GoodsInCleanupListComponent, GoodsInCleanupSelectablePipe, GoodsInCleanupSelectedPipe],
providers: [],

View File

@@ -8,7 +8,7 @@
<h3 class="customer">
<div class="name">
<span *ngIf="orderItem.organisation != null">{{ orderItem.organisation }}</span>
<span *ngIf="orderItem.organisation != null && orderItem.firstName != null && orderItem.lastName != null"> - </span>
<span *ngIf="orderItem.organisation != null && orderItem.firstName != null && orderItem.lastName != null">-</span>
<span *ngIf="orderItem.firstName != null && orderItem.lastName != null">{{ orderItem.lastName }} {{ orderItem.firstName }}</span>
</div>
<div class="buyer-number">{{ orderItem?.buyerNumber }}</div>
@@ -25,6 +25,10 @@
type="text"
[ngModel]="ssc"
(ngModelChange)="sscChange($event)"
[matomoTracker]="['blur']"
matomoCategory="goods-in-list-item"
matomoAction="input"
matomoName="meldenummer"
/>
<span class="ssc-text" [class.err]="sscInvalid$ | async">{{ sscText$ | async }}</span>
@@ -47,7 +51,7 @@
/>
</div>
<div class="title">
<strong class="product-name"> {{ [orderItem.product.contributors, orderItem.product.name] | title }}</strong>
<strong class="product-name">{{ [orderItem.product.contributors, orderItem.product.name] | title }}</strong>
</div>
<div class="comment">
<strong>{{ orderItem.specialComment }}</strong>
@@ -77,7 +81,7 @@
<div class="status spec">
<span>Status</span>
<strong class="processing-status">
<ui-icon size="12px" *ngIf="orderItem.processingStatus | processingStatus: 'icon'; let icon" [icon]="icon"> </ui-icon>
<ui-icon size="12px" *ngIf="orderItem.processingStatus | processingStatus: 'icon'; let icon" [icon]="icon"></ui-icon>
{{ orderItem.processingStatus | processingStatus }}
</strong>
</div>
@@ -97,6 +101,15 @@
</div>
<div class="footer">
<button class="cta-reorder" (click)="showReorderModal(); $event.stopPropagation()" [disabled]="editSsc$ | async">Nachbestellen</button>
<button
class="cta-reorder"
(click)="showReorderModal(); $event.stopPropagation()"
[disabled]="editSsc$ | async"
matomoClickCategory="goods-in-list-item"
matomoClickAction="click"
matomoClickName="nachbestellen"
>
Nachbestellen
</button>
</div>
</ng-container>

View File

@@ -6,9 +6,19 @@ import { FormsModule } from '@angular/forms';
import { GoodsInListItemComponent } from './goods-in-list-item.component';
import { ProductImageModule, NavigateOnClickDirective } from '@cdn/product-image';
import { PipesModule } from '@shared/components/goods-in-out';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule, PipesModule, UiIconModule, ProductImageModule, FormsModule, UiInputModule, NavigateOnClickDirective],
imports: [
CommonModule,
PipesModule,
UiIconModule,
ProductImageModule,
FormsModule,
UiInputModule,
NavigateOnClickDirective,
MatomoModule,
],
exports: [GoodsInListItemComponent],
declarations: [GoodsInListItemComponent],
providers: [],

View File

@@ -1,7 +1,8 @@
<div class="goods-in-list-headline">
<h1 class="goods-in-list-title">Wareneingangsliste</h1>
<p class="goods-in-list-paragraph">
Vergeben Sie Meldenummern oder<br />
Vergeben Sie Meldenummern oder
<br />
bestellen Sie Artikel nach
</p>
</div>
@@ -37,18 +38,43 @@
<ng-template #emptyMessage>
<div class="empty-message">
Momentan steht kein Bestellposten zur Nachbearbeitung aus.<br />
Momentan steht kein Bestellposten zur Nachbearbeitung aus.
<br />
Alle Bestellposten sind ordnungsgemäß bearbeitet.
</div>
</ng-template>
<div *ngIf="!(listEmpty$ | async)" class="actions">
<button *ngIf="!editSsc" class="cta-edit-ssc cta-action-primary" (click)="editSsc = true">Meldenummern vergeben</button>
<button
*ngIf="!editSsc"
class="cta-edit-ssc cta-action-primary"
(click)="editSsc = true"
matomoClickCategory="goods-in-list"
matomoClickAction="click"
matomoClickName="meldenummer-vergeben"
>
Meldenummern vergeben
</button>
<ng-container *ngIf="editSsc">
<button class="cta-cancel-ssc cta-action-secondary" (click)="cancelSsc()">Abbrechen</button>
<button class="cta-save-ssc cta-action-primary" (click)="saveSsc()" [disabled]="editSscDisabled$ | async">
<ui-spinner [show]="showSaveSscSpinner$ | async"> Meldenummern speichern </ui-spinner>
<button
class="cta-cancel-ssc cta-action-secondary"
(click)="cancelSsc()"
matomoClickCategory="goods-in-list"
matomoClickAction="click"
matomoClickName="meldenummer-vergeben-abbrechen"
>
Abbrechen
</button>
<button
class="cta-save-ssc cta-action-primary"
(click)="saveSsc()"
[disabled]="editSscDisabled$ | async"
matomoClickCategory="goods-in-list"
matomoClickAction="click"
matomoClickName="meldenummer-vergeben-speichern"
>
<ui-spinner [show]="showSaveSscSpinner$ | async">Meldenummern speichern</ui-spinner>
</button>
</ng-container>
</div>

View File

@@ -8,6 +8,7 @@ import { GoodsInListItemModule } from './goods-in-list-item/goods-in-list-item.m
import { GoodsInListComponent } from './goods-in-list.component';
import { UiSpinnerModule } from '@ui/spinner';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [
@@ -18,6 +19,7 @@ import { UiSpinnerModule } from '@ui/spinner';
UiScrollContainerModule,
UiOrderByFilterModule,
UiSpinnerModule,
MatomoModule,
],
exports: [],
declarations: [GoodsInListComponent],

View File

@@ -2,7 +2,8 @@
<div class="reservations-headline">
<h1 class="reservations-title">Reservierungsliste</h1>
<p class="reservations-paragraph">
Hier sehen Sie alle zu<br />
Hier sehen Sie alle zu
<br />
reservierenden Bestellposten.
</p>
</div>
@@ -29,14 +30,18 @@
*ngFor="let item of compartmentCodeGroup.items; let firstItem = first"
[item]="item"
[showCompartmentCode]="firstItem"
(selectedChange)="setSelectedItem(item, $event)"
(selectedChange)="
setSelectedItem(item, $event);
tracker.trackEvent({ category: 'goods-in-reservation', action: 'select', name: 'order-item', value: $event ? 1 : 0 })
"
[selectable]="item | goodsInReservationSelectable: selectionRules : selectedItems"
[selected]="item | goodsInReservationSelected: selectedOrderItemSubsetIds"
[showInStock]="takeAwayAvailabilities$ | async"
(click)="navigateToDetails(item)"
[quantityEditable]="item.overallQuantity > 1 && (selectedOrderItemSubsetIds$ | async)?.includes(item.orderItemSubsetId)"
>
</shared-goods-in-out-order-group-item>
matomoTracker
#tracker="matomo"
></shared-goods-in-out-order-group-item>
<div class="divider" *ngIf="!lastCompartmentCode"></div>
</ng-container>
<div class="divider" *ngIf="!lastProcessingStatus"></div>
@@ -58,6 +63,9 @@
[class.cta-action-primary]="action.selected"
[class.cta-action-secondary]="!action.selected"
(click)="handleAction(action)"
matomoClickCategory="goods-in-reservation"
matomoClickAction="click"
[matomoClickName]="action.label"
>
<ui-spinner [show]="(changeActionLoader$ | async) || (loading$ | async)">{{ action.label }}</ui-spinner>
</button>

View File

@@ -8,9 +8,17 @@ import { GoodsInReservationSelectedPipe } from './goods-in-reservation-selected.
import { GoodsInReservationComponent } from './goods-in-reservation.component';
import { UiScrollContainerModule } from '@ui/scroll-container';
import { UiSpinnerModule } from '@ui/spinner';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule, UiCommonModule, UiScrollContainerModule, GoodsInOutOrderGroupModule, UiSpinnerModule],
imports: [
CommonModule,
UiCommonModule,
UiScrollContainerModule,
GoodsInOutOrderGroupModule,
UiSpinnerModule,
MatomoModule,
],
exports: [GoodsInReservationComponent],
declarations: [GoodsInReservationComponent, GoodsInReservationSelectablePipe, GoodsInReservationSelectedPipe],
})

View File

@@ -1,10 +1,22 @@
import { Component, ChangeDetectionStrategy, ViewChild, ElementRef, HostListener, ChangeDetectorRef, ViewRef } from '@angular/core';
import {
Component,
ChangeDetectionStrategy,
ViewChild,
ElementRef,
HostListener,
ChangeDetectorRef,
ViewRef,
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Breadcrumb, BreadcrumbService } from '@core/breadcrumb';
import { Config } from '@core/config';
import { DomainPackageInspectionService } from '@domain/package-inspection';
import { ToasterService } from '@shared/shell';
import { ArrivalStatus, NestedKeyValueDTOOfStringAndString, PackageDetailResponseDTO } from '@generated/swagger/wws-api';
import {
ArrivalStatus,
NestedKeyValueDTOOfStringAndString,
PackageDetailResponseDTO,
} from '@generated/swagger/wws-api';
import { UiModalService } from '@ui/modal';
import { Observable, Subject } from 'rxjs';
import { first, map, shareReplay, startWith, switchMap, retry, tap } from 'rxjs/operators';
@@ -37,7 +49,9 @@ export class PackageDetailsComponent {
childActions$ = this.childActionsSubject.asObservable().pipe(startWith(undefined));
packageActions$: Observable<NestedKeyValueDTOOfStringAndString[]> = this.packageDetails$.pipe(map((details) => details.actions));
packageActions$: Observable<NestedKeyValueDTOOfStringAndString[]> = this.packageDetails$.pipe(
map((details) => details.actions),
);
displayActions$ = this.packageActions$.pipe(
switchMap((actions) => this.childActions$.pipe(map((childActions) => childActions ?? actions))),
@@ -129,7 +143,7 @@ export class PackageDetailsComponent {
return {
previousArrivalStatus: packageDetails.package.arrivalStatus,
newArrivalStatus: (+status as ArrivalStatus) ?? 0,
newArrivalStatus: +status as ArrivalStatus,
};
}

View File

@@ -66,6 +66,9 @@
[class.cta-action-secondary]="!action.selected"
*ngFor="let action of mainActions$ | async"
(click)="handleAction({ action })"
matomoClickCategory="pickup-shelf-in-details"
matomoClickAction="click"
[matomoClickName]="action.label"
>
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">{{ action.label }}</ui-spinner>
</button>
@@ -85,9 +88,9 @@
})
"
>
<ui-spinner [show]="(changeActionLoader$ | async) === action.command"
>{{ latestDisplayedCompartmentInfos$ | async | addToPreviousCompartmentCodeLabelPipe }} zubuchen</ui-spinner
>
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">
{{ latestDisplayedCompartmentInfos$ | async | addToPreviousCompartmentCodeLabelPipe }} zubuchen
</ui-spinner>
</button>
</ng-container>
</div>

View File

@@ -19,6 +19,7 @@ import { RunCheckTrigger } from '../../trigger';
import { PickUpShelfDetailsItemsGroupComponent } from '../../shared/pickup-shelf-details-items-group/pickup-shelf-details-items-group.component';
import { isEqual } from 'lodash';
import { SkeletonLoaderComponent } from '@shared/components/loader';
import { MatomoModule } from 'ngx-matomo-client';
@Component({
selector: 'page-pickup-shelf-in-details',
@@ -39,6 +40,7 @@ import { SkeletonLoaderComponent } from '@shared/components/loader';
UiSpinnerModule,
OnInitDirective,
SkeletonLoaderComponent,
MatomoModule,
],
})
export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseComponent implements OnInit {
@@ -92,18 +94,22 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
latestDisplayedCompartmentInfos$ = this.latestCompartmentInfos$.pipe(
map((latestCompartmentInfos) => {
if (!!latestCompartmentInfos?.latestCompartmentInfo) {
if (latestCompartmentInfos?.latestCompartmentInfo) {
return `${latestCompartmentInfos?.latestCompartmentCode}_${latestCompartmentInfos?.latestCompartmentInfo}`;
}
return latestCompartmentInfos?.latestCompartmentCode;
}),
);
addToPreviousCompartmentActionDisabled$ = combineLatest([this.displayedCompartmentInfo$, this.changeActionDisabled$]).pipe(
map(([compartmentInfo, changeActionDisabled]) => !!compartmentInfo || changeActionDisabled),
);
addToPreviousCompartmentActionDisabled$ = combineLatest([
this.displayedCompartmentInfo$,
this.changeActionDisabled$,
]).pipe(map(([compartmentInfo, changeActionDisabled]) => !!compartmentInfo || changeActionDisabled));
customerNumberAndSelevtedOrderItemIdsChanges$ = combineLatest([this.store.customerNumber$, this.store.selectedOrderItem$]).pipe(
customerNumberAndSelevtedOrderItemIdsChanges$ = combineLatest([
this.store.customerNumber$,
this.store.selectedOrderItem$,
]).pipe(
filter(([_, selectedOrderItem]) => !!selectedOrderItem),
distinctUntilChanged((a, b) => isEqual(a, b)),
map(([customerNumber, selectedOrderItem]) => ({ customerNumber, selectedOrderItem })),
@@ -118,18 +124,21 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
super();
effect(() => {
const { customerNumber } = this.$customerNumberAndSelevtedOrderItemIdsChanges();
const changes = this.$customerNumberAndSelevtedOrderItemIdsChanges();
if (customerNumber) {
if (changes?.customerNumber) {
untracked(() => {
this.store.fetchCoverOrderItems(customerNumber);
this.store.fetchCoverOrderItems(changes.customerNumber);
});
}
});
}
ngOnInit() {
combineLatest([this._activatedRoute.params.pipe(distinctUntilChanged(isEqual)), this.orderItems$.pipe(distinctUntilChanged(isEqual))])
combineLatest([
this._activatedRoute.params.pipe(distinctUntilChanged(isEqual)),
this.orderItems$.pipe(distinctUntilChanged(isEqual)),
])
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(([params, items]) => {
const orderItemSubsetId = +params?.orderItemSubsetId;
@@ -170,7 +179,7 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
this.store.setDisableHeaderStatusDropdown(true);
const context = await this.execAction({ action, latestCompartmentCode, latestCompartmentInfo });
if (!!context) {
if (context) {
if (
action.command.includes('ARRIVED') ||
action.command.includes('PRINT_PRICEDIFFQRCODELABEL') ||
@@ -181,7 +190,7 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
}, 100);
} else {
const item = context?.items.find((_) => true);
if (!!item) {
if (item) {
await this.router.navigate(
this._pickupShelfInNavigationService.detailRoute({
item: {

View File

@@ -28,6 +28,7 @@ import { CacheService } from '@core/cache';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { PickupShelfListItemLoaderComponent } from '../../shared/pickup-shelf-list-item/pickup-shelf-list-item-loader.component';
import { ScrollContainerDirective } from '@shared/directives/scroll-container';
import { MatomoModule } from 'ngx-matomo-client';
@Component({
selector: 'page-pickup-shelf-in-list',
@@ -105,7 +106,8 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
byCompartmentCodeFn = (item: DBHOrderItemListItemDTO) =>
!!item.compartmentInfo ? `${item.compartmentCode}_${item.compartmentInfo}` : item.compartmentCode;
trackByFn: TrackByFunction<DBHOrderItemListItemDTO> = (index, item) => `${item.orderId}${item.orderItemId}${item.orderItemSubsetId}`;
trackByFn: TrackByFunction<DBHOrderItemListItemDTO> = (index, item) =>
`${item.orderId}${item.orderItemId}${item.orderItemSubsetId}`;
trackByGroupFn: TrackByFunction<Group<string, DBHOrderItemListItemDTO>> = (index, group) => group.group;
@@ -123,7 +125,10 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
combineLatest([this.store.processId$, this._activatedRoute.queryParams])
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(async ([_, queryParams]) => {
if (!this.store.list.length || !isEqual(queryParams, this.cleanupQueryParams(this.store.filter.getQueryParams()))) {
if (
!this.store.list.length ||
!isEqual(queryParams, this.cleanupQueryParams(this.store.filter.getQueryParams()))
) {
this.store.setQueryParams(queryParams);
this.store.fetchList({ emitFetchListResponse: false });
}
@@ -168,7 +173,10 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
private _addScrollPositionToCache(): void {
if (this._activatedRoute.outlet === 'primary') {
this._cache.set<number>({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN }, this.scrollContainer?.scrollPosition);
this._cache.set<number>(
{ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN },
this.scrollContainer?.scrollPosition,
);
}
}

View File

@@ -1,7 +1,8 @@
<div class="rounded py-10 px-4 text-center shadow-[0_-2px_24px_0_#dce2e9] h-full">
<h1 class="text-[1.625rem] font-bold">Einbuchen</h1>
<p class="text-lg mt-2 mb-10">
Scannen Sie den Artikel, um diesen einzubuchen <br />
Scannen Sie den Artikel, um diesen einzubuchen
<br />
oder suchen Sie nach Kundennamen.
</p>
<ng-container *ngIf="filter$ | async; let filter">
@@ -26,6 +27,8 @@
(click)="showFilter.emit()"
class="page-pickup-shelf-in-main__filter w-[6.75rem] h-14 rounded font-bold px-5 mb-4 text-lg bg-[#AEB7C1] flex flex-row flex-nowrap items-center justify-center"
[class.active]="hasFilter$ | async"
matomoClickAction="open"
matomoClickCategory="filter"
>
<shared-icon class="mr-2" icon="filter-variant"></shared-icon>
Filter

View File

@@ -7,6 +7,7 @@ import { isEqual } from 'lodash';
import { Observable, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { PickupShelfStore } from '../../store';
import { MatomoModule } from 'ngx-matomo-client';
@Component({
selector: 'page-pickup-shelf-in-main-side-view',
@@ -14,7 +15,7 @@ import { PickupShelfStore } from '../../store';
styleUrls: ['pickup-shelf-in-main-side-view.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'page-pickup-shelf-in-main-side-view' },
imports: [FilterModule, AsyncPipe, NgIf, IconModule],
imports: [FilterModule, AsyncPipe, NgIf, IconModule, MatomoModule],
})
export class PickUpShelfInMainSideViewComponent implements OnInit {
@Output() showFilter = new EventEmitter<void>();

View File

@@ -8,6 +8,7 @@ import { PickUpShelfInMainSideViewComponent } from './pickup-shelf-in-main-side-
import { PickUpShelfInMainComponent } from './pickup-shelf-in-main/pickup-shelf-in-main.component';
import { PickUpShelfInListComponent } from './pickup-shelf-in-list/pickup-shelf-in-list.component';
import { PickupShelfInEditComponent } from './pickup-shelf-in-edit/pickup-shelf-in-edit.component';
import { MatomoRouteData } from 'ngx-matomo-client';
export const routes: Routes = [
{
@@ -18,53 +19,125 @@ export const routes: Routes = [
},
runGuardsAndResolvers: 'always',
children: [
{ path: 'main', component: PickUpShelfInMainComponent, data: { view: 'main' } },
{ path: 'list', component: PickUpShelfInListComponent, data: { view: 'list' } },
{ path: 'list/filter', component: PickupShelfFilterComponent, data: { view: 'filter' } },
{
path: 'main',
component: PickUpShelfInMainComponent,
data: {
view: 'main',
matomo: {
title: 'Abholfach',
} as MatomoRouteData,
},
},
{
path: 'list',
component: PickUpShelfInListComponent,
data: {
view: 'list',
matomo: {
title: 'Abholfach - Trefferliste',
} as MatomoRouteData,
},
},
{
path: 'list/filter',
component: PickupShelfFilterComponent,
data: {
view: 'filter',
matomo: {
title: 'Abholfach - Filter',
} as MatomoRouteData,
},
},
{
path: 'order/:orderId/:orderNumber/item/status/:orderItemProcessingStatus/:orderItemSubsetId/edit',
component: PickupShelfInEditComponent,
data: { view: 'edit' },
data: {
view: 'edit',
matomo: {
title: 'Abholfach - Bearbeiten',
} as MatomoRouteData,
},
},
{
path: 'order/:orderId/compartment/:compartmentCode/:orderItemSubsetId/edit',
component: PickupShelfInEditComponent,
data: { view: 'edit' },
data: {
view: 'edit',
matomo: {
title: 'Abholfach - Bearbeiten',
} as MatomoRouteData,
},
},
{
path: 'order/:orderId/compartment/:compartmentCode/:compartmentInfo/:orderItemSubsetId/edit',
component: PickupShelfInEditComponent,
data: { view: 'edit' },
data: {
view: 'edit',
matomo: {
title: 'Abholfach - Bearbeiten',
} as MatomoRouteData,
},
},
{
path: 'order/:orderId/:orderNumber/item/status/:orderItemProcessingStatus/:orderItemSubsetId/history',
component: PickUpShelfHistoryComponent,
data: { view: 'history' },
data: {
view: 'history',
matomo: {
title: 'Abholfach - Historie',
} as MatomoRouteData,
},
},
{
path: 'order/:orderId/compartment/:compartmentCode/:orderItemSubsetId/history',
component: PickUpShelfHistoryComponent,
data: { view: 'history' },
data: {
view: 'history',
matomo: {
title: 'Abholfach - Historie',
} as MatomoRouteData,
},
},
{
path: 'order/:orderId/compartment/:compartmentCode/:compartmentInfo/:orderItemSubsetId/history',
component: PickUpShelfHistoryComponent,
data: { view: 'history' },
data: {
view: 'history',
matomo: {
title: 'Abholfach - Historie',
} as MatomoRouteData,
},
},
{
path: 'order/:orderId/:orderNumber/item/status/:orderItemProcessingStatus/:orderItemSubsetId',
component: PickupShelfInDetailsComponent,
data: { view: 'details' },
data: {
view: 'details',
matomo: {
title: 'Abholfach - Details',
} as MatomoRouteData,
},
},
{
path: 'order/:orderId/compartment/:compartmentCode/:orderItemSubsetId',
component: PickupShelfInDetailsComponent,
data: { view: 'details' },
data: {
view: 'details',
matomo: {
title: 'Abholfach - Details',
} as MatomoRouteData,
},
},
{
path: 'order/:orderId/compartment/:compartmentCode/:compartmentInfo/:orderItemSubsetId',
component: PickupShelfInDetailsComponent,
data: { view: 'details' },
data: {
view: 'details',
matomo: {
title: 'Abholfach - Details',
} as MatomoRouteData,
},
},
{ path: 'search', component: PickUpShelfInMainSideViewComponent, outlet: 'side' },
{ path: 'list', component: PickUpShelfInListComponent, data: { view: 'list' }, outlet: 'side' },

View File

@@ -46,6 +46,9 @@
[class.cta-action-secondary]="!action.selected"
*ngFor="let action of mainActions$ | async"
(click)="handleAction(action)"
matomoClickCategory="pickup-shelf-out-details"
matomoClickAction="click"
[matomoClickName]="action.label"
>
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">{{ action.label }}</ui-spinner>
</button>

View File

@@ -14,6 +14,7 @@ import { OnInitDirective } from '@shared/directives/element-lifecycle';
import { FormsModule } from '@angular/forms';
import { RunCheckTrigger } from '../../trigger';
import { PickUpShelfDetailsItemsGroupComponent } from '../../shared/pickup-shelf-details-items-group/pickup-shelf-details-items-group.component';
import { MatomoModule } from 'ngx-matomo-client';
@Component({
selector: 'page-pickup-shelf-out-details',
@@ -32,6 +33,7 @@ import { PickUpShelfDetailsItemsGroupComponent } from '../../shared/pickup-shelf
UiSpinnerModule,
OnInitDirective,
FormsModule,
MatomoModule,
],
providers: [],
})
@@ -85,7 +87,8 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
mainActions$ = this.store.mainShelfOutActions$;
trackByFnGroupDBHOrderItemListItemDTO = (index: number, group: { type: string; items: DBHOrderItemListItemDTO[] }) => group.type;
trackByFnGroupDBHOrderItemListItemDTO = (index: number, group: { type: string; items: DBHOrderItemListItemDTO[] }) =>
group.type;
trackByFnDBHOrderItemListItemDTO = (index: number, item: DBHOrderItemListItemDTO) => item.orderItemSubsetId;

View File

@@ -41,7 +41,8 @@
<div sharedScrollContainer class="overflow-scroll" (scrolledToBottom)="loadMore()">
<div class="empty-message" *ngIf="listEmpty$ | async">
Es sind im Moment keine Bestellposten vorhanden,<br />
Es sind im Moment keine Bestellposten vorhanden,
<br />
die bearbeitet werden können.
</div>
<div
@@ -54,7 +55,7 @@
>
<h3>
{{ firstItem?.organisation }}
<ng-container *ngIf="!!firstItem?.organisation && (!!firstItem?.firstName || !!firstItem?.lastName)"> - </ng-container>
<ng-container *ngIf="!!firstItem?.organisation && (!!firstItem?.firstName || !!firstItem?.lastName)">-</ng-container>
{{ firstItem?.lastName }}
{{ firstItem?.firstName }}
</h3>
@@ -88,6 +89,9 @@
[class.cta-action-primary]="action.selected"
[class.cta-action-secondary]="!action.selected"
(click)="handleAction(action)"
matomoClickCategory="pickup-shelf-out-list"
matomoClickAction="click"
[matomoClickName]="action.label"
>
<ui-spinner [show]="(loadingFetchedActionButton$ | async) || (fetching$ | async)">{{ action.label }}</ui-spinner>
</button>

View File

@@ -21,7 +21,11 @@ import { Filter, FilterModule } from '@shared/components/filter';
import { BehaviorSubject, Observable, combineLatest } from 'rxjs';
import { isEqual } from 'lodash';
import { PickUpShelfListItemComponent } from '../../shared/pickup-shelf-list-item/pickup-shelf-list-item.component';
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString, OrderItemProcessingStatusValue } from '@generated/swagger/oms-api';
import {
DBHOrderItemListItemDTO,
KeyValueDTOOfStringAndString,
OrderItemProcessingStatusValue,
} from '@generated/swagger/oms-api';
import { Group, GroupByPipe } from '@shared/pipes/group-by';
import { UiSpinnerModule } from '@ui/spinner';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
@@ -30,6 +34,7 @@ import { ActionHandlerService } from '../../services/action-handler.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CacheService } from '@core/cache';
import { ScrollContainerDirective } from '@shared/directives/scroll-container';
import { MatomoModule } from 'ngx-matomo-client';
@Component({
selector: 'page-pcikup-shelf-out-list',
@@ -49,6 +54,7 @@ import { ScrollContainerDirective } from '@shared/directives/scroll-container';
GroupByPipe,
UiSpinnerModule,
PickupShelfListItemLoaderComponent,
MatomoModule,
],
})
export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
@@ -67,7 +73,9 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
hits$ = this.store.listHits$;
filterPath$ = this.store.processId$.pipe(map((processId) => this._pickupShelfOutNavigationService.filterRoute({ processId })));
filterPath$ = this.store.processId$.pipe(
map((processId) => this._pickupShelfOutNavigationService.filterRoute({ processId })),
);
get primaryOutletActive$() {
return this._environment.matchDesktop$.pipe(map((matches) => matches && this._activatedRoute.outlet === 'primary'));
@@ -115,7 +123,8 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
byCompartmentCodeFn = (item: DBHOrderItemListItemDTO) =>
!!item.compartmentInfo ? `${item.compartmentCode}_${item.compartmentInfo}` : item.compartmentCode;
trackByFn: TrackByFunction<DBHOrderItemListItemDTO> = (index, item) => `${item.orderId}${item.orderItemId}${item.orderItemSubsetId}`;
trackByFn: TrackByFunction<DBHOrderItemListItemDTO> = (index, item) =>
`${item.orderId}${item.orderItemId}${item.orderItemSubsetId}`;
actions$ = combineLatest([this.list$, this.selectedIds$]).pipe(
map(([items, selectedIds]) =>
@@ -170,7 +179,10 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
private _addScrollPositionToCache(): void {
if (this._activatedRoute.outlet === 'primary') {
this._cache.set<number>({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN }, this.scrollContainer?.scrollPosition);
this._cache.set<number>(
{ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN },
this.scrollContainer?.scrollPosition,
);
}
}
@@ -248,10 +260,15 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
this.store.resetSelectedListItems();
this.loadingFetchedActionButton$.next(false);
const processingStatus = (action.key ? +action.key : selectedItems[0].processingStatus) as OrderItemProcessingStatusValue;
const processingStatus = (
action.key ? +action.key : selectedItems[0].processingStatus
) as OrderItemProcessingStatusValue;
selectedItems.forEach((item) => {
this.detailsStore.patchOrderItemSubsetInState({ orderItemSubsetId: item.orderItemSubsetId, changes: { processingStatus } });
this.detailsStore.patchOrderItemSubsetInState({
orderItemSubsetId: item.orderItemSubsetId,
changes: { processingStatus },
});
});
} catch (error) {
console.error(error);

View File

@@ -19,16 +19,24 @@
[routerLink]="customerDetailsRoute.path"
[queryParams]="customerDetailsRoute.queryParams"
[queryParamsHandling]="'merge'"
>Kundendetails</a
matomoClickCategory="pickup-shelf-details-header-nav-menu"
matomoClickAction="click"
matomoClickName="customer-details"
>
Kundendetails
</a>
<a
sharedMenuItem
*ngIf="ordersRoute$ | async; let ordersRoute"
[routerLink]="ordersRoute.path"
[queryParams]="ordersRoute.queryParams"
[queryParamsHandling]="'merge'"
>Bestellungen</a
matomoClickCategory="pickup-shelf-details-header-nav-menu"
matomoClickAction="click"
matomoClickName="customer-orders"
>
Bestellungen
</a>
</shared-menu>
</div>
</ng-template>

View File

@@ -8,6 +8,7 @@ import { IconComponent } from '@shared/components/icon';
import { SharedMenuModule } from '@shared/components/menu';
import { CustomerSearchNavigation } from '@shared/services/navigation';
import { CustomerInfoDTO } from '@generated/swagger/crm-api';
import { MatomoModule } from 'ngx-matomo-client';
import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
@@ -22,7 +23,7 @@ export interface PickUpShelfDetailsHeaderNavMenuComponentState {
styleUrls: ['pickup-shelf-details-header-nav-menu.component.scss'],
host: { class: 'page-pickup-shelf-details-header-nav-menu' },
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [CdkMenuModule, SharedMenuModule, IconComponent, RouterLink, NgIf, AsyncPipe],
imports: [CdkMenuModule, SharedMenuModule, IconComponent, RouterLink, NgIf, AsyncPipe, MatomoModule],
})
export class PickUpShelfDetailsHeaderNavMenuComponent extends ComponentStore<PickUpShelfDetailsHeaderNavMenuComponentState> {
@Input() set customer(value: CustomerInfoDTO) {
@@ -38,13 +39,18 @@ export class PickUpShelfDetailsHeaderNavMenuComponent extends ComponentStore<Pic
readonly showCustomerDetails$ = this.select((state) => state.showCustomerDetails);
ordersRoute$ = this.customer$.pipe(
map((customer) => !!customer && this._navigation.ordersRoute({ processId: Date.now(), customerId: customer?.id, customer })),
map(
(customer) =>
!!customer && this._navigation.ordersRoute({ processId: Date.now(), customerId: customer?.id, customer }),
),
);
customerDetailsRoute$ = combineLatest([this.showCustomerDetails$, this.customer$]).pipe(
map(
([showCustomerDetails, customer]) =>
showCustomerDetails && !!customer && this._navigation.detailsRoute({ processId: Date.now(), customerId: customer?.id, customer }),
showCustomerDetails &&
!!customer &&
this._navigation.detailsRoute({ processId: Date.now(), customerId: customer?.id, customer }),
),
);

View File

@@ -15,6 +15,9 @@
class="page-pickup-shelf-details-header__edit-cta bg-transparent text-brand font-bold border-none text-p1"
*ngIf="editClick.observers.length"
(click)="editClick.emit(orderItem)"
matomoClickCategory="pickup-shelf-details-header"
matomoClickAction="click"
matomoClickName="edit"
>
Bearbeiten
</button>
@@ -30,7 +33,7 @@
<h2 class="page-pickup-shelf-details-header__details-header items-center">
<div class="text-h2">
{{ orderItem?.organisation }}
<ng-container *ngIf="!!orderItem?.organisation && (!!orderItem?.firstName || !!orderItem?.lastName)"> - </ng-container>
<ng-container *ngIf="!!orderItem?.organisation && (!!orderItem?.firstName || !!orderItem?.lastName)">-</ng-container>
{{ orderItem?.lastName }}
{{ orderItem?.firstName }}
</div>
@@ -52,7 +55,7 @@
d="M880-740v520q0 24-18 42t-42 18H140q-24 0-42-18t-18-42v-520q0-24 18-42t42-18h680q24 0 42 18t18 42ZM140-631h680v-109H140v109Zm0 129v282h680v-282H140Zm0 282v-520 520Z"
/>
</svg>
<strong> Bezahlt über KulturPass </strong>
<strong>Bezahlt über KulturPass</strong>
</div>
<div class="page-pickup-shelf-details-header__details-wrapper -mt-3">
@@ -80,6 +83,9 @@
[uiOverlayTrigger]="statusDropdown"
[disabled]="changeStatusDisabled$ | async"
#dropdown="uiOverlayTrigger"
matomoClickCategory="pickup-shelf-details-header"
matomoClickAction="click"
matomoClickName="status-dropdown"
>
<div class="mr-[0.375rem]">
{{ orderItem?.processingStatus | processingStatus }}
@@ -92,7 +98,14 @@
></shared-icon>
</button>
<ui-dropdown #statusDropdown yPosition="below" xPosition="after" [xOffset]="8">
<button uiDropdownItem *ngFor="let action of statusActions$ | async" (click)="handleActionClick(action)">
<button
uiDropdownItem
*ngFor="let action of statusActions$ | async"
(click)="handleActionClick(action)"
matomoClickCategory="pickup-shelf-details-header"
matomoClickAction="select"
[matomoClickName]="action.label"
>
{{ action.label }}
</button>
</ui-dropdown>
@@ -168,6 +181,9 @@
#deadlineDatepickerTrigger="uiOverlayTrigger"
[disabled]="!isKulturpass && (!!orderItem?.features?.paid || (changeDateDisabled$ | async))"
class="cta-pickup-deadline"
matomoClickCategory="pickup-shelf-details-header"
matomoClickAction="click"
matomoClickName="pickup-deadline"
>
<strong class="border-r border-[#AEB7C1] pr-4">
{{ orderItem?.pickUpDeadline | date: 'dd.MM.yy' }}
@@ -183,8 +199,7 @@
[disabledDaysOfWeek]="[0]"
[selected]="orderItem?.pickUpDeadline"
(save)="updatePickupDeadline($event)"
>
</ui-datepicker>
></ui-datepicker>
</div>
</ng-template>
@@ -196,6 +211,9 @@
#preferredPickUpDatePickerTrigger="uiOverlayTrigger"
[disabled]="(!isKulturpass && !!orderItem?.features?.paid) || (changeDateDisabled$ | async)"
class="cta-pickup-preferred"
matomoClickCategory="pickup-shelf-details-header"
matomoClickAction="click"
matomoClickName="pickup-preferred"
>
<strong class="border-r border-[#AEB7C1] pr-4" *ngIf="findLatestPreferredPickUpDate$ | async; let pickUpDate; else: selectTemplate">
{{ pickUpDate | date: 'dd.MM.yy' }}
@@ -214,8 +232,7 @@
[disabledDaysOfWeek]="[0]"
[selected]="(findLatestPreferredPickUpDate$ | async) || today"
(save)="updatePreferredPickUpDate($event)"
>
</ui-datepicker>
></ui-datepicker>
</div>
</ng-template>
@@ -227,6 +244,9 @@
[disabled]="changeDateDisabled$ | async"
[uiOverlayTrigger]="uiDatepicker"
#datepicker="uiOverlayTrigger"
matomoClickCategory="pickup-shelf-details-header"
matomoClickAction="click"
matomoClickName="vsl-lieferdatum"
>
<span class="border-r border-[#AEB7C1] pr-4">
{{ orderItem?.estimatedShippingDate | date: 'dd.MM.yy' }}
@@ -242,8 +262,7 @@
[disabledDaysOfWeek]="[0]"
[selected]="orderItem?.estimatedShippingDate"
(save)="updateEstimatedShippingDate($event)"
>
</ui-datepicker>
></ui-datepicker>
</div>
</ng-template>
</ng-container>

View File

@@ -1,6 +1,19 @@
import { AsyncPipe, DatePipe, NgFor, NgIf, NgSwitch, NgSwitchCase, NgTemplateOutlet } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, inject } from '@angular/core';
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString, NotificationChannel, OrderDTO } from '@generated/swagger/oms-api';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
EventEmitter,
Input,
Output,
inject,
} from '@angular/core';
import {
DBHOrderItemListItemDTO,
KeyValueDTOOfStringAndString,
NotificationChannel,
OrderDTO,
} from '@generated/swagger/oms-api';
import { PickupShelfDetailsStore } from '../../store';
import { map, shareReplay, tap, withLatestFrom } from 'rxjs/operators';
import { BehaviorSubject, Observable, combineLatest } from 'rxjs';
@@ -14,6 +27,7 @@ import { UiDatepickerModule } from '@ui/datepicker';
import { PickUpShelfDetailsHeaderNavMenuComponent } from '../pickup-shelf-details-header-nav-menu/pickup-shelf-details-header-nav-menu.component';
import { SkeletonLoaderComponent } from '@shared/components/loader';
import { toSignal } from '@angular/core/rxjs-interop';
import { MatomoModule } from 'ngx-matomo-client';
@Component({
selector: 'page-pickup-shelf-details-header',
@@ -38,6 +52,7 @@ import { toSignal } from '@angular/core/rxjs-interop';
UiDatepickerModule,
PickUpShelfDetailsHeaderNavMenuComponent,
SkeletonLoaderComponent,
MatomoModule,
],
})
export class PickUpShelfDetailsHeaderComponent {
@@ -82,7 +97,9 @@ export class PickUpShelfDetailsHeaderComponent {
}),
);
notificationsChannel$: Observable<NotificationChannel> = this.order$.pipe(map((order) => order?.notificationChannels ?? 0));
notificationsChannel$: Observable<NotificationChannel> = this.order$.pipe(
map((order) => order?.notificationChannels ?? 0),
);
@Input()
processId?: number;
@@ -117,7 +134,9 @@ export class PickUpShelfDetailsHeaderComponent {
shareReplay(),
);
statusActions$ = this.orderItem$.pipe(map((orderItem) => orderItem?.actions?.filter((action) => action.enabled === false)));
statusActions$ = this.orderItem$.pipe(
map((orderItem) => orderItem?.actions?.filter((action) => action.enabled === false)),
);
crudaUpdate$ = this.orderItem$.pipe(map((orederItem) => !!(orederItem?.cruda & 4)));

View File

@@ -10,10 +10,12 @@
<ng-container *ngIf="hasEmailNotification$ | async">
<img [uiOverlayTrigger]="emailTooltip" src="/assets/images/email_bookmark.svg" />
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #emailTooltip [closeable]="true">
Per E-Mail benachrichtigt <br />
Per E-Mail benachrichtigt
<br />
<ng-container *ngFor="let notifications of emailNotificationDates$ | async">
<ng-container *ngFor="let notificationDate of notifications.dates">
{{ notifications.type | notificationType }} {{ notificationDate | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
{{ notifications.type | notificationType }} {{ notificationDate | date: 'dd.MM.yyyy | HH:mm' }} Uhr
<br />
</ng-container>
</ng-container>
</ui-tooltip>
@@ -21,10 +23,12 @@
<ng-container *ngIf="hasSmsNotification$ | async">
<img [uiOverlayTrigger]="smsTooltip" src="/assets/images/sms_bookmark.svg" />
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #smsTooltip [closeable]="true">
Per SMS benachrichtigt <br />
Per SMS benachrichtigt
<br />
<ng-container *ngFor="let notifications of smsNotificationDates$ | async">
<ng-container *ngFor="let notificationDate of notifications.dates">
{{ notificationDate | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
{{ notificationDate | date: 'dd.MM.yyyy | HH:mm' }} Uhr
<br />
</ng-container>
</ng-container>
</ui-tooltip>
@@ -55,24 +59,38 @@
<input
*ngIf="selectable$ | async"
[ngModel]="selected$ | async"
(ngModelChange)="setSelected($event)"
(ngModelChange)="
setSelected($event);
tracker.trackEvent({
category: 'pickup-shelf-list-item',
action: 'select',
name: orderItem?.product?.name,
value: $event ? 1 : 0,
})
"
class="isa-select-bullet mt-4"
type="checkbox"
matomoTracker
#tracker="matomo"
/>
</div>
</div>
<div class="detail">
<div class="label">Menge</div>
<div class="value">
<ng-container *ngIf="!(canChangeQuantity$ | async)"> {{ orderItem?.quantity }}x </ng-container>
<ng-container *ngIf="!(canChangeQuantity$ | async)">{{ orderItem?.quantity }}x</ng-container>
<ui-quantity-dropdown
*ngIf="canChangeQuantity$ | async"
[showTrash]="false"
[range]="orderItem?.quantity"
[(ngModel)]="quantity"
(ngModelChange)="
tracker.trackEvent({ category: 'pickup-shelf-list-item', action: 'quantity', name: orderItem?.product?.name, value: $event })
"
[showSpinner]="false"
>
</ui-quantity-dropdown>
matomoTracker
#tracker="matomo"
></ui-quantity-dropdown>
<span class="overall-quantity">(von {{ orderItem?.overallQuantity }})</span>
</div>
</div>
@@ -135,8 +153,9 @@
orderItemFeature(orderItem) === 'B2B-Versand' ||
orderItemFeature(orderItem) === 'DIG-Versand'
"
>{{ orderItem?.estimatedDelivery ? 'Lieferung zwischen' : 'Lieferung ab' }}</ng-container
>
{{ orderItem?.estimatedDelivery ? 'Lieferung zwischen' : 'Lieferung ab' }}
</ng-container>
<ng-container *ngIf="orderItemFeature(orderItem) === 'Abholung' || orderItemFeature(orderItem) === 'Rücklage'">
Abholung ab
</ng-container>
@@ -255,6 +274,9 @@
type="submit"
*ngIf="specialCommentControl?.enabled && specialCommentControl.dirty"
(click)="saveSpecialComment()"
matomoClickCategory="pickup-shelf-details-item"
matomoClickAction="save"
matomoClickName="special-comment"
>
Speichern
</button>

View File

@@ -25,6 +25,7 @@ import { Subject, combineLatest } from 'rxjs';
import { PickupShelfDetailsStore } from '../../store';
import { UiQuantityDropdownModule } from '@ui/quantity-dropdown';
import { NotificationTypePipe } from './notification-type.pipe';
import { MatomoModule } from 'ngx-matomo-client';
export interface PickUpShelfDetailsItemComponentState {
orderItem?: DBHOrderItemListItemDTO;
@@ -57,9 +58,13 @@ export interface PickUpShelfDetailsItemComponentState {
UiQuantityDropdownModule,
NotificationTypePipe,
NavigateOnClickDirective,
MatomoModule,
],
})
export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfDetailsItemComponentState> implements OnInit {
export class PickUpShelfDetailsItemComponent
extends ComponentStore<PickUpShelfDetailsItemComponentState>
implements OnInit
{
private _store = inject(PickupShelfDetailsStore);
@ViewChild('autosize') autosize: CdkTextareaAutosize;
@@ -108,7 +113,9 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
hasEmailNotification$ = this.emailNotificationDates$.pipe(map((dates) => dates?.length > 0));
smsNotificationDates$ = this.orderItem$.pipe(switchMap((orderItem) => this._store.getSmsNotificationDate$(orderItem?.orderItemSubsetId)));
smsNotificationDates$ = this.orderItem$.pipe(
switchMap((orderItem) => this._store.getSmsNotificationDate$(orderItem?.orderItemSubsetId)),
);
hasSmsNotification$ = this.smsNotificationDates$.pipe(map((dates) => dates?.length > 0));
@@ -212,7 +219,8 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
orderItemFeature(orderItemListItem: DBHOrderItemListItemDTO) {
const orderItems = this.order?.items;
return orderItems?.find((orderItem) => orderItem.data.id === orderItemListItem.orderItemId)?.data?.features?.orderType;
return orderItems?.find((orderItem) => orderItem.data.id === orderItemListItem.orderItemId)?.data?.features
?.orderType;
}
triggerResize() {

View File

@@ -5,6 +5,10 @@
[class.selected]="tag === $selected() && !inputFocus.focused"
*ngFor="let tag of defaultTags"
(click)="setCompartmentInfo(tag)"
matomoClickCategory="pickup-shelf-details-tags"
matomoClickAction="click"
[matomoClickName]="tag"
[matomoClickValue]="$selected() === tag ? 0 : 1"
>
{{ tag }}
</button>
@@ -23,6 +27,10 @@
placeholder="..."
[size]="$controlSize()"
maxlength="15"
[matomoTracker]="['blur']"
matomoCategory="pickup-shelf-details-tags"
matomoAction="blur"
matomoName="tag-input"
/>
</button>
</div>

View File

@@ -2,6 +2,7 @@ import { NgFor } from '@angular/common';
import { ChangeDetectionStrategy, Component, computed, model } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { UiCommonModule } from '@ui/common';
import { MatomoModule } from 'ngx-matomo-client';
@Component({
selector: 'page-pickup-shelf-details-tags',
@@ -9,7 +10,7 @@ import { UiCommonModule } from '@ui/common';
styleUrls: ['pickup-shelf-details-tags.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'page-pickup-shelf-details-tags' },
imports: [NgFor, UiCommonModule, FormsModule],
imports: [NgFor, UiCommonModule, FormsModule, MatomoModule],
})
export class PickUpShelfDetailsTagsComponent {
$selected = model<string>(undefined, { alias: 'selected' });
@@ -19,7 +20,7 @@ export class PickUpShelfDetailsTagsComponent {
$controlSize = computed(() => {
const value = this.$inputValue();
if (!!value) {
if (value) {
return Math.min(value.length, 15);
}
return 3;

View File

@@ -28,7 +28,7 @@
<div
class="page-pickup-shelf-list-item__item-contributors text-p2 font-normal text-ellipsis overflow-hidden max-w-[24rem] whitespace-nowrap mb-[0.375rem]"
>
<ng-container *ngFor="let contributor of contributors; let last = last"> {{ contributor }}{{ last ? '' : ';' }} </ng-container>
<ng-container *ngFor="let contributor of contributors; let last = last">{{ contributor }}{{ last ? '' : ';' }}</ng-container>
</div>
<div
@@ -104,9 +104,14 @@
<input
(click)="$event.stopPropagation()"
[ngModel]="selectedItem"
(ngModelChange)="setSelected()"
(ngModelChange)="
setSelected();
tracker.trackEvent({ category: 'pickup-shelf-list-item', action: 'select', name: item?.product?.name, value: $event ? 1 : 0 })
"
class="isa-select-bullet"
type="checkbox"
matomoTracker
#tracker="matomo"
/>
</div>

View File

@@ -11,6 +11,7 @@ import { FormsModule } from '@angular/forms';
import { PickupShelfStore } from '../../store';
import { map } from 'rxjs/operators';
import { CacheService } from '@core/cache';
import { MatomoModule } from 'ngx-matomo-client';
@Component({
selector: 'page-pickup-shelf-list-item',
@@ -31,6 +32,7 @@ import { CacheService } from '@core/cache';
UiCommonModule,
PickupShelfProcessingStatusPipe,
NavigateOnClickDirective,
MatomoModule,
],
providers: [PickupShelfProcessingStatusPipe],
})
@@ -41,7 +43,7 @@ export class PickUpShelfListItemComponent {
@Input() item: DBHOrderItemListItemDTO;
@Input() primaryOutletActive: boolean = false;
@Input() primaryOutletActive = false;
@Input() itemDetailsLink: any[] = [];
@@ -79,7 +81,9 @@ export class PickUpShelfListItemComponent {
}
selected$ = this.store.selectedListItems$.pipe(
map((selectedListItems) => selectedListItems?.find((item) => item?.orderItemSubsetId === this.item?.orderItemSubsetId)),
map((selectedListItems) =>
selectedListItems?.find((item) => item?.orderItemSubsetId === this.item?.orderItemSubsetId),
),
);
constructor(
@@ -132,7 +136,9 @@ export class PickUpShelfListItemComponent {
}
setSelected() {
const isSelected = this.store?.selectedListItems.map((items) => items?.orderItemSubsetId).includes(this.item?.orderItemSubsetId);
const isSelected = this.store?.selectedListItems
.map((items) => items?.orderItemSubsetId)
.includes(this.item?.orderItemSubsetId);
if (isSelected) {
this.store.removeSelectedListItem(this.item);
} else {

View File

@@ -6,9 +6,10 @@ import { UiAutocompleteModule } from '@ui/autocomplete';
import { UiCommonModule } from '@ui/common';
import { SearchboxModule } from '@shared/components/searchbox';
import { UiTooltipModule } from '@ui/tooltip';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule, UiCommonModule, SearchboxModule, UiAutocompleteModule, UiTooltipModule],
imports: [CommonModule, UiCommonModule, SearchboxModule, UiAutocompleteModule, UiTooltipModule, MatomoModule],
exports: [FilterInputGroupMainComponent],
declarations: [FilterInputGroupMainComponent],
})

View File

@@ -5,7 +5,9 @@
[attr.data-label]="label"
class="order-by-filter-button"
type="button"
(click)="setActive(label)"
(click)="setActive(label); tracker.trackEvent({ category: 'order-by-filter', action: 'click', name: label })"
matomoTracker
#tracker="matomo"
>
<span>
{{ label }}

View File

@@ -3,9 +3,10 @@ import { NgModule } from '@angular/core';
import { UiIconModule } from '@ui/icon';
import { OrderByFilterComponent } from './order-by-filter.component';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule, UiIconModule],
imports: [CommonModule, UiIconModule, MatomoModule],
exports: [OrderByFilterComponent],
declarations: [OrderByFilterComponent],
providers: [],

View File

@@ -2,9 +2,10 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FilterInputChipComponent } from './filter-input-chip.component';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule],
imports: [CommonModule, MatomoModule],
exports: [FilterInputChipComponent],
declarations: [FilterInputChipComponent],
})

View File

@@ -6,7 +6,16 @@
[attr.data-selected]="uiOption?.selected"
[attr.data-target]="uiOption?.expanded"
>
<ui-checkbox [ngModel]="uiOption?.selected" (ngModelChange)="uiOption?.setSelected($event)" [indeterminate]="hasPartiallyCheckedChildren">
<ui-checkbox
[ngModel]="uiOption?.selected"
(ngModelChange)="
uiOption?.setSelected($event);
tracker.trackEvent({ category: 'filter-option-checkbox', action: 'click', name: uiOption?.label, value: $event ? 1 : 0 })
"
[indeterminate]="hasPartiallyCheckedChildren"
matomoTracker
#tracker="matomo"
>
{{ uiOption?.label }}
</ui-checkbox>
<button

View File

@@ -5,9 +5,10 @@ import { FilterInputOptionBoolComponent } from './filter-input-option-bool.compo
import { UiCheckboxModule } from '@ui/checkbox';
import { FormsModule } from '@angular/forms';
import { UiIconModule } from '@ui/icon';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule, FormsModule, UiCheckboxModule, UiIconModule],
imports: [CommonModule, FormsModule, UiCheckboxModule, UiIconModule, MatomoModule],
exports: [FilterInputOptionBoolComponent],
declarations: [FilterInputOptionBoolComponent],
})

View File

@@ -20,6 +20,9 @@
class="grid items-center justify-center h-10 w-14 my-2 border-l solid border-[#AEB7C1] text-[#596470]"
[uiOverlayTrigger]="dpStart"
#dpStartTrigger="uiOverlayTrigger"
matomoClickCategory="filter-option-date-range"
matomoClickAction="open-datepicker"
matomoClickName="start"
>
<shared-icon icon="calendar-today"></shared-icon>
</button>
@@ -48,6 +51,9 @@
class="grid items-center justify-center h-10 w-14 my-2 border-l solid border-[#AEB7C1] text-[#596470]"
[uiOverlayTrigger]="dpStop"
#dpStartTrigger="uiOverlayTrigger"
matomoClickCategory="filter-option-date-range"
matomoClickAction="open-datepicker"
matomoClickName="stop"
>
<shared-icon icon="calendar-today"></shared-icon>
</button>
@@ -64,8 +70,7 @@
[max]="maxDate"
saveLabel="Übernehmen"
(save)="setStratValue($event)"
>
</ui-datepicker>
></ui-datepicker>
<ui-datepicker
class="dp-right"
yPosition="below"
@@ -75,6 +80,5 @@
formControlName="stop"
(save)="setStopValue($event)"
saveLabel="Übernehmen"
>
</ui-datepicker>
></ui-datepicker>
</div>

View File

@@ -11,6 +11,7 @@ import { IconComponent } from '@shared/components/icon';
import { InputControlModule } from '@shared/components/input-control';
import { UiDateInputDirective } from '@ui/input';
import { DateInputDirective } from '@shared/forms';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [
@@ -24,6 +25,7 @@ import { DateInputDirective } from '@shared/forms';
IconComponent,
UiDateInputDirective,
DateInputDirective,
MatomoModule,
],
exports: [FilterInputOptionDateRangeComponent],
declarations: [FilterInputOptionDateRangeComponent],

View File

@@ -4,9 +4,10 @@ import { CommonModule } from '@angular/common';
import { InputOptionNumberRangeComponent } from './filter-input-option-number-range.component';
import { UiFormControlModule } from '@ui/form-control';
import { FormsModule } from '@angular/forms';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule, UiFormControlModule, FormsModule],
imports: [CommonModule, UiFormControlModule, FormsModule, MatomoModule],
exports: [InputOptionNumberRangeComponent],
declarations: [InputOptionNumberRangeComponent],
})

View File

@@ -6,7 +6,22 @@
[attr.data-selected]="uiOption?.selected"
>
<div>
<ui-switch [ngModel]="uiOption?.selected" (ngModelChange)="uiOption?.setSelected($event)" labelOn="mit" labelOff="ohne"> </ui-switch>
<ui-switch
[ngModel]="uiOption?.selected"
(ngModelChange)="
uiOption?.setSelected($event);
tracker.trackEvent({
category: 'filter-option-tri-state',
action: 'click',
name: uiOption?.label,
value: $event === true ? 1 : $event === false ? -1 : 0,
})
"
labelOn="mit"
labelOff="ohne"
matomoTracker
#tracker="matomo"
></ui-switch>
{{ uiOption?.label }}
</div>
<button

View File

@@ -5,9 +5,10 @@ import { InputOptionTriStateComponent } from './filter-input-option-tri-state.co
import { FormsModule } from '@angular/forms';
import { UiIconModule } from '@ui/icon';
import { UiSwitchModule } from '@ui/switch';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule, UiSwitchModule, FormsModule, UiIconModule],
imports: [CommonModule, UiSwitchModule, FormsModule, UiIconModule, MatomoModule],
exports: [InputOptionTriStateComponent],
declarations: [InputOptionTriStateComponent],
})

View File

@@ -111,7 +111,9 @@ export class Option implements IOption {
selected.push(this);
}
const selectedChildren = this.values?.map((f) => f.getSelectedOptions()).reduce((agg, options) => [...agg, ...options], []);
const selectedChildren = this.values
?.map((f) => f.getSelectedOptions())
.reduce((agg, options) => [...agg, ...options], []);
if (selectedChildren?.length) {
selected.push(...selectedChildren);
@@ -178,7 +180,7 @@ export class Option implements IOption {
}
hasOptions() {
return this._values?.length > 0 ?? false;
return this._values?.length > 0;
}
private boolToStringValue() {

View File

@@ -12,6 +12,10 @@
(focus)="clearHint(); focused.emit(true)"
(blur)="focused.emit(false)"
(keyup)="onKeyup($event)"
(keyup.enter)="tracker.trackEvent({ action: 'keyup enter', name: 'search' })"
matomoTracker
#tracker="matomo"
matomoCategory="searchbox"
/>
<div *ngIf="showHint" class="searchbox-hint" (click)="focus()">{{ hint }}</div>
</div>
@@ -26,10 +30,22 @@
*ngIf="!canScan"
(click)="emitSearch()"
[disabled]="completeValue !== query"
matomoClickAction="click"
matomoClickCategory="searchbox"
matomoClickName="search"
>
<ui-icon icon="search" size="1.5rem"></ui-icon>
</button>
<button tabindex="0" class="searchbox-scan-btn" type="button" *ngIf="canScan" (click)="startScan()">
<button
tabindex="0"
class="searchbox-scan-btn"
type="button"
*ngIf="canScan"
(click)="startScan()"
matomoClickAction="open"
matomoClickCategory="searchbox"
matomoClickName="scanner"
>
<shared-icon icon="barcode-scan" [size]="32"></shared-icon>
</button>
</ng-container>

View File

@@ -5,9 +5,10 @@ import { SearchboxComponent } from './searchbox.component';
import { UiIconModule } from '@ui/icon';
import { FormsModule } from '@angular/forms';
import { IconComponent } from '@shared/components/icon';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule, UiIconModule, IconComponent, FormsModule],
imports: [CommonModule, UiIconModule, IconComponent, FormsModule, MatomoModule],
exports: [SearchboxComponent],
declarations: [SearchboxComponent],
})

View File

@@ -1,24 +1,17 @@
import { AsyncPipe, NgIf } from '@angular/common';
import { NgIf } from '@angular/common';
import {
AfterContentInit,
AfterViewInit,
ChangeDetectionStrategy,
Component,
DestroyRef,
OnInit,
QueryList,
ViewChild,
ViewChildren,
computed,
inject,
signal,
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ActivatedRoute, RouterOutlet } from '@angular/router';
import { RouterOutlet } from '@angular/router';
import { EnvironmentService } from '@core/environment';
import { OnInitDirective } from '@shared/directives/element-lifecycle';
import { NEVER } from 'rxjs';
import { switchMap } from 'rxjs/operators';
@Component({
selector: 'shared-splitscreen',
@@ -26,7 +19,7 @@ import { switchMap } from 'rxjs/operators';
styleUrls: ['splitscreen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'shared-splitscreen' },
imports: [RouterOutlet, AsyncPipe, NgIf],
imports: [RouterOutlet, NgIf],
})
export class SharedSplitscreenComponent implements AfterViewInit {
destroyRef = inject(DestroyRef);

View File

@@ -8,7 +8,14 @@
<div class="hr"></div>
<ui-datepicker-body></ui-datepicker-body>
<div class="text-center mb-px-10">
<button *ngIf="!content" class="rounded-full font-bold text-white bg-brand py-px-15 px-px-25" (click)="onSave()">
<button
*ngIf="!content"
class="rounded-full font-bold text-white bg-brand py-px-15 px-px-25"
(click)="onSave()"
matomoClickCategory="datepicker"
matomoClickAction="click"
matomoClickName="save-date"
>
<ng-container>{{ saveLabel }}</ng-container>
</button>
<ng-content></ng-content>

View File

@@ -5,11 +5,18 @@ import { CommonModule } from '@angular/common';
import { UiDatepickerBodyComponent, UiDatepickerCellDirective, GetCellNamePipe } from './body';
import { UiDatepickerHeaderComponent } from './header';
import { UiIconModule } from '@ui/icon';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule, UiIconModule],
imports: [CommonModule, UiIconModule, MatomoModule],
exports: [UiDatepickerComponent],
declarations: [UiDatepickerComponent, UiDatepickerBodyComponent, UiDatepickerHeaderComponent, UiDatepickerCellDirective, GetCellNamePipe],
declarations: [
UiDatepickerComponent,
UiDatepickerBodyComponent,
UiDatepickerHeaderComponent,
UiDatepickerCellDirective,
GetCellNamePipe,
],
providers: [],
})
export class UiDatepickerModule {}

View File

@@ -10,7 +10,13 @@
[scanner]="scanner"
>
<ui-autocomplete *ngIf="autocompleteProvider">
<button *ngFor="let item of autocompleteResults$ | async" [uiAutocompleteItem]="item.query">
<button
*ngFor="let item of autocompleteResults$ | async"
[uiAutocompleteItem]="item.query"
matomoClickAction="select"
matomoClickCategory="autocomplete"
[matomoClickName]="item.display"
>
{{ item.display }}
</button>
</ui-autocomplete>

View File

@@ -6,9 +6,10 @@ import { UiAutocompleteModule } from '@ui/autocomplete';
import { UiSearchboxNextModule } from '@ui/searchbox';
import { UiCommonModule } from '@ui/common';
import { UiTooltipModule } from '@ui/tooltip';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule, UiCommonModule, UiSearchboxNextModule, UiAutocompleteModule, UiTooltipModule],
imports: [CommonModule, UiCommonModule, UiSearchboxNextModule, UiAutocompleteModule, UiTooltipModule, MatomoModule],
exports: [UiFilterInputGroupMainComponent],
declarations: [UiFilterInputGroupMainComponent],
})

View File

@@ -9,6 +9,10 @@
[attr.data-key]="uiInput?.key"
[attr.data-type]="uiInput?.type"
[attr.data-selected]="uiInput?.selected"
matomoClickCategory="filter-chip"
matomoClickAction="click"
[matomoClickName]="uiInput?.label"
[matomoClickValue]="uiInput?.selected ? 0 : 1"
>
{{ uiInput?.label }}
</button>
@@ -24,6 +28,10 @@
[attr.data-key]="uiInput?.key"
[attr.data-type]="uiInput?.type"
[attr.data-selected]="uiInput?.selected"
matomoClickCategory="filter-chip"
matomoClickAction="click"
[matomoClickName]="uiInput?.label"
[matomoClickValue]="uiInput?.selected ? 0 : 1"
>
{{ option?.label }}
</button>

View File

@@ -2,9 +2,10 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UiFilterInputChipComponent } from './filter-input-chip.component';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule],
imports: [CommonModule, MatomoModule],
exports: [UiFilterInputChipComponent],
declarations: [UiFilterInputChipComponent],
})

View File

@@ -9,7 +9,16 @@
>
<div class="option-wrapper">
<ui-form-control [label]="uiStartOption?.label">
<input type="text" [ngModel]="uiStartOption?.value" (ngModelChange)="uiStartOption?.setValue($event)" />
<input
type="text"
[ngModel]="uiStartOption?.value"
(ngModelChange)="uiStartOption?.setValue($event)"
#startModel="ngModel"
[matomoTracker]="['blur']"
matomoCategory="filter-option-date-rage"
matomoName="start"
[matomoValue]="startModel.value"
/>
</ui-form-control>
</div>
</div>
@@ -23,7 +32,16 @@
>
<div class="option-wrapper">
<ui-form-control [label]="uiStopOption?.label">
<input type="text" [ngModel]="uiStopOption?.value" (ngModelChange)="uiStopOption?.setValue($event)" />
<input
type="text"
[ngModel]="uiStopOption?.value"
(ngModelChange)="uiStopOption?.setValue($event)"
#stopModel="ngModel"
[matomoTracker]="['blur']"
matomoCategory="filter-option-date-rage"
matomoName="stop"
[matomoValue]="stopModel.value"
/>
</ui-form-control>
</div>
</div>

View File

@@ -4,9 +4,10 @@ import { CommonModule } from '@angular/common';
import { UiInputOptionNumberRangeComponent } from './filter-input-option-number-range.component';
import { UiFormControlModule } from '@ui/form-control';
import { FormsModule } from '@angular/forms';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [CommonModule, UiFormControlModule, FormsModule],
imports: [CommonModule, UiFormControlModule, FormsModule, MatomoModule],
exports: [UiInputOptionNumberRangeComponent],
declarations: [UiInputOptionNumberRangeComponent],
})

View File

@@ -92,7 +92,7 @@ export class UiOption implements IUiOption {
getParentInputOptions() {
let parent = this.parent;
while (!!parent) {
while (parent) {
if (parent instanceof UiInputOptions) {
return parent;
}
@@ -104,14 +104,16 @@ export class UiOption implements IUiOption {
return !!this.values?.length;
}
getSelectedOptions(includeSelf: boolean = true): UiOption[] {
getSelectedOptions(includeSelf = true): UiOption[] {
const selected: UiOption[] = [];
if (this.selected && includeSelf) {
selected.push(this);
}
const selectedChildren = this.values?.map((f) => f.getSelectedOptions()).reduce((agg, options) => [...agg, ...options], []);
const selectedChildren = this.values
?.map((f) => f.getSelectedOptions())
.reduce((agg, options) => [...agg, ...options], []);
if (selectedChildren?.length) {
selected.push(...selectedChildren);
@@ -134,7 +136,10 @@ export class UiOption implements IUiOption {
this._selected = value;
const type = this.getParentInput()?.type;
if (!value && (type === UiInputType.DateRange || type === UiInputType.NumberRange || type === UiInputType.IntegerRange)) {
if (
!value &&
(type === UiInputType.DateRange || type === UiInputType.NumberRange || type === UiInputType.IntegerRange)
) {
this.setValue(undefined, { emitEvent: false });
}
@@ -178,7 +183,7 @@ export class UiOption implements IUiOption {
}
hasOptions() {
return this._values?.length > 0 ?? false;
return this._values?.length > 0;
}
private boolToStringValue() {

BIN
bun.lockb
View File

Binary file not shown.

13
db.env Normal file
View File

@@ -0,0 +1,13 @@
MYSQL_ROOT_PASSWORD=
MYSQL_DATABASE=matomo_db
MYSQL_USER=matomo
MYSQL_PASSWORD=omotam
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=true
MATOMO_DATABASE_HOST=db
MATOMO_DATABASE_ADAPTER=mysql
MATOMO_DATABASE_TABLES_PREFIX=matomo_
MATOMO_DATABASE_USERNAME=matomo
MATOMO_DATABASE_PASSWORD=omotam
MATOMO_DATABASE_DBNAME=matomo_db

27
docker-compose.yaml Normal file
View File

@@ -0,0 +1,27 @@
services:
db:
image: mariadb:latest
command: --max-allowed-packet=64MB
restart: always
volumes:
- db:/var/lib/mysql:Z
env_file:
- ./db.env
app:
image: matomo:latest
restart: always
depends_on:
- db
volumes:
- ./.matomo/config:/var/www/html/config:z
- ./.matomo/logs:/var/www/html/logs:z
- matomo:/var/www/html:z
env_file:
- ./db.env
ports:
- 8080:80
volumes:
db:
matomo:

738
package-lock.json generated
View File

@@ -33,6 +33,7 @@
"lodash": "^4.17.21",
"moment": "^2.30.1",
"ng2-pdf-viewer": "^10.4.0",
"ngx-matomo-client": "^7.0.1",
"parse-duration": "^2.1.3",
"rxjs": "~7.8.2",
"scandit-web-datacapture-barcode": "^6.28.1",
@@ -81,8 +82,8 @@
"postcss": "^8.5.3",
"prettier": "~3.5.2",
"pretty-quick": "~4.0.0",
"tailwindcss": "^4.0.8",
"typescript": "~5.5.0",
"tailwindcss": "^3.4.14",
"typescript": "~5.6.0",
"typescript-eslint": "^8.19.0"
},
"engines": {
@@ -90,6 +91,19 @@
"npm": ">=11.0.0"
}
},
"node_modules/@alloc/quick-lru": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@ampproject/remapping": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
@@ -6013,22 +6027,6 @@
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/@npmcli/package-json/node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/@npmcli/package-json/node_modules/json-parse-even-better-errors": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz",
@@ -6062,23 +6060,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@npmcli/package-json/node_modules/path-scurry": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@npmcli/promise-spawn": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-8.0.2.tgz",
@@ -9850,6 +9831,13 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
"dev": true,
"license": "MIT"
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -9875,6 +9863,13 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"dev": true,
"license": "MIT"
},
"node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -10555,22 +10550,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/cacache/node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/cacache/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
@@ -10624,23 +10603,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/cacache/node_modules/path-scurry": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/cacache/node_modules/rimraf": {
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
@@ -10776,6 +10738,16 @@
"tslib": "^2.0.3"
}
},
"node_modules/camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/caniuse-api": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
@@ -11160,6 +11132,16 @@
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/common-path-prefix": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz",
@@ -11756,19 +11738,6 @@
"postcss": "^8.4.31"
}
},
"node_modules/cssnano/node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antonk52"
}
},
"node_modules/csso": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
@@ -11896,6 +11865,15 @@
"node": ">=4.0.0"
}
},
"node_modules/deep-freeze-strict": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/deep-freeze-strict/-/deep-freeze-strict-1.1.1.tgz",
"integrity": "sha512-QemROZMM2IvhAcCFvahdX2Vbm4S/txeq5rFYU9fh4mQP79WTMW5c/HkQ2ICl1zuzcDZdPZ6zarDxQeQMsVYoNA==",
"dev": true,
"license": "public domain",
"optional": true,
"peer": true
},
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -12073,6 +12051,13 @@
"integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==",
"dev": true
},
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
@@ -12116,6 +12101,13 @@
"node": ">=8"
}
},
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"dev": true,
"license": "MIT"
},
"node_modules/dns-packet": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
@@ -15451,6 +15443,22 @@
"node": ">=8"
}
},
"node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/jake": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz",
@@ -16409,6 +16417,19 @@
}
}
},
"node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antonk52"
}
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -16892,17 +16913,6 @@
"loose-envify": "cli.js"
}
},
"node_modules/lower-case": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"tslib": "^2.0.3"
}
},
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -17298,29 +17308,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/minipass-fetch/node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/minipass-fetch/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true,
"license": "ISC"
},
"node_modules/minipass-fetch/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
@@ -17351,23 +17338,6 @@
"node": ">= 18"
}
},
"node_modules/minipass-fetch/node_modules/path-scurry": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/minipass-fetch/node_modules/rimraf": {
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
@@ -17634,6 +17604,18 @@
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
"thenify-all": "^1.0.0"
}
},
"node_modules/nanoid": {
"version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
@@ -17754,6 +17736,19 @@
"tslib": "^2.8.1"
}
},
"node_modules/ngx-matomo-client": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/ngx-matomo-client/-/ngx-matomo-client-7.0.1.tgz",
"integrity": "sha512-xNjGcusxKb/aH2pjeMon8o5iew/rlzYNT14E8oc14tZwwR2fbQW8jBhYn7XXNbfMmsbjoBK+o4xaThmwC7oPpw==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": "^19.0.0",
"@angular/core": "^19.0.0"
}
},
"node_modules/no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
@@ -17762,8 +17757,10 @@
"optional": true,
"peer": true,
"dependencies": {
"lower-case": "^2.0.2",
"tslib": "^2.0.3"
"deep-freeze-strict": "^1.1.1"
},
"peerDependencies": {
"@ngrx/store": ">=4.0.0"
}
},
"node_modules/node-abi": {
@@ -17932,29 +17929,6 @@
"node": ">=16"
}
},
"node_modules/node-gyp/node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/node-gyp/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true,
"license": "ISC"
},
"node_modules/node-gyp/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
@@ -18001,23 +17975,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/node-gyp/node_modules/path-scurry": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/node-gyp/node_modules/rimraf": {
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
@@ -18314,29 +18271,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/npm-registry-fetch/node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/npm-registry-fetch/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true,
"license": "ISC"
},
"node_modules/npm-registry-fetch/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
@@ -18367,23 +18301,6 @@
"node": ">= 18"
}
},
"node_modules/npm-registry-fetch/node_modules/path-scurry": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/npm-registry-fetch/node_modules/rimraf": {
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
@@ -18670,6 +18587,16 @@
"node": ">=0.10.0"
}
},
"node_modules/object-hash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/object-inspect": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
@@ -19140,6 +19067,30 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"node_modules/path-scurry": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/path-scurry/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true,
"license": "ISC"
},
"node_modules/path-to-regexp": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
@@ -19517,6 +19468,80 @@
"postcss": "^8.4.31"
}
},
"node_modules/postcss-import": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
"dev": true,
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
"resolve": "^1.1.7"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"postcss": "^8.0.0"
}
},
"node_modules/postcss-js": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
"dev": true,
"license": "MIT",
"dependencies": {
"camelcase-css": "^2.0.1"
},
"engines": {
"node": "^12 || ^14 || >= 16"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
"peerDependencies": {
"postcss": "^8.4.21"
}
},
"node_modules/postcss-load-config": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"lilconfig": "^3.0.0",
"yaml": "^2.3.4"
},
"engines": {
"node": ">= 14"
},
"peerDependencies": {
"postcss": ">=8.0.9",
"ts-node": ">=9.0.0"
},
"peerDependenciesMeta": {
"postcss": {
"optional": true
},
"ts-node": {
"optional": true
}
}
},
"node_modules/postcss-loader": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz",
@@ -19775,6 +19800,46 @@
"postcss": "^8.1.0"
}
},
"node_modules/postcss-nested": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"postcss-selector-parser": "^6.1.1"
},
"engines": {
"node": ">=12.0"
},
"peerDependencies": {
"postcss": "^8.2.14"
}
},
"node_modules/postcss-nested/node_modules/postcss-selector-parser": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"dev": true,
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
}
},
"node_modules/postcss-normalize-charset": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz",
@@ -22252,29 +22317,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/stylus/node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/stylus/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true,
"license": "ISC"
},
"node_modules/stylus/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
@@ -22291,18 +22333,71 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/stylus/node_modules/path-scurry": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"node_modules/sucrase": {
"version": "3.35.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
"integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
"dev": true,
"license": "BlueOak-1.0.0",
"license": "MIT",
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
"@jridgewell/gen-mapping": "^0.3.2",
"commander": "^4.0.0",
"glob": "^10.3.10",
"lines-and-columns": "^1.1.6",
"mz": "^2.7.0",
"pirates": "^4.0.1",
"ts-interface-checker": "^0.1.9"
},
"bin": {
"sucrase": "bin/sucrase",
"sucrase-node": "bin/sucrase-node"
},
"engines": {
"node": ">=16 || 14 >=14.18"
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/sucrase/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/sucrase/node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"dev": true,
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^3.1.2",
"minimatch": "^9.0.4",
"minipass": "^7.1.2",
"package-json-from-dist": "^1.0.0",
"path-scurry": "^1.11.1"
},
"bin": {
"glob": "dist/esm/bin.mjs"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/sucrase/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -22379,11 +22474,120 @@
}
},
"node_modules/tailwindcss": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.8.tgz",
"integrity": "sha512-Me7N5CKR+D2A1xdWA5t5+kjjT7bwnxZOE6/yDI/ixJdJokszsn2n++mdU5yJwrsTpqFX2B9ZNMBJDwcqk9C9lw==",
"version": "3.4.17",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
"dev": true,
"license": "MIT"
"license": "MIT",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
"chokidar": "^3.6.0",
"didyoumean": "^1.2.2",
"dlv": "^1.1.3",
"fast-glob": "^3.3.2",
"glob-parent": "^6.0.2",
"is-glob": "^4.0.3",
"jiti": "^1.21.6",
"lilconfig": "^3.1.3",
"micromatch": "^4.0.8",
"normalize-path": "^3.0.0",
"object-hash": "^3.0.0",
"picocolors": "^1.1.1",
"postcss": "^8.4.47",
"postcss-import": "^15.1.0",
"postcss-js": "^4.0.1",
"postcss-load-config": "^4.0.2",
"postcss-nested": "^6.2.0",
"postcss-selector-parser": "^6.1.2",
"resolve": "^1.22.8",
"sucrase": "^3.35.0"
},
"bin": {
"tailwind": "lib/cli.js",
"tailwindcss": "lib/cli.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/tailwindcss/node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
"license": "MIT",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/tailwindcss/node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/tailwindcss/node_modules/postcss-selector-parser": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"dev": true,
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
}
},
"node_modules/tailwindcss/node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"license": "MIT",
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/tapable": {
"version": "2.2.1",
@@ -22607,6 +22811,29 @@
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"node_modules/thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"any-promise": "^1.0.0"
}
},
"node_modules/thenify-all": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
"dev": true,
"license": "MIT",
"dependencies": {
"thenify": ">= 3.1.0 < 4"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/thingies": {
"version": "1.21.0",
"resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz",
@@ -22731,6 +22958,13 @@
"typescript": ">=4.8.4"
}
},
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/ts-loader": {
"version": "9.5.2",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz",
@@ -22928,9 +23162,9 @@
"dev": true
},
"node_modules/typescript": {
"version": "5.5.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",

View File

@@ -43,6 +43,7 @@
"lodash": "^4.17.21",
"moment": "^2.30.1",
"ng2-pdf-viewer": "^10.4.0",
"ngx-matomo-client": "^7.0.1",
"parse-duration": "^2.1.3",
"rxjs": "~7.8.2",
"scandit-web-datacapture-barcode": "^6.28.1",
@@ -91,8 +92,8 @@
"postcss": "^8.5.3",
"prettier": "~3.5.2",
"pretty-quick": "~4.0.0",
"tailwindcss": "^4.0.8",
"typescript": "~5.5.0",
"tailwindcss": "^3.4.14",
"typescript": "~5.6.0",
"typescript-eslint": "^8.19.0"
},
"engines": {