Tracking - Filter, Artikelsuche, Abholfach(Einbuchen, Reservierung, Ausräumen,Fehlende)

This commit is contained in:
Lorenz Hilpert
2024-06-24 15:10:51 +02:00
parent c68706b54f
commit 8ccc29c85a
68 changed files with 13230 additions and 142 deletions

View File

@@ -2,6 +2,7 @@
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "apps",
"projects": {
"@swagger/availability": {
"root": "apps/swagger/availability",

View File

@@ -1,4 +1,4 @@
import { inject, isDevMode, NgModule } from '@angular/core';
import { isDevMode, NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import {
CanActivateCartGuard,
@@ -8,8 +8,6 @@ import {
CanActivateCustomerOrdersWithProcessIdGuard,
CanActivateCustomerWithProcessIdGuard,
CanActivateGoodsInGuard,
CanActivateGoodsOutGuard,
CanActivateGoodsOutWithProcessIdGuard,
CanActivateProductGuard,
CanActivateProductWithProcessIdGuard,
CanActivateRemissionGuard,
@@ -22,9 +20,9 @@ import { MainComponent } from './main.component';
import { PreviewComponent } from './preview';
import { BranchSectionResolver, CustomerSectionResolver, ProcessIdResolver } from './resolvers';
import { TokenLoginComponent, TokenLoginModule } from './token-login';
import { ApplicationService } from '@core/application';
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 = [
{
@@ -45,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

@@ -37,6 +37,8 @@ import { NativeContainerService } from 'native-container';
import { ShellModule } from '@shared/shell';
import { MainComponent } from './main.component';
import { IconModule } from '@shared/components/icon';
import { provideMatomo } from 'ngx-matomo-client';
import { withRouter, withRouteData } from 'ngx-matomo-client';
registerLocaleData(localeDe, localeDeExtra);
registerLocaleData(localeDe, 'de', localeDeExtra);
@@ -46,7 +48,7 @@ export function _appInitializerFactory(
auth: AuthService,
injector: Injector,
scanAdapter: ScanAdapterService,
nativeContainer: NativeContainerService
nativeContainer: NativeContainerService,
) {
return async () => {
const statusElement = document.querySelector('#init-status');
@@ -135,6 +137,7 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
useClass: IsaErrorHandler,
},
{ provide: LOCALE_ID, useValue: 'de-DE' },
provideMatomo({ trackerUrl: 'http://localhost:8080', siteId: '1' }, withRouter(), withRouteData()),
],
bootstrap: [AppComponent],
})

View File

@@ -9,9 +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>
@@ -44,7 +42,7 @@
<img
class="max-h-[19.6875rem] max-w-[12.1875rem] rounded"
(load)="loadImage()"
[src]="item.imageId | productImage: 195:315:true"
[src]="item.imageId | productImage: 195 : 315 : true"
alt="product image"
/>
<ui-icon
@@ -107,7 +105,7 @@
<div class="page-article-details__product-price-info flex flex-col mb-4 flex-nowrap self-end">
<div class="page-article-details__product-price font-bold text-xl self-end" *ngIf="price$ | async; let price">
{{ price?.value?.value | currency: price?.value?.currency:'code' }}
{{ price?.value?.value | currency: price?.value?.currency : 'code' }}
</div>
<div *ngIf="price$ | async; let price" class="page-article-details__product-price-bound self-end">
{{ price?.vat?.vatType | vat: (priceMaintained$ | async) }}
@@ -349,6 +347,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" />
@@ -364,6 +365,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>
@@ -371,6 +375,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/shared/components/icon/src/lib/badge/icon-badge.component';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [
@@ -30,6 +31,7 @@ import { IconBadgeComponent } from 'apps/shared/components/icon/src/lib/badge/ic
OrderDeadlinePipeModule,
ArticleDetailsTextComponent,
IconBadgeComponent,
MatomoModule,
],
exports: [ArticleDetailsComponent, ArticleRecommendationsComponent],
declarations: [ArticleDetailsComponent, ArticleRecommendationsComponent],

View File

@@ -1,8 +1,6 @@
<div class="bg-white rounded py-10 px-4 text-center shadow-[0_-2px_24px_0_#dce2e9] h-full">
<h1 class="text-h3 text-[1.625rem] font-bold mb-[0.375rem]">Artikelsuche</h1>
<p class="text-lg mb-10">
Welchen Artikel suchen Sie?
</p>
<p class="text-lg mb-10">Welchen Artikel suchen Sie?</p>
<ng-container *ngIf="filter$ | async; let filter">
<shared-filter-filter-group-main
class="mb-8 w-full"
@@ -38,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

@@ -67,7 +67,7 @@
class="page-search-result-item__item-price desktop-small:text-p3 font-bold justify-self-end"
[class.page-search-result-item__item-price-primary]="primaryOutletActive"
>
{{ item?.catalogAvailability?.price?.value?.value | currency: 'EUR':'code' }}
{{ item?.catalogAvailability?.price?.value?.value | currency: 'EUR' : 'code' }}
</div>
<div class="page-search-result-item__item-select-bullet justify-self-end">
@@ -75,9 +75,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>

View File

@@ -11,7 +11,7 @@
[hint]="searchboxHint$ | async"
[loading]="fetching$ | async"
[inputGroup]="filter?.input | group: 'main'"
(search)="search({filter, clear: true})"
(search)="search({ filter, clear: true })"
[showDescription]="false"
[scanner]="true"
></shared-filter-input-group-main>
@@ -32,8 +32,7 @@
class="page-search-results__items-count inline-flex flex-row items-center pr-5 text-p3"
[class.mb-4]="primaryOutletActive$ | async"
>
{{ hits ??
0 }}
{{ hits ?? 0 }}
Titel
</div>
</div>
@@ -75,6 +74,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,8 +1,6 @@
<div class="header">
<div class="cleanup-headline">
<h1 class="cleanup-title">
Abholfachbereinigung
</h1>
<h1 class="cleanup-title">Abholfachbereinigung</h1>
<p class="cleanup-paragraph">
Bitte nehmen Sie die<br />
Artikel auf die Fläche.
@@ -30,12 +28,17 @@
*ngFor="let item of compartmentCodeGroup.items; let firstItem = first"
[item]="item"
[showCompartmentCode]="firstItem"
(selectedChange)="setSelectedItem(item, $event)"
[selectable]="item | goodsInCleanupSelectable: selectionRules:selectedItems"
(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)"
matomoTracker
#tracker="matomo"
>
</shared-goods-in-out-order-group-item>
<div class="divider" *ngIf="!lastCompartmentCode"></div>
@@ -63,6 +66,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,10 @@ import { UiSpinnerModule } from 'apps/ui/spinner/src/lib/ui-spinner.module';
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

@@ -25,14 +25,16 @@
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>
</div>
<div class="err" *ngIf="supplierIdError$ | async">
Kein Lieferant vorhanden
</div>
<div class="err" *ngIf="supplierIdError$ | async">Kein Lieferant vorhanden</div>
</ng-container>
</div>
</div>
@@ -74,7 +76,7 @@
</div>
<div class="price">
<strong>{{ orderItem.price | currency: 'EUR':'code' }}</strong>
<strong>{{ orderItem.price | currency: 'EUR' : 'code' }}</strong>
</div>
<div class="status spec">
<span>Status</span>
@@ -99,7 +101,14 @@
</div>
<div class="footer">
<button class="cta-reorder" (click)="showReorderModal(); $event.stopPropagation()" [disabled]="editSsc$ | async">
<button
class="cta-reorder"
(click)="showReorderModal(); $event.stopPropagation()"
[disabled]="editSsc$ | async"
matomoClickCategory="goods-in-list-item"
matomoClickAction="click"
matomoClickName="nachbestellen"
>
Nachbestellen
</button>
</div>

View File

@@ -6,9 +6,19 @@ import { UiInputModule } from '@ui/input';
import { FormsModule } from '@angular/forms';
import { GoodsInListItemComponent } from './goods-in-list-item.component';
import { PipesModule } from 'apps/shared/components/goods-in-out/src/lib/pipes/pipes.module';
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,5 @@
<div class="goods-in-list-headline">
<h1 class="goods-in-list-title">
Wareneingangsliste
</h1>
<h1 class="goods-in-list-title">Wareneingangsliste</h1>
<p class="goods-in-list-paragraph">
Vergeben Sie Meldenummern oder<br />
bestellen Sie Artikel nach
@@ -45,18 +43,36 @@
</ng-template>
<div *ngIf="!(listEmpty$ | async)" class="actions">
<button *ngIf="!editSsc" class="cta-edit-ssc cta-action-primary" (click)="editSsc = true">
<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()">
<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">
<ui-spinner [show]="showSaveSscSpinner$ | async">
Meldenummern speichern
</ui-spinner>
<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 { UiSpinnerModule } from 'apps/ui/spinner/src/lib/ui-spinner.module';
import { GoodsInListItemModule } from './goods-in-list-item/goods-in-list-item.module';
import { GoodsInListComponent } from './goods-in-list.component';
import { MatomoModule } from 'ngx-matomo-client';
@NgModule({
imports: [
@@ -18,6 +19,7 @@ import { GoodsInListComponent } from './goods-in-list.component';
UiScrollContainerModule,
UiOrderByFilterModule,
UiSpinnerModule,
MatomoModule,
],
exports: [],
declarations: [GoodsInListComponent],

View File

@@ -1,8 +1,6 @@
<div class="header">
<div class="reservations-headline">
<h1 class="reservations-title">
Reservierungsliste
</h1>
<h1 class="reservations-title">Reservierungsliste</h1>
<p class="reservations-paragraph">
Hier sehen Sie alle zu<br />
reservierenden Bestellposten.
@@ -31,12 +29,17 @@
*ngFor="let item of compartmentCodeGroup.items; let firstItem = first"
[item]="item"
[showCompartmentCode]="firstItem"
(selectedChange)="setSelectedItem(item, $event)"
[selectable]="item | goodsInReservationSelectable: selectionRules:selectedItems"
(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)"
matomoTracker
#tracker="matomo"
>
</shared-goods-in-out-order-group-item>
<div class="divider" *ngIf="!lastCompartmentCode"></div>
@@ -49,9 +52,7 @@
</ui-scroll-container>
<ng-template #emptyMessage>
<div class="empty-message">
Es sind derzeit keine Reservierungen offen.
</div>
<div class="empty-message">Es sind derzeit keine Reservierungen offen.</div>
</ng-template>
<div class="actions" *ngIf="actions$ | async; let actions">
@@ -62,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,10 @@ import { GoodsInReservationSelectablePipe } from './goods-in-reservation-selecta
import { GoodsInReservationSelectedPipe } from './goods-in-reservation-selected.pipe';
import { GoodsInReservationComponent } from './goods-in-reservation.component';
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

@@ -61,7 +61,10 @@
[class.cta-action-primary]="action.selected"
[class.cta-action-secondary]="!action.selected"
*ngFor="let action of mainActions$ | async"
(click)="handleAction({action})"
(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>
@@ -73,7 +76,13 @@
class="cta-action shadow-action"
[class.cta-action-primary]="action.selected"
[class.cta-action-secondary]="!action.selected"
(click)="handleAction({action, latestCompartmentCode: latestCompartmentInfos?.latestCompartmentCode, latestCompartmentInfo: latestCompartmentInfos?.latestCompartmentInfo })"
(click)="
handleAction({
action,
latestCompartmentCode: latestCompartmentInfos?.latestCompartmentCode,
latestCompartmentInfo: latestCompartmentInfos?.latestCompartmentInfo,
})
"
>
<ui-spinner [show]="(changeActionLoader$ | async) === action.command"
>{{ latestDisplayedCompartmentInfos$ | async | addToPreviousCompartmentCodeLabelPipe }} zubuchen</ui-spinner

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',
@@ -40,6 +41,7 @@ import { SkeletonLoaderComponent } from '@shared/components/loader';
UiSpinnerModule,
OnInitDirective,
SkeletonLoaderComponent,
MatomoModule,
],
})
export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseComponent implements OnInit, AfterViewInit {
@@ -61,7 +63,7 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
fetchingCoverItems$ = this.store.fetchingCoverOrderItems$;
viewFetching$ = combineLatest([this.fetchingItems$, this.orderItems$]).pipe(
map(([fetchingItems, orderItems]) => fetchingItems && orderItems.length === 0)
map(([fetchingItems, orderItems]) => fetchingItems && orderItems.length === 0),
);
selectedCompartmentInfo = this.store.selectedCompartmentInfo;
@@ -93,14 +95,17 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
return `${latestCompartmentInfos?.latestCompartmentCode}_${latestCompartmentInfos?.latestCompartmentInfo}`;
}
return latestCompartmentInfos?.latestCompartmentCode;
})
}),
);
addToPreviousCompartmentActionDisabled$ = combineLatest([this.displayedCompartmentInfo$, this.changeActionDisabled$]).pipe(
map(([compartmentInfo, changeActionDisabled]) => !!compartmentInfo || changeActionDisabled)
map(([compartmentInfo, changeActionDisabled]) => !!compartmentInfo || changeActionDisabled),
);
constructor(private _uiModal: UiModalService, private _activatedRoute: ActivatedRoute) {
constructor(
private _uiModal: UiModalService,
private _activatedRoute: ActivatedRoute,
) {
super();
}
@@ -110,7 +115,7 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
.subscribe(([params, items]) => {
const orderItemSubsetId = +params?.orderItemSubsetId;
const selectedItem: DBHOrderItemListItemDTO = items?.find(
(item: DBHOrderItemListItemDTO) => item?.orderItemSubsetId === orderItemSubsetId
(item: DBHOrderItemListItemDTO) => item?.orderItemSubsetId === orderItemSubsetId,
);
// Trigger functions only if a new item gets selected
@@ -176,7 +181,7 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
},
side: false,
}).path,
{ queryParamsHandling: 'preserve' }
{ queryParamsHandling: 'preserve' },
);
this.listStore.patchOrderItem({
orderItemSubsetId: item.orderItemSubsetId,
@@ -224,17 +229,17 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
switch (type) {
case 'delivery':
this.store.selectedOrderItems.forEach((item) =>
this.store.patchOrderItemSubset({ item, changes: { estimatedShippingDate: date.toISOString() } })
this.store.patchOrderItemSubset({ item, changes: { estimatedShippingDate: date.toISOString() } }),
);
break;
case 'pickup':
this.store.selectedOrderItems.forEach((item) =>
this.store.patchOrderItemSubset({ item, changes: { compartmentStop: date.toISOString() } })
this.store.patchOrderItemSubset({ item, changes: { compartmentStop: date.toISOString() } }),
);
break;
case 'preferred':
this.store.selectedOrderItems.forEach((item) =>
this.store.patchOrderItemSubset({ item, changes: { preferredPickUpDate: date.toISOString() } })
this.store.patchOrderItemSubset({ item, changes: { preferredPickUpDate: date.toISOString() } }),
);
break;
default:
@@ -258,9 +263,9 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
orderItemSubsetId: orderItem.orderItemSubsetId,
compartmentInfo: orderItem.compartmentInfo,
},
{ side: this.side }
{ side: this.side },
).path,
{ queryParams: { buyerNumber: orderItem?.buyerNumber }, queryParamsHandling: 'merge' }
{ queryParams: { buyerNumber: orderItem?.buyerNumber }, queryParamsHandling: 'merge' },
);
}
@@ -275,9 +280,9 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
orderItemSubsetId: orderItem.orderItemSubsetId,
compartmentInfo: orderItem.compartmentInfo,
},
{ side: this.side }
{ side: this.side },
).path,
{ queryParams: { orderItemSubsetId: orderItem.orderItemSubsetId }, queryParamsHandling: 'merge' }
{ queryParams: { orderItemSubsetId: orderItem.orderItemSubsetId }, queryParamsHandling: 'merge' },
);
}
@@ -301,7 +306,7 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
},
side: this.side,
}).path,
{ queryParamsHandling: 'preserve' }
{ queryParamsHandling: 'preserve' },
);
} else {
if (orderItems.every((item) => item.processingStatus === 128)) {

View File

@@ -50,7 +50,7 @@ export class PickupShelfInEditComponent extends PickupShelfDetailsBaseComponent
},
side: this.side,
}).path,
{ queryParamsHandling: 'preserve' }
{ queryParamsHandling: 'preserve' },
);
}
}

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',
@@ -76,7 +77,7 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
map(([filter, defaultFilter]) => {
const filterQueryParams = filter?.getQueryParams();
return !isEqual(filterQueryParams, Filter.create(defaultFilter).getQueryParams());
})
}),
);
fetching$: Observable<boolean> = this.store.fetchingList$;
@@ -117,7 +118,7 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
private _activatedRoute: ActivatedRoute,
private _pickUpShelfInNavigation: PickupShelfInNavigationService,
private _cache: CacheService,
private _router: Router
private _router: Router,
) {}
ngOnInit() {

View File

@@ -26,6 +26,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',
@@ -15,7 +16,7 @@ import { PickupShelfStore } from '../../store';
standalone: true,
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>();
@@ -35,7 +36,7 @@ export class PickUpShelfInMainSideViewComponent implements OnInit {
map(([filter, defaultFilter]) => {
const filterQueryParams = filter?.getQueryParams();
return !isEqual(filterQueryParams, Filter.create(defaultFilter).getQueryParams());
})
}),
);
fetching$: Observable<boolean> = this.store.fetchingList$;

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

@@ -47,6 +47,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',
@@ -33,6 +34,7 @@ import { PickUpShelfDetailsItemsGroupComponent } from '../../shared/pickup-shelf
UiSpinnerModule,
OnInitDirective,
FormsModule,
MatomoModule,
],
providers: [],
})
@@ -65,14 +67,14 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
}
return groups;
})
}),
);
fetchingOrder$ = this.store.fetchingOrder$;
fetchingItems$ = this.store.fetchingOrderItems$;
viewFetching$ = combineLatest([this.orderItems$, this.fetchingItems$]).pipe(
map(([orderItems, fetchingItems]) => orderItems?.length === 0 && fetchingItems)
map(([orderItems, fetchingItems]) => orderItems?.length === 0 && fetchingItems),
);
selectedCompartmentInfo = this.store.selectedCompartmentInfo;
@@ -129,7 +131,7 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
compartmentInfo: item.compartmentInfo,
},
}).path,
{ queryParamsHandling: 'preserve' }
{ queryParamsHandling: 'preserve' },
);
}
}
@@ -156,17 +158,17 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
switch (type) {
case 'delivery': // vsl. Lieferdatum
this.store.orderItems.forEach((item) =>
this.store.patchOrderItemSubset({ item, changes: { estimatedShippingDate: date.toISOString() } })
this.store.patchOrderItemSubset({ item, changes: { estimatedShippingDate: date.toISOString() } }),
);
break;
case 'pickup': // Abholfrist
this.store.orderItems.forEach((item) =>
this.store.patchOrderItemSubset({ item, changes: { compartmentStop: date.toISOString() } })
this.store.patchOrderItemSubset({ item, changes: { compartmentStop: date.toISOString() } }),
);
break;
case 'preferred': // Zurücklegen bis
this.store.orderItems.forEach((item) =>
this.store.patchOrderItemSubset({ item, changes: { preferredPickUpDate: date.toISOString() } })
this.store.patchOrderItemSubset({ item, changes: { preferredPickUpDate: date.toISOString() } }),
);
break;
default:
@@ -191,7 +193,7 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
compartmentInfo: orderItem.compartmentInfo,
},
}).path,
{ queryParams: { buyerNumber: orderItem?.buyerNumber }, queryParamsHandling: 'merge' }
{ queryParams: { buyerNumber: orderItem?.buyerNumber }, queryParamsHandling: 'merge' },
);
}
@@ -207,7 +209,7 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
compartmentInfo: orderItem.compartmentInfo,
},
}).path,
{ queryParams: { orderItemSubsetId: orderItem.orderItemSubsetId }, queryParamsHandling: 'merge' }
{ queryParams: { orderItemSubsetId: orderItem.orderItemSubsetId }, queryParamsHandling: 'merge' },
);
}

View File

@@ -34,8 +34,7 @@
class="page-pickup-shelf-out-list__items-count inline-flex flex-row items-center pr-5 text-p3"
[class.mb-4]="primaryOutletActive$ | async"
>
{{ hits ??
0 }}
{{ hits ?? 0 }}
Titel
</div>
</div>
@@ -89,6 +88,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

@@ -30,6 +30,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',
@@ -50,6 +51,7 @@ import { ScrollContainerDirective } from '@shared/directives/scroll-container';
GroupByPipe,
UiSpinnerModule,
PickupShelfListItemLoaderComponent,
MatomoModule,
],
})
export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
@@ -80,7 +82,7 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
map(([filter, defaultFilter]) => {
const filterQueryParams = filter?.getQueryParams();
return !isEqual(filterQueryParams, Filter.create(defaultFilter).getQueryParams());
})
}),
);
fetching$: Observable<boolean> = this.store.fetchingList$;
@@ -120,9 +122,9 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
actions$ = combineLatest([this.list$, this.selectedIds$]).pipe(
map(([items, selectedIds]) =>
items?.find((item) => selectedIds.find((orderItemSubsetId) => item.orderItemSubsetId === orderItemSubsetId))
items?.find((item) => selectedIds.find((orderItemSubsetId) => item.orderItemSubsetId === orderItemSubsetId)),
),
map((item) => item?.actions?.filter((action) => this.selectionRules(action)))
map((item) => item?.actions?.filter((action) => this.selectionRules(action))),
);
loadingFetchedActionButton$ = new BehaviorSubject<boolean>(false);
@@ -135,7 +137,7 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
private _uiModal: UiModalService,
private _pickUpShelfOutNavigation: PickUpShelfOutNavigationService,
private _cache: CacheService,
private _router: Router
private _router: Router,
) {}
ngOnInit() {
@@ -190,7 +192,7 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
getSelectedItem$(item: DBHOrderItemListItemDTO) {
return this.store.selectedListItems$.pipe(
map((selectedListItems) => selectedListItems?.find((i) => i?.orderItemSubsetId === item?.orderItemSubsetId))
map((selectedListItems) => selectedListItems?.find((i) => i?.orderItemSubsetId === item?.orderItemSubsetId)),
);
}
@@ -204,7 +206,7 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
} else {
return item?.actions?.some((action) => this.selectionRules(action));
}
})
}),
);
}

View File

@@ -19,6 +19,9 @@
[routerLink]="customerDetailsRoute.path"
[queryParams]="customerDetailsRoute.queryParams"
[queryParamsHandling]="'merge'"
matomoClickCategory="pickup-shelf-details-header-nav-menu"
matomoClickAction="click"
matomoClickName="customer-details"
>Kundendetails</a
>
<a
@@ -27,6 +30,9 @@
[routerLink]="ordersRoute.path"
[queryParams]="ordersRoute.queryParams"
[queryParamsHandling]="'merge'"
matomoClickCategory="pickup-shelf-details-header-nav-menu"
matomoClickAction="click"
matomoClickName="customer-orders"
>Bestellungen</a
>
</shared-menu>

View File

@@ -8,6 +8,7 @@ import { IconComponent } from '@shared/components/icon';
import { SharedMenuModule } from '@shared/components/menu';
import { CustomerSearchNavigation } from '@shared/services';
import { CustomerInfoDTO } from '@swagger/crm';
import { MatomoModule } from 'ngx-matomo-client';
import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
@@ -23,7 +24,7 @@ export interface PickUpShelfDetailsHeaderNavMenuComponentState {
standalone: true,
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) {
@@ -39,14 +40,14 @@ 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 }),
),
);
constructor(private _navigation: CustomerSearchNavigation) {

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>
@@ -76,6 +79,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 }}
@@ -88,7 +94,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>
@@ -164,6 +177,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' }}
@@ -192,6 +208,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' }}
@@ -223,6 +242,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' }}

View File

@@ -14,6 +14,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',
@@ -39,6 +40,7 @@ import { toSignal } from '@angular/core/rxjs-interop';
UiDatepickerModule,
PickUpShelfDetailsHeaderNavMenuComponent,
SkeletonLoaderComponent,
MatomoModule,
],
})
export class PickUpShelfDetailsHeaderComponent {
@@ -75,12 +77,12 @@ export class PickUpShelfDetailsHeaderComponent {
latestDate = new Date(
subsetItems?.reduce((a, b) => {
return new Date(a.data.preferredPickUpDate) > new Date(b.data.preferredPickUpDate) ? a : b;
})?.data?.preferredPickUpDate
})?.data?.preferredPickUpDate,
);
}
return latestDate;
})
}),
);
notificationsChannel$: Observable<NotificationChannel> = this.order$.pipe(map((order) => order?.notificationChannels ?? 0));
@@ -115,7 +117,7 @@ export class PickUpShelfDetailsHeaderComponent {
features$ = this.customer$.pipe(
map((customer) => customer?.features || []),
map((features) => features.filter((f) => f.enabled && !!f.description)),
shareReplay()
shareReplay(),
);
statusActions$ = this.orderItem$.pipe(map((orderItem) => orderItem?.actions?.filter((action) => action.enabled === false)));
@@ -123,14 +125,17 @@ export class PickUpShelfDetailsHeaderComponent {
crudaUpdate$ = this.orderItem$.pipe(map((orederItem) => !!(orederItem?.cruda & 4)));
editButtonDisabled$ = combineLatest([this.changeStatusLoader$, this.crudaUpdate$]).pipe(
map(([changeStatusLoader, crudaUpdate]) => changeStatusLoader || !crudaUpdate)
map(([changeStatusLoader, crudaUpdate]) => changeStatusLoader || !crudaUpdate),
);
canEditStatus$ = combineLatest([this.statusActions$, this.crudaUpdate$]).pipe(
map(([statusActions, crudaUpdate]) => statusActions?.length > 0 && crudaUpdate)
map(([statusActions, crudaUpdate]) => statusActions?.length > 0 && crudaUpdate),
);
constructor(private dateAdapter: DateAdapter, private cdr: ChangeDetectorRef) {}
constructor(
private dateAdapter: DateAdapter,
private cdr: ChangeDetectorRef,
) {}
async handleActionClick(action?: KeyValueDTOOfStringAndString) {
this.changeStatusLoader$.next(true);

View File

@@ -50,16 +50,24 @@
<div>{{ orderItem.product?.name }}</div>
</h3>
<div class="history-wrapper flex flex-col items-end justify-center">
<button class="cta-history text-p1" (click)="historyClick.emit(orderItem)">
Historie
</button>
<button class="cta-history text-p1" (click)="historyClick.emit(orderItem)">Historie</button>
<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>
@@ -72,7 +80,12 @@
[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"
matomoTracker
#tracker="matomo"
>
</ui-quantity-dropdown>
<span class="overall-quantity">(von {{ orderItem?.overallQuantity }})</span>
@@ -257,6 +270,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;
@@ -58,6 +59,7 @@ export interface PickUpShelfDetailsItemComponentState {
UiQuantityDropdownModule,
NotificationTypePipe,
NavigateOnClickDirective,
MatomoModule,
],
})
export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfDetailsItemComponentState> implements OnInit {
@@ -104,7 +106,7 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
readonly orderItem$ = this.select((s) => s.orderItem);
emailNotificationDates$ = this.orderItem$.pipe(
switchMap((orderItem) => this._store.getEmailNotificationDate$(orderItem?.orderItemSubsetId))
switchMap((orderItem) => this._store.getEmailNotificationDate$(orderItem?.orderItemSubsetId)),
);
hasEmailNotification$ = this.emailNotificationDates$.pipe(map((dates) => dates?.length > 0));
@@ -114,7 +116,7 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
hasSmsNotification$ = this.smsNotificationDates$.pipe(map((dates) => dates?.length > 0));
canChangeQuantity$ = combineLatest([this.orderItem$, this._store.fetchPartial$]).pipe(
map(([item, partialPickup]) => ([16, 8192].includes(item?.processingStatus) || partialPickup) && item.quantity > 1)
map(([item, partialPickup]) => ([16, 8192].includes(item?.processingStatus) || partialPickup) && item.quantity > 1),
);
get quantity() {
@@ -141,7 +143,7 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
}
readonly selected$ = combineLatest([this.orderItem$, this._store.selectedOrderItemIds$]).pipe(
map(([orderItem, selectedItems]) => selectedItems.includes(orderItem?.orderItemSubsetId))
map(([orderItem, selectedItems]) => selectedItems.includes(orderItem?.orderItemSubsetId)),
);
@Output()
@@ -156,7 +158,7 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
}
readonly selectable$ = combineLatest([this._store.orderItems$, this._store.fetchPartial$]).pipe(
map(([orderItems, fetchPartial]) => orderItems.length > 1 && this.isItemSelectable && fetchPartial)
map(([orderItems, fetchPartial]) => orderItems.length > 1 && this.isItemSelectable && fetchPartial),
);
get receipts() {

View File

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

View File

@@ -2,6 +2,7 @@ import { AsyncPipe, NgFor } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, forwardRef } from '@angular/core';
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';
@@ -12,7 +13,7 @@ import { first, map } from 'rxjs/operators';
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'page-pickup-shelf-details-tags' },
standalone: true,
imports: [NgFor, UiCommonModule, FormsModule, AsyncPipe],
imports: [NgFor, UiCommonModule, FormsModule, AsyncPipe, MatomoModule],
providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => PickUpShelfDetailsTagsComponent), multi: true }],
})
export class PickUpShelfDetailsTagsComponent implements OnInit, OnDestroy, ControlValueAccessor {
@@ -59,7 +60,7 @@ export class PickUpShelfDetailsTagsComponent implements OnInit, OnDestroy, Contr
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

@@ -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',
@@ -32,6 +33,7 @@ import { CacheService } from '@core/cache';
UiCommonModule,
PickupShelfProcessingStatusPipe,
NavigateOnClickDirective,
MatomoModule,
],
providers: [PickupShelfProcessingStatusPipe],
})
@@ -80,13 +82,13 @@ 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(
private _elRef: ElementRef,
private _environment: EnvironmentService,
private _processingStatusPipe: PickupShelfProcessingStatusPipe
private _processingStatusPipe: PickupShelfProcessingStatusPipe,
) {}
onDetailsClick() {
@@ -124,7 +126,7 @@ export class PickUpShelfListItemComponent {
store: 'PickupShelfDetailsStore',
},
[this.item],
{ persist: false, ttl: 1000 }
{ persist: false, ttl: 1000 },
);
}

View File

@@ -11,15 +11,19 @@
[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>
</shared-searchbox>
<ng-container *ngIf="showDescription && uiInput?.description">
<button [uiOverlayTrigger]="tooltipContent" #tooltip="uiOverlayTrigger" class="info-tooltip-button" type="button">
i
</button>
<button [uiOverlayTrigger]="tooltipContent" #tooltip="uiOverlayTrigger" class="info-tooltip-button" type="button">i</button>
<ui-tooltip #tooltipContent yPosition="above" xPosition="after" [yOffset]="-16">
{{ uiInput.description }}
</ui-tooltip>

View File

@@ -6,9 +6,10 @@ import { UiAutocompleteModule } from '@ui/autocomplete';
import { UiTooltipModule } from 'apps/ui/tooltip/src/public-api';
import { UiCommonModule } from '@ui/common';
import { SearchboxModule } from '@shared/components/searchbox';
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

@@ -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 { 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>

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

@@ -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 { 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,23 @@
[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

@@ -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

@@ -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,9 +5,10 @@ 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],
providers: [],

24
config/.htaccess Normal file
View File

@@ -0,0 +1,24 @@
# This file is auto generated by Matomo, do not edit directly
# Please report any issue or improvement directly to the Matomo team.
# First, deny access to all files in this directory
<Files "*">
<IfModule mod_version.c>
<IfVersion < 2.4>
Order Deny,Allow
Deny from All
</IfVersion>
<IfVersion >= 2.4>
Require all denied
</IfVersion>
</IfModule>
<IfModule !mod_version.c>
<IfModule !mod_authz_core.c>
Order Deny,Allow
Deny from All
</IfModule>
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
</IfModule>
</Files>

80
config/config.ini.php Normal file
View File

@@ -0,0 +1,80 @@
; <?php exit; ?> DO NOT REMOVE THIS LINE
; file automatically generated or modified by Matomo; you can manually override the default values in global.ini.php by redefining them in this file.
[database]
host = "db"
username = "matomo"
password = "95a79f96da8344e49c3e10c7f3ef3f60"
dbname = "matomo"
tables_prefix = "matomo_"
charset = "utf8mb4"
[General]
salt = "39e9934fb5c1a4128492dae16fe3b7dc"
trusted_hosts[] = "localhost:8080"
[PluginsInstalled]
PluginsInstalled[] = "Diagnostics"
PluginsInstalled[] = "Login"
PluginsInstalled[] = "CoreAdminHome"
PluginsInstalled[] = "UsersManager"
PluginsInstalled[] = "SitesManager"
PluginsInstalled[] = "Installation"
PluginsInstalled[] = "Monolog"
PluginsInstalled[] = "Intl"
PluginsInstalled[] = "JsTrackerInstallCheck"
PluginsInstalled[] = "CoreVue"
PluginsInstalled[] = "CorePluginsAdmin"
PluginsInstalled[] = "CoreHome"
PluginsInstalled[] = "WebsiteMeasurable"
PluginsInstalled[] = "IntranetMeasurable"
PluginsInstalled[] = "CoreVisualizations"
PluginsInstalled[] = "Proxy"
PluginsInstalled[] = "API"
PluginsInstalled[] = "Widgetize"
PluginsInstalled[] = "Transitions"
PluginsInstalled[] = "LanguagesManager"
PluginsInstalled[] = "Actions"
PluginsInstalled[] = "Dashboard"
PluginsInstalled[] = "MultiSites"
PluginsInstalled[] = "Referrers"
PluginsInstalled[] = "UserLanguage"
PluginsInstalled[] = "DevicesDetection"
PluginsInstalled[] = "Goals"
PluginsInstalled[] = "Ecommerce"
PluginsInstalled[] = "SEO"
PluginsInstalled[] = "Events"
PluginsInstalled[] = "UserCountry"
PluginsInstalled[] = "GeoIp2"
PluginsInstalled[] = "VisitsSummary"
PluginsInstalled[] = "VisitFrequency"
PluginsInstalled[] = "VisitTime"
PluginsInstalled[] = "VisitorInterest"
PluginsInstalled[] = "RssWidget"
PluginsInstalled[] = "Feedback"
PluginsInstalled[] = "TwoFactorAuth"
PluginsInstalled[] = "CoreUpdater"
PluginsInstalled[] = "CoreConsole"
PluginsInstalled[] = "ScheduledReports"
PluginsInstalled[] = "UserCountryMap"
PluginsInstalled[] = "Live"
PluginsInstalled[] = "PrivacyManager"
PluginsInstalled[] = "ImageGraph"
PluginsInstalled[] = "Annotations"
PluginsInstalled[] = "MobileMessaging"
PluginsInstalled[] = "Overlay"
PluginsInstalled[] = "SegmentEditor"
PluginsInstalled[] = "Insights"
PluginsInstalled[] = "Morpheus"
PluginsInstalled[] = "Contents"
PluginsInstalled[] = "BulkTracking"
PluginsInstalled[] = "Resolution"
PluginsInstalled[] = "DevicePlugins"
PluginsInstalled[] = "Heartbeat"
PluginsInstalled[] = "Marketplace"
PluginsInstalled[] = "ProfessionalServices"
PluginsInstalled[] = "UserId"
PluginsInstalled[] = "CustomJsTracker"
PluginsInstalled[] = "Tour"
PluginsInstalled[] = "PagePerformance"
PluginsInstalled[] = "CustomDimensions"

View File

@@ -0,0 +1,12 @@
<?php
return array(
'Matomo\Cache\Backend' => Piwik\DI::autowire('Matomo\Cache\Backend\ArrayCache'),
'Piwik\Translation\Loader\LoaderInterface' => Piwik\DI::autowire('Piwik\Translation\Loader\LoaderCache')
->constructorParameter('loader', Piwik\DI::get('Piwik\Translation\Loader\DevelopmentLoader')),
'Piwik\Translation\Loader\DevelopmentLoader' => Piwik\DI::create()
->constructor(Piwik\DI::get('Piwik\Translation\Loader\JsonFileLoader')),
);

1313
config/global.ini.php Normal file
View File

File diff suppressed because it is too large Load Diff

265
config/global.php Normal file
View File

@@ -0,0 +1,265 @@
<?php
use Matomo\Cache\Eager;
use Piwik\SettingsServer;
return [
'path.root' => PIWIK_DOCUMENT_ROOT,
'path.misc.user' => 'misc/user/',
'path.tmp' => function (\Piwik\Container\Container $c) {
$root = PIWIK_USER_PATH;
// TODO remove that special case and instead have plugins override 'path.tmp' to add the instance id
if ($c->has('ini.General.instance_id')) {
$instanceId = $c->get('ini.General.instance_id');
$instanceId = $instanceId ? '/' . $instanceId : '';
} else {
$instanceId = '';
}
/** @var Piwik\Config\ $config */
$config = $c->get('Piwik\Config');
$general = $config->General;
$tmp = empty($general['tmp_path']) ? '/tmp' : $general['tmp_path'];
return $root . $tmp . $instanceId;
},
'path.tmp.templates' => Piwik\DI::string('{path.tmp}/templates_c'),
'path.cache' => Piwik\DI::string('{path.tmp}/cache/tracker/'),
'view.clearcompiledtemplates.enable' => true,
'twig.cache' => Piwik\DI::string('{path.tmp.templates}'),
'Matomo\Cache\Eager' => function (\Piwik\Container\Container $c) {
$backend = $c->get('Matomo\Cache\Backend');
$cacheId = $c->get('cache.eager.cache_id');
if (SettingsServer::isTrackerApiRequest()) {
$eventToPersist = 'Tracker.end';
$cacheId .= 'tracker';
} else {
$eventToPersist = 'Request.dispatch.end';
$cacheId .= 'ui';
}
$cache = new Eager($backend, $cacheId);
\Piwik\Piwik::addAction($eventToPersist, function () use ($cache) {
$cache->persistCacheIfNeeded(43200);
});
return $cache;
},
'Matomo\Cache\Backend' => function (\Piwik\Container\Container $c) {
// If Piwik is not installed yet, it's possible the tmp/ folder is not writable
// we prevent failing with an unclear message eg. coming from doctrine-cache
// by forcing to use a cache backend which always works ie. array
if (!\Piwik\SettingsPiwik::isMatomoInstalled()) {
$backend = 'array';
} else {
try {
$backend = $c->get('ini.Cache.backend');
} catch (\Piwik\Exception\DI\NotFoundException $ex) {
$backend = 'chained'; // happens if global.ini.php is not available
}
}
return \Piwik\Cache::buildBackend($backend);
},
'cache.eager.cache_id' => function () {
return 'eagercache-' . str_replace(['.', '-'], '', \Piwik\Version::VERSION) . '-';
},
/**
* A list of API query parameters that map to entity IDs, for example, `idGoal` for goals.
*
* If your plugin introduces new entities that can be fetched or manipulated by ID through
* API requests, you should add the query parameters that represent the new entity's IDs
* to this array.
*/
'entities.idNames' => Piwik\DI::add(['idGoal', 'idDimension']),
/**
* If your plugin uses custom query parameters in API requests (that is, query parameters not used
* by a core plugin), and you want to be able to use those query parameters in system tests, you
* will need to add them, via DI, to this array. Otherwise, in system tests, they will be
* silently ignored.
*
* Note: if the query parameter has been added to `'entities.idNames'`, it does not need to be added
* here as well.
*/
'DocumentationGenerator.customParameters' => [],
\Piwik\Log\LoggerInterface::class => Piwik\DI::create(\Piwik\Log\NullLogger::class),
'Piwik\Translation\Loader\LoaderInterface' => Piwik\DI::autowire('Piwik\Translation\Loader\LoaderCache')
->constructorParameter('loader', Piwik\DI::get('Piwik\Translation\Loader\JsonFileLoader')),
'DeviceDetector\Cache\Cache' => Piwik\DI::autowire('Piwik\DeviceDetector\DeviceDetectorCache')->constructor(86400),
// specify plugins to load on demand via DI config. mostly for tests.
'plugins.shouldLoadOnDemand' => [],
// allow users to override plugin hardcoded value and avoid loading on demand
'plugins.shouldNotLoadOnDemand' => [],
'observers.global' => [],
'dev.disable_plugin_update_checks' => false,
/**
* By setting this option to false, the check that the DB schema version matches the version of the source code will
* be no longer performed. Thus it allows you to execute for example a newer version of Matomo with an older Matomo
* database version. Please note disabling this setting is not recommended because often an older DB version is not
* compatible with newer source code.
* If you disable this setting, make sure to execute the updates after updating the source code. The setting can be
* useful if you want to update Matomo without any outage when you know the current source code update will still
* run fine for a short time while in the background the database updates are running.
*/
'EnableDbVersionCheck' => true,
'fileintegrity.ignore' => Piwik\DI::add([
'*.htaccess',
'*web.config',
'bootstrap.php',
'favicon.ico',
'robots.txt',
'.bowerrc',
'.lfsconfig',
'.phpstorm.meta.php',
'config/config.ini.php',
'config/config.php',
'config/common.ini.php',
'config/*.config.ini.php',
'config/manifest.inc.php',
'misc/*.dat',
'misc/*.dat.gz',
'misc/*.mmdb',
'misc/*.mmdb.gz',
'misc/*.bin',
'misc/user/*png',
'misc/user/*svg',
'misc/user/*js',
'misc/user/*/config.ini.php',
'misc/package',
'misc/package/WebAppGallery/*.xml',
'misc/package/WebAppGallery/install.sql',
'plugins/ImageGraph/fonts/unifont.ttf',
'plugins/*/config/tracker.php',
'plugins/*/config/config.php',
'vendor/autoload.php',
'vendor/composer/autoload_real.php',
'vendor/szymach/c-pchart/app/*',
'tmp/*',
// Search engine sites verification
'google*.html',
'BingSiteAuth.xml',
'yandex*.html',
// common files on shared hosters
'php.ini',
'.user.ini',
'error_log',
// Files below are not expected but they used to be present in older Piwik versions and may be still here
// As they are not going to cause any trouble we won't report them as 'File to delete'
'*.coveralls.yml',
'*.scrutinizer.yml',
'*.gitignore',
'*.gitkeep',
'*.gitmodules',
'*.gitattributes',
'*.git-blame-ignore-revs',
'*.bower.json',
'*.travis.yml',
]),
'Piwik\EventDispatcher' => Piwik\DI::autowire()->constructorParameter('observers', Piwik\DI::get('observers.global')),
'login.allowlist.ips' => function (\Piwik\Container\Container $c) {
/** @var Piwik\Config\ $config */
$config = $c->get('Piwik\Config');
$general = $config->General;
$ips = [];
if (!empty($general['login_allowlist_ip']) && is_array($general['login_allowlist_ip'])) {
$ips = $general['login_allowlist_ip'];
} elseif (!empty($general['login_whitelist_ip']) && is_array($general['login_whitelist_ip'])) {
// for BC
$ips = $general['login_whitelist_ip'];
}
$ipsResolved = [];
foreach ($ips as $ip) {
$ip = trim($ip);
if (filter_var($ip, FILTER_VALIDATE_IP) || \Matomo\Network\IPUtils::getIPRangeBounds($ip) !== null) {
$ipsResolved[] = $ip;
} else {
$lazyCache = \Piwik\Cache::getLazyCache();
$cacheKey = 'DNS.' . md5($ip);
$resolvedIps = $lazyCache->fetch($cacheKey);
if (!is_array($resolvedIps)) {
$resolvedIps = [];
$ipFromHost = @gethostbyname($ip);
if (!empty($ipFromHost) && $ipFromHost !== $ip) {
$resolvedIps[] = $ipFromHost;
}
if (function_exists('dns_get_record')) {
$entry = @dns_get_record($ip, DNS_AAAA);
if (
!empty($entry['0']['ipv6'])
&& filter_var($entry['0']['ipv6'], FILTER_VALIDATE_IP)
) {
$resolvedIps[] = $entry['0']['ipv6'];
}
}
$lazyCache->save($cacheKey, $resolvedIps, 30);
}
$ipsResolved = array_merge($ipsResolved, $resolvedIps);
}
}
return $ipsResolved;
},
/**
* This defines a list of hostnames Matomo's Http class will deny requests to. Wildcards (*) can be used in the
* beginning to match any subdomain level or in the end to match any tlds
*/
'http.blocklist.hosts' => [
'*.amazonaws.com',
],
'Piwik\Tracker\VisitorRecognizer' => Piwik\DI::autowire()
->constructorParameter('trustCookiesOnly', Piwik\DI::get('ini.Tracker.trust_visitors_cookies'))
->constructorParameter('visitStandardLength', Piwik\DI::get('ini.Tracker.visit_standard_length'))
->constructorParameter('lookbackNSecondsCustom', Piwik\DI::get('ini.Tracker.window_look_back_for_visitor')),
'Piwik\Tracker\Settings' => Piwik\DI::autowire()
->constructorParameter(
'isSameFingerprintsAcrossWebsites',
Piwik\DI::get('ini.Tracker.enable_fingerprinting_across_websites')
),
'archiving.performance.logger' => null,
\Piwik\CronArchive\Performance\Logger::class => Piwik\DI::autowire()
->constructorParameter('logger', Piwik\DI::get('archiving.performance.logger')),
\Piwik\Concurrency\LockBackend::class => \Piwik\DI::get(\Piwik\Concurrency\LockBackend\MySqlLockBackend::class),
\Piwik\Segment\SegmentsList::class => function () {
return \Piwik\Segment\SegmentsList::get();
}
];

10939
config/manifest.inc.php Normal file
View File

File diff suppressed because it is too large Load Diff

11
db.env Normal file
View File

@@ -0,0 +1,11 @@
MYSQL_PASSWORD=95a79f96da8344e49c3e10c7f3ef3f60
MYSQL_DATABASE=matomo
MYSQL_USER=matomo
MATOMO_DATABASE_ADAPTER=mysql
MATOMO_DATABASE_TABLES_PREFIX=matomo_
MATOMO_DATABASE_USERNAME=matomo
MATOMO_DATABASE_PASSWORD=95a79f96da8344e49c3e10c7f3ef3f60
MATOMO_DATABASE_DBNAME=matomo
MARIADB_ROOT_PASSWORD=95a79f96da8344e49c3e10c7f3ef3f60
MARIADB_AUTO_UPGRADE=1
MARIADB_INITDB_SKIP_TZINFO=1

31
docker-compose.yaml Normal file
View File

@@ -0,0 +1,31 @@
services:
db:
image: mariadb:10.11
command: --max-allowed-packet=64MB
restart: always
volumes:
- db:/var/lib/mysql:Z
environment:
- MYSQL_ROOT_PASSWORD=
- MARIADB_AUTO_UPGRADE=1
- MARIADB_DISABLE_UPGRADE_BACKUP=1
env_file:
- ./db.env
app:
image: matomo
restart: always
volumes:
- ./config:/var/www/html/config:z
- ./logs:/var/www/html/logs:z
- matomo:/var/www/html:z
environment:
- MATOMO_DATABASE_HOST=db
env_file:
- ./db.env
ports:
- 8080:80
volumes:
db:
matomo:

21
package-lock.json generated
View File

@@ -30,6 +30,7 @@
"lodash": "^4.17.21",
"moment": "^2.30.1",
"ng2-pdf-viewer": "^10.2.2",
"ngx-matomo-client": "^6.2.0",
"parse-duration": "^1.1.0",
"rxjs": "~7.8.0",
"scandit-sdk": "^5.15.0",
@@ -10920,6 +10921,18 @@
"@ngrx/store": ">=4.0.0"
}
},
"node_modules/ngx-matomo-client": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/ngx-matomo-client/-/ngx-matomo-client-6.2.0.tgz",
"integrity": "sha512-fPuTuns5WEEvwc/1N2dONyjA/JLMjrK1hTXo9n6jAGuDtymLZhGsoZvRfmLF1fvNuFi0hOO1xRzwgDnUOlsqAQ==",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": "^17.0.0 || ^18.0.0",
"@angular/core": "^17.0.0 || ^18.0.0"
}
},
"node_modules/nice-napi": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
@@ -23991,6 +24004,14 @@
"deep-freeze-strict": "^1.1.1"
}
},
"ngx-matomo-client": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/ngx-matomo-client/-/ngx-matomo-client-6.2.0.tgz",
"integrity": "sha512-fPuTuns5WEEvwc/1N2dONyjA/JLMjrK1hTXo9n6jAGuDtymLZhGsoZvRfmLF1fvNuFi0hOO1xRzwgDnUOlsqAQ==",
"requires": {
"tslib": "^2.3.0"
}
},
"nice-napi": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",

View File

@@ -81,6 +81,7 @@
"lodash": "^4.17.21",
"moment": "^2.30.1",
"ng2-pdf-viewer": "^10.2.2",
"ngx-matomo-client": "^6.2.0",
"parse-duration": "^1.1.0",
"rxjs": "~7.8.0",
"scandit-sdk": "^5.15.0",