@propbinder/mobile-design 0.2.91 → 0.2.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, computed, effect, Injectable, inject, Input, Component, input, Directive, EventEmitter, HostListener, Output, PLATFORM_ID, ElementRef, output, CUSTOM_ELEMENTS_SCHEMA, TemplateRef, ContentChild, forwardRef, ViewChild, afterNextRender, ViewEncapsulation, model, createComponent, ChangeDetectionStrategy, NgZone, HostBinding } from '@angular/core';
2
+ import { signal, computed, effect, Injectable, inject, Input, Component, input, output, Directive, EventEmitter, HostListener, Output, PLATFORM_ID, ElementRef, CUSTOM_ELEMENTS_SCHEMA, TemplateRef, ContentChild, forwardRef, ViewChild, afterNextRender, ViewEncapsulation, model, createComponent, ChangeDetectionStrategy, NgZone, Pipe, HostBinding, InjectionToken } from '@angular/core';
3
3
  import * as i1$1 from '@angular/common';
4
4
  import { CommonModule, isPlatformBrowser } from '@angular/common';
5
5
  import * as i1$3 from '@angular/router';
@@ -689,39 +689,91 @@ class DsMobileGlassSpinnerComponent {
689
689
  */
690
690
  borderRadius = input(0, ...(ngDevMode ? [{ debugName: "borderRadius" }] : []));
691
691
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileGlassSpinnerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
692
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.16", type: DsMobileGlassSpinnerComponent, isStandalone: true, selector: "ds-mobile-glass-spinner", inputs: { spinnerSize: { classPropertyName: "spinnerSize", publicName: "spinnerSize", isSignal: true, isRequired: false, transformFunction: null }, borderRadius: { classPropertyName: "borderRadius", publicName: "borderRadius", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
693
- <div
694
- class="glass-spinner-overlay"
695
- [style.border-radius.px]="borderRadius()"
696
- role="status"
697
- aria-live="polite"
698
- aria-label="Loading">
699
- <div
700
- class="spinner"
701
- [style.width.px]="spinnerSize()"
702
- [style.height.px]="spinnerSize()">
703
- </div>
704
- </div>
692
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.16", type: DsMobileGlassSpinnerComponent, isStandalone: true, selector: "ds-mobile-glass-spinner", inputs: { spinnerSize: { classPropertyName: "spinnerSize", publicName: "spinnerSize", isSignal: true, isRequired: false, transformFunction: null }, borderRadius: { classPropertyName: "borderRadius", publicName: "borderRadius", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
693
+ <div
694
+ class="glass-spinner-overlay"
695
+ [style.border-radius.px]="borderRadius()"
696
+ role="status"
697
+ aria-live="polite"
698
+ aria-label="Loading">
699
+ <div
700
+ class="spinner"
701
+ [style.width.px]="spinnerSize()"
702
+ [style.height.px]="spinnerSize()">
703
+ </div>
704
+ </div>
705
705
  `, isInline: true, styles: [":host{display:contents}.glass-spinner-overlay{position:absolute;inset:0;background:#fff6;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;z-index:9999;border-radius:inherit}.spinner{border:2px solid var(--color-border-neutral-secondary, #E5E5E5);border-top-color:var(--color-accent, #6B5FF5);border-radius:50%;animation:spin .6s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}@media (prefers-color-scheme: dark){.glass-spinner-overlay{background:#0000004d}.spinner{border-color:#fff3;border-top-color:var(--color-accent, #6B5FF5)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
706
706
  }
707
707
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileGlassSpinnerComponent, decorators: [{
708
708
  type: Component,
709
- args: [{ selector: 'ds-mobile-glass-spinner', standalone: true, imports: [CommonModule], template: `
710
- <div
711
- class="glass-spinner-overlay"
712
- [style.border-radius.px]="borderRadius()"
713
- role="status"
714
- aria-live="polite"
715
- aria-label="Loading">
716
- <div
717
- class="spinner"
718
- [style.width.px]="spinnerSize()"
719
- [style.height.px]="spinnerSize()">
720
- </div>
721
- </div>
709
+ args: [{ selector: 'ds-mobile-glass-spinner', standalone: true, imports: [CommonModule], template: `
710
+ <div
711
+ class="glass-spinner-overlay"
712
+ [style.border-radius.px]="borderRadius()"
713
+ role="status"
714
+ aria-live="polite"
715
+ aria-label="Loading">
716
+ <div
717
+ class="spinner"
718
+ [style.width.px]="spinnerSize()"
719
+ [style.height.px]="spinnerSize()">
720
+ </div>
721
+ </div>
722
722
  `, styles: [":host{display:contents}.glass-spinner-overlay{position:absolute;inset:0;background:#fff6;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;z-index:9999;border-radius:inherit}.spinner{border:2px solid var(--color-border-neutral-secondary, #E5E5E5);border-top-color:var(--color-accent, #6B5FF5);border-radius:50%;animation:spin .6s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}@media (prefers-color-scheme: dark){.glass-spinner-overlay{background:#0000004d}.spinner{border-color:#fff3;border-top-color:var(--color-accent, #6B5FF5)}}\n"] }]
723
723
  }], propDecorators: { spinnerSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "spinnerSize", required: false }] }], borderRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "borderRadius", required: false }] }] } });
724
724
 
725
+ class DsMobileCountBadgeComponent {
726
+ count = input.required(...(ngDevMode ? [{ debugName: "count" }] : []));
727
+ variant = input('accent', ...(ngDevMode ? [{ debugName: "variant" }] : []));
728
+ display = computed(() => {
729
+ const n = this.count();
730
+ return n > 99 ? '99+' : `${n}`;
731
+ }, ...(ngDevMode ? [{ debugName: "display" }] : []));
732
+ label = computed(() => `${this.count()} notifikationer`, ...(ngDevMode ? [{ debugName: "label" }] : []));
733
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileCountBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
734
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.16", type: DsMobileCountBadgeComponent, isStandalone: true, selector: "ds-mobile-count-badge", inputs: { count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: true, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "'count-badge count-badge--' + variant()", "attr.aria-label": "label()" } }, ngImport: i0, template: `{{ display() }}`, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;min-width:24px;height:16px;padding:0 6px;border-radius:10px;font-family:Brockmann,system-ui,-apple-system,sans-serif;font-weight:600;font-size:var(--font-size-xs);line-height:1;color:var(--color-on-accent, #ffffff);white-space:nowrap;box-sizing:border-box}:host(.count-badge--accent){background:var(--color-accent, #6B5FF5)}:host(.count-badge--notification){width:16px;height:16px;min-width:16px;padding:0;border-radius:4px;background:var(--content-feedback-bold-red, #b91c1c)}:host(.count-badge--muted){background:rgba(var(--header-content-color-rgb, 255, 255, 255),.2);color:var(--header-content-color, white)}\n"] });
735
+ }
736
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileCountBadgeComponent, decorators: [{
737
+ type: Component,
738
+ args: [{ selector: 'ds-mobile-count-badge', standalone: true, host: {
739
+ '[class]': "'count-badge count-badge--' + variant()",
740
+ '[attr.aria-label]': "label()",
741
+ }, template: `{{ display() }}`, styles: [":host{display:inline-flex;align-items:center;justify-content:center;min-width:24px;height:16px;padding:0 6px;border-radius:10px;font-family:Brockmann,system-ui,-apple-system,sans-serif;font-weight:600;font-size:var(--font-size-xs);line-height:1;color:var(--color-on-accent, #ffffff);white-space:nowrap;box-sizing:border-box}:host(.count-badge--accent){background:var(--color-accent, #6B5FF5)}:host(.count-badge--notification){width:16px;height:16px;min-width:16px;padding:0;border-radius:4px;background:var(--content-feedback-bold-red, #b91c1c)}:host(.count-badge--muted){background:rgba(var(--header-content-color-rgb, 255, 255, 255),.2);color:var(--header-content-color, white)}\n"] }]
742
+ }], propDecorators: { count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }] } });
743
+
744
+ class DsMobileNotificationButtonComponent {
745
+ count = input(0, ...(ngDevMode ? [{ debugName: "count" }] : []));
746
+ clicked = output();
747
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNotificationButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
748
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileNotificationButtonComponent, isStandalone: true, selector: "ds-mobile-notification-button", inputs: { count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, ngImport: i0, template: `
749
+ <ds-icon-button
750
+ icon="remixNotificationLine"
751
+ variant="secondary"
752
+ size="md"
753
+ (clicked)="clicked.emit()"
754
+ [ariaLabel]="'Notifikationer'"
755
+ />
756
+ @if (count() > 0) {
757
+ <ds-mobile-count-badge [count]="count()" variant="notification" />
758
+ }
759
+ `, isInline: true, styles: [":host{position:relative;display:inline-flex;align-items:center;justify-content:center}ds-icon-button{--text-color-default-secondary: var(--color-header-content, white);--text-color-default-primary: var(--color-header-content, white);--color-background-neutral-secondary-hover: rgba(var(--color-header-content-rgb, 255, 255, 255), .1);--color-background-neutral-secondary: transparent}ds-icon-button::ng-deep button{border-radius:50%!important}ds-mobile-count-badge{position:absolute;top:0;right:0;pointer-events:none}\n"], dependencies: [{ kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsMobileCountBadgeComponent, selector: "ds-mobile-count-badge", inputs: ["count", "variant"] }] });
760
+ }
761
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNotificationButtonComponent, decorators: [{
762
+ type: Component,
763
+ args: [{ selector: 'ds-mobile-notification-button', standalone: true, imports: [DsIconButtonComponent, DsMobileCountBadgeComponent], template: `
764
+ <ds-icon-button
765
+ icon="remixNotificationLine"
766
+ variant="secondary"
767
+ size="md"
768
+ (clicked)="clicked.emit()"
769
+ [ariaLabel]="'Notifikationer'"
770
+ />
771
+ @if (count() > 0) {
772
+ <ds-mobile-count-badge [count]="count()" variant="notification" />
773
+ }
774
+ `, styles: [":host{position:relative;display:inline-flex;align-items:center;justify-content:center}ds-icon-button{--text-color-default-secondary: var(--color-header-content, white);--text-color-default-primary: var(--color-header-content, white);--color-background-neutral-secondary-hover: rgba(var(--color-header-content-rgb, 255, 255, 255), .1);--color-background-neutral-secondary: transparent}ds-icon-button::ng-deep button{border-radius:50%!important}ds-mobile-count-badge{position:absolute;top:0;right:0;pointer-events:none}\n"] }]
775
+ }], propDecorators: { count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }] } });
776
+
725
777
  /**
726
778
  * MobilePageBase
727
779
  *
@@ -5835,6 +5887,8 @@ class DsMobilePageMainComponent extends MobilePageBase {
5835
5887
  avatarInitials = input('U', ...(ngDevMode ? [{ debugName: "avatarInitials" }] : []));
5836
5888
  avatarSrc = input('', ...(ngDevMode ? [{ debugName: "avatarSrc" }] : []));
5837
5889
  avatarIconName = input('remixUser3Line', ...(ngDevMode ? [{ debugName: "avatarIconName" }] : []));
5890
+ // Inputs - Notifications
5891
+ notificationCount = input(0, ...(ngDevMode ? [{ debugName: "notificationCount" }] : []));
5838
5892
  // Inputs - Features
5839
5893
  showRefresh = input(true, ...(ngDevMode ? [{ debugName: "showRefresh" }] : []));
5840
5894
  showCondensedHeader = input(true, ...(ngDevMode ? [{ debugName: "showCondensedHeader" }] : []));
@@ -5885,6 +5939,7 @@ class DsMobilePageMainComponent extends MobilePageBase {
5885
5939
  */
5886
5940
  profileMenuItems = input(undefined, ...(ngDevMode ? [{ debugName: "profileMenuItems" }] : []));
5887
5941
  // Outputs
5942
+ notificationClick = output();
5888
5943
  avatarClick = output();
5889
5944
  /**
5890
5945
  * Emitted when a profile menu action is selected.
@@ -6074,7 +6129,7 @@ class DsMobilePageMainComponent extends MobilePageBase {
6074
6129
  this.refresh.emit(event);
6075
6130
  }
6076
6131
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobilePageMainComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
6077
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobilePageMainComponent, isStandalone: true, selector: "ds-mobile-page-main", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, headerTitle: { classPropertyName: "headerTitle", publicName: "headerTitle", isSignal: true, isRequired: false, transformFunction: null }, headerSubtitle: { classPropertyName: "headerSubtitle", publicName: "headerSubtitle", isSignal: true, isRequired: false, transformFunction: null }, firstEntry: { classPropertyName: "firstEntry", publicName: "firstEntry", isSignal: true, isRequired: false, transformFunction: null }, avatarType: { classPropertyName: "avatarType", publicName: "avatarType", isSignal: true, isRequired: false, transformFunction: null }, avatarInitials: { classPropertyName: "avatarInitials", publicName: "avatarInitials", isSignal: true, isRequired: false, transformFunction: null }, avatarSrc: { classPropertyName: "avatarSrc", publicName: "avatarSrc", isSignal: true, isRequired: false, transformFunction: null }, avatarIconName: { classPropertyName: "avatarIconName", publicName: "avatarIconName", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, showCondensedHeader: { classPropertyName: "showCondensedHeader", publicName: "showCondensedHeader", isSignal: true, isRequired: false, transformFunction: null }, scrollThreshold: { classPropertyName: "scrollThreshold", publicName: "scrollThreshold", isSignal: true, isRequired: false, transformFunction: null }, headerFadeDistance: { classPropertyName: "headerFadeDistance", publicName: "headerFadeDistance", isSignal: true, isRequired: false, transformFunction: null }, contentPadding: { classPropertyName: "contentPadding", publicName: "contentPadding", isSignal: true, isRequired: false, transformFunction: null }, profileMenuItems: { classPropertyName: "profileMenuItems", publicName: "profileMenuItems", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { avatarClick: "avatarClick", profileActionSelected: "profileActionSelected", refresh: "refresh", scroll: "scroll" }, host: { properties: { "style.--content-wrapper-padding": "contentPadding()" } }, viewQueries: [{ propertyName: "ionContent", first: true, predicate: IonContent, descendants: true }], usesInheritance: true, ngImport: i0, template: `
6132
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobilePageMainComponent, isStandalone: true, selector: "ds-mobile-page-main", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, headerTitle: { classPropertyName: "headerTitle", publicName: "headerTitle", isSignal: true, isRequired: false, transformFunction: null }, headerSubtitle: { classPropertyName: "headerSubtitle", publicName: "headerSubtitle", isSignal: true, isRequired: false, transformFunction: null }, firstEntry: { classPropertyName: "firstEntry", publicName: "firstEntry", isSignal: true, isRequired: false, transformFunction: null }, avatarType: { classPropertyName: "avatarType", publicName: "avatarType", isSignal: true, isRequired: false, transformFunction: null }, avatarInitials: { classPropertyName: "avatarInitials", publicName: "avatarInitials", isSignal: true, isRequired: false, transformFunction: null }, avatarSrc: { classPropertyName: "avatarSrc", publicName: "avatarSrc", isSignal: true, isRequired: false, transformFunction: null }, avatarIconName: { classPropertyName: "avatarIconName", publicName: "avatarIconName", isSignal: true, isRequired: false, transformFunction: null }, notificationCount: { classPropertyName: "notificationCount", publicName: "notificationCount", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, showCondensedHeader: { classPropertyName: "showCondensedHeader", publicName: "showCondensedHeader", isSignal: true, isRequired: false, transformFunction: null }, scrollThreshold: { classPropertyName: "scrollThreshold", publicName: "scrollThreshold", isSignal: true, isRequired: false, transformFunction: null }, headerFadeDistance: { classPropertyName: "headerFadeDistance", publicName: "headerFadeDistance", isSignal: true, isRequired: false, transformFunction: null }, contentPadding: { classPropertyName: "contentPadding", publicName: "contentPadding", isSignal: true, isRequired: false, transformFunction: null }, profileMenuItems: { classPropertyName: "profileMenuItems", publicName: "profileMenuItems", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { notificationClick: "notificationClick", avatarClick: "avatarClick", profileActionSelected: "profileActionSelected", refresh: "refresh", scroll: "scroll" }, host: { properties: { "style.--content-wrapper-padding": "contentPadding()" } }, viewQueries: [{ propertyName: "ionContent", first: true, predicate: IonContent, descendants: true }], usesInheritance: true, ngImport: i0, template: `
6078
6133
  <!-- Fixed header at top -->
6079
6134
  <ion-header>
6080
6135
  <ion-toolbar>
@@ -6085,8 +6140,12 @@ class DsMobilePageMainComponent extends MobilePageBase {
6085
6140
  <!-- Title - fades in on scroll -->
6086
6141
  <ion-title class="header-main__title">{{ title() }}</ion-title>
6087
6142
 
6088
- <!-- Avatar -->
6143
+ <!-- Notification + Avatar -->
6089
6144
  <div class="header-main__actions">
6145
+ <ds-mobile-notification-button
6146
+ [count]="notificationCount()"
6147
+ (clicked)="notificationClick.emit()"
6148
+ />
6090
6149
  <ds-avatar
6091
6150
  [size]="'md'"
6092
6151
  [type]="avatarType()"
@@ -6151,11 +6210,11 @@ class DsMobilePageMainComponent extends MobilePageBase {
6151
6210
  </div>
6152
6211
  </div>
6153
6212
  </ion-content>
6154
- `, isInline: true, styles: [":host{display:flex;flex-direction:column;align-items:center;height:100%;background:var(--color-header-surface);width:100%}:host ion-header{background:var(--color-header-surface);box-shadow:none;height:72px;min-height:72px;margin-top:var(--app-header-top-offset)}:host ion-header ion-toolbar{--background: var(--color-header-surface);--border-width: 0;--box-shadow: none;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 72px;height:72px;min-height:72px;padding:0}@media (min-width: 768px){:host ion-header{height:88px;min-height:88px}:host ion-header ion-toolbar{--min-height: 88px;height:88px;min-height:88px}}@media (min-width: 768px){:host ion-header{display:none;height:auto}}:host .header-main,:host .header-details,.header-details{display:flex;align-items:center;justify-content:space-between;background:var(--color-header-surface);position:relative}:host .header-main__title,:host .header-details .header-title,.header-details .header-title{position:absolute;left:50%;transform:translate(-50%);font-size:var(--font-size-base);font-weight:600;color:var(--color-header-content);margin:0;padding:0;--color: var(--color-header-content)}.header-details .header-title{transform:translate(-50%) translateY(-100%);opacity:0!important;pointer-events:none;transition:transform .2s ease,opacity .2s ease!important}.header-scrolled .header-details .header-title{opacity:1!important;pointer-events:auto;transform:translate(-50%) translateY(0)}:host .header-details .back-button,.header-details .back-button{background:none;border:none;padding:0;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--color-header-content);transition:opacity var(--transition-duration-fast, .2s) var(--ease-smooth, ease);z-index:10;position:relative}:host .header-details .back-button:hover,.header-details .back-button:hover{opacity:.8}:host .header-details .back-button:active,.header-details .back-button:active{opacity:.6}:host ion-content{--background: var(--color-header-surface);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;border-radius:24px 24px 0 0;overflow:hidden}:host ion-content::part(scroll){display:flex;flex-direction:column;min-height:100%;-webkit-overflow-scrolling:touch}.plt-ios :host ion-content{--background: var(--color-background-neutral-primary)}@media (min-width: 768px){:host ion-content{border-radius:24px 24px 0 0}}:host ion-header[collapse=condense]{display:none}@media (min-width: 768px){:host ion-header[collapse=condense]{display:none}}:host ion-refresher{z-index:0}:host ion-refresher-content{--color: var(--color-header-content)}:host .content-wrapper{width:100%;position:relative;z-index:20;flex:1;display:flex;flex-direction:column;background:var(--color-background-neutral-primary);border-radius:24px 24px 0 0;overflow:visible;transform:translateZ(0);will-change:transform;isolation:isolate;box-shadow:0 300px 0 0 var(--color-background-neutral-primary);padding-top:0;padding-left:var(--content-wrapper-padding, 20px);padding-right:var(--content-wrapper-padding, 20px);padding-bottom:calc(var(--mobile-content-spacing) + var(--mobile-tab-bar-height) + var(--app-safe-bottom, 0px))}:host .content-inner{max-width:640px;margin:0 auto;width:100%}@media (min-width: 768px){:host .content-wrapper{padding-top:0;padding-left:var(--content-wrapper-padding, 20px);padding-right:var(--content-wrapper-padding, 20px)}}:host .header-expandable{background:var(--color-header-surface);padding:32px 20px 24px;color:var(--header-content-color, white);position:sticky;top:0;z-index:5;transition:opacity .1s ease-out,transform .1s ease-out;flex:0 0 auto;min-height:-moz-min-content;min-height:min-content}:host .header-expandable-inner{display:flex;flex-direction:column;gap:20px;max-width:640px;margin:0 auto}:host .header-expandable__text{margin-bottom:0;gap:4px;display:flex;flex-direction:column}:host .header-expandable__title{font-size:var(--font-size-2xl);font-weight:600;color:var(--header-content-color, white);margin:0}:host .header-expandable__subtitle{font-size:var(--font-size-sm);font-weight:400;color:var(--header-content-color, white);opacity:.85;margin:0}@media (min-width: 768px){:host .header-expandable{padding:48px 20px 32px}:host .header-expandable__title{font-size:var(--font-size-3xl)}:host .header-expandable__subtitle{font-size:var(--font-size-base)}}@media (min-width: 992px){:host .header-expandable{padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){:host .header-expandable{padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){:host .header-expandable{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){:host .header-expandable{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}\n", ".header-main{padding:0 20px;height:72px}.header-main__title{transform:translate(-50%) translateY(-100%);opacity:0;transition:transform .6s ease,opacity .6s ease;padding:0;--color: var(--color-header-content)}.header-scrolled .header-main__title{opacity:1;transform:translate(-50%) translateY(0)}.header-main__actions{display:flex;align-items:center;gap:8px}.header-main__actions ds-avatar{cursor:pointer;-webkit-tap-highlight-color:transparent}@keyframes logoSlideIn{0%{transform:translateY(-200%);scale:.75;opacity:0}to{transform:translateY(0);scale:1;opacity:1}}.logo--first-entry{animation:logoSlideIn .75s var(--spring-curve-bouncy) .7s both}@media (min-width: 768px){.header-main{padding:16px 24px;height:88px}.header-main__title{display:none}}ion-refresher{--color: var(--color-header-content)}ion-refresher-content{--color: var(--color-header-content)}ion-refresher-content::part(spinner){color:var(--color-header-content)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: DsLogoComponent, selector: "ds-logo", inputs: ["variant", "size", "customHeight", "customWidth"] }, { kind: "component", type: DsMobileGlassSpinnerComponent, selector: "ds-mobile-glass-spinner", inputs: ["spinnerSize", "borderRadius"] }] });
6213
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;align-items:center;height:100%;background:var(--color-header-surface);width:100%}:host ion-header{background:var(--color-header-surface);box-shadow:none;height:72px;min-height:72px;margin-top:var(--app-header-top-offset)}:host ion-header ion-toolbar{--background: var(--color-header-surface);--border-width: 0;--box-shadow: none;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 72px;height:72px;min-height:72px;padding:0}@media (min-width: 768px){:host ion-header{height:88px;min-height:88px}:host ion-header ion-toolbar{--min-height: 88px;height:88px;min-height:88px}}@media (min-width: 768px){:host ion-header{display:none;height:auto}}:host .header-main,:host .header-details,.header-details{display:flex;align-items:center;justify-content:space-between;background:var(--color-header-surface);position:relative}:host .header-main__title,:host .header-details .header-title,.header-details .header-title{position:absolute;left:50%;transform:translate(-50%);font-size:var(--font-size-base);font-weight:600;color:var(--color-header-content);margin:0;padding:0;--color: var(--color-header-content)}.header-details .header-title{transform:translate(-50%) translateY(-100%);opacity:0!important;pointer-events:none;transition:transform .2s ease,opacity .2s ease!important}.header-scrolled .header-details .header-title{opacity:1!important;pointer-events:auto;transform:translate(-50%) translateY(0)}:host .header-details .back-button,.header-details .back-button{background:none;border:none;padding:0;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--color-header-content);transition:opacity var(--transition-duration-fast, .2s) var(--ease-smooth, ease);z-index:10;position:relative}:host .header-details .back-button:hover,.header-details .back-button:hover{opacity:.8}:host .header-details .back-button:active,.header-details .back-button:active{opacity:.6}:host ion-content{--background: var(--color-header-surface);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;border-radius:24px 24px 0 0;overflow:hidden}:host ion-content::part(scroll){display:flex;flex-direction:column;min-height:100%;-webkit-overflow-scrolling:touch}.plt-ios :host ion-content{--background: var(--color-background-neutral-primary)}@media (min-width: 768px){:host ion-content{border-radius:24px 24px 0 0}}:host ion-header[collapse=condense]{display:none}@media (min-width: 768px){:host ion-header[collapse=condense]{display:none}}:host ion-refresher{z-index:0}:host ion-refresher-content{--color: var(--color-header-content)}:host .content-wrapper{width:100%;position:relative;z-index:20;flex:1;display:flex;flex-direction:column;background:var(--color-background-neutral-primary);border-radius:24px 24px 0 0;overflow:visible;transform:translateZ(0);will-change:transform;isolation:isolate;box-shadow:0 300px 0 0 var(--color-background-neutral-primary);padding-top:0;padding-left:var(--content-wrapper-padding, 20px);padding-right:var(--content-wrapper-padding, 20px);padding-bottom:calc(var(--mobile-content-spacing) + var(--mobile-tab-bar-height) + var(--app-safe-bottom, 0px))}:host .content-inner{max-width:640px;margin:0 auto;width:100%}@media (min-width: 768px){:host .content-wrapper{padding-top:0;padding-left:var(--content-wrapper-padding, 20px);padding-right:var(--content-wrapper-padding, 20px)}}:host .header-expandable{background:var(--color-header-surface);padding:32px 20px 24px;color:var(--header-content-color, white);position:sticky;top:0;z-index:5;transition:opacity .1s ease-out,transform .1s ease-out;flex:0 0 auto;min-height:-moz-min-content;min-height:min-content}:host .header-expandable-inner{display:flex;flex-direction:column;gap:20px;max-width:640px;margin:0 auto}:host .header-expandable__text{margin-bottom:0;gap:4px;display:flex;flex-direction:column}:host .header-expandable__title{font-size:var(--font-size-2xl);font-weight:600;color:var(--header-content-color, white);margin:0}:host .header-expandable__subtitle{font-size:var(--font-size-sm);font-weight:400;color:var(--header-content-color, white);opacity:.85;margin:0}@media (min-width: 768px){:host .header-expandable{padding:48px 20px 32px}:host .header-expandable__title{font-size:var(--font-size-3xl)}:host .header-expandable__subtitle{font-size:var(--font-size-base)}}@media (min-width: 992px){:host .header-expandable{padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){:host .header-expandable{padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){:host .header-expandable{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){:host .header-expandable{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}\n", ".header-main{padding:0 20px;height:72px}.header-main__title{transform:translate(-50%) translateY(-100%);opacity:0;transition:transform .6s ease,opacity .6s ease;padding:0;--color: var(--color-header-content)}.header-scrolled .header-main__title{opacity:1;transform:translate(-50%) translateY(0)}.header-main__actions{display:flex;align-items:center;gap:8px}.header-main__actions ds-avatar{cursor:pointer;-webkit-tap-highlight-color:transparent}@keyframes logoSlideIn{0%{transform:translateY(-200%);scale:.75;opacity:0}to{transform:translateY(0);scale:1;opacity:1}}.logo--first-entry{animation:logoSlideIn .75s var(--spring-curve-bouncy) .7s both}@media (min-width: 768px){.header-main{padding:16px 24px;height:88px}.header-main__title{display:none}}ion-refresher{--color: var(--color-header-content)}ion-refresher-content{--color: var(--color-header-content)}ion-refresher-content::part(spinner){color:var(--color-header-content)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: DsLogoComponent, selector: "ds-logo", inputs: ["variant", "size", "customHeight", "customWidth"] }, { kind: "component", type: DsMobileGlassSpinnerComponent, selector: "ds-mobile-glass-spinner", inputs: ["spinnerSize", "borderRadius"] }, { kind: "component", type: DsMobileNotificationButtonComponent, selector: "ds-mobile-notification-button", inputs: ["count"], outputs: ["clicked"] }] });
6155
6214
  }
6156
6215
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobilePageMainComponent, decorators: [{
6157
6216
  type: Component,
6158
- args: [{ selector: 'ds-mobile-page-main', standalone: true, imports: [CommonModule, IonHeader, IonToolbar, IonTitle, IonContent, IonRefresher, IonRefresherContent, DsAvatarComponent, DsLogoComponent, DsMobileGlassSpinnerComponent], host: {
6217
+ args: [{ selector: 'ds-mobile-page-main', standalone: true, imports: [CommonModule, IonHeader, IonToolbar, IonTitle, IonContent, IonRefresher, IonRefresherContent, DsAvatarComponent, DsLogoComponent, DsMobileGlassSpinnerComponent, DsMobileNotificationButtonComponent], host: {
6159
6218
  '[style.--content-wrapper-padding]': 'contentPadding()'
6160
6219
  }, template: `
6161
6220
  <!-- Fixed header at top -->
@@ -6168,8 +6227,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
6168
6227
  <!-- Title - fades in on scroll -->
6169
6228
  <ion-title class="header-main__title">{{ title() }}</ion-title>
6170
6229
 
6171
- <!-- Avatar -->
6230
+ <!-- Notification + Avatar -->
6172
6231
  <div class="header-main__actions">
6232
+ <ds-mobile-notification-button
6233
+ [count]="notificationCount()"
6234
+ (clicked)="notificationClick.emit()"
6235
+ />
6173
6236
  <ds-avatar
6174
6237
  [size]="'md'"
6175
6238
  [type]="avatarType()"
@@ -6238,7 +6301,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
6238
6301
  }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { ionContent: [{
6239
6302
  type: ViewChild,
6240
6303
  args: [IonContent]
6241
- }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], headerTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerTitle", required: false }] }], headerSubtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerSubtitle", required: false }] }], firstEntry: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstEntry", required: false }] }], avatarType: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarType", required: false }] }], avatarInitials: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarInitials", required: false }] }], avatarSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarSrc", required: false }] }], avatarIconName: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarIconName", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], showCondensedHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCondensedHeader", required: false }] }], scrollThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollThreshold", required: false }] }], headerFadeDistance: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerFadeDistance", required: false }] }], contentPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentPadding", required: false }] }], profileMenuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "profileMenuItems", required: false }] }], avatarClick: [{ type: i0.Output, args: ["avatarClick"] }], profileActionSelected: [{ type: i0.Output, args: ["profileActionSelected"] }], refresh: [{ type: i0.Output, args: ["refresh"] }], scroll: [{ type: i0.Output, args: ["scroll"] }] } });
6304
+ }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], headerTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerTitle", required: false }] }], headerSubtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerSubtitle", required: false }] }], firstEntry: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstEntry", required: false }] }], avatarType: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarType", required: false }] }], avatarInitials: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarInitials", required: false }] }], avatarSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarSrc", required: false }] }], avatarIconName: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarIconName", required: false }] }], notificationCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "notificationCount", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], showCondensedHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCondensedHeader", required: false }] }], scrollThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollThreshold", required: false }] }], headerFadeDistance: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerFadeDistance", required: false }] }], contentPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentPadding", required: false }] }], profileMenuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "profileMenuItems", required: false }] }], notificationClick: [{ type: i0.Output, args: ["notificationClick"] }], avatarClick: [{ type: i0.Output, args: ["avatarClick"] }], profileActionSelected: [{ type: i0.Output, args: ["profileActionSelected"] }], refresh: [{ type: i0.Output, args: ["refresh"] }], scroll: [{ type: i0.Output, args: ["scroll"] }] } });
6242
6305
 
6243
6306
  /**
6244
6307
  * DsMobileInlineTabsComponent
@@ -6297,16 +6360,16 @@ class DsMobileInlineTabsComponent {
6297
6360
  }
6298
6361
  </div>
6299
6362
  } @else if (tab.badge && tab.badge > 0) {
6300
- <span class="tab-badge">{{ tab.badge }}</span>
6363
+ <ds-mobile-count-badge [count]="tab.badge" variant="muted" />
6301
6364
  }
6302
6365
  </button>
6303
6366
  }
6304
6367
  </div>
6305
- `, isInline: true, styles: [".filter-tabs{display:flex;align-items:center;gap:8px;flex-wrap:wrap;height:40px}.filter-tab{min-width:48px;justify-content:center;padding:0 12px;height:32px;border-radius:20px;background:transparent;border:none;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;color:rgba(var(--header-content-color-rgb, 255, 255, 255),.6);cursor:pointer;transition:all .2s ease;display:flex;align-items:center;gap:6px;white-space:nowrap}.filter-tab.active{background:var(--color-header-accent, #5d5fef);color:var(--color-on-header-accent, white)}.filter-tab:hover:not(.active){background:rgba(var(--header-content-color-rgb, 255, 255, 255),.1)}.tab-badge{min-width:24px;height:16px;padding:0 6px;border-radius:10px;background:rgba(var(--header-content-color-rgb, 255, 255, 255),.2);color:var(--header-content-color, white);font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:600;display:flex;align-items:center;justify-content:center;line-height:1}.filter-tab.active .tab-badge{background:rgba(var(--color-on-header-accent-rgb, 255, 255, 255),.3);color:var(--color-on-header-accent, white)}.filter-tab.has-envelope{gap:0}.envelope-wrapper{position:relative;display:inline-flex;align-items:center;justify-content:center;margin-left:4px}.envelope-badge{position:absolute;top:-3px;right:-5px;background-color:#ff3b30;color:#fff;font-size:10px;font-weight:700;border-radius:9999px;min-width:12px;height:12px;display:flex;align-items:center;justify-content:center;padding:0;line-height:1;box-shadow:0 1px 2px #00000026;z-index:10}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }] });
6368
+ `, isInline: true, styles: [".filter-tabs{display:flex;align-items:center;gap:8px;flex-wrap:wrap;height:40px}.filter-tab{min-width:48px;justify-content:center;padding:0 12px;height:32px;border-radius:20px;background:transparent;border:none;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;color:rgba(var(--header-content-color-rgb, 255, 255, 255),.6);cursor:pointer;transition:all .2s ease;display:flex;align-items:center;gap:6px;white-space:nowrap}.filter-tab.active{background:var(--color-header-accent, #5d5fef);color:var(--color-on-header-accent, white)}.filter-tab:hover:not(.active){background:rgba(var(--header-content-color-rgb, 255, 255, 255),.1)}.filter-tab.active ds-mobile-count-badge{--header-content-color-rgb: var(--color-on-header-accent-rgb, 255, 255, 255);--header-content-color: var(--color-on-header-accent, white)}.filter-tab.has-envelope{gap:0}.envelope-wrapper{position:relative;display:inline-flex;align-items:center;justify-content:center;margin-left:4px}.envelope-badge{position:absolute;top:-3px;right:-5px;background-color:#ff3b30;color:#fff;font-size:10px;font-weight:700;border-radius:9999px;min-width:12px;height:12px;display:flex;align-items:center;justify-content:center;padding:0;line-height:1;box-shadow:0 1px 2px #00000026;z-index:10}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsMobileCountBadgeComponent, selector: "ds-mobile-count-badge", inputs: ["count", "variant"] }] });
6306
6369
  }
6307
6370
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileInlineTabsComponent, decorators: [{
6308
6371
  type: Component,
6309
- args: [{ selector: 'ds-mobile-inline-tabs', standalone: true, imports: [CommonModule, DsIconComponent], template: `
6372
+ args: [{ selector: 'ds-mobile-inline-tabs', standalone: true, imports: [CommonModule, DsIconComponent, DsMobileCountBadgeComponent], template: `
6310
6373
  <div class="filter-tabs">
6311
6374
  @for (tab of tabs(); track tab.id) {
6312
6375
  <button
@@ -6327,12 +6390,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
6327
6390
  }
6328
6391
  </div>
6329
6392
  } @else if (tab.badge && tab.badge > 0) {
6330
- <span class="tab-badge">{{ tab.badge }}</span>
6393
+ <ds-mobile-count-badge [count]="tab.badge" variant="muted" />
6331
6394
  }
6332
6395
  </button>
6333
6396
  }
6334
6397
  </div>
6335
- `, styles: [".filter-tabs{display:flex;align-items:center;gap:8px;flex-wrap:wrap;height:40px}.filter-tab{min-width:48px;justify-content:center;padding:0 12px;height:32px;border-radius:20px;background:transparent;border:none;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;color:rgba(var(--header-content-color-rgb, 255, 255, 255),.6);cursor:pointer;transition:all .2s ease;display:flex;align-items:center;gap:6px;white-space:nowrap}.filter-tab.active{background:var(--color-header-accent, #5d5fef);color:var(--color-on-header-accent, white)}.filter-tab:hover:not(.active){background:rgba(var(--header-content-color-rgb, 255, 255, 255),.1)}.tab-badge{min-width:24px;height:16px;padding:0 6px;border-radius:10px;background:rgba(var(--header-content-color-rgb, 255, 255, 255),.2);color:var(--header-content-color, white);font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:600;display:flex;align-items:center;justify-content:center;line-height:1}.filter-tab.active .tab-badge{background:rgba(var(--color-on-header-accent-rgb, 255, 255, 255),.3);color:var(--color-on-header-accent, white)}.filter-tab.has-envelope{gap:0}.envelope-wrapper{position:relative;display:inline-flex;align-items:center;justify-content:center;margin-left:4px}.envelope-badge{position:absolute;top:-3px;right:-5px;background-color:#ff3b30;color:#fff;font-size:10px;font-weight:700;border-radius:9999px;min-width:12px;height:12px;display:flex;align-items:center;justify-content:center;padding:0;line-height:1;box-shadow:0 1px 2px #00000026;z-index:10}\n"] }]
6398
+ `, styles: [".filter-tabs{display:flex;align-items:center;gap:8px;flex-wrap:wrap;height:40px}.filter-tab{min-width:48px;justify-content:center;padding:0 12px;height:32px;border-radius:20px;background:transparent;border:none;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;color:rgba(var(--header-content-color-rgb, 255, 255, 255),.6);cursor:pointer;transition:all .2s ease;display:flex;align-items:center;gap:6px;white-space:nowrap}.filter-tab.active{background:var(--color-header-accent, #5d5fef);color:var(--color-on-header-accent, white)}.filter-tab:hover:not(.active){background:rgba(var(--header-content-color-rgb, 255, 255, 255),.1)}.filter-tab.active ds-mobile-count-badge{--header-content-color-rgb: var(--color-on-header-accent-rgb, 255, 255, 255);--header-content-color: var(--color-on-header-accent, white)}.filter-tab.has-envelope{gap:0}.envelope-wrapper{position:relative;display:inline-flex;align-items:center;justify-content:center;margin-left:4px}.envelope-badge{position:absolute;top:-3px;right:-5px;background-color:#ff3b30;color:#fff;font-size:10px;font-weight:700;border-radius:9999px;min-width:12px;height:12px;display:flex;align-items:center;justify-content:center;padding:0;line-height:1;box-shadow:0 1px 2px #00000026;z-index:10}\n"] }]
6336
6399
  }], propDecorators: { tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: true }] }], activeTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTab", required: true }] }], tabChange: [{ type: i0.Output, args: ["tabChange"] }] } });
6337
6400
 
6338
6401
  /**
@@ -11756,7 +11819,7 @@ class DsMobileModalBaseComponent extends MobileModalBase {
11756
11819
  this.headerMeta() ||
11757
11820
  this.hasContentInSlot(this.headerLeading) ||
11758
11821
  this.hasContentInSlot(this.headerMain) ||
11759
- this.hasContentInSlot(this.headerTrailing));
11822
+ !!this.headerTrailing);
11760
11823
  }
11761
11824
  /**
11762
11825
  * Check whether header-leading slot has actual projected content.
@@ -11764,6 +11827,9 @@ class DsMobileModalBaseComponent extends MobileModalBase {
11764
11827
  hasHeaderLeadingContent() {
11765
11828
  return this.hasContentInSlot(this.headerLeading);
11766
11829
  }
11830
+ hasHeaderTrailingContent() {
11831
+ return !!this.headerTrailing;
11832
+ }
11767
11833
  /**
11768
11834
  * Check if a content child slot has actual content
11769
11835
  */
@@ -19246,11 +19312,14 @@ class DsMobileTabBarComponent {
19246
19312
  * ```
19247
19313
  */
19248
19314
  profileMenuItems;
19315
+ // Notification inputs
19316
+ notificationCount = 0;
19249
19317
  moreMenuItems = [];
19250
19318
  moreMenuLabel = 'Mere';
19251
19319
  moreMenuIcon = 'remixGridLine';
19252
19320
  moreMenuIconActive = 'remixGridFill';
19253
19321
  // Outputs
19322
+ notificationClick = new EventEmitter();
19254
19323
  avatarClick = new EventEmitter();
19255
19324
  /**
19256
19325
  * Emitted when a profile menu action is selected.
@@ -19836,7 +19905,7 @@ class DsMobileTabBarComponent {
19836
19905
  }
19837
19906
  }
19838
19907
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileTabBarComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
19839
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileTabBarComponent, isStandalone: true, selector: "ds-mobile-tab-bar", inputs: { tabs: "tabs", avatarType: "avatarType", avatarInitials: "avatarInitials", avatarSrc: "avatarSrc", avatarIconName: "avatarIconName", profileMenuItems: "profileMenuItems", moreMenuItems: "moreMenuItems", moreMenuLabel: "moreMenuLabel", moreMenuIcon: "moreMenuIcon", moreMenuIconActive: "moreMenuIconActive" }, outputs: { avatarClick: "avatarClick", profileActionSelected: "profileActionSelected", moreMenuItemSelected: "moreMenuItemSelected" }, ngImport: i0, template: `
19908
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileTabBarComponent, isStandalone: true, selector: "ds-mobile-tab-bar", inputs: { tabs: "tabs", avatarType: "avatarType", avatarInitials: "avatarInitials", avatarSrc: "avatarSrc", avatarIconName: "avatarIconName", profileMenuItems: "profileMenuItems", notificationCount: "notificationCount", moreMenuItems: "moreMenuItems", moreMenuLabel: "moreMenuLabel", moreMenuIcon: "moreMenuIcon", moreMenuIconActive: "moreMenuIconActive" }, outputs: { notificationClick: "notificationClick", avatarClick: "avatarClick", profileActionSelected: "profileActionSelected", moreMenuItemSelected: "moreMenuItemSelected" }, ngImport: i0, template: `
19840
19909
  <ion-tab-bar [attr.slot]="isDesktop() ? 'top' : 'bottom'" class="ds-tab-bar" [class.ds-tab-bar--desktop]="isDesktop()">
19841
19910
  <!-- Logo (desktop only, full logo in header) -->
19842
19911
  <div class="ds-tab-bar__logo">
@@ -19879,16 +19948,20 @@ class DsMobileTabBarComponent {
19879
19948
  }
19880
19949
  </div>
19881
19950
 
19882
- <!-- Avatar (desktop only, positioned via CSS) -->
19951
+ <!-- Notification + Avatar (desktop only, positioned via CSS) -->
19883
19952
  <div class="ds-tab-bar__actions">
19953
+ <ds-mobile-notification-button
19954
+ [count]="notificationCount"
19955
+ (clicked)="notificationClick.emit()"
19956
+ />
19884
19957
  <ds-avatar [size]="'md'" [type]="avatarType" [initials]="avatarInitials" [src]="avatarSrc" [iconName]="avatarIconName" (click)="handleAvatarClick()" />
19885
19958
  </div>
19886
19959
  </ion-tab-bar>
19887
- `, isInline: true, styles: [":host{--ds-tab-bar-height: 64px}@media (min-width: 768px){:host{display:block;--ds-tab-bar-height: 64px}}@media (max-width: 767px){:host{display:contents}}ion-tabs.ds-tabs-wrapper{height:100%;background:var(--color-header-surface)}ion-tab-button:before,ion-tab-button:after{content:none!important;display:none!important}ion-tab-button[title]:before,ion-tab-button[title]:after{display:none!important}ion-tab-button::part(native):before,ion-tab-button::part(native):after{display:none!important}.ds-tab-bar{--background: var(--color-background-neutral-primary);transition:transform .2s ease-in-out}ion-tab-bar[slot=bottom]{border-top:1px solid var(--border-color-default);padding-top:8px;padding-bottom:max(8px,calc(var(--app-safe-bottom, 0px) - 16px));padding-left:12px;padding-right:12px}@media (max-width: 767px){ion-tab-bar[slot=bottom]{position:fixed;bottom:0;left:0;right:0;z-index:100}}@media (max-width: 767px){:host-context(.plt-android) ion-tab-bar[slot=bottom]{padding-bottom:max(8px,var(--app-safe-bottom, 0px))!important}}@media (max-width: 767px){:host-context(ion-tabs:has(ds-mobile-page-details)) .ds-tab-bar{transform:translateY(100%);transition:transform .3s ease}}.ds-tab-bar__logo,.ds-tab-bar__actions{display:none}.ds-tab-bar__tabs{display:flex;width:100%;justify-content:space-around;align-items:center}@media (min-width: 769px){ion-tab-bar[slot=bottom]{padding-bottom:var(--app-safe-bottom, 0px)}}@media (display-mode: standalone){ion-tab-bar[slot=bottom]{padding-bottom:var(--app-safe-bottom, 0px)!important}}@media (min-width: 768px){:host[slot=top]{order:-1!important}:host ion-tab-bar{position:relative!important;bottom:auto!important;top:0!important}ion-tab-bar[slot=top]{--background: var(--color-header-surface);position:relative!important;display:flex!important;align-items:center;padding:12px 24px;height:64px;max-width:none;bottom:auto!important;top:0!important}ion-tab-bar[slot=bottom]{position:relative!important;bottom:auto!important}ion-tabs>div:not([slot]){order:1!important}.ds-tab-bar__logo{display:flex;position:absolute;left:24px;align-items:center;color:var(--header-content-color, white)}.ds-tab-bar__actions{display:flex;position:absolute;right:24px;align-items:center;gap:12px}.ds-tab-bar__tabs{display:flex;gap:8px;align-items:center;max-width:640px;width:100%;margin:0 auto;justify-content:center;padding-left:var(--content-padding-md);padding-right:var(--content-padding-md)}.logomark{height:28px;width:auto;flex-shrink:0}}@media (min-width: 992px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg);justify-content:center}}@media (min-width: 1440px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}ion-tab-button{--color: var(--text-color-default-tertiary);--color-selected: var(--color-accent);--padding-start: 0;--padding-end: 0;--ripple-color: transparent;display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:visible;pointer-events:auto}ion-tab-button[title]:before{content:attr(title);position:absolute;opacity:0;pointer-events:none}.tab-icon-ripple{position:absolute;left:50%;top:50%;width:40px;height:40px;border-radius:50%;background:var(--color-accent);transform:translate(-50%,-50%) scale(0);opacity:0;pointer-events:none;transition:all .6s cubic-bezier(.36,1.2,.04,1.4);z-index:0}.tab-selected .tab-icon-ripple{transform:translate(-50%,-50%) scale(2);opacity:.05;animation:ripple-fade .6s cubic-bezier(.36,.5,.04,1.8) forwards}@keyframes ripple-fade{0%{opacity:0;transform:translate(-50%,-50%) scale(0)}30%{opacity:.1}to{opacity:0;transform:translate(-50%,-50%) scale(2)}}ion-tab-button::part(native){overflow:visible}ion-tab-button ion-ripple-effect{color:var(--color-accent);border-radius:1000px}ion-tab-button ion-label{font-size:11px;font-weight:500;letter-spacing:-.5px;margin-top:0}.tab-icon-wrapper{position:relative;width:24px;height:24px;display:flex;align-items:center;justify-content:center;z-index:1;margin-bottom:4px}.tab-icon-inactive,.tab-icon-active{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);transition:all .8s cubic-bezier(.36,1,.04,1)}.tab-icon-inactive{opacity:1;transform:translate(-50%,-50%) scale(1)}.tab-icon-active{opacity:0;transform:translate(-50%,calc(-50% - 12px)) scale(.5)}.tab-selected .tab-icon-inactive{opacity:0;transform:translate(-50%,-50%) scale(.5)}.tab-selected .tab-icon-active{opacity:1;transform:translate(-50%,-50%) scale(1)}@media (min-width: 768px){.ds-tab-button{flex-direction:row;height:40px;padding:0 16px!important;border-radius:40px;transition:all .2s ease;width:-moz-fit-content;width:fit-content;min-width:-moz-fit-content;min-width:fit-content;flex:0 0 auto;--color: rgba(var(--color-header-content-rgb, 255, 255, 255), .7);--color-selected: var(--color-header-content, white);color:rgba(var(--color-header-content-rgb, 255, 255, 255),.7);background:transparent;position:relative;overflow:hidden}.tab-icon-wrapper,.tab-icon-ripple{width:20px;height:20px}.ds-tab-button::part(native){border-radius:40px}.ds-tab-button:hover:not(.tab-selected){--color: var(--color-header-content, white);--color-selected: var(--color-header-content, white);color:var(--color-header-content, white);background:rgba(var(--color-header-content-rgb, 255, 255, 255),.1)}.ds-tab-button:hover:not(.tab-selected) ion-label{color:var(--color-header-content, white)}.ds-tab-button:hover:not(.tab-selected) ds-icon{--icon-color: var(--color-header-content, white);color:var(--color-header-content, white)}.ds-tab-button:hover:not(.tab-selected) ds-icon svg{fill:var(--color-header-content, white)}.ds-tab-button.tab-selected,.ds-tab-button.tab-selected:hover{background:var(--color-header-accent);--color-selected: var(--color-on-header-accent, white);--color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ion-label{color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ds-icon{--icon-color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ds-icon svg{fill:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover .tab-icon-active{opacity:1!important;visibility:visible!important}.ds-tab-button.tab-selected:hover .tab-icon-inactive{opacity:0!important}.ds-tab-button.tab-selected ion-label{color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected ds-icon{--icon-color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected ds-icon svg{fill:var(--color-on-header-accent, white)}.ds-tab-button .button-native{width:auto;padding:0}.ds-tab-button ion-label{font-size:var(--font-size-sm);font-weight:500;margin:0;color:inherit}.ds-tab-button .tab-icon-wrapper{margin-right:4px;margin-bottom:0}.ds-tab-button ion-ripple-effect{color:rgba(var(--header-content-color-rgb, 255, 255, 255),.3);border-radius:1000px;transform:scale(1.5)}}@media (min-width: 768px){.ds-tab-bar__actions ds-avatar{cursor:pointer;transition:transform .2s ease}.ds-tab-bar__actions ds-avatar:hover{transform:scale(1.05)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IonTabBar, selector: "ion-tab-bar", inputs: ["color", "mode", "selectedTab", "translucent"] }, { kind: "component", type: IonTabButton, selector: "ion-tab-button", inputs: ["disabled", "download", "href", "layout", "mode", "rel", "selected", "tab", "target"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: DsLogoComponent, selector: "ds-logo", inputs: ["variant", "size", "customHeight", "customWidth"] }] });
19960
+ `, isInline: true, styles: [":host{--ds-tab-bar-height: 64px}@media (min-width: 768px){:host{display:block;--ds-tab-bar-height: 64px}}@media (max-width: 767px){:host{display:contents}}ion-tabs.ds-tabs-wrapper{height:100%;background:var(--color-header-surface)}ion-tab-button:before,ion-tab-button:after{content:none!important;display:none!important}ion-tab-button[title]:before,ion-tab-button[title]:after{display:none!important}ion-tab-button::part(native):before,ion-tab-button::part(native):after{display:none!important}.ds-tab-bar{--background: var(--color-background-neutral-primary);transition:transform .2s ease-in-out}ion-tab-bar[slot=bottom]{border-top:1px solid var(--border-color-default);padding-top:8px;padding-bottom:max(8px,calc(var(--app-safe-bottom, 0px) - 16px));padding-left:12px;padding-right:12px}@media (max-width: 767px){ion-tab-bar[slot=bottom]{position:fixed;bottom:0;left:0;right:0;z-index:100}}@media (max-width: 767px){:host-context(.plt-android) ion-tab-bar[slot=bottom]{padding-bottom:max(8px,var(--app-safe-bottom, 0px))!important}}@media (max-width: 767px){:host-context(ion-tabs:has(ds-mobile-page-details)) .ds-tab-bar{transform:translateY(100%);transition:transform .3s ease}}.ds-tab-bar__logo,.ds-tab-bar__actions{display:none}.ds-tab-bar__tabs{display:flex;width:100%;justify-content:space-around;align-items:center}@media (min-width: 769px){ion-tab-bar[slot=bottom]{padding-bottom:var(--app-safe-bottom, 0px)}}@media (display-mode: standalone){ion-tab-bar[slot=bottom]{padding-bottom:var(--app-safe-bottom, 0px)!important}}@media (min-width: 768px){:host[slot=top]{order:-1!important}:host ion-tab-bar{position:relative!important;bottom:auto!important;top:0!important}ion-tab-bar[slot=top]{--background: var(--color-header-surface);position:relative!important;display:flex!important;align-items:center;padding:12px 24px;height:64px;max-width:none;bottom:auto!important;top:0!important}ion-tab-bar[slot=bottom]{position:relative!important;bottom:auto!important}ion-tabs>div:not([slot]){order:1!important}.ds-tab-bar__logo{display:flex;position:absolute;left:24px;align-items:center;color:var(--header-content-color, white)}.ds-tab-bar__actions{display:flex;position:absolute;right:24px;align-items:center;gap:12px}.ds-tab-bar__tabs{display:flex;gap:8px;align-items:center;max-width:640px;width:100%;margin:0 auto;justify-content:center;padding-left:var(--content-padding-md);padding-right:var(--content-padding-md)}.logomark{height:28px;width:auto;flex-shrink:0}}@media (min-width: 992px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg);justify-content:center}}@media (min-width: 1440px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}ion-tab-button{--color: var(--text-color-default-tertiary);--color-selected: var(--color-accent);--padding-start: 0;--padding-end: 0;--ripple-color: transparent;display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:visible;pointer-events:auto}ion-tab-button[title]:before{content:attr(title);position:absolute;opacity:0;pointer-events:none}.tab-icon-ripple{position:absolute;left:50%;top:50%;width:40px;height:40px;border-radius:50%;background:var(--color-accent);transform:translate(-50%,-50%) scale(0);opacity:0;pointer-events:none;transition:all .6s cubic-bezier(.36,1.2,.04,1.4);z-index:0}.tab-selected .tab-icon-ripple{transform:translate(-50%,-50%) scale(2);opacity:.05;animation:ripple-fade .6s cubic-bezier(.36,.5,.04,1.8) forwards}@keyframes ripple-fade{0%{opacity:0;transform:translate(-50%,-50%) scale(0)}30%{opacity:.1}to{opacity:0;transform:translate(-50%,-50%) scale(2)}}ion-tab-button::part(native){overflow:visible}ion-tab-button ion-ripple-effect{color:var(--color-accent);border-radius:1000px}ion-tab-button ion-label{font-size:11px;font-weight:500;letter-spacing:-.5px;margin-top:0}.tab-icon-wrapper{position:relative;width:24px;height:24px;display:flex;align-items:center;justify-content:center;z-index:1;margin-bottom:4px}.tab-icon-inactive,.tab-icon-active{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);transition:all .8s cubic-bezier(.36,1,.04,1)}.tab-icon-inactive{opacity:1;transform:translate(-50%,-50%) scale(1)}.tab-icon-active{opacity:0;transform:translate(-50%,calc(-50% - 12px)) scale(.5)}.tab-selected .tab-icon-inactive{opacity:0;transform:translate(-50%,-50%) scale(.5)}.tab-selected .tab-icon-active{opacity:1;transform:translate(-50%,-50%) scale(1)}@media (min-width: 768px){.ds-tab-button{flex-direction:row;height:40px;padding:0 16px!important;border-radius:40px;transition:all .2s ease;width:-moz-fit-content;width:fit-content;min-width:-moz-fit-content;min-width:fit-content;flex:0 0 auto;--color: rgba(var(--color-header-content-rgb, 255, 255, 255), .7);--color-selected: var(--color-header-content, white);color:rgba(var(--color-header-content-rgb, 255, 255, 255),.7);background:transparent;position:relative;overflow:hidden}.tab-icon-wrapper,.tab-icon-ripple{width:20px;height:20px}.ds-tab-button::part(native){border-radius:40px}.ds-tab-button:hover:not(.tab-selected){--color: var(--color-header-content, white);--color-selected: var(--color-header-content, white);color:var(--color-header-content, white);background:rgba(var(--color-header-content-rgb, 255, 255, 255),.1)}.ds-tab-button:hover:not(.tab-selected) ion-label{color:var(--color-header-content, white)}.ds-tab-button:hover:not(.tab-selected) ds-icon{--icon-color: var(--color-header-content, white);color:var(--color-header-content, white)}.ds-tab-button:hover:not(.tab-selected) ds-icon svg{fill:var(--color-header-content, white)}.ds-tab-button.tab-selected,.ds-tab-button.tab-selected:hover{background:var(--color-header-accent);--color-selected: var(--color-on-header-accent, white);--color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ion-label{color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ds-icon{--icon-color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ds-icon svg{fill:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover .tab-icon-active{opacity:1!important;visibility:visible!important}.ds-tab-button.tab-selected:hover .tab-icon-inactive{opacity:0!important}.ds-tab-button.tab-selected ion-label{color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected ds-icon{--icon-color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected ds-icon svg{fill:var(--color-on-header-accent, white)}.ds-tab-button .button-native{width:auto;padding:0}.ds-tab-button ion-label{font-size:var(--font-size-sm);font-weight:500;margin:0;color:inherit}.ds-tab-button .tab-icon-wrapper{margin-right:4px;margin-bottom:0}.ds-tab-button ion-ripple-effect{color:rgba(var(--header-content-color-rgb, 255, 255, 255),.3);border-radius:1000px;transform:scale(1.5)}}@media (min-width: 768px){.ds-tab-bar__actions ds-avatar{cursor:pointer;transition:transform .2s ease}.ds-tab-bar__actions ds-avatar:hover{transform:scale(1.05)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IonTabBar, selector: "ion-tab-bar", inputs: ["color", "mode", "selectedTab", "translucent"] }, { kind: "component", type: IonTabButton, selector: "ion-tab-button", inputs: ["disabled", "download", "href", "layout", "mode", "rel", "selected", "tab", "target"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: DsLogoComponent, selector: "ds-logo", inputs: ["variant", "size", "customHeight", "customWidth"] }, { kind: "component", type: DsMobileNotificationButtonComponent, selector: "ds-mobile-notification-button", inputs: ["count"], outputs: ["clicked"] }] });
19888
19961
  }
19889
19962
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileTabBarComponent, decorators: [{
19890
19963
  type: Component,
19891
- args: [{ selector: 'ds-mobile-tab-bar', standalone: true, imports: [CommonModule, IonTabBar, IonTabButton, IonLabel, DsIconComponent, DsAvatarComponent, DsLogoComponent], template: `
19964
+ args: [{ selector: 'ds-mobile-tab-bar', standalone: true, imports: [CommonModule, IonTabBar, IonTabButton, IonLabel, DsIconComponent, DsAvatarComponent, DsLogoComponent, DsMobileNotificationButtonComponent], template: `
19892
19965
  <ion-tab-bar [attr.slot]="isDesktop() ? 'top' : 'bottom'" class="ds-tab-bar" [class.ds-tab-bar--desktop]="isDesktop()">
19893
19966
  <!-- Logo (desktop only, full logo in header) -->
19894
19967
  <div class="ds-tab-bar__logo">
@@ -19931,8 +20004,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
19931
20004
  }
19932
20005
  </div>
19933
20006
 
19934
- <!-- Avatar (desktop only, positioned via CSS) -->
20007
+ <!-- Notification + Avatar (desktop only, positioned via CSS) -->
19935
20008
  <div class="ds-tab-bar__actions">
20009
+ <ds-mobile-notification-button
20010
+ [count]="notificationCount"
20011
+ (clicked)="notificationClick.emit()"
20012
+ />
19936
20013
  <ds-avatar [size]="'md'" [type]="avatarType" [initials]="avatarInitials" [src]="avatarSrc" [iconName]="avatarIconName" (click)="handleAvatarClick()" />
19937
20014
  </div>
19938
20015
  </ion-tab-bar>
@@ -19949,6 +20026,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
19949
20026
  type: Input
19950
20027
  }], profileMenuItems: [{
19951
20028
  type: Input
20029
+ }], notificationCount: [{
20030
+ type: Input
19952
20031
  }], moreMenuItems: [{
19953
20032
  type: Input
19954
20033
  }], moreMenuLabel: [{
@@ -19957,6 +20036,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
19957
20036
  type: Input
19958
20037
  }], moreMenuIconActive: [{
19959
20038
  type: Input
20039
+ }], notificationClick: [{
20040
+ type: Output
19960
20041
  }], avatarClick: [{
19961
20042
  type: Output
19962
20043
  }], profileActionSelected: [{
@@ -20025,7 +20106,7 @@ class DsMobileTabsComponent {
20025
20106
  (avatarClick)="handleAvatarClick()"
20026
20107
  />
20027
20108
  </ion-tabs>
20028
- `, isInline: true, styles: [":host{display:block;height:100vh;height:100dvh}ion-tabs{height:100%;background:var(--color-header-surface)}@media (max-width: 767px){ion-tabs:has(ds-mobile-page-details) ds-mobile-tab-bar{--tab-bar-transform: translateY(100%)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IonTabs, selector: "ion-tabs" }, { kind: "component", type: IonTab, selector: "ion-tab", inputs: ["component", "tab"] }, { kind: "component", type: DsMobileTabBarComponent, selector: "ds-mobile-tab-bar", inputs: ["tabs", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "profileMenuItems", "moreMenuItems", "moreMenuLabel", "moreMenuIcon", "moreMenuIconActive"], outputs: ["avatarClick", "profileActionSelected", "moreMenuItemSelected"] }] });
20109
+ `, isInline: true, styles: [":host{display:block;height:100vh;height:100dvh}ion-tabs{height:100%;background:var(--color-header-surface)}@media (max-width: 767px){ion-tabs:has(ds-mobile-page-details) ds-mobile-tab-bar{--tab-bar-transform: translateY(100%)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IonTabs, selector: "ion-tabs" }, { kind: "component", type: IonTab, selector: "ion-tab", inputs: ["component", "tab"] }, { kind: "component", type: DsMobileTabBarComponent, selector: "ds-mobile-tab-bar", inputs: ["tabs", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "profileMenuItems", "notificationCount", "moreMenuItems", "moreMenuLabel", "moreMenuIcon", "moreMenuIconActive"], outputs: ["notificationClick", "avatarClick", "profileActionSelected", "moreMenuItemSelected"] }] });
20029
20110
  }
20030
20111
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileTabsComponent, decorators: [{
20031
20112
  type: Component,
@@ -21806,7 +21887,7 @@ class DsMobileEmptyStateComponent {
21806
21887
  }
21807
21888
  </div>
21808
21889
  </div>
21809
- `, isInline: true, styles: [":host{display:block}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center;gap:24px}.empty-state-image{width:96px;height:96px;margin:0 auto}.empty-state-text{display:flex;flex-direction:column;gap:8px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a);margin:0}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
21890
+ `, isInline: true, styles: [":host{display:block}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center;gap:24px}.empty-state-image{width:120px;height:120px;margin:0 auto}.empty-state-text{display:flex;flex-direction:column;gap:8px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a);margin:0}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
21810
21891
  }
21811
21892
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileEmptyStateComponent, decorators: [{
21812
21893
  type: Component,
@@ -21825,7 +21906,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
21825
21906
  }
21826
21907
  </div>
21827
21908
  </div>
21828
- `, styles: [":host{display:block}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center;gap:24px}.empty-state-image{width:96px;height:96px;margin:0 auto}.empty-state-text{display:flex;flex-direction:column;gap:8px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a);margin:0}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin:0}\n"] }]
21909
+ `, styles: [":host{display:block}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center;gap:24px}.empty-state-image{width:120px;height:120px;margin:0 auto}.empty-state-text{display:flex;flex-direction:column;gap:8px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a);margin:0}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin:0}\n"] }]
21829
21910
  }], propDecorators: { imageSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageSrc", required: false }] }], imageAlt: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageAlt", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }] } });
21830
21911
 
21831
21912
  /**
@@ -22702,16 +22783,16 @@ class DsMobileCardInlineBannerComponent {
22702
22783
 
22703
22784
  <div content-trailing class="item-trailing">
22704
22785
  @if (unreadCount() && unreadCount()! > 0) {
22705
- <span class="unread-badge">{{ unreadCount() }}</span>
22786
+ <ds-mobile-count-badge [count]="unreadCount()!" variant="accent" />
22706
22787
  }
22707
22788
  <ds-icon name="remixArrowRightSLine" size="20px" />
22708
22789
  </div>
22709
22790
  </ds-mobile-card-inline>
22710
- `, isInline: true, styles: [".unread-badge{min-width:24px;height:16px;padding:0 6px;border-radius:10px;background:var(--color-accent, #6B5FF5);color:var(--color-on-accent, #ffffff);font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:600;display:flex;align-items:center;justify-content:center;line-height:1;margin-right:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: DsMobileCardInlineComponent, selector: "ds-mobile-card-inline", inputs: ["variant", "disabled"], outputs: ["cardClick"] }] });
22791
+ `, isInline: true, styles: ["ds-mobile-count-badge{margin-right:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: DsMobileCardInlineComponent, selector: "ds-mobile-card-inline", inputs: ["variant", "disabled"], outputs: ["cardClick"] }, { kind: "component", type: DsMobileCountBadgeComponent, selector: "ds-mobile-count-badge", inputs: ["count", "variant"] }] });
22711
22792
  }
22712
22793
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileCardInlineBannerComponent, decorators: [{
22713
22794
  type: Component,
22714
- args: [{ selector: 'ds-mobile-card-inline-banner', standalone: true, imports: [CommonModule, DsIconComponent, DsAvatarComponent, DsMobileCardInlineComponent], template: `
22795
+ args: [{ selector: 'ds-mobile-card-inline-banner', standalone: true, imports: [CommonModule, DsIconComponent, DsAvatarComponent, DsMobileCardInlineComponent, DsMobileCountBadgeComponent], template: `
22715
22796
  <ds-mobile-card-inline
22716
22797
  [variant]="layout()"
22717
22798
  (cardClick)="handleBannerClick()">
@@ -22736,12 +22817,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
22736
22817
 
22737
22818
  <div content-trailing class="item-trailing">
22738
22819
  @if (unreadCount() && unreadCount()! > 0) {
22739
- <span class="unread-badge">{{ unreadCount() }}</span>
22820
+ <ds-mobile-count-badge [count]="unreadCount()!" variant="accent" />
22740
22821
  }
22741
22822
  <ds-icon name="remixArrowRightSLine" size="20px" />
22742
22823
  </div>
22743
22824
  </ds-mobile-card-inline>
22744
- `, styles: [".unread-badge{min-width:24px;height:16px;padding:0 6px;border-radius:10px;background:var(--color-accent, #6B5FF5);color:var(--color-on-accent, #ffffff);font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:600;display:flex;align-items:center;justify-content:center;line-height:1;margin-right:8px}\n"] }]
22825
+ `, styles: ["ds-mobile-count-badge{margin-right:8px}\n"] }]
22745
22826
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], timestamp: [{ type: i0.Input, args: [{ isSignal: true, alias: "timestamp", required: false }] }], unreadCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "unreadCount", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], bannerClick: [{ type: i0.Output, args: ["bannerClick"] }] } });
22746
22827
 
22747
22828
  /**
@@ -26092,7 +26173,7 @@ class DsMobilePriceSheetComponent {
26092
26173
  inputmode="numeric"
26093
26174
  pattern="[0-9]*"
26094
26175
  maxlength="5"
26095
- [attr.size]="Math.min(customPrice()?.length || 2, 5)"
26176
+ [attr.size]="Math.min(customPrice().length || 2, 5)"
26096
26177
  [(ngModel)]="customPrice"
26097
26178
  placeholder="75"
26098
26179
  class="price-input-native"
@@ -26147,7 +26228,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
26147
26228
  inputmode="numeric"
26148
26229
  pattern="[0-9]*"
26149
26230
  maxlength="5"
26150
- [attr.size]="Math.min(customPrice()?.length || 2, 5)"
26231
+ [attr.size]="Math.min(customPrice().length || 2, 5)"
26151
26232
  [(ngModel)]="customPrice"
26152
26233
  placeholder="75"
26153
26234
  class="price-input-native"
@@ -28624,7 +28705,713 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
28624
28705
  `, styles: [":host{display:block;width:100%}.property-banner{display:flex;align-items:center;gap:8px;height:44px;padding:4px 10px 4px 6px;background:rgba(var(--header-content-color-rgb, 255, 255, 255),.1);border:none;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border-radius:12px;cursor:pointer;transition:background .2s ease;-webkit-tap-highlight-color:transparent}.property-banner:hover{background:rgba(var(--header-content-color-rgb, 255, 255, 255),.12)}.property-banner:active{background:rgba(var(--header-content-color-rgb, 255, 255, 255),.15)}.property-photo{width:32px;height:32px;border-radius:8px;object-fit:cover;flex-shrink:0;background:rgba(var(--header-content-color-rgb, 255, 255, 255),.1)}.property-text{display:flex;flex-direction:column;flex:1;min-width:0}.property-address{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;color:var(--header-content-color, white);line-height:1.4;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.property-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:rgba(var(--header-content-color-rgb, 255, 255, 255),.65);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.property-chevron{flex-shrink:0;width:16px;height:16px;color:rgba(var(--header-content-color-rgb, 255, 255, 255),.5)}\n"] }]
28625
28706
  }], propDecorators: { address: [{ type: i0.Input, args: [{ isSignal: true, alias: "address", required: true }] }], photoUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "photoUrl", required: true }] }], tenantCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "tenantCount", required: false }] }] } });
28626
28707
 
28627
- // Mobile Page Components
28708
+ class DsMobileNotificationPromptComponent {
28709
+ whitelabel = inject(WhitelabelService);
28710
+ dismissing = signal(false, ...(ngDevMode ? [{ debugName: "dismissing" }] : []));
28711
+ heading = input('Vær den første til at vide det', ...(ngDevMode ? [{ debugName: "heading" }] : []));
28712
+ subtitle = input('Hold dig opdateret med henvendelser, bookinger, nyheder og alt derimellem.', ...(ngDevMode ? [{ debugName: "subtitle" }] : []));
28713
+ allowLabel = input('Slå notifikationer til', ...(ngDevMode ? [{ debugName: "allowLabel" }] : []));
28714
+ dismissLabel = input('Ikke lige nu', ...(ngDevMode ? [{ debugName: "dismissLabel" }] : []));
28715
+ allow = output();
28716
+ dismiss = output();
28717
+ static EXIT_DURATION = 800;
28718
+ handleAllow() {
28719
+ if (this.dismissing())
28720
+ return;
28721
+ this.dismissing.set(true);
28722
+ setTimeout(() => this.allow.emit(), DsMobileNotificationPromptComponent.EXIT_DURATION);
28723
+ }
28724
+ handleDismiss() {
28725
+ if (this.dismissing())
28726
+ return;
28727
+ this.dismissing.set(true);
28728
+ setTimeout(() => this.dismiss.emit(), DsMobileNotificationPromptComponent.EXIT_DURATION);
28729
+ }
28730
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNotificationPromptComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
28731
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.16", type: DsMobileNotificationPromptComponent, isStandalone: true, selector: "ds-mobile-notification-prompt", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, allowLabel: { classPropertyName: "allowLabel", publicName: "allowLabel", isSignal: true, isRequired: false, transformFunction: null }, dismissLabel: { classPropertyName: "dismissLabel", publicName: "dismissLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { allow: "allow", dismiss: "dismiss" }, ngImport: i0, template: `
28732
+ <div class="notification-prompt" [class.is-dismissing]="dismissing()">
28733
+ <div
28734
+ class="notification-prompt__backdrop"
28735
+ [style.background]="whitelabel.headerSurface()">
28736
+ </div>
28737
+
28738
+ <!-- Illustration: floating icon tiles -->
28739
+ <div class="notification-prompt__illustration">
28740
+ <div class="icon-tiles">
28741
+
28742
+ <!-- Tile 1: service/heart-handshake (top-center, small 72px) -->
28743
+ <div class="tile-entry tile-entry--1">
28744
+ <div class="icon-tile icon-tile--1">
28745
+ <div class="icon-tile__icon">
28746
+ <svg viewBox="0 0 45 45" fill="none">
28747
+ <path d="M5.928 8.379c4.545-4.545 11.787-4.75 16.575-.615 4.785-4.135 12.026-3.93 16.571.615 4.537 4.537 4.749 11.76.637 16.549L25.153 39.536a3.75 3.75 0 0 1-5.098.19l-.206-.19L5.291 24.928c-4.112-4.789-3.9-12.012.637-16.549zm2.652 2.651c-3.2 3.201-3.292 8.334-.274 11.645l.274.288L22.5 36.884l9.943-9.945-6.628-6.628-1.988 1.989a4.688 4.688 0 0 1-6.628-6.628l3.94-3.943c-3.212-2.57-7.866-2.453-10.945.245l-.287.275zm15.91 5.303a1.875 1.875 0 0 1 2.651 0l7.954 7.954 1.327-1.325c3.295-3.295 3.295-8.637 0-11.932-3.2-3.201-8.333-3.292-11.645-.274l-.287.274-5.967 5.967a1.875 1.875 0 0 0-.146 2.497l.146.164a1.875 1.875 0 0 0 2.497.145l.164-.145 3.314-3.315z" fill="currentColor"/>
28748
+ </svg>
28749
+ </div>
28750
+ </div>
28751
+ </div>
28752
+
28753
+ <!-- Tile 2: calendar (right-bottom, medium 88px) -->
28754
+ <div class="tile-entry tile-entry--2">
28755
+ <div class="icon-tile icon-tile--medium icon-tile--2">
28756
+ <div class="icon-tile__icon">
28757
+ <svg viewBox="0 0 24 24" fill="none">
28758
+ <path d="M9 1v2h6V1h2v2h4a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h4V1h2zm11 9H4v9h16v-9zm-4.964 1.136l1.414 1.414-4.95 4.95-3.536-3.536L9.38 12.55l2.121 2.122 3.536-3.536zM7 5H4v3h16V5h-3v1h-2V5H9v1H7V5z" fill="currentColor"/>
28759
+ </svg>
28760
+ </div>
28761
+ </div>
28762
+ </div>
28763
+
28764
+ <!-- Tile 3: documents (bottom-left, small 72px) -->
28765
+ <div class="tile-entry tile-entry--3">
28766
+ <div class="icon-tile icon-tile--3">
28767
+ <div class="icon-tile__icon">
28768
+ <svg viewBox="0 0 24 24" fill="none">
28769
+ <path d="M20 22H4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1zM19 4H5v16h14V4zM7 6h4v4H7V6zm0 6h10v2H7v-2zm0 4h10v2H7v-2zm6-9h4v2h-4V7z" fill="currentColor"/>
28770
+ </svg>
28771
+ </div>
28772
+ </div>
28773
+ </div>
28774
+
28775
+ <!-- Tile 4: messages (right-upper, medium 88px) -->
28776
+ <div class="tile-entry tile-entry--4">
28777
+ <div class="icon-tile icon-tile--medium icon-tile--4">
28778
+ <div class="icon-tile__icon">
28779
+ <svg viewBox="0 0 24 24" fill="none">
28780
+ <path d="M7.291 20.824L2 22l1.176-5.291A9.956 9.956 0 0 1 2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10a9.956 9.956 0 0 1-4.709-1.176zM7.581 18.71l.29.158A7.956 7.956 0 0 0 12 20c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8c0 1.458.39 2.823 1.131 4.13l.159.288-.69 3.1 3.1-.69z" fill="currentColor"/>
28781
+ </svg>
28782
+ </div>
28783
+ </div>
28784
+ </div>
28785
+
28786
+ <!-- Tile 5: home (left-middle, medium 88px) -->
28787
+ <div class="tile-entry tile-entry--5">
28788
+ <div class="icon-tile icon-tile--medium icon-tile--5">
28789
+ <div class="icon-tile__icon">
28790
+ <svg viewBox="0 0 24 24" fill="none">
28791
+ <path d="M19 21H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9a1 1 0 0 1-1 1zM6 19h12v-9.157l-6-5.454-6 5.454V19zm2-4h8v2H8v-2z" fill="currentColor"/>
28792
+ </svg>
28793
+ </div>
28794
+ </div>
28795
+ </div>
28796
+
28797
+ <!-- Hero: bell icon (center, 120px) -->
28798
+ <div class="tile-entry tile-entry--hero">
28799
+ <div class="icon-tile icon-tile--hero">
28800
+ <div class="icon-tile__icon">
28801
+ <svg viewBox="0 0 24 24" fill="none">
28802
+ <path d="M20 17h2v2H2v-2h2v-7a8 8 0 1 1 16 0v7zm-2 0v-7a6 6 0 1 0-12 0v7h12zm-9 4h6v2H9v-2z" fill="currentColor"/>
28803
+ </svg>
28804
+ </div>
28805
+ <span class="icon-tile__badge">18</span>
28806
+ </div>
28807
+ </div>
28808
+
28809
+ </div>
28810
+ </div>
28811
+
28812
+ <!-- Heading + subtitle -->
28813
+ <div class="notification-prompt__texts">
28814
+ <h1 class="notification-prompt__heading">{{ heading() }}</h1>
28815
+ <p class="notification-prompt__subtitle">{{ subtitle() }}</p>
28816
+ </div>
28817
+
28818
+ <!-- CTA buttons -->
28819
+ <div class="notification-prompt__actions">
28820
+ <ds-button
28821
+ variant="primary"
28822
+ size="md"
28823
+ (clicked)="handleAllow()">
28824
+ {{ allowLabel() }}
28825
+ </ds-button>
28826
+ <ds-button
28827
+ class="dismiss-btn"
28828
+ variant="ghost"
28829
+ size="md"
28830
+ (clicked)="handleDismiss()">
28831
+ {{ dismissLabel() }}
28832
+ </ds-button>
28833
+ </div>
28834
+ </div>
28835
+ `, isInline: true, styles: ["@keyframes fadeSlideUp{0%{opacity:0;transform:translateY(32px)}to{opacity:1;transform:translateY(0)}}@keyframes tileEntry{0%{opacity:0;transform:translateY(40px) scale(.85)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes float1{0%{transform:var(--tile-transform) translate(0) rotate(0)}11%{transform:var(--tile-transform) translate(1.4px,-3px) rotate(.9deg)}23%{transform:var(--tile-transform) translate(2.2px,-6.5px) rotate(1.8deg)}37%{transform:var(--tile-transform) translate(.6px,-8.5px) rotate(.4deg)}50%{transform:var(--tile-transform) translate(-1.2px,-7px) rotate(-.8deg)}63%{transform:var(--tile-transform) translate(-1.8px,-4px) rotate(-1.6deg)}76%{transform:var(--tile-transform) translate(-.4px,-1.5px) rotate(-.5deg)}88%{transform:var(--tile-transform) translate(.8px,-.5px) rotate(.3deg)}to{transform:var(--tile-transform) translate(0) rotate(0)}}@keyframes float2{0%{transform:var(--tile-transform) translate(0) rotate(0)}12%{transform:var(--tile-transform) translate(-1px,-2.5px) rotate(-.7deg)}25%{transform:var(--tile-transform) translate(-2px,-5.5px) rotate(-1.5deg)}38%{transform:var(--tile-transform) translate(-.8px,-7px) rotate(-.3deg)}50%{transform:var(--tile-transform) translate(1px,-6px) rotate(1deg)}62%{transform:var(--tile-transform) translate(1.6px,-3.5px) rotate(1.6deg)}75%{transform:var(--tile-transform) translate(.5px,-1.5px) rotate(.6deg)}87%{transform:var(--tile-transform) translate(-.5px,-.3px) rotate(-.2deg)}to{transform:var(--tile-transform) translate(0) rotate(0)}}@keyframes float3{0%{transform:var(--tile-transform) translate(0) rotate(0)}10%{transform:var(--tile-transform) translate(.8px,-3px) rotate(1.2deg)}22%{transform:var(--tile-transform) translate(1.5px,-7px) rotate(2deg)}35%{transform:var(--tile-transform) translate(.3px,-10px) rotate(.8deg)}48%{transform:var(--tile-transform) translate(-1.5px,-9.5px) rotate(-.6deg)}60%{transform:var(--tile-transform) translate(-2px,-6px) rotate(-1.5deg)}72%{transform:var(--tile-transform) translate(-1px,-3px) rotate(-.8deg)}85%{transform:var(--tile-transform) translate(.3px,-1px) rotate(.3deg)}to{transform:var(--tile-transform) translate(0) rotate(0)}}@keyframes float4{0%{transform:var(--tile-transform) translate(0) rotate(0)}13%{transform:var(--tile-transform) translate(-.8px,-2px) rotate(-.8deg)}26%{transform:var(--tile-transform) translate(-1.5px,-5px) rotate(-1.6deg)}40%{transform:var(--tile-transform) translate(.2px,-7.5px) rotate(-.2deg)}52%{transform:var(--tile-transform) translate(1.8px,-6.5px) rotate(1.2deg)}65%{transform:var(--tile-transform) translate(1.2px,-4px) rotate(.8deg)}78%{transform:var(--tile-transform) translate(-.3px,-1.8px) rotate(-.4deg)}90%{transform:var(--tile-transform) translate(-.6px,-.4px) rotate(-.1deg)}to{transform:var(--tile-transform) translate(0) rotate(0)}}@keyframes float5{0%{transform:var(--tile-transform) translate(0) rotate(0)}9%{transform:var(--tile-transform) translate(1.2px,-2px) rotate(.6deg)}20%{transform:var(--tile-transform) translate(2px,-5px) rotate(1.5deg)}33%{transform:var(--tile-transform) translate(.8px,-8.5px) rotate(.5deg)}45%{transform:var(--tile-transform) translate(-1px,-9.5px) rotate(-1deg)}58%{transform:var(--tile-transform) translate(-1.8px,-7px) rotate(-1.8deg)}70%{transform:var(--tile-transform) translate(-.8px,-4px) rotate(-.8deg)}82%{transform:var(--tile-transform) translate(.4px,-1.5px) rotate(.2deg)}92%{transform:var(--tile-transform) translate(.6px,-.3px) rotate(.1deg)}to{transform:var(--tile-transform) translate(0) rotate(0)}}@keyframes floatHero{0%{transform:translate(-50%) translate(0) rotate(0) scale(1)}10%{transform:translate(-50%) translate(.8px,-4px) rotate(.7deg) scale(1.006)}22%{transform:translate(-50%) translate(1.5px,-9px) rotate(1.4deg) scale(1.014)}35%{transform:translate(-50%) translate(.2px,-13px) rotate(.3deg) scale(1.02)}48%{transform:translate(-50%) translate(-1.5px,-12px) rotate(-1deg) scale(1.016)}60%{transform:translate(-50%) translate(-2px,-8px) rotate(-1.6deg) scale(1.01)}72%{transform:translate(-50%) translate(-.6px,-4px) rotate(-.5deg) scale(1.005)}85%{transform:translate(-50%) translate(.5px,-1.5px) rotate(.3deg) scale(1.002)}94%{transform:translate(-50%) translate(.3px,-.3px) rotate(.1deg) scale(1.001)}to{transform:translate(-50%) translate(0) rotate(0) scale(1)}}:host{display:flex;align-items:center;justify-content:center;position:fixed;inset:0;z-index:9999}.notification-prompt{width:100%;max-width:390px;height:100%;display:flex;flex-direction:column;gap:48px;overflow:clip;position:relative}.notification-prompt__backdrop{position:fixed;inset:0;z-index:-1}.notification-prompt__illustration{position:relative;flex:1 0 0;min-height:0;display:flex;align-items:center;justify-content:center;padding:0 24px}.icon-tiles{position:relative;width:100%;max-width:342px;height:100%}.tile-entry{position:absolute;opacity:0;animation:tileEntry .7s cubic-bezier(.22,1,.36,1) forwards;will-change:transform,opacity}.tile-entry--hero{inset:0;animation-delay:.2s;animation-duration:.8s}.tile-entry--1{inset:0;animation-delay:.35s}.tile-entry--4{inset:0;animation-delay:.3s}.tile-entry--5{inset:0;animation-delay:.4s}.tile-entry--3{inset:0;animation-delay:.45s}.tile-entry--2{inset:0;animation-delay:.5s}.icon-tile{position:absolute;display:flex;align-items:center;justify-content:center;border-radius:20%;background:linear-gradient(180deg,#0000 95%,#00000026),linear-gradient(180deg,#fff6,#fff0 4.5%),linear-gradient(180deg,#ffffff26,#fff0),linear-gradient(180deg,#0000,#0000001a),var(--color-accent, #6B5FF5);will-change:transform}.icon-tile__icon{width:62.5%;height:62.5%;display:flex;align-items:center;justify-content:center}.icon-tile__icon svg{width:100%;height:100%;color:#fff;display:block}.icon-tile--1{--tile-transform: translate(-50%, 0) rotate(7.74deg);width:72px;height:72px;top:22%;left:50%;filter:blur(2px);animation:float1 6.8s linear infinite}.icon-tile--2{--tile-transform: rotate(9.27deg);width:88px;height:88px;bottom:6%;right:0;filter:blur(1px);animation:float2 7.4s linear infinite}.icon-tile--3{--tile-transform: rotate(-6.18deg);width:72px;height:72px;bottom:0;left:0;filter:blur(2px);animation:float3 8.6s linear infinite}.icon-tile--4{--tile-transform: rotate(20.57deg);width:88px;height:88px;top:36%;right:0;filter:blur(1px);animation:float4 7.1s linear infinite}.icon-tile--5{--tile-transform: rotate(-8.24deg);width:88px;height:88px;top:44%;left:0;filter:blur(1px);animation:float5 9.2s linear infinite}.icon-tile--hero{width:120px;height:120px;left:50%;bottom:14%;filter:none;border-radius:30px;z-index:2;animation:floatHero 6.2s linear infinite}.icon-tile__badge{position:absolute;top:12.5%;right:12%;min-width:26px;height:26px;padding:0 7px;border-radius:8px;background:var(--content-feedback-bold-red, #b91c1c);display:flex;align-items:center;justify-content:center;font-family:Brockmann,system-ui,-apple-system,sans-serif;font-weight:600;font-size:18px;line-height:1;letter-spacing:-.04em;color:#fff;white-space:nowrap;box-sizing:border-box}.notification-prompt__texts{position:relative;z-index:1;text-align:center;padding:0 16px;flex-shrink:0;opacity:0;animation:fadeSlideUp .6s .6s cubic-bezier(.22,1,.36,1) forwards}.notification-prompt__heading{font-family:Brockmann,system-ui,-apple-system,sans-serif;font-weight:600;font-size:32px;line-height:1.15;letter-spacing:-.02em;color:#fff;margin:0 0 4px}.notification-prompt__subtitle{font-family:Brockmann,system-ui,-apple-system,sans-serif;font-weight:400;font-size:18px;line-height:1.4;letter-spacing:-.02em;color:#eef0ff;margin:0}.notification-prompt__actions{position:relative;z-index:1;display:flex;flex-direction:column;gap:8px;padding:0 24px 24px;padding-bottom:max(24px,env(safe-area-inset-bottom,24px));flex-shrink:0;opacity:0;animation:fadeSlideUp .6s .75s cubic-bezier(.22,1,.36,1) forwards}.notification-prompt__actions ds-button{display:block;width:100%}.notification-prompt__actions ds-button::ng-deep button{width:100%;border-radius:8px}.notification-prompt__actions ds-button.dismiss-btn::ng-deep button{color:#fff;background:transparent}@keyframes exitUp{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-48px)}}@keyframes exitFade{0%{opacity:1}to{opacity:0}}.is-dismissing .notification-prompt__illustration{animation:exitUp .5s cubic-bezier(.55,0,1,.45) forwards}.is-dismissing .notification-prompt__texts{animation:exitUp .45s .15s cubic-bezier(.55,0,1,.45) forwards}.is-dismissing .notification-prompt__actions{animation:exitUp .45s .3s cubic-bezier(.55,0,1,.45) forwards}.is-dismissing .notification-prompt__backdrop{animation:exitFade .35s .45s cubic-bezier(.55,0,1,.45) forwards}@media (prefers-reduced-motion: reduce){.tile-entry,.icon-tile,.icon-tile--hero,.notification-prompt__texts,.notification-prompt__actions{animation:none!important;opacity:1!important}}\n"], dependencies: [{ kind: "component", type: DsButtonComponent, selector: "ds-button", inputs: ["variant", "size", "disabled", "loading", "pressed", "expanded", "leadingIcon", "trailingIcon", "ariaLabel", "iconOnly"], outputs: ["clicked", "focused", "blurred"] }] });
28836
+ }
28837
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNotificationPromptComponent, decorators: [{
28838
+ type: Component,
28839
+ args: [{ selector: 'ds-mobile-notification-prompt', standalone: true, imports: [DsButtonComponent], template: `
28840
+ <div class="notification-prompt" [class.is-dismissing]="dismissing()">
28841
+ <div
28842
+ class="notification-prompt__backdrop"
28843
+ [style.background]="whitelabel.headerSurface()">
28844
+ </div>
28845
+
28846
+ <!-- Illustration: floating icon tiles -->
28847
+ <div class="notification-prompt__illustration">
28848
+ <div class="icon-tiles">
28849
+
28850
+ <!-- Tile 1: service/heart-handshake (top-center, small 72px) -->
28851
+ <div class="tile-entry tile-entry--1">
28852
+ <div class="icon-tile icon-tile--1">
28853
+ <div class="icon-tile__icon">
28854
+ <svg viewBox="0 0 45 45" fill="none">
28855
+ <path d="M5.928 8.379c4.545-4.545 11.787-4.75 16.575-.615 4.785-4.135 12.026-3.93 16.571.615 4.537 4.537 4.749 11.76.637 16.549L25.153 39.536a3.75 3.75 0 0 1-5.098.19l-.206-.19L5.291 24.928c-4.112-4.789-3.9-12.012.637-16.549zm2.652 2.651c-3.2 3.201-3.292 8.334-.274 11.645l.274.288L22.5 36.884l9.943-9.945-6.628-6.628-1.988 1.989a4.688 4.688 0 0 1-6.628-6.628l3.94-3.943c-3.212-2.57-7.866-2.453-10.945.245l-.287.275zm15.91 5.303a1.875 1.875 0 0 1 2.651 0l7.954 7.954 1.327-1.325c3.295-3.295 3.295-8.637 0-11.932-3.2-3.201-8.333-3.292-11.645-.274l-.287.274-5.967 5.967a1.875 1.875 0 0 0-.146 2.497l.146.164a1.875 1.875 0 0 0 2.497.145l.164-.145 3.314-3.315z" fill="currentColor"/>
28856
+ </svg>
28857
+ </div>
28858
+ </div>
28859
+ </div>
28860
+
28861
+ <!-- Tile 2: calendar (right-bottom, medium 88px) -->
28862
+ <div class="tile-entry tile-entry--2">
28863
+ <div class="icon-tile icon-tile--medium icon-tile--2">
28864
+ <div class="icon-tile__icon">
28865
+ <svg viewBox="0 0 24 24" fill="none">
28866
+ <path d="M9 1v2h6V1h2v2h4a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h4V1h2zm11 9H4v9h16v-9zm-4.964 1.136l1.414 1.414-4.95 4.95-3.536-3.536L9.38 12.55l2.121 2.122 3.536-3.536zM7 5H4v3h16V5h-3v1h-2V5H9v1H7V5z" fill="currentColor"/>
28867
+ </svg>
28868
+ </div>
28869
+ </div>
28870
+ </div>
28871
+
28872
+ <!-- Tile 3: documents (bottom-left, small 72px) -->
28873
+ <div class="tile-entry tile-entry--3">
28874
+ <div class="icon-tile icon-tile--3">
28875
+ <div class="icon-tile__icon">
28876
+ <svg viewBox="0 0 24 24" fill="none">
28877
+ <path d="M20 22H4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1zM19 4H5v16h14V4zM7 6h4v4H7V6zm0 6h10v2H7v-2zm0 4h10v2H7v-2zm6-9h4v2h-4V7z" fill="currentColor"/>
28878
+ </svg>
28879
+ </div>
28880
+ </div>
28881
+ </div>
28882
+
28883
+ <!-- Tile 4: messages (right-upper, medium 88px) -->
28884
+ <div class="tile-entry tile-entry--4">
28885
+ <div class="icon-tile icon-tile--medium icon-tile--4">
28886
+ <div class="icon-tile__icon">
28887
+ <svg viewBox="0 0 24 24" fill="none">
28888
+ <path d="M7.291 20.824L2 22l1.176-5.291A9.956 9.956 0 0 1 2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10a9.956 9.956 0 0 1-4.709-1.176zM7.581 18.71l.29.158A7.956 7.956 0 0 0 12 20c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8c0 1.458.39 2.823 1.131 4.13l.159.288-.69 3.1 3.1-.69z" fill="currentColor"/>
28889
+ </svg>
28890
+ </div>
28891
+ </div>
28892
+ </div>
28893
+
28894
+ <!-- Tile 5: home (left-middle, medium 88px) -->
28895
+ <div class="tile-entry tile-entry--5">
28896
+ <div class="icon-tile icon-tile--medium icon-tile--5">
28897
+ <div class="icon-tile__icon">
28898
+ <svg viewBox="0 0 24 24" fill="none">
28899
+ <path d="M19 21H5a1 1 0 0 1-1-1v-9H1l10.327-9.388a1 1 0 0 1 1.346 0L23 11h-3v9a1 1 0 0 1-1 1zM6 19h12v-9.157l-6-5.454-6 5.454V19zm2-4h8v2H8v-2z" fill="currentColor"/>
28900
+ </svg>
28901
+ </div>
28902
+ </div>
28903
+ </div>
28904
+
28905
+ <!-- Hero: bell icon (center, 120px) -->
28906
+ <div class="tile-entry tile-entry--hero">
28907
+ <div class="icon-tile icon-tile--hero">
28908
+ <div class="icon-tile__icon">
28909
+ <svg viewBox="0 0 24 24" fill="none">
28910
+ <path d="M20 17h2v2H2v-2h2v-7a8 8 0 1 1 16 0v7zm-2 0v-7a6 6 0 1 0-12 0v7h12zm-9 4h6v2H9v-2z" fill="currentColor"/>
28911
+ </svg>
28912
+ </div>
28913
+ <span class="icon-tile__badge">18</span>
28914
+ </div>
28915
+ </div>
28916
+
28917
+ </div>
28918
+ </div>
28919
+
28920
+ <!-- Heading + subtitle -->
28921
+ <div class="notification-prompt__texts">
28922
+ <h1 class="notification-prompt__heading">{{ heading() }}</h1>
28923
+ <p class="notification-prompt__subtitle">{{ subtitle() }}</p>
28924
+ </div>
28925
+
28926
+ <!-- CTA buttons -->
28927
+ <div class="notification-prompt__actions">
28928
+ <ds-button
28929
+ variant="primary"
28930
+ size="md"
28931
+ (clicked)="handleAllow()">
28932
+ {{ allowLabel() }}
28933
+ </ds-button>
28934
+ <ds-button
28935
+ class="dismiss-btn"
28936
+ variant="ghost"
28937
+ size="md"
28938
+ (clicked)="handleDismiss()">
28939
+ {{ dismissLabel() }}
28940
+ </ds-button>
28941
+ </div>
28942
+ </div>
28943
+ `, styles: ["@keyframes fadeSlideUp{0%{opacity:0;transform:translateY(32px)}to{opacity:1;transform:translateY(0)}}@keyframes tileEntry{0%{opacity:0;transform:translateY(40px) scale(.85)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes float1{0%{transform:var(--tile-transform) translate(0) rotate(0)}11%{transform:var(--tile-transform) translate(1.4px,-3px) rotate(.9deg)}23%{transform:var(--tile-transform) translate(2.2px,-6.5px) rotate(1.8deg)}37%{transform:var(--tile-transform) translate(.6px,-8.5px) rotate(.4deg)}50%{transform:var(--tile-transform) translate(-1.2px,-7px) rotate(-.8deg)}63%{transform:var(--tile-transform) translate(-1.8px,-4px) rotate(-1.6deg)}76%{transform:var(--tile-transform) translate(-.4px,-1.5px) rotate(-.5deg)}88%{transform:var(--tile-transform) translate(.8px,-.5px) rotate(.3deg)}to{transform:var(--tile-transform) translate(0) rotate(0)}}@keyframes float2{0%{transform:var(--tile-transform) translate(0) rotate(0)}12%{transform:var(--tile-transform) translate(-1px,-2.5px) rotate(-.7deg)}25%{transform:var(--tile-transform) translate(-2px,-5.5px) rotate(-1.5deg)}38%{transform:var(--tile-transform) translate(-.8px,-7px) rotate(-.3deg)}50%{transform:var(--tile-transform) translate(1px,-6px) rotate(1deg)}62%{transform:var(--tile-transform) translate(1.6px,-3.5px) rotate(1.6deg)}75%{transform:var(--tile-transform) translate(.5px,-1.5px) rotate(.6deg)}87%{transform:var(--tile-transform) translate(-.5px,-.3px) rotate(-.2deg)}to{transform:var(--tile-transform) translate(0) rotate(0)}}@keyframes float3{0%{transform:var(--tile-transform) translate(0) rotate(0)}10%{transform:var(--tile-transform) translate(.8px,-3px) rotate(1.2deg)}22%{transform:var(--tile-transform) translate(1.5px,-7px) rotate(2deg)}35%{transform:var(--tile-transform) translate(.3px,-10px) rotate(.8deg)}48%{transform:var(--tile-transform) translate(-1.5px,-9.5px) rotate(-.6deg)}60%{transform:var(--tile-transform) translate(-2px,-6px) rotate(-1.5deg)}72%{transform:var(--tile-transform) translate(-1px,-3px) rotate(-.8deg)}85%{transform:var(--tile-transform) translate(.3px,-1px) rotate(.3deg)}to{transform:var(--tile-transform) translate(0) rotate(0)}}@keyframes float4{0%{transform:var(--tile-transform) translate(0) rotate(0)}13%{transform:var(--tile-transform) translate(-.8px,-2px) rotate(-.8deg)}26%{transform:var(--tile-transform) translate(-1.5px,-5px) rotate(-1.6deg)}40%{transform:var(--tile-transform) translate(.2px,-7.5px) rotate(-.2deg)}52%{transform:var(--tile-transform) translate(1.8px,-6.5px) rotate(1.2deg)}65%{transform:var(--tile-transform) translate(1.2px,-4px) rotate(.8deg)}78%{transform:var(--tile-transform) translate(-.3px,-1.8px) rotate(-.4deg)}90%{transform:var(--tile-transform) translate(-.6px,-.4px) rotate(-.1deg)}to{transform:var(--tile-transform) translate(0) rotate(0)}}@keyframes float5{0%{transform:var(--tile-transform) translate(0) rotate(0)}9%{transform:var(--tile-transform) translate(1.2px,-2px) rotate(.6deg)}20%{transform:var(--tile-transform) translate(2px,-5px) rotate(1.5deg)}33%{transform:var(--tile-transform) translate(.8px,-8.5px) rotate(.5deg)}45%{transform:var(--tile-transform) translate(-1px,-9.5px) rotate(-1deg)}58%{transform:var(--tile-transform) translate(-1.8px,-7px) rotate(-1.8deg)}70%{transform:var(--tile-transform) translate(-.8px,-4px) rotate(-.8deg)}82%{transform:var(--tile-transform) translate(.4px,-1.5px) rotate(.2deg)}92%{transform:var(--tile-transform) translate(.6px,-.3px) rotate(.1deg)}to{transform:var(--tile-transform) translate(0) rotate(0)}}@keyframes floatHero{0%{transform:translate(-50%) translate(0) rotate(0) scale(1)}10%{transform:translate(-50%) translate(.8px,-4px) rotate(.7deg) scale(1.006)}22%{transform:translate(-50%) translate(1.5px,-9px) rotate(1.4deg) scale(1.014)}35%{transform:translate(-50%) translate(.2px,-13px) rotate(.3deg) scale(1.02)}48%{transform:translate(-50%) translate(-1.5px,-12px) rotate(-1deg) scale(1.016)}60%{transform:translate(-50%) translate(-2px,-8px) rotate(-1.6deg) scale(1.01)}72%{transform:translate(-50%) translate(-.6px,-4px) rotate(-.5deg) scale(1.005)}85%{transform:translate(-50%) translate(.5px,-1.5px) rotate(.3deg) scale(1.002)}94%{transform:translate(-50%) translate(.3px,-.3px) rotate(.1deg) scale(1.001)}to{transform:translate(-50%) translate(0) rotate(0) scale(1)}}:host{display:flex;align-items:center;justify-content:center;position:fixed;inset:0;z-index:9999}.notification-prompt{width:100%;max-width:390px;height:100%;display:flex;flex-direction:column;gap:48px;overflow:clip;position:relative}.notification-prompt__backdrop{position:fixed;inset:0;z-index:-1}.notification-prompt__illustration{position:relative;flex:1 0 0;min-height:0;display:flex;align-items:center;justify-content:center;padding:0 24px}.icon-tiles{position:relative;width:100%;max-width:342px;height:100%}.tile-entry{position:absolute;opacity:0;animation:tileEntry .7s cubic-bezier(.22,1,.36,1) forwards;will-change:transform,opacity}.tile-entry--hero{inset:0;animation-delay:.2s;animation-duration:.8s}.tile-entry--1{inset:0;animation-delay:.35s}.tile-entry--4{inset:0;animation-delay:.3s}.tile-entry--5{inset:0;animation-delay:.4s}.tile-entry--3{inset:0;animation-delay:.45s}.tile-entry--2{inset:0;animation-delay:.5s}.icon-tile{position:absolute;display:flex;align-items:center;justify-content:center;border-radius:20%;background:linear-gradient(180deg,#0000 95%,#00000026),linear-gradient(180deg,#fff6,#fff0 4.5%),linear-gradient(180deg,#ffffff26,#fff0),linear-gradient(180deg,#0000,#0000001a),var(--color-accent, #6B5FF5);will-change:transform}.icon-tile__icon{width:62.5%;height:62.5%;display:flex;align-items:center;justify-content:center}.icon-tile__icon svg{width:100%;height:100%;color:#fff;display:block}.icon-tile--1{--tile-transform: translate(-50%, 0) rotate(7.74deg);width:72px;height:72px;top:22%;left:50%;filter:blur(2px);animation:float1 6.8s linear infinite}.icon-tile--2{--tile-transform: rotate(9.27deg);width:88px;height:88px;bottom:6%;right:0;filter:blur(1px);animation:float2 7.4s linear infinite}.icon-tile--3{--tile-transform: rotate(-6.18deg);width:72px;height:72px;bottom:0;left:0;filter:blur(2px);animation:float3 8.6s linear infinite}.icon-tile--4{--tile-transform: rotate(20.57deg);width:88px;height:88px;top:36%;right:0;filter:blur(1px);animation:float4 7.1s linear infinite}.icon-tile--5{--tile-transform: rotate(-8.24deg);width:88px;height:88px;top:44%;left:0;filter:blur(1px);animation:float5 9.2s linear infinite}.icon-tile--hero{width:120px;height:120px;left:50%;bottom:14%;filter:none;border-radius:30px;z-index:2;animation:floatHero 6.2s linear infinite}.icon-tile__badge{position:absolute;top:12.5%;right:12%;min-width:26px;height:26px;padding:0 7px;border-radius:8px;background:var(--content-feedback-bold-red, #b91c1c);display:flex;align-items:center;justify-content:center;font-family:Brockmann,system-ui,-apple-system,sans-serif;font-weight:600;font-size:18px;line-height:1;letter-spacing:-.04em;color:#fff;white-space:nowrap;box-sizing:border-box}.notification-prompt__texts{position:relative;z-index:1;text-align:center;padding:0 16px;flex-shrink:0;opacity:0;animation:fadeSlideUp .6s .6s cubic-bezier(.22,1,.36,1) forwards}.notification-prompt__heading{font-family:Brockmann,system-ui,-apple-system,sans-serif;font-weight:600;font-size:32px;line-height:1.15;letter-spacing:-.02em;color:#fff;margin:0 0 4px}.notification-prompt__subtitle{font-family:Brockmann,system-ui,-apple-system,sans-serif;font-weight:400;font-size:18px;line-height:1.4;letter-spacing:-.02em;color:#eef0ff;margin:0}.notification-prompt__actions{position:relative;z-index:1;display:flex;flex-direction:column;gap:8px;padding:0 24px 24px;padding-bottom:max(24px,env(safe-area-inset-bottom,24px));flex-shrink:0;opacity:0;animation:fadeSlideUp .6s .75s cubic-bezier(.22,1,.36,1) forwards}.notification-prompt__actions ds-button{display:block;width:100%}.notification-prompt__actions ds-button::ng-deep button{width:100%;border-radius:8px}.notification-prompt__actions ds-button.dismiss-btn::ng-deep button{color:#fff;background:transparent}@keyframes exitUp{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-48px)}}@keyframes exitFade{0%{opacity:1}to{opacity:0}}.is-dismissing .notification-prompt__illustration{animation:exitUp .5s cubic-bezier(.55,0,1,.45) forwards}.is-dismissing .notification-prompt__texts{animation:exitUp .45s .15s cubic-bezier(.55,0,1,.45) forwards}.is-dismissing .notification-prompt__actions{animation:exitUp .45s .3s cubic-bezier(.55,0,1,.45) forwards}.is-dismissing .notification-prompt__backdrop{animation:exitFade .35s .45s cubic-bezier(.55,0,1,.45) forwards}@media (prefers-reduced-motion: reduce){.tile-entry,.icon-tile,.icon-tile--hero,.notification-prompt__texts,.notification-prompt__actions{animation:none!important;opacity:1!important}}\n"] }]
28944
+ }], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], allowLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowLabel", required: false }] }], dismissLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissLabel", required: false }] }], allow: [{ type: i0.Output, args: ["allow"] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }] } });
28945
+
28946
+ const NOTIFICATION_ICON_MAP = {
28947
+ inquiry_update: 'remixFileList3Line',
28948
+ inquiry_assigned: 'remixFileList3Line',
28949
+ inquiry_resolved: 'remixFileList3Line',
28950
+ community_post: 'remixCommunityLine',
28951
+ community_comment: 'remixCommunityLine',
28952
+ community_like: 'remixCommunityLine',
28953
+ community_mention: 'remixCommunityLine',
28954
+ booking_confirmed: 'remixCalendarCheckLine',
28955
+ booking_cancelled: 'remixCalendarCheckLine',
28956
+ booking_reminder: 'remixCalendarCheckLine',
28957
+ facility_available: 'remixCalendarCheckLine',
28958
+ message_new: 'remixMessage3Line',
28959
+ message_group: 'remixMessage3Line',
28960
+ service_update: 'remixServiceLine',
28961
+ handbook_update: 'remixBook2Line',
28962
+ system_announcement: 'remixHomeSmile2Line',
28963
+ system_welcome: 'remixHomeSmile2Line',
28964
+ invite_received: 'remixHomeSmile2Line',
28965
+ family_access: 'remixGroupLine',
28966
+ };
28967
+
28968
+ function minutesAgo(m) { return new Date(Date.now() - m * 60_000); }
28969
+ function hoursAgo(h) { return new Date(Date.now() - h * 3_600_000); }
28970
+ function daysAgo(d, hour = 12, minute = 0) {
28971
+ const date = new Date();
28972
+ date.setDate(date.getDate() - d);
28973
+ date.setHours(hour, minute, 0, 0);
28974
+ return date;
28975
+ }
28976
+ const SAMPLE_NOTIFICATIONS = [
28977
+ // Today — inquiry #2 "Problem med vandtryk"
28978
+ { id: '1', type: 'inquiry_update', leading: 'icon', targetId: '2', title: 'Problem med vandtryk', message: 'Din henvendelse er blevet opdateret af ejendomskontoret.', createdAt: minutesAgo(5), read: false },
28979
+ // Today — Mette Larsen commented on post-1
28980
+ { id: '2', type: 'community_comment', leading: 'avatar', targetId: 'post-1', avatarInitials: 'ML', title: 'Mette Larsen', message: 'Kommenterede: "Velkommen til fællesskabet!"', createdAt: minutesAgo(23), read: false },
28981
+ // Today — Marcus Lindqvist sent message in conv-2
28982
+ { id: '3', type: 'message_new', leading: 'avatar', targetId: 'conv-2', avatarInitials: 'ML', title: 'Marcus Lindqvist', message: 'Kan du tjekke støjproblemet igen?', createdAt: minutesAgo(10), read: false },
28983
+ // Today — booking-1 "Festlokale på taget" confirmed
28984
+ { id: '4', type: 'booking_confirmed', leading: 'image', targetId: 'booking-1', imageSrc: '/Assets/Dummy-photos/rooftop-party.jpg', title: 'Festlokale på taget', message: 'Din booking d. 14. feb kl. 9:00–17:00 er bekræftet.', createdAt: hoursAgo(2), read: true },
28985
+ // Today — Anders Jensen commented on post-2
28986
+ { id: '5', type: 'community_comment', leading: 'avatar', targetId: 'post-2', avatarInitials: 'AJ', title: 'Anders Jensen', message: 'Kommenterede: "Wow, den udsigt er fantastisk!"', createdAt: hoursAgo(3), read: true },
28987
+ // Yesterday — Blik Partner A/S assigned to inquiry #2
28988
+ { id: '6', type: 'inquiry_assigned', leading: 'avatar', targetId: '2', avatarInitials: 'BP', title: 'Blik Partner A/S', message: 'Er blevet tildelt din henvendelse om VVS.', createdAt: daysAgo(1, 16, 45), read: true },
28989
+ // Yesterday — Sophie Andersen posted in community (post-2)
28990
+ { id: '7', type: 'community_post', leading: 'avatar', targetId: 'post-2', avatarInitials: 'SA', title: 'Sophie Andersen', message: 'Nyt opslag med billeder fra ejendommen.', createdAt: daysAgo(1, 14, 20), read: true },
28991
+ // Yesterday — booking-3 "Boremaskinen" reminder
28992
+ { id: '8', type: 'booking_reminder', leading: 'image', targetId: 'booking-3', imageSrc: '/Assets/Dummy-photos/handyman.jpg', title: 'Boremaskinen', message: 'Du har en booking d. 25. mar kl. 10:00–14:00.', createdAt: daysAgo(1, 9, 0), read: true },
28993
+ // Yesterday — facility-1 "Boremaskinen" available
28994
+ { id: '9', type: 'facility_available', leading: 'image', targetId: 'facility-1', imageSrc: '/Assets/Dummy-photos/handyman.jpg', title: 'Boremaskinen', message: 'Boremaskinen er nu ledig til booking.', createdAt: daysAgo(1, 8, 15), read: true },
28995
+ // Yesterday — group "Vaskeri & møde" message
28996
+ { id: '10', type: 'message_group', leading: 'avatar', targetId: 'conv-group-demo', avatarInitials: 'VM', title: 'Vaskeri & møde', message: 'Anna Berg: "Vi mødes kl. 19 i fællesrummet."', createdAt: daysAgo(1, 19, 30), read: true },
28997
+ // Earlier — inquiry #3 resolved
28998
+ { id: '11', type: 'inquiry_resolved', leading: 'icon', targetId: '3', title: 'Varme virker ikke ordentligt', message: 'Din henvendelse er nu markeret som løst.', createdAt: daysAgo(3), read: true },
28999
+ // Earlier — handbook updated
29000
+ { id: '12', type: 'handbook_update', leading: 'logo', title: 'Håndbog opdateret', message: 'Afsnittet "Forsyninger" er blevet opdateret med ny information.', createdAt: daysAgo(4), read: true },
29001
+ // Earlier — ElektroTek ApS service update
29002
+ { id: '13', type: 'service_update', leading: 'image', targetId: 'v-4', imageSrc: '/Assets/dummy-logos/electrician-logo.svg', title: 'ElektroTek ApS', message: 'El-service er nu tilgængelig via Services.', createdAt: daysAgo(5), read: true },
29003
+ // Earlier — Thomas Hansen commented on post-2
29004
+ { id: '14', type: 'community_comment', leading: 'avatar', targetId: 'post-2', avatarInitials: 'TH', title: 'Thomas Hansen', message: 'Kommenterede: "Smuk! Jeg kan også se byens silhuet fra min lejlighed 🌆"', createdAt: daysAgo(6), read: true },
29005
+ // Earlier — booking-2 "Gæsteparkering" cancelled
29006
+ { id: '15', type: 'booking_cancelled', leading: 'image', targetId: 'booking-2', imageSrc: '/Assets/Dummy-photos/parking.jpg', title: 'Gæsteparkering', message: 'Din booking d. 20. mar kl. 8:00–20:00 er blevet annulleret.', createdAt: daysAgo(7), read: true },
29007
+ // Earlier — system announcement
29008
+ { id: '16', type: 'system_announcement', leading: 'logo', title: 'Systemmeddelelse', message: 'Planlagt vedligeholdelse d. 1. maj kl. 02:00–04:00.', createdAt: daysAgo(8), read: true },
29009
+ // Earlier — Karl Johansson message in conv-4
29010
+ { id: '17', type: 'message_new', leading: 'avatar', targetId: 'conv-4', avatarInitials: 'KJ', title: 'Karl Johansson', message: 'Hej! Er vaskerummet ledigt i morgen kl. 10?', createdAt: daysAgo(9), read: true },
29011
+ // Earlier — Sara Lindqvist family access
29012
+ { id: '18', type: 'family_access', leading: 'avatar', title: 'Sara Lindqvist', avatarInitials: 'SL', message: 'Sara Lindqvist har fået adgang til din konto.', createdAt: daysAgo(10), read: true },
29013
+ // Earlier — system welcome
29014
+ { id: '19', type: 'system_welcome', leading: 'logo', title: 'Velkommen!', message: 'Velkommen til Propbinder – vi glæder os til at hjælpe dig.', createdAt: daysAgo(11), read: true },
29015
+ ];
29016
+
29017
+ class NotificationService {
29018
+ notifications = signal(SAMPLE_NOTIFICATIONS, ...(ngDevMode ? [{ debugName: "notifications" }] : []));
29019
+ unreadCount = computed(() => this.notifications().filter(n => !n.read).length, ...(ngDevMode ? [{ debugName: "unreadCount" }] : []));
29020
+ /**
29021
+ * Add a new notification to the top of the list.
29022
+ * Intended for downstream push integration — call this when a
29023
+ * real push payload arrives via Capacitor's `pushNotificationReceived` listener.
29024
+ */
29025
+ addNotification(item) {
29026
+ this.notifications.update(list => [item, ...list]);
29027
+ }
29028
+ markAsRead(id) {
29029
+ this.notifications.update(list => list.map(n => n.id === id ? { ...n, read: true } : n));
29030
+ }
29031
+ markAllAsRead() {
29032
+ this.notifications.update(list => list.map(n => ({ ...n, read: true })));
29033
+ }
29034
+ /**
29035
+ * Remove a single notification from the list permanently.
29036
+ */
29037
+ removeNotification(id) {
29038
+ this.notifications.update(list => list.filter(n => n.id !== id));
29039
+ }
29040
+ /**
29041
+ * Remove all notifications from the list permanently.
29042
+ */
29043
+ clearAll() {
29044
+ this.notifications.set([]);
29045
+ }
29046
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
29047
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationService, providedIn: 'root' });
29048
+ }
29049
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationService, decorators: [{
29050
+ type: Injectable,
29051
+ args: [{ providedIn: 'root' }]
29052
+ }] });
29053
+
29054
+ class RelativeTimePipe {
29055
+ transform(date) {
29056
+ if (!date)
29057
+ return '';
29058
+ const now = Date.now();
29059
+ const diffMs = now - date.getTime();
29060
+ const diffSec = Math.floor(diffMs / 1000);
29061
+ const diffMin = Math.floor(diffSec / 60);
29062
+ const diffHr = Math.floor(diffMin / 60);
29063
+ const diffDays = Math.floor(diffHr / 24);
29064
+ if (diffSec < 60)
29065
+ return 'Nu';
29066
+ if (diffMin < 60)
29067
+ return `${diffMin} min siden`;
29068
+ if (diffHr < 24)
29069
+ return `${diffHr} ${diffHr === 1 ? 'time' : 'timer'} siden`;
29070
+ const today = new Date();
29071
+ today.setHours(0, 0, 0, 0);
29072
+ const yesterday = new Date(today.getTime() - 86_400_000);
29073
+ if (date.getTime() >= yesterday.getTime() && date.getTime() < today.getTime()) {
29074
+ return `I går, ${pad(date.getHours())}:${pad(date.getMinutes())}`;
29075
+ }
29076
+ if (diffDays < 7)
29077
+ return `${diffDays} dage siden`;
29078
+ const day = date.getDate();
29079
+ const monthNames = ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'];
29080
+ return `${day}. ${monthNames[date.getMonth()]}`;
29081
+ }
29082
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: RelativeTimePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
29083
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.16", ngImport: i0, type: RelativeTimePipe, isStandalone: true, name: "relativeTime" });
29084
+ }
29085
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: RelativeTimePipe, decorators: [{
29086
+ type: Pipe,
29087
+ args: [{ name: 'relativeTime', standalone: true, pure: true }]
29088
+ }] });
29089
+ function pad(n) {
29090
+ return n < 10 ? `0${n}` : `${n}`;
29091
+ }
29092
+ /**
29093
+ * Bucket a date into 'today' | 'yesterday' | 'earlier' for grouping notifications.
29094
+ */
29095
+ function dateBucket(date) {
29096
+ const now = new Date();
29097
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
29098
+ const yesterday = new Date(today.getTime() - 86_400_000);
29099
+ if (date.getTime() >= today.getTime())
29100
+ return 'today';
29101
+ if (date.getTime() >= yesterday.getTime())
29102
+ return 'yesterday';
29103
+ return 'earlier';
29104
+ }
29105
+
29106
+ class DsMobileNotificationModalComponent {
29107
+ modalController = inject(ModalController);
29108
+ notificationService = inject(NotificationService);
29109
+ bottomSheet = inject(DsMobileBottomSheetService);
29110
+ groups = computed(() => {
29111
+ const items = this.notificationService.notifications();
29112
+ if (!items || items.length === 0)
29113
+ return [];
29114
+ const todayItems = [];
29115
+ const yesterdayItems = [];
29116
+ const earlierItems = [];
29117
+ for (const item of items) {
29118
+ const bucket = dateBucket(item.createdAt);
29119
+ if (bucket === 'today')
29120
+ todayItems.push(item);
29121
+ else if (bucket === 'yesterday')
29122
+ yesterdayItems.push(item);
29123
+ else
29124
+ earlierItems.push(item);
29125
+ }
29126
+ const groups = [];
29127
+ if (todayItems.length > 0)
29128
+ groups.push({ label: 'I dag', items: todayItems });
29129
+ if (yesterdayItems.length > 0)
29130
+ groups.push({ label: 'I går', items: yesterdayItems });
29131
+ if (earlierItems.length > 0)
29132
+ groups.push({ label: 'Tidligere', items: earlierItems });
29133
+ return groups;
29134
+ }, ...(ngDevMode ? [{ debugName: "groups" }] : []));
29135
+ iconFor(type) {
29136
+ return NOTIFICATION_ICON_MAP[type] ?? 'remixNotificationLine';
29137
+ }
29138
+ TYPE_LABELS = {
29139
+ inquiry_update: 'Henvendelse',
29140
+ inquiry_assigned: 'Henvendelse',
29141
+ inquiry_resolved: 'Henvendelse',
29142
+ community_post: 'Fællesskab',
29143
+ community_comment: 'Kommentar',
29144
+ community_like: 'Fællesskab',
29145
+ community_mention: 'Omtale',
29146
+ booking_confirmed: 'Booking',
29147
+ booking_cancelled: 'Booking',
29148
+ booking_reminder: 'Booking',
29149
+ facility_available: 'Facilitet',
29150
+ message_new: 'Besked',
29151
+ message_group: 'Gruppebesked',
29152
+ service_update: 'Service',
29153
+ handbook_update: 'Håndbog',
29154
+ system_announcement: 'System',
29155
+ system_welcome: 'System',
29156
+ invite_received: 'Invitation',
29157
+ family_access: 'Familie',
29158
+ };
29159
+ labelFor(type) {
29160
+ return this.TYPE_LABELS[type] ?? '';
29161
+ }
29162
+ handleNotificationClick(item) {
29163
+ this.modalController.dismiss(item);
29164
+ }
29165
+ async handleLongPress(item) {
29166
+ const actions = [
29167
+ { action: 'mark_read', title: item.read ? 'Marker som ulæst' : 'Marker som læst', icon: item.read ? 'remixMailUnreadLine' : 'remixMailOpenLine', destructive: false },
29168
+ { action: 'remove', title: 'Fjern notifikation', icon: 'remixDeleteBinLine', destructive: true },
29169
+ ];
29170
+ const sheet = await this.bottomSheet.create({
29171
+ component: DsMobileActionsBottomSheetComponent,
29172
+ componentProps: { customActionGroups: [{ actions }] },
29173
+ breakpoints: [0, 1],
29174
+ initialBreakpoint: 1,
29175
+ handle: true,
29176
+ backdropDismiss: true,
29177
+ cssClass: 'auto-height',
29178
+ });
29179
+ const result = await sheet.onWillDismiss();
29180
+ if (result.role === 'select' && result.data) {
29181
+ const action = result.data.action;
29182
+ if (action === 'mark_read') {
29183
+ if (item.read) {
29184
+ this.notificationService.notifications.update(list => list.map(n => n.id === item.id ? { ...n, read: false } : n));
29185
+ }
29186
+ else {
29187
+ this.notificationService.markAsRead(item.id);
29188
+ }
29189
+ }
29190
+ else if (action === 'remove') {
29191
+ this.notificationService.removeNotification(item.id);
29192
+ }
29193
+ }
29194
+ }
29195
+ async openBulkActions() {
29196
+ const actions = [
29197
+ { action: 'mark_all_read', title: 'Marker alle som læst', icon: 'remixMailOpenLine', destructive: false },
29198
+ { action: 'clear_all', title: 'Ryd alle notifikationer', icon: 'remixDeleteBinLine', destructive: true },
29199
+ ];
29200
+ const sheet = await this.bottomSheet.create({
29201
+ component: DsMobileActionsBottomSheetComponent,
29202
+ componentProps: { customActionGroups: [{ actions }] },
29203
+ breakpoints: [0, 1],
29204
+ initialBreakpoint: 1,
29205
+ handle: true,
29206
+ backdropDismiss: true,
29207
+ cssClass: 'auto-height',
29208
+ });
29209
+ const result = await sheet.onWillDismiss();
29210
+ if (result.role === 'select' && result.data) {
29211
+ const action = result.data.action;
29212
+ if (action === 'mark_all_read') {
29213
+ this.notificationService.markAllAsRead();
29214
+ }
29215
+ else if (action === 'clear_all') {
29216
+ this.notificationService.clearAll();
29217
+ }
29218
+ }
29219
+ }
29220
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNotificationModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
29221
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileNotificationModalComponent, isStandalone: true, selector: "ds-mobile-notification-modal", ngImport: i0, template: `
29222
+ <ds-mobile-modal-base
29223
+ headerTitle="Notifikationer"
29224
+ [showHeader]="true"
29225
+ closeButtonLabel="Luk">
29226
+
29227
+ <ds-icon-button
29228
+ header-trailing
29229
+ class="more-button"
29230
+ icon="remixMoreFill"
29231
+ variant="secondary"
29232
+ size="lg"
29233
+ (clicked)="openBulkActions()"
29234
+ aria-label="Flere handlinger"
29235
+ />
29236
+
29237
+ @if (groups().length > 0) {
29238
+ @for (group of groups(); track group.label) {
29239
+ <div class="notification-group">
29240
+ <div class="notification-group__label">{{ group.label }}</div>
29241
+
29242
+ @for (item of group.items; track item.id) {
29243
+ <ds-mobile-list-item
29244
+ [interactive]="true"
29245
+ [showDivider]="!$last"
29246
+ [enableLongPress]="true"
29247
+ [showDesktopMoreButton]="false"
29248
+ align="top"
29249
+ (itemClick)="handleNotificationClick(item)"
29250
+ (longPress)="handleLongPress(item)">
29251
+
29252
+ @if (item.leading === 'icon') {
29253
+ <ds-avatar
29254
+ content-leading
29255
+ type="icon"
29256
+ [iconName]="item.iconName || iconFor(item.type)"
29257
+ size="md"
29258
+ />
29259
+ }
29260
+
29261
+ @if (item.leading === 'avatar') {
29262
+ <ds-avatar
29263
+ content-leading
29264
+ [type]="item.avatarSrc ? 'photo' : 'initials'"
29265
+ [initials]="item.avatarInitials || ''"
29266
+ [src]="item.avatarSrc || ''"
29267
+ size="md"
29268
+ />
29269
+ }
29270
+
29271
+ @if (item.leading === 'image') {
29272
+ <div content-leading class="notification-image">
29273
+ <img [src]="item.imageSrc" [alt]="item.title" />
29274
+ </div>
29275
+ }
29276
+
29277
+ @if (item.leading === 'logo') {
29278
+ <div content-leading class="notification-logo">
29279
+ <ds-app-icon size="lg" />
29280
+ </div>
29281
+ }
29282
+
29283
+ <div content-main class="notification-content">
29284
+ <div class="notification-header">
29285
+ <div class="notification-header__details">
29286
+ <span class="notification-header__name" [class.notification-header__name--unread]="!item.read">
29287
+ {{ item.title }}
29288
+ </span>
29289
+ <span class="notification-header__meta">
29290
+ {{ labelFor(item.type) }}
29291
+ <span class="notification-header__separator">&middot;</span>
29292
+ {{ item.createdAt | relativeTime }}
29293
+ </span>
29294
+ </div>
29295
+ </div>
29296
+ <p class="notification-message">{{ item.message }}</p>
29297
+ </div>
29298
+
29299
+ @if (!item.read) {
29300
+ <div content-trailing class="notification-unread-dot"></div>
29301
+ }
29302
+ </ds-mobile-list-item>
29303
+ }
29304
+ </div>
29305
+ }
29306
+ } @else {
29307
+ <ds-mobile-empty-state
29308
+ imageSrc="/Assets/empty-state-notification.svg"
29309
+ imageAlt="Ingen notifikationer"
29310
+ title="Alt er stille herinde"
29311
+ description="Når der sker noget nyt, finder du det her."
29312
+ />
29313
+ }
29314
+ </ds-mobile-modal-base>
29315
+ `, isInline: true, styles: [":host ::ng-deep .header-content{gap:8px!important}.more-button{flex-shrink:0;border-radius:50%}.more-button::ng-deep button{border-radius:50%!important;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.notification-group{padding:0 20px}.notification-group+.notification-group{margin-top:8px;border-top:1px solid var(--color-border-default-secondary, #E5E7EB)}.notification-group__label{font-family:Brockmann,system-ui,-apple-system,sans-serif;font-weight:600;font-size:13px;line-height:1;letter-spacing:-.02em;color:var(--text-color-default-tertiary, #999);text-transform:none;padding:16px 0 4px}.notification-content{display:flex;flex-direction:column;gap:2px;min-width:0}.notification-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:4px}.notification-header__details{display:flex;flex-direction:column;align-items:flex-start;gap:2px;flex:1;min-width:0}.notification-header__name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm, 14px);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-primary, #202227);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.notification-header__name--unread{font-weight:600}.notification-header__meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs, 12px);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--text-color-default-tertiary, #737373);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center;gap:4px}.notification-header__separator{color:var(--text-color-default-tertiary, #737373);opacity:.5}.notification-message{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm, 14px);font-weight:400;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-secondary, #545B66);margin:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis}.notification-image{width:32px;height:32px;border-radius:8px;overflow:hidden;flex-shrink:0}.notification-image img{width:100%;height:100%;-o-object-fit:cover;object-fit:cover;display:block}.notification-logo{flex-shrink:0}.notification-unread-dot{width:8px;height:8px;border-radius:50%;background:var(--color-accent, #6B5FF5);flex-shrink:0;margin-top:6px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsMobileModalBaseComponent, selector: "ds-mobile-modal-base", inputs: ["headerTitleInteractive", "showHeader"], outputs: ["titleClick"] }, { kind: "component", type: DsMobileListItemComponent, selector: "ds-mobile-list-item", inputs: ["leadingSize", "variant", "align", "flushTop", "interactive", "disabled", "loading", "enableLongPress", "showDesktopMoreButton", "moreActions", "moreButtonAriaLabel", "interactiveOffset", "title", "subtitle", "showDivider", "dividerSpacing"], outputs: ["itemClick", "moreButtonClick"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: DsAppIconComponent, selector: "ds-app-icon", inputs: ["size"] }, { kind: "component", type: DsMobileEmptyStateComponent, selector: "ds-mobile-empty-state", inputs: ["imageSrc", "imageAlt", "title", "description"] }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }, { kind: "pipe", type: RelativeTimePipe, name: "relativeTime" }] });
29316
+ }
29317
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNotificationModalComponent, decorators: [{
29318
+ type: Component,
29319
+ args: [{ selector: 'ds-mobile-notification-modal', standalone: true, imports: [CommonModule, DsMobileModalBaseComponent, DsMobileListItemComponent, DsAvatarComponent, DsAppIconComponent, DsMobileEmptyStateComponent, DsIconButtonComponent, RelativeTimePipe], template: `
29320
+ <ds-mobile-modal-base
29321
+ headerTitle="Notifikationer"
29322
+ [showHeader]="true"
29323
+ closeButtonLabel="Luk">
29324
+
29325
+ <ds-icon-button
29326
+ header-trailing
29327
+ class="more-button"
29328
+ icon="remixMoreFill"
29329
+ variant="secondary"
29330
+ size="lg"
29331
+ (clicked)="openBulkActions()"
29332
+ aria-label="Flere handlinger"
29333
+ />
29334
+
29335
+ @if (groups().length > 0) {
29336
+ @for (group of groups(); track group.label) {
29337
+ <div class="notification-group">
29338
+ <div class="notification-group__label">{{ group.label }}</div>
29339
+
29340
+ @for (item of group.items; track item.id) {
29341
+ <ds-mobile-list-item
29342
+ [interactive]="true"
29343
+ [showDivider]="!$last"
29344
+ [enableLongPress]="true"
29345
+ [showDesktopMoreButton]="false"
29346
+ align="top"
29347
+ (itemClick)="handleNotificationClick(item)"
29348
+ (longPress)="handleLongPress(item)">
29349
+
29350
+ @if (item.leading === 'icon') {
29351
+ <ds-avatar
29352
+ content-leading
29353
+ type="icon"
29354
+ [iconName]="item.iconName || iconFor(item.type)"
29355
+ size="md"
29356
+ />
29357
+ }
29358
+
29359
+ @if (item.leading === 'avatar') {
29360
+ <ds-avatar
29361
+ content-leading
29362
+ [type]="item.avatarSrc ? 'photo' : 'initials'"
29363
+ [initials]="item.avatarInitials || ''"
29364
+ [src]="item.avatarSrc || ''"
29365
+ size="md"
29366
+ />
29367
+ }
29368
+
29369
+ @if (item.leading === 'image') {
29370
+ <div content-leading class="notification-image">
29371
+ <img [src]="item.imageSrc" [alt]="item.title" />
29372
+ </div>
29373
+ }
29374
+
29375
+ @if (item.leading === 'logo') {
29376
+ <div content-leading class="notification-logo">
29377
+ <ds-app-icon size="lg" />
29378
+ </div>
29379
+ }
29380
+
29381
+ <div content-main class="notification-content">
29382
+ <div class="notification-header">
29383
+ <div class="notification-header__details">
29384
+ <span class="notification-header__name" [class.notification-header__name--unread]="!item.read">
29385
+ {{ item.title }}
29386
+ </span>
29387
+ <span class="notification-header__meta">
29388
+ {{ labelFor(item.type) }}
29389
+ <span class="notification-header__separator">&middot;</span>
29390
+ {{ item.createdAt | relativeTime }}
29391
+ </span>
29392
+ </div>
29393
+ </div>
29394
+ <p class="notification-message">{{ item.message }}</p>
29395
+ </div>
29396
+
29397
+ @if (!item.read) {
29398
+ <div content-trailing class="notification-unread-dot"></div>
29399
+ }
29400
+ </ds-mobile-list-item>
29401
+ }
29402
+ </div>
29403
+ }
29404
+ } @else {
29405
+ <ds-mobile-empty-state
29406
+ imageSrc="/Assets/empty-state-notification.svg"
29407
+ imageAlt="Ingen notifikationer"
29408
+ title="Alt er stille herinde"
29409
+ description="Når der sker noget nyt, finder du det her."
29410
+ />
29411
+ }
29412
+ </ds-mobile-modal-base>
29413
+ `, styles: [":host ::ng-deep .header-content{gap:8px!important}.more-button{flex-shrink:0;border-radius:50%}.more-button::ng-deep button{border-radius:50%!important;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.notification-group{padding:0 20px}.notification-group+.notification-group{margin-top:8px;border-top:1px solid var(--color-border-default-secondary, #E5E7EB)}.notification-group__label{font-family:Brockmann,system-ui,-apple-system,sans-serif;font-weight:600;font-size:13px;line-height:1;letter-spacing:-.02em;color:var(--text-color-default-tertiary, #999);text-transform:none;padding:16px 0 4px}.notification-content{display:flex;flex-direction:column;gap:2px;min-width:0}.notification-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:4px}.notification-header__details{display:flex;flex-direction:column;align-items:flex-start;gap:2px;flex:1;min-width:0}.notification-header__name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm, 14px);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-primary, #202227);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.notification-header__name--unread{font-weight:600}.notification-header__meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs, 12px);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--text-color-default-tertiary, #737373);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center;gap:4px}.notification-header__separator{color:var(--text-color-default-tertiary, #737373);opacity:.5}.notification-message{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm, 14px);font-weight:400;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-secondary, #545B66);margin:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis}.notification-image{width:32px;height:32px;border-radius:8px;overflow:hidden;flex-shrink:0}.notification-image img{width:100%;height:100%;-o-object-fit:cover;object-fit:cover;display:block}.notification-logo{flex-shrink:0}.notification-unread-dot{width:8px;height:8px;border-radius:50%;background:var(--color-accent, #6B5FF5);flex-shrink:0;margin-top:6px}\n"] }]
29414
+ }] });
28628
29415
 
28629
29416
  /**
28630
29417
  * PostsService
@@ -29151,6 +29938,614 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
29151
29938
  }]
29152
29939
  }], ctorParameters: () => [] });
29153
29940
 
29941
+ class DsMobileBookingDetailSheetComponent {
29942
+ modalController;
29943
+ /** `sheet` = bottom sheet. `modal` = `ds-modal-base` shell (active + history bookings). */
29944
+ presentation = 'sheet';
29945
+ data;
29946
+ /** When true the modal sizes to its content instead of filling the screen. */
29947
+ autoHeight = false;
29948
+ get isModalPresentation() {
29949
+ return this.presentation === 'modal';
29950
+ }
29951
+ constructor(modalController) {
29952
+ this.modalController = modalController;
29953
+ }
29954
+ close() {
29955
+ this.modalController.dismiss(null, 'backdrop');
29956
+ }
29957
+ cancelBooking() {
29958
+ this.modalController.dismiss(null, 'cancel');
29959
+ }
29960
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileBookingDetailSheetComponent, deps: [{ token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Component });
29961
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileBookingDetailSheetComponent, isStandalone: true, selector: "ds-mobile-booking-detail-sheet", inputs: { presentation: "presentation", data: "data", autoHeight: "autoHeight" }, host: { properties: { "class.presentation-modal": "this.isModalPresentation" } }, ngImport: i0, template: `
29962
+ @if (presentation === 'modal') {
29963
+ <ds-mobile-modal-base
29964
+ [headerTitle]="data.facilityTitle"
29965
+ [closeButtonLabel]="'Luk'"
29966
+ [isAutoHeight]="autoHeight"
29967
+ [keyboardContentBehavior]="'follow'">
29968
+
29969
+ <ds-mobile-section
29970
+ [showBorder]="false"
29971
+ padding="20px 20px 0 20px">
29972
+ <div class="hero-image-container">
29973
+ @if (data.heroImage) {
29974
+ <img [src]="data.heroImage" [alt]="data.facilityTitle" class="hero-image" />
29975
+ } @else {
29976
+ <div class="hero-image-placeholder">
29977
+ <ds-icon name="remixImageLine" size="48px" color="rgba(255,255,255,0.5)" />
29978
+ </div>
29979
+ }
29980
+ </div>
29981
+ </ds-mobile-section>
29982
+
29983
+ <ds-mobile-section padding="20px 20px 20px 20px">
29984
+ <div class="booking-summary-card">
29985
+ <div class="details-section">
29986
+ <h3 class="details-heading">Booking detaljer</h3>
29987
+ <div class="info-rows">
29988
+ @if (data.bookingDate) {
29989
+ <div class="info-row">
29990
+ <ds-icon name="remixCalendarLine" size="16px" color="tertiary" />
29991
+ <span>{{ data.bookingDate }}</span>
29992
+ </div>
29993
+ }
29994
+ @if (data.bookingTime) {
29995
+ <div class="info-row">
29996
+ <ds-icon name="remixTimeLine" size="16px" color="tertiary" />
29997
+ <span>{{ data.bookingTime }}</span>
29998
+ </div>
29999
+ }
30000
+ @if (data.price) {
30001
+ <div class="info-row">
30002
+ <ds-icon name="remixPriceTag3Line" size="16px" color="tertiary" />
30003
+ <span>{{ data.price }}</span>
30004
+ </div>
30005
+ }
30006
+ @if (data.bookingType) {
30007
+ <div class="info-row">
30008
+ <ds-icon name="remixCheckboxCircleLine" size="16px" color="tertiary" />
30009
+ <span>{{ data.bookingType }}</span>
30010
+ </div>
30011
+ }
30012
+ @for (req of data.requirements || []; track req) {
30013
+ <div class="info-row">
30014
+ <ds-icon name="remixLockLine" size="16px" color="tertiary" />
30015
+ <span>{{ req }}</span>
30016
+ </div>
30017
+ }
30018
+ </div>
30019
+ </div>
30020
+ </div>
30021
+ </ds-mobile-section>
30022
+
30023
+ @if (data.canCancel) {
30024
+ <div class="booking-action">
30025
+ <div class="booking-actions-row">
30026
+ <ds-button
30027
+ class="cancel-primary"
30028
+ size="md"
30029
+ variant="secondary"
30030
+ (clicked)="cancelBooking()">
30031
+ Annuller booking
30032
+ </ds-button>
30033
+ </div>
30034
+ </div>
30035
+ }
30036
+ </ds-mobile-modal-base>
30037
+ } @else {
30038
+ <ds-mobile-bottom-sheet-wrapper [showDragHandle]="true">
30039
+ <div class="detail-header">
30040
+ <h2 class="detail-title">{{ data.facilityTitle }}</h2>
30041
+ <ds-icon-button
30042
+ icon="remixCloseLine"
30043
+ variant="ghost"
30044
+ size="sm"
30045
+ (clicked)="close()"
30046
+ aria-label="Luk"
30047
+ />
30048
+ </div>
30049
+
30050
+ <div class="detail-content">
30051
+ <div class="hero-image-container">
30052
+ @if (data.heroImage) {
30053
+ <img [src]="data.heroImage" [alt]="data.facilityTitle" class="hero-image" />
30054
+ } @else {
30055
+ <div class="hero-image-placeholder">
30056
+ <ds-icon name="remixImageLine" size="48px" color="rgba(255,255,255,0.5)" />
30057
+ </div>
30058
+ }
30059
+ </div>
30060
+
30061
+ <div class="booking-summary-card">
30062
+ <div class="details-section">
30063
+ <h3 class="details-heading">Booking detaljer</h3>
30064
+ <div class="info-rows">
30065
+ @if (data.bookingDate) {
30066
+ <div class="info-row">
30067
+ <ds-icon name="remixCalendarLine" size="16px" color="tertiary" />
30068
+ <span>{{ data.bookingDate }}</span>
30069
+ </div>
30070
+ }
30071
+ @if (data.bookingTime) {
30072
+ <div class="info-row">
30073
+ <ds-icon name="remixTimeLine" size="16px" color="tertiary" />
30074
+ <span>{{ data.bookingTime }}</span>
30075
+ </div>
30076
+ }
30077
+ @if (data.price) {
30078
+ <div class="info-row">
30079
+ <ds-icon name="remixPriceTag3Line" size="16px" color="tertiary" />
30080
+ <span>{{ data.price }}</span>
30081
+ </div>
30082
+ }
30083
+ @if (data.bookingType) {
30084
+ <div class="info-row">
30085
+ <ds-icon name="remixCheckboxCircleLine" size="16px" color="tertiary" />
30086
+ <span>{{ data.bookingType }}</span>
30087
+ </div>
30088
+ }
30089
+ @for (req of data.requirements || []; track req) {
30090
+ <div class="info-row">
30091
+ <ds-icon name="remixLockLine" size="16px" color="tertiary" />
30092
+ <span>{{ req }}</span>
30093
+ </div>
30094
+ }
30095
+ </div>
30096
+ </div>
30097
+ </div>
30098
+
30099
+ @if (data.canCancel) {
30100
+ <div class="footer-button-container">
30101
+ <div class="booking-actions-column">
30102
+ <ds-button
30103
+ size="md"
30104
+ variant="secondary"
30105
+ (clicked)="cancelBooking()">
30106
+ Annuller booking
30107
+ </ds-button>
30108
+ </div>
30109
+ </div>
30110
+ }
30111
+ </div>
30112
+ </ds-mobile-bottom-sheet-wrapper>
30113
+ }
30114
+ `, isInline: true, styles: [":host{display:block;height:auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;box-sizing:border-box}.detail-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px 8px}.detail-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-lg, 18px);font-weight:600;color:var(--text-color-default-primary, #202227);margin:0;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-right:12px}.detail-content{padding:8px 20px 24px;display:flex;flex-direction:column;gap:20px}.hero-image-container{width:100%;aspect-ratio:16 / 9;border-radius:12px;overflow:hidden;background:var(--color-surface-secondary, #f5f5f5)}.hero-image{width:100%;height:100%;object-fit:cover;display:block}.hero-image-placeholder{width:100%;height:100%;min-height:0;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,color-mix(in srgb,var(--color-accent, #6B5FF5) 30%,transparent),color-mix(in srgb,var(--color-accent, #6B5FF5) 60%,transparent))}.booking-summary-card{background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:16px;padding:16px}.details-section{display:flex;flex-direction:column;gap:12px}.details-heading{font-family:Brockmann,sans-serif;font-size:var(--font-size-base, 16px);font-weight:600;color:var(--text-color-default-primary, #202227);margin:0}.info-rows{display:flex;flex-direction:column;gap:8px}.info-row{display:flex;align-items:center;gap:10px;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm, 14px);color:var(--text-color-default-secondary, #545B66);line-height:1.4}.info-row ds-icon{flex-shrink:0}.footer-button-container{width:100%;padding:16px 20px 0}.footer-button-container ::ng-deep ds-button{display:block;width:100%}.footer-button-container ::ng-deep ds-button button{width:100%;border-radius:100px;height:44px;min-height:44px;max-height:44px}.booking-action{padding:16px 20px;padding-bottom:calc(16px + max(8px,env(safe-area-inset-bottom,0px) - 24px));background:var(--color-surface-primary, #ffffff)}.booking-actions-row{display:flex;flex-direction:row;align-items:center;gap:12px;width:100%}.booking-action .cancel-primary{flex:1;min-width:0;display:block}.booking-action .cancel-primary ::ng-deep button{width:100%;border-radius:100px;height:44px;min-height:44px;max-height:44px;padding-left:16px;padding-right:16px}:host.presentation-modal{display:block;height:auto;width:100%;min-height:0;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsMobileBottomSheetWrapperComponent, selector: "ds-mobile-bottom-sheet-wrapper", inputs: ["showDragHandle"] }, { kind: "component", type: DsMobileModalBaseComponent, selector: "ds-mobile-modal-base", inputs: ["headerTitleInteractive", "showHeader"], outputs: ["titleClick"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsButtonComponent, selector: "ds-button", inputs: ["variant", "size", "disabled", "loading", "pressed", "expanded", "leadingIcon", "trailingIcon", "ariaLabel", "iconOnly"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }] });
30115
+ }
30116
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileBookingDetailSheetComponent, decorators: [{
30117
+ type: Component,
30118
+ args: [{ selector: 'ds-mobile-booking-detail-sheet', standalone: true, imports: [
30119
+ CommonModule,
30120
+ DsMobileBottomSheetWrapperComponent,
30121
+ DsMobileModalBaseComponent,
30122
+ DsMobileSectionComponent,
30123
+ DsButtonComponent,
30124
+ DsIconComponent,
30125
+ DsIconButtonComponent,
30126
+ ], template: `
30127
+ @if (presentation === 'modal') {
30128
+ <ds-mobile-modal-base
30129
+ [headerTitle]="data.facilityTitle"
30130
+ [closeButtonLabel]="'Luk'"
30131
+ [isAutoHeight]="autoHeight"
30132
+ [keyboardContentBehavior]="'follow'">
30133
+
30134
+ <ds-mobile-section
30135
+ [showBorder]="false"
30136
+ padding="20px 20px 0 20px">
30137
+ <div class="hero-image-container">
30138
+ @if (data.heroImage) {
30139
+ <img [src]="data.heroImage" [alt]="data.facilityTitle" class="hero-image" />
30140
+ } @else {
30141
+ <div class="hero-image-placeholder">
30142
+ <ds-icon name="remixImageLine" size="48px" color="rgba(255,255,255,0.5)" />
30143
+ </div>
30144
+ }
30145
+ </div>
30146
+ </ds-mobile-section>
30147
+
30148
+ <ds-mobile-section padding="20px 20px 20px 20px">
30149
+ <div class="booking-summary-card">
30150
+ <div class="details-section">
30151
+ <h3 class="details-heading">Booking detaljer</h3>
30152
+ <div class="info-rows">
30153
+ @if (data.bookingDate) {
30154
+ <div class="info-row">
30155
+ <ds-icon name="remixCalendarLine" size="16px" color="tertiary" />
30156
+ <span>{{ data.bookingDate }}</span>
30157
+ </div>
30158
+ }
30159
+ @if (data.bookingTime) {
30160
+ <div class="info-row">
30161
+ <ds-icon name="remixTimeLine" size="16px" color="tertiary" />
30162
+ <span>{{ data.bookingTime }}</span>
30163
+ </div>
30164
+ }
30165
+ @if (data.price) {
30166
+ <div class="info-row">
30167
+ <ds-icon name="remixPriceTag3Line" size="16px" color="tertiary" />
30168
+ <span>{{ data.price }}</span>
30169
+ </div>
30170
+ }
30171
+ @if (data.bookingType) {
30172
+ <div class="info-row">
30173
+ <ds-icon name="remixCheckboxCircleLine" size="16px" color="tertiary" />
30174
+ <span>{{ data.bookingType }}</span>
30175
+ </div>
30176
+ }
30177
+ @for (req of data.requirements || []; track req) {
30178
+ <div class="info-row">
30179
+ <ds-icon name="remixLockLine" size="16px" color="tertiary" />
30180
+ <span>{{ req }}</span>
30181
+ </div>
30182
+ }
30183
+ </div>
30184
+ </div>
30185
+ </div>
30186
+ </ds-mobile-section>
30187
+
30188
+ @if (data.canCancel) {
30189
+ <div class="booking-action">
30190
+ <div class="booking-actions-row">
30191
+ <ds-button
30192
+ class="cancel-primary"
30193
+ size="md"
30194
+ variant="secondary"
30195
+ (clicked)="cancelBooking()">
30196
+ Annuller booking
30197
+ </ds-button>
30198
+ </div>
30199
+ </div>
30200
+ }
30201
+ </ds-mobile-modal-base>
30202
+ } @else {
30203
+ <ds-mobile-bottom-sheet-wrapper [showDragHandle]="true">
30204
+ <div class="detail-header">
30205
+ <h2 class="detail-title">{{ data.facilityTitle }}</h2>
30206
+ <ds-icon-button
30207
+ icon="remixCloseLine"
30208
+ variant="ghost"
30209
+ size="sm"
30210
+ (clicked)="close()"
30211
+ aria-label="Luk"
30212
+ />
30213
+ </div>
30214
+
30215
+ <div class="detail-content">
30216
+ <div class="hero-image-container">
30217
+ @if (data.heroImage) {
30218
+ <img [src]="data.heroImage" [alt]="data.facilityTitle" class="hero-image" />
30219
+ } @else {
30220
+ <div class="hero-image-placeholder">
30221
+ <ds-icon name="remixImageLine" size="48px" color="rgba(255,255,255,0.5)" />
30222
+ </div>
30223
+ }
30224
+ </div>
30225
+
30226
+ <div class="booking-summary-card">
30227
+ <div class="details-section">
30228
+ <h3 class="details-heading">Booking detaljer</h3>
30229
+ <div class="info-rows">
30230
+ @if (data.bookingDate) {
30231
+ <div class="info-row">
30232
+ <ds-icon name="remixCalendarLine" size="16px" color="tertiary" />
30233
+ <span>{{ data.bookingDate }}</span>
30234
+ </div>
30235
+ }
30236
+ @if (data.bookingTime) {
30237
+ <div class="info-row">
30238
+ <ds-icon name="remixTimeLine" size="16px" color="tertiary" />
30239
+ <span>{{ data.bookingTime }}</span>
30240
+ </div>
30241
+ }
30242
+ @if (data.price) {
30243
+ <div class="info-row">
30244
+ <ds-icon name="remixPriceTag3Line" size="16px" color="tertiary" />
30245
+ <span>{{ data.price }}</span>
30246
+ </div>
30247
+ }
30248
+ @if (data.bookingType) {
30249
+ <div class="info-row">
30250
+ <ds-icon name="remixCheckboxCircleLine" size="16px" color="tertiary" />
30251
+ <span>{{ data.bookingType }}</span>
30252
+ </div>
30253
+ }
30254
+ @for (req of data.requirements || []; track req) {
30255
+ <div class="info-row">
30256
+ <ds-icon name="remixLockLine" size="16px" color="tertiary" />
30257
+ <span>{{ req }}</span>
30258
+ </div>
30259
+ }
30260
+ </div>
30261
+ </div>
30262
+ </div>
30263
+
30264
+ @if (data.canCancel) {
30265
+ <div class="footer-button-container">
30266
+ <div class="booking-actions-column">
30267
+ <ds-button
30268
+ size="md"
30269
+ variant="secondary"
30270
+ (clicked)="cancelBooking()">
30271
+ Annuller booking
30272
+ </ds-button>
30273
+ </div>
30274
+ </div>
30275
+ }
30276
+ </div>
30277
+ </ds-mobile-bottom-sheet-wrapper>
30278
+ }
30279
+ `, styles: [":host{display:block;height:auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;box-sizing:border-box}.detail-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px 8px}.detail-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-lg, 18px);font-weight:600;color:var(--text-color-default-primary, #202227);margin:0;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-right:12px}.detail-content{padding:8px 20px 24px;display:flex;flex-direction:column;gap:20px}.hero-image-container{width:100%;aspect-ratio:16 / 9;border-radius:12px;overflow:hidden;background:var(--color-surface-secondary, #f5f5f5)}.hero-image{width:100%;height:100%;object-fit:cover;display:block}.hero-image-placeholder{width:100%;height:100%;min-height:0;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,color-mix(in srgb,var(--color-accent, #6B5FF5) 30%,transparent),color-mix(in srgb,var(--color-accent, #6B5FF5) 60%,transparent))}.booking-summary-card{background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:16px;padding:16px}.details-section{display:flex;flex-direction:column;gap:12px}.details-heading{font-family:Brockmann,sans-serif;font-size:var(--font-size-base, 16px);font-weight:600;color:var(--text-color-default-primary, #202227);margin:0}.info-rows{display:flex;flex-direction:column;gap:8px}.info-row{display:flex;align-items:center;gap:10px;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm, 14px);color:var(--text-color-default-secondary, #545B66);line-height:1.4}.info-row ds-icon{flex-shrink:0}.footer-button-container{width:100%;padding:16px 20px 0}.footer-button-container ::ng-deep ds-button{display:block;width:100%}.footer-button-container ::ng-deep ds-button button{width:100%;border-radius:100px;height:44px;min-height:44px;max-height:44px}.booking-action{padding:16px 20px;padding-bottom:calc(16px + max(8px,env(safe-area-inset-bottom,0px) - 24px));background:var(--color-surface-primary, #ffffff)}.booking-actions-row{display:flex;flex-direction:row;align-items:center;gap:12px;width:100%}.booking-action .cancel-primary{flex:1;min-width:0;display:block}.booking-action .cancel-primary ::ng-deep button{width:100%;border-radius:100px;height:44px;min-height:44px;max-height:44px;padding-left:16px;padding-right:16px}:host.presentation-modal{display:block;height:auto;width:100%;min-height:0;overflow:hidden}\n"] }]
30280
+ }], ctorParameters: () => [{ type: i1.ModalController }], propDecorators: { presentation: [{
30281
+ type: Input
30282
+ }], data: [{
30283
+ type: Input
30284
+ }], autoHeight: [{
30285
+ type: Input
30286
+ }], isModalPresentation: [{
30287
+ type: HostBinding,
30288
+ args: ['class.presentation-modal']
30289
+ }] } });
30290
+
30291
+ class DsMobileBookingDetailSheetService extends BaseModalService {
30292
+ bottomSheet;
30293
+ constructor(modalController, bottomSheet) {
30294
+ super(modalController);
30295
+ this.bottomSheet = bottomSheet;
30296
+ }
30297
+ /**
30298
+ * Bottom-sheet presentation (draggable breakpoints). Prefer `openAsModal` for booking lists.
30299
+ */
30300
+ async open(data) {
30301
+ const modal = await this.bottomSheet.create({
30302
+ component: DsMobileBookingDetailSheetComponent,
30303
+ componentProps: { data },
30304
+ autoHeight: true,
30305
+ backdropDismiss: true,
30306
+ backdropBlur: true,
30307
+ });
30308
+ const result = await modal.onWillDismiss();
30309
+ return { role: result.role, data: result.data ?? undefined };
30310
+ }
30311
+ /** `ds-modal-base` shell — used for active and past bookings. Auto-heights to content. */
30312
+ async openAsModal(data) {
30313
+ const modal = await this.createModal(DsMobileBookingDetailSheetComponent, { data, presentation: 'modal', autoHeight: true }, { keyboardClose: true, autoHeight: true });
30314
+ await modal.present();
30315
+ const result = await modal.onWillDismiss();
30316
+ return { role: result.role, data: result.data ?? undefined };
30317
+ }
30318
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileBookingDetailSheetService, deps: [{ token: i1.ModalController }, { token: DsMobileBottomSheetService }], target: i0.ɵɵFactoryTarget.Injectable });
30319
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileBookingDetailSheetService, providedIn: 'root' });
30320
+ }
30321
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileBookingDetailSheetService, decorators: [{
30322
+ type: Injectable,
30323
+ args: [{
30324
+ providedIn: 'root',
30325
+ }]
30326
+ }], ctorParameters: () => [{ type: i1.ModalController }, { type: DsMobileBottomSheetService }] });
30327
+
30328
+ const VENDOR_MODAL_SERVICE = new InjectionToken('VendorModalService');
30329
+ const BOOKING_DETAIL_MAP = {
30330
+ 'booking-1': {
30331
+ id: 'booking-1',
30332
+ facilityTitle: 'Festlokale på taget',
30333
+ heroImage: '/Assets/Dummy-photos/rooftop-party.jpg',
30334
+ bookingDate: '14. februar',
30335
+ bookingTime: '9:00 - 17:00',
30336
+ price: '200 kr. per booking',
30337
+ canCancel: true,
30338
+ },
30339
+ 'booking-2': {
30340
+ id: 'booking-2',
30341
+ facilityTitle: 'Gæsteparkering',
30342
+ heroImage: '/Assets/Dummy-photos/parking.jpg',
30343
+ bookingDate: '20. marts',
30344
+ bookingTime: '8:00 - 20:00',
30345
+ price: '50 kr. per booking',
30346
+ canCancel: true,
30347
+ },
30348
+ 'booking-3': {
30349
+ id: 'booking-3',
30350
+ facilityTitle: 'Boremaskinen',
30351
+ heroImage: '/Assets/Dummy-photos/handyman.jpg',
30352
+ bookingDate: '25. marts',
30353
+ bookingTime: '10:00 - 14:00',
30354
+ price: 'Gratis',
30355
+ canCancel: true,
30356
+ },
30357
+ };
30358
+ const VENDOR_DETAIL_MAP = {
30359
+ 'v-1': {
30360
+ vendorName: 'CleanTeam ApS',
30361
+ vendorDescription: '<h3>Rengøring</h3><p>CleanTeam ApS tilbyder professionel trappevask og vinduespolering for ejendommen. Servicen udføres ugentligt og kan tilpasses jeres behov.</p><h3>Pris</h3><p>350 kr. per besøg</p>',
30362
+ vendorImage: '/Assets/Dummy-photos/clean-team.jpg',
30363
+ vendorLogo: '/Assets/dummy-logos/cleanteam-logo.svg',
30364
+ },
30365
+ 'v-2': {
30366
+ vendorName: 'Nordisk Rengøring',
30367
+ vendorDescription: '<h3>Rengøring</h3><p>Nordisk Rengøring leverer fleksibel erhvervs- og boligrengøring. Vi tilpasser rengøringen til jeres specifikke behov.</p><h3>Pris</h3><p>275 kr. per time</p>',
30368
+ vendorImage: '/Assets/Dummy-photos/nordic-cleaning.jpg',
30369
+ vendorLogo: '/Assets/dummy-logos/nordiccleaning-logo.svg',
30370
+ },
30371
+ 'v-3': {
30372
+ vendorName: 'Blik Partner A/S',
30373
+ vendorDescription: '<h3>VVS</h3><p>BlikPartner A/S er jeres VVS-partner til alt fra løbende vedligeholdelse til akutte udkald. Vi dækker reparation af vandrør, afløb, radiatorer og blandingsbatterier.</p><h3>Pris</h3><p>450 kr. per udkald + tid</p>',
30374
+ vendorImage: '/Assets/Dummy-photos/plumbing.jpg',
30375
+ },
30376
+ 'v-4': {
30377
+ vendorName: 'ElektroTek ApS',
30378
+ vendorDescription: '<h3>Elektriker</h3><p>ElektroTek ApS varetager el-installationer, fejlsøgning og lovpligtige eftersyn for ejendommen. Vi udfører også småopgaver som udskiftning af kontakter og lampeudtag.</p><h3>Pris</h3><p>395 kr. per time</p>',
30379
+ vendorImage: '/Assets/Dummy-photos/electrician.jpg',
30380
+ vendorLogo: '/Assets/dummy-logos/electrician-logo.svg',
30381
+ },
30382
+ 'v-5': {
30383
+ vendorName: 'HaveService Danmark',
30384
+ vendorDescription: '<h3>Have & Grønne arealer</h3><p>HaveService Danmark står for den løbende pleje af fællesarealer og haver — herunder græsslåning, hækklipning, ukrudtsbekæmpelse og sæsonbeplantning.</p><h3>Pris</h3><p>500 kr. per besøg</p>',
30385
+ vendorImage: '/Assets/Dummy-photos/gardener.jpg',
30386
+ vendorLogo: '/Assets/dummy-logos/gardener-logo.svg',
30387
+ },
30388
+ };
30389
+ class DsMobileNotificationModalService extends BaseModalService {
30390
+ router = inject(Router);
30391
+ notificationService = inject(NotificationService);
30392
+ postsService = inject(PostsService);
30393
+ userService = inject(UserService);
30394
+ postModal = inject(DsMobilePostDetailModalService);
30395
+ peerMessaging = inject(PeerMessagingService);
30396
+ peerChat = inject(PeerChatLauncherService);
30397
+ bookingDetailSheet = inject(DsMobileBookingDetailSheetService);
30398
+ vendorModal = inject(VENDOR_MODAL_SERVICE, { optional: true });
30399
+ constructor(modalController) {
30400
+ super(modalController);
30401
+ }
30402
+ /**
30403
+ * Open the notification modal. The modal reads notifications reactively
30404
+ * from NotificationService, so it updates live when items are marked
30405
+ * read or removed via long-press actions.
30406
+ *
30407
+ * Returns the tapped notification (if the user navigated to one), or null.
30408
+ */
30409
+ async open() {
30410
+ const modal = await this.createModal(DsMobileNotificationModalComponent, {});
30411
+ await modal.present();
30412
+ const result = await modal.onWillDismiss();
30413
+ const tapped = result.data ?? null;
30414
+ if (tapped) {
30415
+ this.notificationService.markAsRead(tapped.id);
30416
+ await this.routeTo(tapped);
30417
+ }
30418
+ return tapped;
30419
+ }
30420
+ /**
30421
+ * Navigate to the screen associated with a notification item.
30422
+ * Public so downstream devs can call it from a Capacitor
30423
+ * `pushNotificationActionPerformed` listener for deep-link handling.
30424
+ */
30425
+ async routeTo(item) {
30426
+ const id = item.targetId;
30427
+ switch (item.type) {
30428
+ case 'inquiry_update':
30429
+ case 'inquiry_assigned':
30430
+ case 'inquiry_resolved':
30431
+ await this.router.navigate(['/inquiry-detail', id ?? '1']);
30432
+ break;
30433
+ case 'community_post':
30434
+ case 'community_comment':
30435
+ case 'community_like':
30436
+ case 'community_mention':
30437
+ if (id) {
30438
+ await this.openPostModal(id);
30439
+ }
30440
+ else {
30441
+ await this.router.navigate(['/announcements']);
30442
+ }
30443
+ break;
30444
+ case 'booking_confirmed':
30445
+ case 'booking_cancelled':
30446
+ case 'booking_reminder':
30447
+ case 'facility_available':
30448
+ if (id) {
30449
+ await this.openBookingDetail(item);
30450
+ }
30451
+ else {
30452
+ await this.router.navigate(['/booking']);
30453
+ }
30454
+ break;
30455
+ case 'message_new':
30456
+ case 'message_group':
30457
+ if (id) {
30458
+ await this.openChat(id);
30459
+ }
30460
+ else {
30461
+ await this.router.navigate(['/messages']);
30462
+ }
30463
+ break;
30464
+ case 'service_update':
30465
+ if (id) {
30466
+ await this.openVendorModal(id);
30467
+ }
30468
+ else {
30469
+ await this.router.navigate(['/services']);
30470
+ }
30471
+ break;
30472
+ case 'handbook_update':
30473
+ await this.router.navigate(['/handbook']);
30474
+ break;
30475
+ case 'family_access':
30476
+ await this.router.navigate(['/family-access']);
30477
+ break;
30478
+ default:
30479
+ await this.router.navigate(['/home']);
30480
+ break;
30481
+ }
30482
+ }
30483
+ async openChat(conversationId) {
30484
+ const conv = this.peerMessaging.conversations().find(c => c.id === conversationId);
30485
+ if (conv) {
30486
+ await this.peerChat.openConversation(conv);
30487
+ }
30488
+ else {
30489
+ await this.router.navigate(['/messages']);
30490
+ }
30491
+ }
30492
+ async openVendorModal(vendorId) {
30493
+ const vendor = VENDOR_DETAIL_MAP[vendorId];
30494
+ if (vendor && this.vendorModal) {
30495
+ await this.vendorModal.open(vendor);
30496
+ }
30497
+ else {
30498
+ await this.router.navigate(['/services']);
30499
+ }
30500
+ }
30501
+ async openBookingDetail(item) {
30502
+ const booking = BOOKING_DETAIL_MAP[item.targetId];
30503
+ if (booking) {
30504
+ await this.bookingDetailSheet.openAsModal(booking);
30505
+ }
30506
+ else {
30507
+ await this.router.navigate(['/booking']);
30508
+ }
30509
+ }
30510
+ async openPostModal(postId) {
30511
+ const post = this.postsService.getPostById(postId);
30512
+ if (!post) {
30513
+ await this.router.navigate(['/announcements']);
30514
+ return;
30515
+ }
30516
+ const data = {
30517
+ postId: post.id,
30518
+ authorName: post.authorName,
30519
+ authorRole: post.authorRole,
30520
+ timestamp: post.timestamp,
30521
+ content: post.content,
30522
+ avatarInitials: post.avatarInitials,
30523
+ avatarType: post.avatarType === 'icon' ? undefined : post.avatarType,
30524
+ avatarSrc: post.avatarSrc,
30525
+ imageSrc: post.imageSrc,
30526
+ imageAlt: post.imageAlt,
30527
+ isLiked: post.isLiked,
30528
+ likeCount: post.likeCount,
30529
+ commentCount: post.commentCount,
30530
+ comments: post.comments,
30531
+ };
30532
+ await this.postModal.open(data, {
30533
+ currentUserName: this.userService.displayName(),
30534
+ currentUserInitials: this.userService.avatarInitials(),
30535
+ });
30536
+ }
30537
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNotificationModalService, deps: [{ token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Injectable });
30538
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNotificationModalService, providedIn: 'root' });
30539
+ }
30540
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNotificationModalService, decorators: [{
30541
+ type: Injectable,
30542
+ args: [{
30543
+ providedIn: 'root',
30544
+ }]
30545
+ }], ctorParameters: () => [{ type: i1.ModalController }] });
30546
+
30547
+ // Mobile Page Components
30548
+
29154
30549
  class MobileCommunityPageComponent {
29155
30550
  router;
29156
30551
  route;
@@ -29161,6 +30556,8 @@ class MobileCommunityPageComponent {
29161
30556
  postsService;
29162
30557
  pageComponent;
29163
30558
  pinnedSwiper;
30559
+ notificationModal = inject(DsMobileNotificationModalService);
30560
+ notificationService = inject(NotificationService);
29164
30561
  // Get posts from service (using computed for safe initialization)
29165
30562
  allPosts = computed(() => this.postsService.posts(), ...(ngDevMode ? [{ debugName: "allPosts" }] : []));
29166
30563
  // Get pinned posts - filter by isPinned flag
@@ -29203,6 +30600,11 @@ class MobileCommunityPageComponent {
29203
30600
  // Complete the infinite scroll
29204
30601
  event.target.complete();
29205
30602
  }
30603
+ async handleNotificationClick() {
30604
+ const tapped = await this.notificationModal.open();
30605
+ if (tapped)
30606
+ console.log('Notification tapped:', tapped.type, tapped.id);
30607
+ }
29206
30608
  handleRefresh(event) {
29207
30609
  console.log('Pull-to-refresh triggered');
29208
30610
  // Check if offline and complete immediately
@@ -29444,8 +30846,10 @@ class MobileCommunityPageComponent {
29444
30846
  <ds-mobile-page-main
29445
30847
  #pageComponent
29446
30848
  title="Fællesskab"
30849
+ [notificationCount]="notificationService.unreadCount()"
29447
30850
  [avatarInitials]="userService.avatarInitials()"
29448
30851
  [avatarType]="userService.avatarType()"
30852
+ (notificationClick)="handleNotificationClick()"
29449
30853
  (refresh)="handleRefresh($event)">
29450
30854
 
29451
30855
  <!-- Offline indicator -->
@@ -29698,7 +31102,7 @@ class MobileCommunityPageComponent {
29698
31102
  }
29699
31103
  </ds-mobile-section>
29700
31104
  </ds-mobile-page-main>
29701
- `, isInline: true, styles: [".pinned-posts-swiper-wrapper{padding:0;position:relative}.swiper-nav-buttons{display:contents}.swiper-nav-button{position:absolute;top:50%;transform:translateY(-50%);z-index:10}.swiper-nav-button:first-child{left:-48px}.swiper-nav-button:last-child{right:-48px}::ng-deep .swiper-nav-button button{border-radius:50%!important;width:48px!important;height:48px!important;padding:0!important}@media (max-width: 767px){.swiper-nav-buttons{display:none}}::ng-deep .pinned-posts-swiper .swiper-slide{width:100%;max-width:600px;height:auto}@media (min-width: 768px){::ng-deep .pinned-posts-swiper .swiper-slide{max-width:100%}}.swiper-post-item{width:100%;height:auto}::ng-deep .pinned-posts-swiper .swiper-slide ds-mobile-interactive-list-item-post{height:auto}::ng-deep .pinned-posts-swiper .swiper-wrapper{height:auto;align-items:flex-start}.post-list-wrapper{display:flex;flex-direction:column}.clickable-image{cursor:pointer;transition:transform .2s ease,opacity .2s ease;border-radius:8px;display:block;width:100%;aspect-ratio:16/9;object-fit:cover}.clickable-image:active{transform:scale(.98);opacity:.9}.community-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-image{width:96px;height:96px;margin-bottom:24px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin:16px 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}ion-infinite-scroll{--color: var(--color-primary-surface)}ion-infinite-scroll-content{--color: var(--color-primary-surface)}ion-infinite-scroll-content::part(spinner){color:var(--color-primary-surface)}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileInteractiveListItemPostComponent, selector: "ds-mobile-interactive-list-item-post", inputs: ["authorName", "authorRole", "timestamp", "avatarInitials", "avatarType", "avatarSrc", "avatarIconName", "showBadge", "variant", "align", "clickable", "enableLongPress", "moreActions"], outputs: ["postClick", "commentClick", "longPress"] }, { kind: "component", type: DsMobilePostComposerComponent, selector: "ds-mobile-post-composer", inputs: ["avatarInitials", "avatarType", "avatarSrc", "avatarIconName", "placeholder", "buttonText"], outputs: ["composerClick"] }, { kind: "component", type: DsMobileSwiperComponent, selector: "ds-mobile-swiper", inputs: ["slideWidth", "gap", "pagination", "autoHeight", "progressiveOpacity", "progressiveScale"] }, { kind: "component", type: PostContentComponent, selector: "post-content" }, { kind: "component", type: PostTextComponent, selector: "post-text" }, { kind: "component", type: PostMediaComponent, selector: "post-media" }, { kind: "component", type: PostAttachmentsComponent, selector: "post-attachments" }, { kind: "component", type: PostActionsComponent, selector: "post-actions" }, { kind: "component", type: ActionLikeComponent, selector: "action-like", inputs: ["active", "count"], outputs: ["activeChange", "countChange", "likeClick"] }, { kind: "component", type: ActionCommentComponent, selector: "action-comment", inputs: ["count"], outputs: ["commentClick"] }, { kind: "component", type: DsMobileCardInlineFileComponent, selector: "ds-mobile-card-inline-file", inputs: ["fileName", "fileSize", "variant", "layout", "fileUrl"], outputs: ["fileClick"] }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsMobileInlinePhotoComponent, selector: "ds-mobile-inline-photo", inputs: ["images", "loadingStates", "author", "maxVisible", "useGrid"], outputs: ["photoClick"] }, { kind: "component", type: DsMobileOfflineBannerComponent, selector: "ds-mobile-offline-banner", inputs: ["icon", "title", "message"] }, { kind: "component", type: IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }] });
31105
+ `, isInline: true, styles: [".pinned-posts-swiper-wrapper{padding:0;position:relative}.swiper-nav-buttons{display:contents}.swiper-nav-button{position:absolute;top:50%;transform:translateY(-50%);z-index:10}.swiper-nav-button:first-child{left:-48px}.swiper-nav-button:last-child{right:-48px}::ng-deep .swiper-nav-button button{border-radius:50%!important;width:48px!important;height:48px!important;padding:0!important}@media (max-width: 767px){.swiper-nav-buttons{display:none}}::ng-deep .pinned-posts-swiper .swiper-slide{width:100%;max-width:600px;height:auto}@media (min-width: 768px){::ng-deep .pinned-posts-swiper .swiper-slide{max-width:100%}}.swiper-post-item{width:100%;height:auto}::ng-deep .pinned-posts-swiper .swiper-slide ds-mobile-interactive-list-item-post{height:auto}::ng-deep .pinned-posts-swiper .swiper-wrapper{height:auto;align-items:flex-start}.post-list-wrapper{display:flex;flex-direction:column}.clickable-image{cursor:pointer;transition:transform .2s ease,opacity .2s ease;border-radius:8px;display:block;width:100%;aspect-ratio:16/9;object-fit:cover}.clickable-image:active{transform:scale(.98);opacity:.9}.community-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-image{width:96px;height:96px;margin-bottom:24px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin:16px 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}ion-infinite-scroll{--color: var(--color-primary-surface)}ion-infinite-scroll-content{--color: var(--color-primary-surface)}ion-infinite-scroll-content::part(spinner){color:var(--color-primary-surface)}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "notificationCount", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["notificationClick", "avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileInteractiveListItemPostComponent, selector: "ds-mobile-interactive-list-item-post", inputs: ["authorName", "authorRole", "timestamp", "avatarInitials", "avatarType", "avatarSrc", "avatarIconName", "showBadge", "variant", "align", "clickable", "enableLongPress", "moreActions"], outputs: ["postClick", "commentClick", "longPress"] }, { kind: "component", type: DsMobilePostComposerComponent, selector: "ds-mobile-post-composer", inputs: ["avatarInitials", "avatarType", "avatarSrc", "avatarIconName", "placeholder", "buttonText"], outputs: ["composerClick"] }, { kind: "component", type: DsMobileSwiperComponent, selector: "ds-mobile-swiper", inputs: ["slideWidth", "gap", "pagination", "autoHeight", "progressiveOpacity", "progressiveScale"] }, { kind: "component", type: PostContentComponent, selector: "post-content" }, { kind: "component", type: PostTextComponent, selector: "post-text" }, { kind: "component", type: PostMediaComponent, selector: "post-media" }, { kind: "component", type: PostAttachmentsComponent, selector: "post-attachments" }, { kind: "component", type: PostActionsComponent, selector: "post-actions" }, { kind: "component", type: ActionLikeComponent, selector: "action-like", inputs: ["active", "count"], outputs: ["activeChange", "countChange", "likeClick"] }, { kind: "component", type: ActionCommentComponent, selector: "action-comment", inputs: ["count"], outputs: ["commentClick"] }, { kind: "component", type: DsMobileCardInlineFileComponent, selector: "ds-mobile-card-inline-file", inputs: ["fileName", "fileSize", "variant", "layout", "fileUrl"], outputs: ["fileClick"] }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsMobileInlinePhotoComponent, selector: "ds-mobile-inline-photo", inputs: ["images", "loadingStates", "author", "maxVisible", "useGrid"], outputs: ["photoClick"] }, { kind: "component", type: DsMobileOfflineBannerComponent, selector: "ds-mobile-offline-banner", inputs: ["icon", "title", "message"] }, { kind: "component", type: IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }] });
29702
31106
  }
29703
31107
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MobileCommunityPageComponent, decorators: [{
29704
31108
  type: Component,
@@ -29725,8 +31129,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
29725
31129
  <ds-mobile-page-main
29726
31130
  #pageComponent
29727
31131
  title="Fællesskab"
31132
+ [notificationCount]="notificationService.unreadCount()"
29728
31133
  [avatarInitials]="userService.avatarInitials()"
29729
31134
  [avatarType]="userService.avatarType()"
31135
+ (notificationClick)="handleNotificationClick()"
29730
31136
  (refresh)="handleRefresh($event)">
29731
31137
 
29732
31138
  <!-- Offline indicator -->
@@ -29991,6 +31397,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
29991
31397
  class MobileHandbookPageComponent {
29992
31398
  userService;
29993
31399
  pageComponent;
31400
+ notificationModal = inject(DsMobileNotificationModalService);
31401
+ notificationService = inject(NotificationService);
29994
31402
  // Utilities folder data
29995
31403
  utilitiesItems = [
29996
31404
  {
@@ -30233,6 +31641,11 @@ class MobileHandbookPageComponent {
30233
31641
  constructor(userService) {
30234
31642
  this.userService = userService;
30235
31643
  }
31644
+ async handleNotificationClick() {
31645
+ const tapped = await this.notificationModal.open();
31646
+ if (tapped)
31647
+ console.log('Notification tapped:', tapped.type, tapped.id);
31648
+ }
30236
31649
  handleRefresh(event) {
30237
31650
  console.log('Pull-to-refresh triggered');
30238
31651
  // Check if offline and complete immediately
@@ -30248,7 +31661,7 @@ class MobileHandbookPageComponent {
30248
31661
  }
30249
31662
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MobileHandbookPageComponent, deps: [{ token: UserService }], target: i0.ɵɵFactoryTarget.Component });
30250
31663
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: MobileHandbookPageComponent, isStandalone: true, selector: "app-mobile-handbook-page", viewQueries: [{ propertyName: "pageComponent", first: true, predicate: ["pageComponent"], descendants: true }], ngImport: i0, template: `
30251
- <ds-mobile-page-main #pageComponent title="Håndbog" [avatarInitials]="userService.avatarInitials()" [avatarType]="userService.avatarType()" (refresh)="handleRefresh($event)">
31664
+ <ds-mobile-page-main #pageComponent title="Håndbog" [notificationCount]="notificationService.unreadCount()" [avatarInitials]="userService.avatarInitials()" [avatarType]="userService.avatarType()" (notificationClick)="handleNotificationClick()" (refresh)="handleRefresh($event)">
30252
31665
  <!-- Offline indicator -->
30253
31666
  @if (pageComponent.isOffline()) {
30254
31667
  <ds-mobile-offline-banner offline-indicator title="Ingen internetforbindelse" message="Nogle funktioner kan være utilgængelige"> </ds-mobile-offline-banner>
@@ -30269,12 +31682,12 @@ class MobileHandbookPageComponent {
30269
31682
  </div>
30270
31683
  </ds-mobile-section>
30271
31684
  </ds-mobile-page-main>
30272
- `, isInline: true, styles: [".folders-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px;justify-items:center}@media (min-width: 768px){.folders-grid{grid-template-columns:repeat(3,1fr)}}ds-mobile-handbook-folder{width:100%;min-width:0}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileHandbookFolderComponent, selector: "ds-mobile-handbook-folder", inputs: ["variant", "customColor", "iconName", "itemCount", "label", "items", "loading", "error"] }, { kind: "component", type: DsMobileOfflineBannerComponent, selector: "ds-mobile-offline-banner", inputs: ["icon", "title", "message"] }] });
31685
+ `, isInline: true, styles: [".folders-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px;justify-items:center}@media (min-width: 768px){.folders-grid{grid-template-columns:repeat(3,1fr)}}ds-mobile-handbook-folder{width:100%;min-width:0}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "notificationCount", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["notificationClick", "avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileHandbookFolderComponent, selector: "ds-mobile-handbook-folder", inputs: ["variant", "customColor", "iconName", "itemCount", "label", "items", "loading", "error"] }, { kind: "component", type: DsMobileOfflineBannerComponent, selector: "ds-mobile-offline-banner", inputs: ["icon", "title", "message"] }] });
30273
31686
  }
30274
31687
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MobileHandbookPageComponent, decorators: [{
30275
31688
  type: Component,
30276
31689
  args: [{ selector: 'app-mobile-handbook-page', standalone: true, imports: [DsMobilePageMainComponent, DsMobileSectionComponent, DsMobileHandbookFolderComponent, DsMobileOfflineBannerComponent], template: `
30277
- <ds-mobile-page-main #pageComponent title="Håndbog" [avatarInitials]="userService.avatarInitials()" [avatarType]="userService.avatarType()" (refresh)="handleRefresh($event)">
31690
+ <ds-mobile-page-main #pageComponent title="Håndbog" [notificationCount]="notificationService.unreadCount()" [avatarInitials]="userService.avatarInitials()" [avatarType]="userService.avatarType()" (notificationClick)="handleNotificationClick()" (refresh)="handleRefresh($event)">
30278
31691
  <!-- Offline indicator -->
30279
31692
  @if (pageComponent.isOffline()) {
30280
31693
  <ds-mobile-offline-banner offline-indicator title="Ingen internetforbindelse" message="Nogle funktioner kan være utilgængelige"> </ds-mobile-offline-banner>
@@ -30866,6 +32279,8 @@ class MobileHomePageComponent {
30866
32279
  modalCtrl = inject(ModalController);
30867
32280
  vendorModal = inject(DsMobileServiceVendorModalService);
30868
32281
  newInquiryModal = inject(DsMobileNewInquiryModalService);
32282
+ notificationModal = inject(DsMobileNotificationModalService);
32283
+ notificationService = inject(NotificationService);
30869
32284
  constructor(router, navCtrl, userService, postsService, postModal, trackingPermissionService, bottomSheet, familyAccessService, peerMessaging, peerChat, lightboxService) {
30870
32285
  this.router = router;
30871
32286
  this.navCtrl = navCtrl;
@@ -30893,6 +32308,11 @@ class MobileHomePageComponent {
30893
32308
  }, delayMs);
30894
32309
  });
30895
32310
  }
32311
+ async handleNotificationClick() {
32312
+ const tapped = await this.notificationModal.open();
32313
+ if (tapped)
32314
+ console.log('Notification tapped:', tapped.type, tapped.id);
32315
+ }
30896
32316
  handleRefresh(event) {
30897
32317
  console.log('Pull-to-refresh triggered');
30898
32318
  setTimeout(() => {
@@ -31037,8 +32457,10 @@ class MobileHomePageComponent {
31037
32457
  #pageComponent
31038
32458
  title="Hjem"
31039
32459
  headerTitle="Velkommen, Lars"
32460
+ [notificationCount]="notificationService.unreadCount()"
31040
32461
  [avatarInitials]="userService.avatarInitials()"
31041
32462
  [avatarType]="userService.avatarType()"
32463
+ (notificationClick)="handleNotificationClick()"
31042
32464
  (refresh)="handleRefresh($event)"
31043
32465
  >
31044
32466
 
@@ -31241,7 +32663,7 @@ class MobileHomePageComponent {
31241
32663
  </ds-mobile-page-main>
31242
32664
  } <!-- end @if (!isCoveringScreen()) -->
31243
32665
 
31244
- `, isInline: true, styles: [".posts-list,.messages-preview-list{display:flex;flex-direction:column}.property-banner-nav{display:block;width:100%;border-radius:12px;cursor:pointer;-webkit-tap-highlight-color:transparent}.property-banner-nav:focus-visible{outline:2px solid var(--color-accent, #6B5FF5);outline-offset:2px}.inquiries-list,.services-preview-list{display:flex;flex-direction:column}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px;text-align:center}.empty-state ds-button{display:block;margin-top:16px}.empty-state ds-button::ng-deep .btn{width:100%;border-radius:9999px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin-top:-16px;z-index:4}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}@keyframes slideDown{0%{transform:translateY(-100%);opacity:0}to{transform:translateY(0);opacity:1}}.welcome-toast{padding:10px 14px;border-radius:12px;background:var(--color-background-brand-secondary, #EEF0FF);display:flex;align-items:flex-start;gap:10px;font-size:14px;font-weight:500;color:var(--color-accent, #6B5FF5);animation:slideDown .2s ease-out}.toast-icon{width:20px;height:20px;border-radius:50%;background:var(--color-accent, #6B5FF5);display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff;margin-top:1px}.welcome-toast-content{flex:1;display:flex;flex-direction:column;gap:2px}.welcome-toast-heading{font-family:Brockmann,sans-serif;font-size:14px;font-weight:600;color:var(--color-brand-content, #3B3691);margin:0}.welcome-toast-text{font-family:Brockmann,sans-serif;font-size:13px;line-height:1.4;color:var(--color-brand-content, #3B3691);margin:0;opacity:.8}.welcome-toast-text strong{font-weight:600;opacity:1}.toast-dismiss{margin-left:auto;background:none;border:none;cursor:pointer;flex-shrink:0;color:var(--color-accent, #6B5FF5);display:flex;align-items:center;justify-content:center}.home-content--animating{animation:homeReveal .3s var(--spring-curve-smooth) both}@keyframes homeReveal{0%{opacity:0;transform:translateY(128px)}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "component", type: DsButtonComponent, selector: "ds-button", inputs: ["variant", "size", "disabled", "loading", "pressed", "expanded", "leadingIcon", "trailingIcon", "ariaLabel", "iconOnly"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileIllustrationComponent, selector: "ds-mobile-illustration", inputs: ["variant", "size", "alt"] }, { kind: "component", type: DsMobilePropertyBannerComponent, selector: "ds-mobile-property-banner", inputs: ["address", "photoUrl", "tenantCount"] }, { kind: "component", type: DsMobileInteractiveListItemPostComponent, selector: "ds-mobile-interactive-list-item-post", inputs: ["authorName", "authorRole", "timestamp", "avatarInitials", "avatarType", "avatarSrc", "avatarIconName", "showBadge", "variant", "align", "clickable", "enableLongPress", "moreActions"], outputs: ["postClick", "commentClick", "longPress"] }, { kind: "component", type: DsMobileInteractiveListItemInquiryComponent, selector: "ds-mobile-interactive-list-item-inquiry", inputs: ["title", "description", "status", "statusLabel", "timestamp", "iconName", "iconColor", "variant", "align", "clickable", "showChevron", "enableLongPress", "moreActions"], outputs: ["inquiryClick", "longPress"] }, { kind: "component", type: DsMobileInteractiveListItemMessageComponent, selector: "ds-mobile-interactive-list-item-message", inputs: ["senderName", "senderRole", "timestamp", "message", "avatarInitials", "avatarType", "avatarSrc", "unread", "unreadStyle", "clickable", "align", "showAvatarBadge", "groupStackMembers", "groupCustomAvatarUrl", "groupStackExcludeParticipantId"], outputs: ["messageClick", "longPress"] }, { kind: "component", type: DsMobileInteractiveListItemServiceComponent, selector: "ds-mobile-interactive-list-item-service", inputs: ["title", "description", "logo", "showChevron"], outputs: ["serviceClick"] }, { kind: "component", type: DsMobileOfflineBannerComponent, selector: "ds-mobile-offline-banner", inputs: ["icon", "title", "message"] }, { kind: "component", type: PostContentComponent, selector: "post-content" }, { kind: "component", type: PostTextComponent, selector: "post-text" }, { kind: "component", type: PostActionsComponent, selector: "post-actions" }, { kind: "component", type: ActionLikeComponent, selector: "action-like", inputs: ["active", "count"], outputs: ["activeChange", "countChange", "likeClick"] }, { kind: "component", type: ActionCommentComponent, selector: "action-comment", inputs: ["count"], outputs: ["commentClick"] }] });
32666
+ `, isInline: true, styles: [".posts-list,.messages-preview-list{display:flex;flex-direction:column}.property-banner-nav{display:block;width:100%;border-radius:12px;cursor:pointer;-webkit-tap-highlight-color:transparent}.property-banner-nav:focus-visible{outline:2px solid var(--color-accent, #6B5FF5);outline-offset:2px}.inquiries-list,.services-preview-list{display:flex;flex-direction:column}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px;text-align:center}.empty-state ds-button{display:block;margin-top:16px}.empty-state ds-button::ng-deep .btn{width:100%;border-radius:9999px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin-top:-16px;z-index:4}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}@keyframes slideDown{0%{transform:translateY(-100%);opacity:0}to{transform:translateY(0);opacity:1}}.welcome-toast{padding:10px 14px;border-radius:12px;background:var(--color-background-brand-secondary, #EEF0FF);display:flex;align-items:flex-start;gap:10px;font-size:14px;font-weight:500;color:var(--color-accent, #6B5FF5);animation:slideDown .2s ease-out}.toast-icon{width:20px;height:20px;border-radius:50%;background:var(--color-accent, #6B5FF5);display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff;margin-top:1px}.welcome-toast-content{flex:1;display:flex;flex-direction:column;gap:2px}.welcome-toast-heading{font-family:Brockmann,sans-serif;font-size:14px;font-weight:600;color:var(--color-brand-content, #3B3691);margin:0}.welcome-toast-text{font-family:Brockmann,sans-serif;font-size:13px;line-height:1.4;color:var(--color-brand-content, #3B3691);margin:0;opacity:.8}.welcome-toast-text strong{font-weight:600;opacity:1}.toast-dismiss{margin-left:auto;background:none;border:none;cursor:pointer;flex-shrink:0;color:var(--color-accent, #6B5FF5);display:flex;align-items:center;justify-content:center}.home-content--animating{animation:homeReveal .3s var(--spring-curve-smooth) both}@keyframes homeReveal{0%{opacity:0;transform:translateY(128px)}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "component", type: DsButtonComponent, selector: "ds-button", inputs: ["variant", "size", "disabled", "loading", "pressed", "expanded", "leadingIcon", "trailingIcon", "ariaLabel", "iconOnly"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "notificationCount", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["notificationClick", "avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileIllustrationComponent, selector: "ds-mobile-illustration", inputs: ["variant", "size", "alt"] }, { kind: "component", type: DsMobilePropertyBannerComponent, selector: "ds-mobile-property-banner", inputs: ["address", "photoUrl", "tenantCount"] }, { kind: "component", type: DsMobileInteractiveListItemPostComponent, selector: "ds-mobile-interactive-list-item-post", inputs: ["authorName", "authorRole", "timestamp", "avatarInitials", "avatarType", "avatarSrc", "avatarIconName", "showBadge", "variant", "align", "clickable", "enableLongPress", "moreActions"], outputs: ["postClick", "commentClick", "longPress"] }, { kind: "component", type: DsMobileInteractiveListItemInquiryComponent, selector: "ds-mobile-interactive-list-item-inquiry", inputs: ["title", "description", "status", "statusLabel", "timestamp", "iconName", "iconColor", "variant", "align", "clickable", "showChevron", "enableLongPress", "moreActions"], outputs: ["inquiryClick", "longPress"] }, { kind: "component", type: DsMobileInteractiveListItemMessageComponent, selector: "ds-mobile-interactive-list-item-message", inputs: ["senderName", "senderRole", "timestamp", "message", "avatarInitials", "avatarType", "avatarSrc", "unread", "unreadStyle", "clickable", "align", "showAvatarBadge", "groupStackMembers", "groupCustomAvatarUrl", "groupStackExcludeParticipantId"], outputs: ["messageClick", "longPress"] }, { kind: "component", type: DsMobileInteractiveListItemServiceComponent, selector: "ds-mobile-interactive-list-item-service", inputs: ["title", "description", "logo", "showChevron"], outputs: ["serviceClick"] }, { kind: "component", type: DsMobileOfflineBannerComponent, selector: "ds-mobile-offline-banner", inputs: ["icon", "title", "message"] }, { kind: "component", type: PostContentComponent, selector: "post-content" }, { kind: "component", type: PostTextComponent, selector: "post-text" }, { kind: "component", type: PostActionsComponent, selector: "post-actions" }, { kind: "component", type: ActionLikeComponent, selector: "action-like", inputs: ["active", "count"], outputs: ["activeChange", "countChange", "likeClick"] }, { kind: "component", type: ActionCommentComponent, selector: "action-comment", inputs: ["count"], outputs: ["commentClick"] }] });
31245
32667
  }
31246
32668
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MobileHomePageComponent, decorators: [{
31247
32669
  type: Component,
@@ -31255,7 +32677,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
31255
32677
  DsMobileInteractiveListItemPostComponent,
31256
32678
  DsMobileInteractiveListItemInquiryComponent,
31257
32679
  DsMobileInteractiveListItemMessageComponent,
31258
- DsMobileInteractiveListItemBookingComponent,
31259
32680
  DsMobileInteractiveListItemServiceComponent,
31260
32681
  DsMobileOfflineBannerComponent,
31261
32682
  PostContentComponent,
@@ -31277,8 +32698,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
31277
32698
  #pageComponent
31278
32699
  title="Hjem"
31279
32700
  headerTitle="Velkommen, Lars"
32701
+ [notificationCount]="notificationService.unreadCount()"
31280
32702
  [avatarInitials]="userService.avatarInitials()"
31281
32703
  [avatarType]="userService.avatarType()"
32704
+ (notificationClick)="handleNotificationClick()"
31282
32705
  (refresh)="handleRefresh($event)"
31283
32706
  >
31284
32707
 
@@ -31493,6 +32916,8 @@ class MobileInquiriesPageComponent {
31493
32916
  newInquiryModal;
31494
32917
  pageComponent;
31495
32918
  inquiriesService = inject(InquiriesService);
32919
+ notificationModal = inject(DsMobileNotificationModalService);
32920
+ notificationService = inject(NotificationService);
31496
32921
  constructor(userService, navCtrl, newInquiryModal) {
31497
32922
  this.userService = userService;
31498
32923
  this.navCtrl = navCtrl;
@@ -31528,6 +32953,11 @@ class MobileInquiriesPageComponent {
31528
32953
  console.log('Showing actions for inquiry:', inquiryId);
31529
32954
  // Show bottom sheet with actions (edit, delete, etc.)
31530
32955
  }
32956
+ async handleNotificationClick() {
32957
+ const tapped = await this.notificationModal.open();
32958
+ if (tapped)
32959
+ console.log('Notification tapped:', tapped.type, tapped.id);
32960
+ }
31531
32961
  handleRefresh(event) {
31532
32962
  console.log('Pull-to-refresh triggered on inquiries page');
31533
32963
  // Check if offline and complete immediately
@@ -31567,8 +32997,10 @@ class MobileInquiriesPageComponent {
31567
32997
  <ds-mobile-page-main
31568
32998
  #pageComponent
31569
32999
  title="Henvendelser"
33000
+ [notificationCount]="notificationService.unreadCount()"
31570
33001
  [avatarInitials]="userService.avatarInitials()"
31571
33002
  [avatarType]="userService.avatarType()"
33003
+ (notificationClick)="handleNotificationClick()"
31572
33004
  (refresh)="handleRefresh($event)">
31573
33005
 
31574
33006
  <!-- Offline indicator -->
@@ -31632,7 +33064,7 @@ class MobileInquiriesPageComponent {
31632
33064
  ariaLabel="Create new inquiry"
31633
33065
  (clicked)="createNewInquiry()">
31634
33066
  </ds-mobile-fab>
31635
- `, isInline: true, styles: [".inquiry-list-wrapper{display:flex;flex-direction:column;margin-top:-12px}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin-top:-16px;z-index:4}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileIllustrationComponent, selector: "ds-mobile-illustration", inputs: ["variant", "size", "alt"] }, { kind: "component", type: DsMobileInteractiveListItemInquiryComponent, selector: "ds-mobile-interactive-list-item-inquiry", inputs: ["title", "description", "status", "statusLabel", "timestamp", "iconName", "iconColor", "variant", "align", "clickable", "showChevron", "enableLongPress", "moreActions"], outputs: ["inquiryClick", "longPress"] }, { kind: "component", type: DsMobileInlineTabsComponent, selector: "ds-mobile-inline-tabs", inputs: ["tabs", "activeTab"], outputs: ["tabChange"] }, { kind: "component", type: DsMobileOfflineBannerComponent, selector: "ds-mobile-offline-banner", inputs: ["icon", "title", "message"] }, { kind: "component", type: DsMobileFabComponent, selector: "ds-mobile-fab", inputs: ["icon", "position", "size", "ariaLabel", "disabled"], outputs: ["clicked"] }] });
33067
+ `, isInline: true, styles: [".inquiry-list-wrapper{display:flex;flex-direction:column;margin-top:-12px}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin-top:-16px;z-index:4}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "notificationCount", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["notificationClick", "avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileIllustrationComponent, selector: "ds-mobile-illustration", inputs: ["variant", "size", "alt"] }, { kind: "component", type: DsMobileInteractiveListItemInquiryComponent, selector: "ds-mobile-interactive-list-item-inquiry", inputs: ["title", "description", "status", "statusLabel", "timestamp", "iconName", "iconColor", "variant", "align", "clickable", "showChevron", "enableLongPress", "moreActions"], outputs: ["inquiryClick", "longPress"] }, { kind: "component", type: DsMobileInlineTabsComponent, selector: "ds-mobile-inline-tabs", inputs: ["tabs", "activeTab"], outputs: ["tabChange"] }, { kind: "component", type: DsMobileOfflineBannerComponent, selector: "ds-mobile-offline-banner", inputs: ["icon", "title", "message"] }, { kind: "component", type: DsMobileFabComponent, selector: "ds-mobile-fab", inputs: ["icon", "position", "size", "ariaLabel", "disabled"], outputs: ["clicked"] }] });
31636
33068
  }
31637
33069
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MobileInquiriesPageComponent, decorators: [{
31638
33070
  type: Component,
@@ -31650,8 +33082,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
31650
33082
  <ds-mobile-page-main
31651
33083
  #pageComponent
31652
33084
  title="Henvendelser"
33085
+ [notificationCount]="notificationService.unreadCount()"
31653
33086
  [avatarInitials]="userService.avatarInitials()"
31654
33087
  [avatarType]="userService.avatarType()"
33088
+ (notificationClick)="handleNotificationClick()"
31655
33089
  (refresh)="handleRefresh($event)">
31656
33090
 
31657
33091
  <!-- Offline indicator -->
@@ -32323,6 +33757,87 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
32323
33757
  `, styles: [".activity-list{display:flex;flex-direction:column;gap:12px;position:relative}.activity-list ds-mobile-list-item:not(:last-child) [content-leading]:after{content:\"\";position:absolute;top:40px;left:50%;transform:translate(-50%);width:1px;height:calc(100% - 2px);background:var(--border-color-default, #e5e5e5);z-index:0}.activity-icon-wrapper{width:100%;height:100%;border-radius:8px;background:var(--color-background-neutral-secondary, #f5f5f5);display:flex;align-items:center;justify-content:center;flex-shrink:0;position:relative;z-index:1}.avatar-wrapper{position:relative;display:flex;align-items:start;justify-content:center;flex-shrink:0;width:100%;height:100%;z-index:1}.avatar-badge{position:absolute;bottom:-6px;right:-6px;width:20px;height:20px;border-radius:8px;background:var(--color-brand-secondary, #5d5fef);display:flex;align-items:center;justify-content:center;border:2px solid var(--color-background-primary, #ffffff)}.avatar-badge svg{width:10px;position:relative;top:1px;fill:#fff}.activity-content{display:flex;flex-direction:column;gap:4px}.activity-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-primary, #202227);margin:0}.activity-title .actor-name{font-weight:600;color:var(--text-color-default-primary, #202227)}.activity-title .activity-text{color:var(--text-color-default-secondary, #545B66);font-weight:400}.activity-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-secondary, #545B66);margin:0}.activity-timestamp{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--text-color-default-tertiary, #737373);display:flex;align-items:center;gap:4px;margin-top:2px}.detail-label{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--text-color-default-tertiary, #737373)}.detail-value{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:24px;letter-spacing:-.3px;color:var(--text-color-default-primary, #202227)}.detail-tag{display:inline-flex;align-items:center;padding:4px 12px;border-radius:12px;background:var(--color-background-neutral-secondary, #f5f5f5);font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;color:var(--text-color-default-secondary, #525866);margin-top:4px;margin-bottom:10px;width:-moz-fit-content;width:fit-content}.photo-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:8px}.photo-add{width:100%;aspect-ratio:1;border-radius:12px;border:1px dashed var(--border-color-default, #e5e5e5);background:var(--color-background-neutral-secondary, #f5f5f5);display:flex;align-items:center;justify-content:center;cursor:pointer}.photo-item{width:100%;aspect-ratio:1;border-radius:12px;-o-object-fit:cover;object-fit:cover}.empty-messages{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;gap:12px}.empty-messages-image{width:96px;height:96px;-o-object-fit:contain;object-fit:contain}.empty-messages-text{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm, 14px);color:var(--text-color-default-secondary, #71727a);margin:0}\n"] }]
32324
33758
  }], ctorParameters: () => [{ type: UserService }, { type: DsMobileLightboxService }, { type: DsMobileChatModalService }] });
32325
33759
 
33760
+ const STORAGE_KEY = 'ds-notification-prompt-dismissed';
33761
+ const COOLDOWN_MS = 24 * 60 * 60 * 1000; // 24 hours
33762
+ /**
33763
+ * Manages the notification permission prompt lifecycle.
33764
+ *
33765
+ * - Stores the user's "Not now" dismissal timestamp in localStorage
33766
+ * - Re-shows the prompt after a 24-hour cooldown
33767
+ * - Skips the prompt entirely if native permissions are already granted
33768
+ *
33769
+ * Downstream integration: call `checkPermissions()` before checking
33770
+ * `shouldShowPrompt()` to incorporate native permission status.
33771
+ */
33772
+ class NotificationPromptService {
33773
+ permissionGranted = false;
33774
+ /**
33775
+ * Record that the user dismissed the prompt. Stores the current
33776
+ * timestamp so the prompt can be suppressed for 24 hours.
33777
+ */
33778
+ dismiss() {
33779
+ try {
33780
+ localStorage.setItem(STORAGE_KEY, JSON.stringify({ dismissedAt: Date.now() }));
33781
+ }
33782
+ catch {
33783
+ // localStorage unavailable (SSR, private browsing quota)
33784
+ }
33785
+ }
33786
+ /**
33787
+ * Whether the prompt should be displayed right now.
33788
+ * Returns false if:
33789
+ * - Native permission is already granted
33790
+ * - The user dismissed less than 24 hours ago
33791
+ */
33792
+ shouldShowPrompt() {
33793
+ if (this.permissionGranted)
33794
+ return false;
33795
+ try {
33796
+ const raw = localStorage.getItem(STORAGE_KEY);
33797
+ if (!raw)
33798
+ return true;
33799
+ const { dismissedAt } = JSON.parse(raw);
33800
+ return Date.now() - dismissedAt >= COOLDOWN_MS;
33801
+ }
33802
+ catch {
33803
+ return true;
33804
+ }
33805
+ }
33806
+ /**
33807
+ * Call this on app launch to check native notification permission status.
33808
+ * If already granted, the prompt will never be shown.
33809
+ */
33810
+ async checkPermissions() {
33811
+ try {
33812
+ const { PushNotifications } = await import('@capacitor/push-notifications');
33813
+ const result = await PushNotifications.checkPermissions();
33814
+ this.permissionGranted = result.receive === 'granted';
33815
+ }
33816
+ catch {
33817
+ // Not on native — leave as false so the prompt can still show
33818
+ }
33819
+ }
33820
+ /**
33821
+ * Mark that the user has granted permission (called after a successful
33822
+ * allow flow so the prompt is permanently hidden for this session).
33823
+ */
33824
+ markGranted() {
33825
+ this.permissionGranted = true;
33826
+ try {
33827
+ localStorage.removeItem(STORAGE_KEY);
33828
+ }
33829
+ catch {
33830
+ // noop
33831
+ }
33832
+ }
33833
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationPromptService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
33834
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationPromptService, providedIn: 'root' });
33835
+ }
33836
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationPromptService, decorators: [{
33837
+ type: Injectable,
33838
+ args: [{ providedIn: 'root' }]
33839
+ }] });
33840
+
32326
33841
  /**
32327
33842
  * Whitelabel Demo Modal Component
32328
33843
  *
@@ -33462,7 +34977,11 @@ class MobileTabsExampleComponent {
33462
34977
  navCtrl;
33463
34978
  whitelabelDemoModal = inject(WhitelabelDemoModalService);
33464
34979
  trackingPermissionService = inject(TrackingPermissionService);
34980
+ notificationModal = inject(DsMobileNotificationModalService);
34981
+ notificationService = inject(NotificationService);
34982
+ notificationPrompt = inject(NotificationPromptService);
33465
34983
  pageLoading = inject(PageLoadingService);
34984
+ showNotificationPrompt = signal(false, ...(ngDevMode ? [{ debugName: "showNotificationPrompt" }] : []));
33466
34985
  trackedProfileMenuItems = computed(() => {
33467
34986
  const accountActions = [
33468
34987
  {
@@ -33542,15 +35061,15 @@ class MobileTabsExampleComponent {
33542
35061
  this.userService.setProfileMenuItems(this.trackedProfileMenuItems());
33543
35062
  });
33544
35063
  }
33545
- ngOnInit() {
35064
+ async ngOnInit() {
33546
35065
  console.log('MobileTabsExampleComponent ngOnInit');
33547
- // Configure user avatar globally - this is now the single source of truth
33548
35066
  this.userService.setDisplayName('Lucas Møller');
33549
35067
  this.userService.setAddress('Toftegårds Allé 5A, 3. tv.');
33550
35068
  this.userService.setAvatarInitials('LM');
33551
35069
  this.userService.setAvatarType('initials');
33552
- // Initial status refresh ensures menu reflects past ATT choice.
33553
35070
  void this.trackingPermissionService.refreshTrackingStatus();
35071
+ await this.notificationPrompt.checkPermissions();
35072
+ this.showNotificationPrompt.set(this.notificationPrompt.shouldShowPrompt());
33554
35073
  }
33555
35074
  tabs = [
33556
35075
  {
@@ -33657,6 +35176,34 @@ class MobileTabsExampleComponent {
33657
35176
  });
33658
35177
  }
33659
35178
  }
35179
+ async handleNotificationAllow() {
35180
+ console.log('Notification permission: requesting native dialog...');
35181
+ this.showNotificationPrompt.set(false);
35182
+ try {
35183
+ const { PushNotifications } = await import('@capacitor/push-notifications');
35184
+ const permission = await PushNotifications.requestPermissions();
35185
+ console.log('Push permission result:', permission.receive);
35186
+ if (permission.receive === 'granted') {
35187
+ await PushNotifications.register();
35188
+ this.notificationPrompt.markGranted();
35189
+ console.log('Push notifications registered');
35190
+ }
35191
+ }
35192
+ catch (err) {
35193
+ console.warn('Push notifications not available (web/simulator):', err);
35194
+ }
35195
+ }
35196
+ handleNotificationDismiss() {
35197
+ console.log('Notification permission: dismissed (24hr cooldown)');
35198
+ this.notificationPrompt.dismiss();
35199
+ this.showNotificationPrompt.set(false);
35200
+ }
35201
+ async handleNotificationClick() {
35202
+ const tapped = await this.notificationModal.open();
35203
+ if (tapped) {
35204
+ console.log('Notification tapped:', tapped.type, tapped.id);
35205
+ }
35206
+ }
33660
35207
  /** Called by the error overlay retry button — clears error and re-navigates to home */
33661
35208
  handleRetry() {
33662
35209
  this.pageLoading.hasError.set(false);
@@ -33678,26 +35225,35 @@ class MobileTabsExampleComponent {
33678
35225
  </div>
33679
35226
  }
33680
35227
 
35228
+ <!-- Notification permission prompt (session-only, reappears on next launch) -->
35229
+ @if (showNotificationPrompt() && !pageLoading.isCoveringScreen()) {
35230
+ <ds-mobile-notification-prompt
35231
+ (allow)="handleNotificationAllow()"
35232
+ (dismiss)="handleNotificationDismiss()" />
35233
+ }
35234
+
33681
35235
  <ion-tabs class="ds-tabs-wrapper">
33682
35236
  <!-- Tab bar is hidden during the loading state -->
33683
35237
  @if (!pageLoading.isCoveringScreen()) {
33684
35238
  <ds-mobile-tab-bar
33685
35239
  [tabs]="tabs"
33686
35240
  [moreMenuItems]="moreMenuItems"
35241
+ [notificationCount]="notificationService.unreadCount()"
33687
35242
  [avatarInitials]="userService.avatarInitials()"
33688
35243
  [avatarType]="userService.avatarType()"
33689
35244
  [profileMenuItems]="profileMenuItems"
35245
+ (notificationClick)="handleNotificationClick()"
33690
35246
  (profileActionSelected)="handleProfileAction($event)"
33691
35247
  (moreMenuItemSelected)="handleMoreMenuAction($event)"
33692
35248
  >
33693
35249
  </ds-mobile-tab-bar>
33694
35250
  }
33695
35251
  </ion-tabs>
33696
- `, isInline: true, styles: [":host{display:block;height:100dvh;width:100vw;position:relative}.app-error{position:fixed;inset:0;z-index:9999;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:40px 32px;background:var(--color-header-surface, #2D2356)}.app-error__title{font-family:Brockmann,sans-serif;font-size:var(--font-size-lg, 18px);font-weight:600;color:var(--color-header-content, #fff);text-align:center;margin:0}.app-error__description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:#ffffffa6;text-align:center;margin:0 0 8px}.app-error ds-button::ng-deep .btn{border-radius:9999px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonTabs, selector: "ion-tabs" }, { kind: "component", type: DsMobileTabBarComponent, selector: "ds-mobile-tab-bar", inputs: ["tabs", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "profileMenuItems", "moreMenuItems", "moreMenuLabel", "moreMenuIcon", "moreMenuIconActive"], outputs: ["avatarClick", "profileActionSelected", "moreMenuItemSelected"] }, { kind: "component", type: DsMobileAppLoadingComponent, selector: "ds-mobile-app-loading", inputs: ["isExiting"], outputs: ["exitComplete"] }, { kind: "component", type: DsButtonComponent, selector: "ds-button", inputs: ["variant", "size", "disabled", "loading", "pressed", "expanded", "leadingIcon", "trailingIcon", "ariaLabel", "iconOnly"], outputs: ["clicked", "focused", "blurred"] }] });
35252
+ `, isInline: true, styles: [":host{display:block;height:100dvh;width:100vw;position:relative}.app-error{position:fixed;inset:0;z-index:9999;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:40px 32px;background:var(--color-header-surface, #2D2356)}.app-error__title{font-family:Brockmann,sans-serif;font-size:var(--font-size-lg, 18px);font-weight:600;color:var(--color-header-content, #fff);text-align:center;margin:0}.app-error__description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:#ffffffa6;text-align:center;margin:0 0 8px}.app-error ds-button::ng-deep .btn{border-radius:9999px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonTabs, selector: "ion-tabs" }, { kind: "component", type: DsMobileTabBarComponent, selector: "ds-mobile-tab-bar", inputs: ["tabs", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "profileMenuItems", "notificationCount", "moreMenuItems", "moreMenuLabel", "moreMenuIcon", "moreMenuIconActive"], outputs: ["notificationClick", "avatarClick", "profileActionSelected", "moreMenuItemSelected"] }, { kind: "component", type: DsMobileAppLoadingComponent, selector: "ds-mobile-app-loading", inputs: ["isExiting"], outputs: ["exitComplete"] }, { kind: "component", type: DsMobileNotificationPromptComponent, selector: "ds-mobile-notification-prompt", inputs: ["heading", "subtitle", "allowLabel", "dismissLabel"], outputs: ["allow", "dismiss"] }, { kind: "component", type: DsButtonComponent, selector: "ds-button", inputs: ["variant", "size", "disabled", "loading", "pressed", "expanded", "leadingIcon", "trailingIcon", "ariaLabel", "iconOnly"], outputs: ["clicked", "focused", "blurred"] }] });
33697
35253
  }
33698
35254
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MobileTabsExampleComponent, decorators: [{
33699
35255
  type: Component,
33700
- args: [{ selector: 'app-mobile-tabs-example', standalone: true, imports: [CommonModule, IonTabs, DsMobileTabBarComponent, DsMobileAppLoadingComponent, DsButtonComponent], template: `
35256
+ args: [{ selector: 'app-mobile-tabs-example', standalone: true, imports: [CommonModule, IonTabs, DsMobileTabBarComponent, DsMobileAppLoadingComponent, DsMobileNotificationPromptComponent, DsButtonComponent], template: `
33701
35257
  <!-- Full-screen loading screen rendered at layout level so it covers the tab bar.
33702
35258
  Kept in DOM during exiting so the exit animation can play. -->
33703
35259
  @if (pageLoading.isLoading() || pageLoading.isExiting()) {
@@ -33712,15 +35268,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
33712
35268
  </div>
33713
35269
  }
33714
35270
 
35271
+ <!-- Notification permission prompt (session-only, reappears on next launch) -->
35272
+ @if (showNotificationPrompt() && !pageLoading.isCoveringScreen()) {
35273
+ <ds-mobile-notification-prompt
35274
+ (allow)="handleNotificationAllow()"
35275
+ (dismiss)="handleNotificationDismiss()" />
35276
+ }
35277
+
33715
35278
  <ion-tabs class="ds-tabs-wrapper">
33716
35279
  <!-- Tab bar is hidden during the loading state -->
33717
35280
  @if (!pageLoading.isCoveringScreen()) {
33718
35281
  <ds-mobile-tab-bar
33719
35282
  [tabs]="tabs"
33720
35283
  [moreMenuItems]="moreMenuItems"
35284
+ [notificationCount]="notificationService.unreadCount()"
33721
35285
  [avatarInitials]="userService.avatarInitials()"
33722
35286
  [avatarType]="userService.avatarType()"
33723
35287
  [profileMenuItems]="profileMenuItems"
35288
+ (notificationClick)="handleNotificationClick()"
33724
35289
  (profileActionSelected)="handleProfileAction($event)"
33725
35290
  (moreMenuItemSelected)="handleMoreMenuAction($event)"
33726
35291
  >
@@ -33730,393 +35295,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
33730
35295
  `, styles: [":host{display:block;height:100dvh;width:100vw;position:relative}.app-error{position:fixed;inset:0;z-index:9999;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:40px 32px;background:var(--color-header-surface, #2D2356)}.app-error__title{font-family:Brockmann,sans-serif;font-size:var(--font-size-lg, 18px);font-weight:600;color:var(--color-header-content, #fff);text-align:center;margin:0}.app-error__description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:#ffffffa6;text-align:center;margin:0 0 8px}.app-error ds-button::ng-deep .btn{border-radius:9999px}\n"] }]
33731
35296
  }], ctorParameters: () => [{ type: UserService }, { type: i1$3.Router }, { type: i1.NavController }] });
33732
35297
 
33733
- class DsMobileBookingDetailSheetComponent {
33734
- modalController;
33735
- /** `sheet` = bottom sheet. `modal` = `ds-modal-base` shell (active + history bookings). */
33736
- presentation = 'sheet';
33737
- data;
33738
- /** When true the modal sizes to its content instead of filling the screen. */
33739
- autoHeight = false;
33740
- get isModalPresentation() {
33741
- return this.presentation === 'modal';
33742
- }
33743
- constructor(modalController) {
33744
- this.modalController = modalController;
33745
- }
33746
- close() {
33747
- this.modalController.dismiss(null, 'backdrop');
33748
- }
33749
- cancelBooking() {
33750
- this.modalController.dismiss(null, 'cancel');
33751
- }
33752
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileBookingDetailSheetComponent, deps: [{ token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Component });
33753
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileBookingDetailSheetComponent, isStandalone: true, selector: "ds-mobile-booking-detail-sheet", inputs: { presentation: "presentation", data: "data", autoHeight: "autoHeight" }, host: { properties: { "class.presentation-modal": "this.isModalPresentation" } }, ngImport: i0, template: `
33754
- @if (presentation === 'modal') {
33755
- <ds-mobile-modal-base
33756
- [headerTitle]="data.facilityTitle"
33757
- [closeButtonLabel]="'Luk'"
33758
- [isAutoHeight]="autoHeight"
33759
- [keyboardContentBehavior]="'follow'">
33760
-
33761
- <ds-mobile-section
33762
- [showBorder]="false"
33763
- padding="20px 20px 0 20px">
33764
- <div class="hero-image-container">
33765
- @if (data.heroImage) {
33766
- <img [src]="data.heroImage" [alt]="data.facilityTitle" class="hero-image" />
33767
- } @else {
33768
- <div class="hero-image-placeholder">
33769
- <ds-icon name="remixImageLine" size="48px" color="rgba(255,255,255,0.5)" />
33770
- </div>
33771
- }
33772
- </div>
33773
- </ds-mobile-section>
33774
-
33775
- <ds-mobile-section padding="20px 20px 20px 20px">
33776
- <div class="booking-summary-card">
33777
- <div class="details-section">
33778
- <h3 class="details-heading">Booking detaljer</h3>
33779
- <div class="info-rows">
33780
- @if (data.bookingDate) {
33781
- <div class="info-row">
33782
- <ds-icon name="remixCalendarLine" size="16px" color="tertiary" />
33783
- <span>{{ data.bookingDate }}</span>
33784
- </div>
33785
- }
33786
- @if (data.bookingTime) {
33787
- <div class="info-row">
33788
- <ds-icon name="remixTimeLine" size="16px" color="tertiary" />
33789
- <span>{{ data.bookingTime }}</span>
33790
- </div>
33791
- }
33792
- @if (data.price) {
33793
- <div class="info-row">
33794
- <ds-icon name="remixPriceTag3Line" size="16px" color="tertiary" />
33795
- <span>{{ data.price }}</span>
33796
- </div>
33797
- }
33798
- @if (data.bookingType) {
33799
- <div class="info-row">
33800
- <ds-icon name="remixCheckboxCircleLine" size="16px" color="tertiary" />
33801
- <span>{{ data.bookingType }}</span>
33802
- </div>
33803
- }
33804
- @for (req of data.requirements || []; track req) {
33805
- <div class="info-row">
33806
- <ds-icon name="remixLockLine" size="16px" color="tertiary" />
33807
- <span>{{ req }}</span>
33808
- </div>
33809
- }
33810
- </div>
33811
- </div>
33812
- </div>
33813
- </ds-mobile-section>
33814
-
33815
- @if (data.canCancel) {
33816
- <div class="booking-action">
33817
- <div class="booking-actions-row">
33818
- <ds-button
33819
- class="cancel-primary"
33820
- size="md"
33821
- variant="secondary"
33822
- (clicked)="cancelBooking()">
33823
- Annuller booking
33824
- </ds-button>
33825
- </div>
33826
- </div>
33827
- }
33828
- </ds-mobile-modal-base>
33829
- } @else {
33830
- <ds-mobile-bottom-sheet-wrapper [showDragHandle]="true">
33831
- <div class="detail-header">
33832
- <h2 class="detail-title">{{ data.facilityTitle }}</h2>
33833
- <ds-icon-button
33834
- icon="remixCloseLine"
33835
- variant="ghost"
33836
- size="sm"
33837
- (clicked)="close()"
33838
- aria-label="Luk"
33839
- />
33840
- </div>
33841
-
33842
- <div class="detail-content">
33843
- <div class="hero-image-container">
33844
- @if (data.heroImage) {
33845
- <img [src]="data.heroImage" [alt]="data.facilityTitle" class="hero-image" />
33846
- } @else {
33847
- <div class="hero-image-placeholder">
33848
- <ds-icon name="remixImageLine" size="48px" color="rgba(255,255,255,0.5)" />
33849
- </div>
33850
- }
33851
- </div>
33852
-
33853
- <div class="booking-summary-card">
33854
- <div class="details-section">
33855
- <h3 class="details-heading">Booking detaljer</h3>
33856
- <div class="info-rows">
33857
- @if (data.bookingDate) {
33858
- <div class="info-row">
33859
- <ds-icon name="remixCalendarLine" size="16px" color="tertiary" />
33860
- <span>{{ data.bookingDate }}</span>
33861
- </div>
33862
- }
33863
- @if (data.bookingTime) {
33864
- <div class="info-row">
33865
- <ds-icon name="remixTimeLine" size="16px" color="tertiary" />
33866
- <span>{{ data.bookingTime }}</span>
33867
- </div>
33868
- }
33869
- @if (data.price) {
33870
- <div class="info-row">
33871
- <ds-icon name="remixPriceTag3Line" size="16px" color="tertiary" />
33872
- <span>{{ data.price }}</span>
33873
- </div>
33874
- }
33875
- @if (data.bookingType) {
33876
- <div class="info-row">
33877
- <ds-icon name="remixCheckboxCircleLine" size="16px" color="tertiary" />
33878
- <span>{{ data.bookingType }}</span>
33879
- </div>
33880
- }
33881
- @for (req of data.requirements || []; track req) {
33882
- <div class="info-row">
33883
- <ds-icon name="remixLockLine" size="16px" color="tertiary" />
33884
- <span>{{ req }}</span>
33885
- </div>
33886
- }
33887
- </div>
33888
- </div>
33889
- </div>
33890
-
33891
- @if (data.canCancel) {
33892
- <div class="footer-button-container">
33893
- <div class="booking-actions-column">
33894
- <ds-button
33895
- size="md"
33896
- variant="secondary"
33897
- (clicked)="cancelBooking()">
33898
- Annuller booking
33899
- </ds-button>
33900
- </div>
33901
- </div>
33902
- }
33903
- </div>
33904
- </ds-mobile-bottom-sheet-wrapper>
33905
- }
33906
- `, isInline: true, styles: [":host{display:block;height:auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;box-sizing:border-box}.detail-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px 8px}.detail-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-lg, 18px);font-weight:600;color:var(--text-color-default-primary, #202227);margin:0;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-right:12px}.detail-content{padding:8px 20px 24px;display:flex;flex-direction:column;gap:20px}.hero-image-container{width:100%;aspect-ratio:16 / 9;border-radius:12px;overflow:hidden;background:var(--color-surface-secondary, #f5f5f5)}.hero-image{width:100%;height:100%;object-fit:cover;display:block}.hero-image-placeholder{width:100%;height:100%;min-height:0;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,color-mix(in srgb,var(--color-accent, #6B5FF5) 30%,transparent),color-mix(in srgb,var(--color-accent, #6B5FF5) 60%,transparent))}.booking-summary-card{background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:16px;padding:16px}.details-section{display:flex;flex-direction:column;gap:12px}.details-heading{font-family:Brockmann,sans-serif;font-size:var(--font-size-base, 16px);font-weight:600;color:var(--text-color-default-primary, #202227);margin:0}.info-rows{display:flex;flex-direction:column;gap:8px}.info-row{display:flex;align-items:center;gap:10px;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm, 14px);color:var(--text-color-default-secondary, #545B66);line-height:1.4}.info-row ds-icon{flex-shrink:0}.footer-button-container{width:100%;padding:16px 20px 0}.footer-button-container ::ng-deep ds-button{display:block;width:100%}.footer-button-container ::ng-deep ds-button button{width:100%;border-radius:100px;height:44px;min-height:44px;max-height:44px}.booking-action{padding:16px 20px;padding-bottom:calc(16px + max(8px,env(safe-area-inset-bottom,0px) - 24px));background:var(--color-surface-primary, #ffffff)}.booking-actions-row{display:flex;flex-direction:row;align-items:center;gap:12px;width:100%}.booking-action .cancel-primary{flex:1;min-width:0;display:block}.booking-action .cancel-primary ::ng-deep button{width:100%;border-radius:100px;height:44px;min-height:44px;max-height:44px;padding-left:16px;padding-right:16px}:host.presentation-modal{display:block;height:auto;width:100%;min-height:0;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsMobileBottomSheetWrapperComponent, selector: "ds-mobile-bottom-sheet-wrapper", inputs: ["showDragHandle"] }, { kind: "component", type: DsMobileModalBaseComponent, selector: "ds-mobile-modal-base", inputs: ["headerTitleInteractive", "showHeader"], outputs: ["titleClick"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsButtonComponent, selector: "ds-button", inputs: ["variant", "size", "disabled", "loading", "pressed", "expanded", "leadingIcon", "trailingIcon", "ariaLabel", "iconOnly"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }] });
33907
- }
33908
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileBookingDetailSheetComponent, decorators: [{
33909
- type: Component,
33910
- args: [{ selector: 'ds-mobile-booking-detail-sheet', standalone: true, imports: [
33911
- CommonModule,
33912
- DsMobileBottomSheetWrapperComponent,
33913
- DsMobileModalBaseComponent,
33914
- DsMobileSectionComponent,
33915
- DsButtonComponent,
33916
- DsIconComponent,
33917
- DsIconButtonComponent,
33918
- ], template: `
33919
- @if (presentation === 'modal') {
33920
- <ds-mobile-modal-base
33921
- [headerTitle]="data.facilityTitle"
33922
- [closeButtonLabel]="'Luk'"
33923
- [isAutoHeight]="autoHeight"
33924
- [keyboardContentBehavior]="'follow'">
33925
-
33926
- <ds-mobile-section
33927
- [showBorder]="false"
33928
- padding="20px 20px 0 20px">
33929
- <div class="hero-image-container">
33930
- @if (data.heroImage) {
33931
- <img [src]="data.heroImage" [alt]="data.facilityTitle" class="hero-image" />
33932
- } @else {
33933
- <div class="hero-image-placeholder">
33934
- <ds-icon name="remixImageLine" size="48px" color="rgba(255,255,255,0.5)" />
33935
- </div>
33936
- }
33937
- </div>
33938
- </ds-mobile-section>
33939
-
33940
- <ds-mobile-section padding="20px 20px 20px 20px">
33941
- <div class="booking-summary-card">
33942
- <div class="details-section">
33943
- <h3 class="details-heading">Booking detaljer</h3>
33944
- <div class="info-rows">
33945
- @if (data.bookingDate) {
33946
- <div class="info-row">
33947
- <ds-icon name="remixCalendarLine" size="16px" color="tertiary" />
33948
- <span>{{ data.bookingDate }}</span>
33949
- </div>
33950
- }
33951
- @if (data.bookingTime) {
33952
- <div class="info-row">
33953
- <ds-icon name="remixTimeLine" size="16px" color="tertiary" />
33954
- <span>{{ data.bookingTime }}</span>
33955
- </div>
33956
- }
33957
- @if (data.price) {
33958
- <div class="info-row">
33959
- <ds-icon name="remixPriceTag3Line" size="16px" color="tertiary" />
33960
- <span>{{ data.price }}</span>
33961
- </div>
33962
- }
33963
- @if (data.bookingType) {
33964
- <div class="info-row">
33965
- <ds-icon name="remixCheckboxCircleLine" size="16px" color="tertiary" />
33966
- <span>{{ data.bookingType }}</span>
33967
- </div>
33968
- }
33969
- @for (req of data.requirements || []; track req) {
33970
- <div class="info-row">
33971
- <ds-icon name="remixLockLine" size="16px" color="tertiary" />
33972
- <span>{{ req }}</span>
33973
- </div>
33974
- }
33975
- </div>
33976
- </div>
33977
- </div>
33978
- </ds-mobile-section>
33979
-
33980
- @if (data.canCancel) {
33981
- <div class="booking-action">
33982
- <div class="booking-actions-row">
33983
- <ds-button
33984
- class="cancel-primary"
33985
- size="md"
33986
- variant="secondary"
33987
- (clicked)="cancelBooking()">
33988
- Annuller booking
33989
- </ds-button>
33990
- </div>
33991
- </div>
33992
- }
33993
- </ds-mobile-modal-base>
33994
- } @else {
33995
- <ds-mobile-bottom-sheet-wrapper [showDragHandle]="true">
33996
- <div class="detail-header">
33997
- <h2 class="detail-title">{{ data.facilityTitle }}</h2>
33998
- <ds-icon-button
33999
- icon="remixCloseLine"
34000
- variant="ghost"
34001
- size="sm"
34002
- (clicked)="close()"
34003
- aria-label="Luk"
34004
- />
34005
- </div>
34006
-
34007
- <div class="detail-content">
34008
- <div class="hero-image-container">
34009
- @if (data.heroImage) {
34010
- <img [src]="data.heroImage" [alt]="data.facilityTitle" class="hero-image" />
34011
- } @else {
34012
- <div class="hero-image-placeholder">
34013
- <ds-icon name="remixImageLine" size="48px" color="rgba(255,255,255,0.5)" />
34014
- </div>
34015
- }
34016
- </div>
34017
-
34018
- <div class="booking-summary-card">
34019
- <div class="details-section">
34020
- <h3 class="details-heading">Booking detaljer</h3>
34021
- <div class="info-rows">
34022
- @if (data.bookingDate) {
34023
- <div class="info-row">
34024
- <ds-icon name="remixCalendarLine" size="16px" color="tertiary" />
34025
- <span>{{ data.bookingDate }}</span>
34026
- </div>
34027
- }
34028
- @if (data.bookingTime) {
34029
- <div class="info-row">
34030
- <ds-icon name="remixTimeLine" size="16px" color="tertiary" />
34031
- <span>{{ data.bookingTime }}</span>
34032
- </div>
34033
- }
34034
- @if (data.price) {
34035
- <div class="info-row">
34036
- <ds-icon name="remixPriceTag3Line" size="16px" color="tertiary" />
34037
- <span>{{ data.price }}</span>
34038
- </div>
34039
- }
34040
- @if (data.bookingType) {
34041
- <div class="info-row">
34042
- <ds-icon name="remixCheckboxCircleLine" size="16px" color="tertiary" />
34043
- <span>{{ data.bookingType }}</span>
34044
- </div>
34045
- }
34046
- @for (req of data.requirements || []; track req) {
34047
- <div class="info-row">
34048
- <ds-icon name="remixLockLine" size="16px" color="tertiary" />
34049
- <span>{{ req }}</span>
34050
- </div>
34051
- }
34052
- </div>
34053
- </div>
34054
- </div>
34055
-
34056
- @if (data.canCancel) {
34057
- <div class="footer-button-container">
34058
- <div class="booking-actions-column">
34059
- <ds-button
34060
- size="md"
34061
- variant="secondary"
34062
- (clicked)="cancelBooking()">
34063
- Annuller booking
34064
- </ds-button>
34065
- </div>
34066
- </div>
34067
- }
34068
- </div>
34069
- </ds-mobile-bottom-sheet-wrapper>
34070
- }
34071
- `, styles: [":host{display:block;height:auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;box-sizing:border-box}.detail-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px 8px}.detail-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-lg, 18px);font-weight:600;color:var(--text-color-default-primary, #202227);margin:0;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-right:12px}.detail-content{padding:8px 20px 24px;display:flex;flex-direction:column;gap:20px}.hero-image-container{width:100%;aspect-ratio:16 / 9;border-radius:12px;overflow:hidden;background:var(--color-surface-secondary, #f5f5f5)}.hero-image{width:100%;height:100%;object-fit:cover;display:block}.hero-image-placeholder{width:100%;height:100%;min-height:0;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,color-mix(in srgb,var(--color-accent, #6B5FF5) 30%,transparent),color-mix(in srgb,var(--color-accent, #6B5FF5) 60%,transparent))}.booking-summary-card{background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:16px;padding:16px}.details-section{display:flex;flex-direction:column;gap:12px}.details-heading{font-family:Brockmann,sans-serif;font-size:var(--font-size-base, 16px);font-weight:600;color:var(--text-color-default-primary, #202227);margin:0}.info-rows{display:flex;flex-direction:column;gap:8px}.info-row{display:flex;align-items:center;gap:10px;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm, 14px);color:var(--text-color-default-secondary, #545B66);line-height:1.4}.info-row ds-icon{flex-shrink:0}.footer-button-container{width:100%;padding:16px 20px 0}.footer-button-container ::ng-deep ds-button{display:block;width:100%}.footer-button-container ::ng-deep ds-button button{width:100%;border-radius:100px;height:44px;min-height:44px;max-height:44px}.booking-action{padding:16px 20px;padding-bottom:calc(16px + max(8px,env(safe-area-inset-bottom,0px) - 24px));background:var(--color-surface-primary, #ffffff)}.booking-actions-row{display:flex;flex-direction:row;align-items:center;gap:12px;width:100%}.booking-action .cancel-primary{flex:1;min-width:0;display:block}.booking-action .cancel-primary ::ng-deep button{width:100%;border-radius:100px;height:44px;min-height:44px;max-height:44px;padding-left:16px;padding-right:16px}:host.presentation-modal{display:block;height:auto;width:100%;min-height:0;overflow:hidden}\n"] }]
34072
- }], ctorParameters: () => [{ type: i1.ModalController }], propDecorators: { presentation: [{
34073
- type: Input
34074
- }], data: [{
34075
- type: Input
34076
- }], autoHeight: [{
34077
- type: Input
34078
- }], isModalPresentation: [{
34079
- type: HostBinding,
34080
- args: ['class.presentation-modal']
34081
- }] } });
34082
-
34083
- class DsMobileBookingDetailSheetService extends BaseModalService {
34084
- bottomSheet;
34085
- constructor(modalController, bottomSheet) {
34086
- super(modalController);
34087
- this.bottomSheet = bottomSheet;
34088
- }
34089
- /**
34090
- * Bottom-sheet presentation (draggable breakpoints). Prefer `openAsModal` for booking lists.
34091
- */
34092
- async open(data) {
34093
- const modal = await this.bottomSheet.create({
34094
- component: DsMobileBookingDetailSheetComponent,
34095
- componentProps: { data },
34096
- autoHeight: true,
34097
- backdropDismiss: true,
34098
- backdropBlur: true,
34099
- });
34100
- const result = await modal.onWillDismiss();
34101
- return { role: result.role, data: result.data ?? undefined };
34102
- }
34103
- /** `ds-modal-base` shell — used for active and past bookings. Auto-heights to content. */
34104
- async openAsModal(data) {
34105
- const modal = await this.createModal(DsMobileBookingDetailSheetComponent, { data, presentation: 'modal', autoHeight: true }, { keyboardClose: true, autoHeight: true });
34106
- await modal.present();
34107
- const result = await modal.onWillDismiss();
34108
- return { role: result.role, data: result.data ?? undefined };
34109
- }
34110
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileBookingDetailSheetService, deps: [{ token: i1.ModalController }, { token: DsMobileBottomSheetService }], target: i0.ɵɵFactoryTarget.Injectable });
34111
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileBookingDetailSheetService, providedIn: 'root' });
34112
- }
34113
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileBookingDetailSheetService, decorators: [{
34114
- type: Injectable,
34115
- args: [{
34116
- providedIn: 'root',
34117
- }]
34118
- }], ctorParameters: () => [{ type: i1.ModalController }, { type: DsMobileBottomSheetService }] });
34119
-
34120
35298
  class DsMobileBookingCancelConfirmationComponent {
34121
35299
  facilityTitle;
34122
35300
  facilityThumbnail;
@@ -34217,6 +35395,8 @@ class MobileBookingPageComponent {
34217
35395
  userService;
34218
35396
  pageComponent;
34219
35397
  bookingsSwiper;
35398
+ notificationModal = inject(DsMobileNotificationModalService);
35399
+ notificationService = inject(NotificationService);
34220
35400
  cancellingBookingId = signal(null, ...(ngDevMode ? [{ debugName: "cancellingBookingId" }] : []));
34221
35401
  lang = signal(resolveLanguage(), ...(ngDevMode ? [{ debugName: "lang" }] : []));
34222
35402
  historyLinkText = computed(() => HISTORY_LINK[this.lang()] ?? HISTORY_LINK['da'], ...(ngDevMode ? [{ debugName: "historyLinkText" }] : []));
@@ -34422,6 +35602,11 @@ class MobileBookingPageComponent {
34422
35602
  }
34423
35603
  ], ...(ngDevMode ? [{ debugName: "availableFacilities" }] : []));
34424
35604
  visibleFacilities = computed(() => this.availableFacilities().filter((f) => !f.archived), ...(ngDevMode ? [{ debugName: "visibleFacilities" }] : []));
35605
+ async handleNotificationClick() {
35606
+ const tapped = await this.notificationModal.open();
35607
+ if (tapped)
35608
+ console.log('Notification tapped:', tapped.type, tapped.id);
35609
+ }
34425
35610
  handleRefresh(event) {
34426
35611
  console.log('Pull-to-refresh triggered');
34427
35612
  // Check if offline and complete immediately
@@ -34712,8 +35897,10 @@ class MobileBookingPageComponent {
34712
35897
  <ds-mobile-page-main
34713
35898
  #pageComponent
34714
35899
  [title]="'Bookinger'"
35900
+ [notificationCount]="notificationService.unreadCount()"
34715
35901
  [avatarInitials]="userService.avatarInitials()"
34716
35902
  [avatarType]="userService.avatarType()"
35903
+ (notificationClick)="handleNotificationClick()"
34717
35904
  (refresh)="handleRefresh($event)">
34718
35905
 
34719
35906
  @if (pageComponent.isOffline()) {
@@ -34818,7 +36005,7 @@ class MobileBookingPageComponent {
34818
36005
  ariaLabel="Opret facilitet"
34819
36006
  (clicked)="openFacilityCreationModal()">
34820
36007
  </ds-mobile-fab>
34821
- `, isInline: true, styles: [".bookings-swiper-wrapper{padding:0;position:relative}.swiper-nav-buttons{display:contents}.swiper-nav-button{position:absolute;top:50%;transform:translateY(-50%);z-index:10}.swiper-nav-button:first-child{left:-48px}.swiper-nav-button:last-child{right:-48px}::ng-deep .swiper-nav-button button{border-radius:50%!important;width:48px!important;height:48px!important;padding:0!important}@media (max-width: 767px){.swiper-nav-buttons{display:none}}::ng-deep .bookings-swiper .swiper-slide{width:100%;max-width:600px;height:auto;pointer-events:auto}@media (min-width: 768px){::ng-deep .bookings-swiper .swiper-slide{max-width:100%}}::ng-deep .bookings-swiper .swiper-slide ds-mobile-interactive-list-item-booking{width:100%;pointer-events:auto}.booking-slide-content{position:relative;width:100%}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileInteractiveListItemBookingComponent, selector: "ds-mobile-interactive-list-item-booking", inputs: ["thumbnail", "facilityTitle", "description", "bookingDate", "bookingTime", "availabilityStatus", "statusLabel", "variant", "align", "clickable", "showChevron", "enableLongPress", "moreActions"], outputs: ["bookingClick", "longPress"] }, { kind: "component", type: DsMobileSwiperComponent, selector: "ds-mobile-swiper", inputs: ["slideWidth", "gap", "pagination", "autoHeight", "progressiveOpacity", "progressiveScale"] }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsMobileOfflineBannerComponent, selector: "ds-mobile-offline-banner", inputs: ["icon", "title", "message"] }, { kind: "component", type: DsMobileFabComponent, selector: "ds-mobile-fab", inputs: ["icon", "position", "size", "ariaLabel", "disabled"], outputs: ["clicked"] }, { kind: "component", type: DsMobileLoaderOverlayComponent, selector: "ds-mobile-loader-overlay", inputs: ["spinnerSize", "borderRadius"] }] });
36008
+ `, isInline: true, styles: [".bookings-swiper-wrapper{padding:0;position:relative}.swiper-nav-buttons{display:contents}.swiper-nav-button{position:absolute;top:50%;transform:translateY(-50%);z-index:10}.swiper-nav-button:first-child{left:-48px}.swiper-nav-button:last-child{right:-48px}::ng-deep .swiper-nav-button button{border-radius:50%!important;width:48px!important;height:48px!important;padding:0!important}@media (max-width: 767px){.swiper-nav-buttons{display:none}}::ng-deep .bookings-swiper .swiper-slide{width:100%;max-width:600px;height:auto;pointer-events:auto}@media (min-width: 768px){::ng-deep .bookings-swiper .swiper-slide{max-width:100%}}::ng-deep .bookings-swiper .swiper-slide ds-mobile-interactive-list-item-booking{width:100%;pointer-events:auto}.booking-slide-content{position:relative;width:100%}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "notificationCount", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["notificationClick", "avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileInteractiveListItemBookingComponent, selector: "ds-mobile-interactive-list-item-booking", inputs: ["thumbnail", "facilityTitle", "description", "bookingDate", "bookingTime", "availabilityStatus", "statusLabel", "variant", "align", "clickable", "showChevron", "enableLongPress", "moreActions"], outputs: ["bookingClick", "longPress"] }, { kind: "component", type: DsMobileSwiperComponent, selector: "ds-mobile-swiper", inputs: ["slideWidth", "gap", "pagination", "autoHeight", "progressiveOpacity", "progressiveScale"] }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsMobileOfflineBannerComponent, selector: "ds-mobile-offline-banner", inputs: ["icon", "title", "message"] }, { kind: "component", type: DsMobileFabComponent, selector: "ds-mobile-fab", inputs: ["icon", "position", "size", "ariaLabel", "disabled"], outputs: ["clicked"] }, { kind: "component", type: DsMobileLoaderOverlayComponent, selector: "ds-mobile-loader-overlay", inputs: ["spinnerSize", "borderRadius"] }] });
34822
36009
  }
34823
36010
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MobileBookingPageComponent, decorators: [{
34824
36011
  type: Component,
@@ -34835,8 +36022,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
34835
36022
  <ds-mobile-page-main
34836
36023
  #pageComponent
34837
36024
  [title]="'Bookinger'"
36025
+ [notificationCount]="notificationService.unreadCount()"
34838
36026
  [avatarInitials]="userService.avatarInitials()"
34839
36027
  [avatarType]="userService.avatarType()"
36028
+ (notificationClick)="handleNotificationClick()"
34840
36029
  (refresh)="handleRefresh($event)">
34841
36030
 
34842
36031
  @if (pageComponent.isOffline()) {
@@ -36726,9 +37915,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
36726
37915
  class TenantChatPageComponent {
36727
37916
  modalCtrl = inject(ModalController);
36728
37917
  userService = inject(UserService);
37918
+ notificationModal = inject(DsMobileNotificationModalService);
37919
+ notificationService = inject(NotificationService);
36729
37920
  peerMessaging = inject(PeerMessagingService);
36730
37921
  peerChat = inject(PeerChatLauncherService);
36731
37922
  isPeerGroupConversation = isPeerGroupConversation;
37923
+ async handleNotificationClick() {
37924
+ const tapped = await this.notificationModal.open();
37925
+ if (tapped)
37926
+ console.log('Notification tapped:', tapped.type, tapped.id);
37927
+ }
36732
37928
  handleRefresh(event) {
36733
37929
  setTimeout(() => event.target.complete(), 1000);
36734
37930
  }
@@ -36755,8 +37951,10 @@ class TenantChatPageComponent {
36755
37951
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: TenantChatPageComponent, isStandalone: true, selector: "app-tenant-chat-page", host: { classAttribute: "ion-page" }, ngImport: i0, template: `
36756
37952
  <ds-mobile-page-main
36757
37953
  title="Beskeder"
37954
+ [notificationCount]="notificationService.unreadCount()"
36758
37955
  [avatarInitials]="userService.avatarInitials()"
36759
37956
  [avatarType]="userService.avatarType()"
37957
+ (notificationClick)="handleNotificationClick()"
36760
37958
  (refresh)="handleRefresh($event)">
36761
37959
 
36762
37960
  <ds-mobile-section contentGap="0px" padding="12px 20px 20px 20px" [showBorder]="false">
@@ -36815,7 +38013,7 @@ class TenantChatPageComponent {
36815
38013
  ariaLabel="Vælg beboer at skrive med"
36816
38014
  (clicked)="goToTenants()">
36817
38015
  </ds-mobile-fab>
36818
- `, isInline: true, styles: [".empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:64px 20px;text-align:center}.empty-state-image{width:96px;height:96px;margin-bottom:24px;opacity:.4}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileInteractiveListItemMessageComponent, selector: "ds-mobile-interactive-list-item-message", inputs: ["senderName", "senderRole", "timestamp", "message", "avatarInitials", "avatarType", "avatarSrc", "unread", "unreadStyle", "clickable", "align", "showAvatarBadge", "groupStackMembers", "groupCustomAvatarUrl", "groupStackExcludeParticipantId"], outputs: ["messageClick", "longPress"] }, { kind: "component", type: DsMobileFabComponent, selector: "ds-mobile-fab", inputs: ["icon", "position", "size", "ariaLabel", "disabled"], outputs: ["clicked"] }] });
38016
+ `, isInline: true, styles: [".empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:64px 20px;text-align:center}.empty-state-image{width:96px;height:96px;margin-bottom:24px;opacity:.4}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "notificationCount", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["notificationClick", "avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileInteractiveListItemMessageComponent, selector: "ds-mobile-interactive-list-item-message", inputs: ["senderName", "senderRole", "timestamp", "message", "avatarInitials", "avatarType", "avatarSrc", "unread", "unreadStyle", "clickable", "align", "showAvatarBadge", "groupStackMembers", "groupCustomAvatarUrl", "groupStackExcludeParticipantId"], outputs: ["messageClick", "longPress"] }, { kind: "component", type: DsMobileFabComponent, selector: "ds-mobile-fab", inputs: ["icon", "position", "size", "ariaLabel", "disabled"], outputs: ["clicked"] }] });
36819
38017
  }
36820
38018
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: TenantChatPageComponent, decorators: [{
36821
38019
  type: Component,
@@ -36829,8 +38027,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
36829
38027
  }, template: `
36830
38028
  <ds-mobile-page-main
36831
38029
  title="Beskeder"
38030
+ [notificationCount]="notificationService.unreadCount()"
36832
38031
  [avatarInitials]="userService.avatarInitials()"
36833
38032
  [avatarType]="userService.avatarType()"
38033
+ (notificationClick)="handleNotificationClick()"
36834
38034
  (refresh)="handleRefresh($event)">
36835
38035
 
36836
38036
  <ds-mobile-section contentGap="0px" padding="12px 20px 20px 20px" [showBorder]="false">
@@ -36972,6 +38172,8 @@ const MOCK_VENDORS = [
36972
38172
  class ServicesPageComponent {
36973
38173
  pageComponent;
36974
38174
  vendorModal = inject(DsMobileServiceVendorModalService);
38175
+ notificationModal = inject(DsMobileNotificationModalService);
38176
+ notificationService = inject(NotificationService);
36975
38177
  newInquiryModal = inject(DsMobileNewInquiryModalService);
36976
38178
  inquiriesService = inject(InquiriesService);
36977
38179
  navCtrl = inject(NavController);
@@ -37018,6 +38220,11 @@ class ServicesPageComponent {
37018
38220
  },
37019
38221
  });
37020
38222
  }
38223
+ async handleNotificationClick() {
38224
+ const tapped = await this.notificationModal.open();
38225
+ if (tapped)
38226
+ console.log('Notification tapped:', tapped.type, tapped.id);
38227
+ }
37021
38228
  handleRefresh(event) {
37022
38229
  setTimeout(() => {
37023
38230
  event.target?.complete?.();
@@ -37028,8 +38235,10 @@ class ServicesPageComponent {
37028
38235
  <ds-mobile-page-main
37029
38236
  #pageComponent
37030
38237
  [title]="lbl.pageTitle"
38238
+ [notificationCount]="notificationService.unreadCount()"
37031
38239
  [avatarInitials]="userService.avatarInitials()"
37032
38240
  [avatarType]="userService.avatarType()"
38241
+ (notificationClick)="handleNotificationClick()"
37033
38242
  (refresh)="handleRefresh($event)"
37034
38243
  >
37035
38244
  @if (pageComponent.isOffline()) {
@@ -37061,7 +38270,7 @@ class ServicesPageComponent {
37061
38270
  </ds-mobile-section>
37062
38271
  }
37063
38272
  </ds-mobile-page-main>
37064
- `, isInline: true, styles: [".empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;text-align:center}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin-top:-16px;z-index:4}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileIllustrationComponent, selector: "ds-mobile-illustration", inputs: ["variant", "size", "alt"] }, { kind: "component", type: DsMobileInteractiveListItemServiceComponent, selector: "ds-mobile-interactive-list-item-service", inputs: ["title", "description", "logo", "showChevron"], outputs: ["serviceClick"] }, { kind: "component", type: DsMobileOfflineBannerComponent, selector: "ds-mobile-offline-banner", inputs: ["icon", "title", "message"] }] });
38273
+ `, isInline: true, styles: [".empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;text-align:center}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin-top:-16px;z-index:4}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "firstEntry", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "notificationCount", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "contentPadding", "profileMenuItems"], outputs: ["notificationClick", "avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "paddingDesktop", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileIllustrationComponent, selector: "ds-mobile-illustration", inputs: ["variant", "size", "alt"] }, { kind: "component", type: DsMobileInteractiveListItemServiceComponent, selector: "ds-mobile-interactive-list-item-service", inputs: ["title", "description", "logo", "showChevron"], outputs: ["serviceClick"] }, { kind: "component", type: DsMobileOfflineBannerComponent, selector: "ds-mobile-offline-banner", inputs: ["icon", "title", "message"] }] });
37065
38274
  }
37066
38275
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ServicesPageComponent, decorators: [{
37067
38276
  type: Component,
@@ -37075,8 +38284,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
37075
38284
  <ds-mobile-page-main
37076
38285
  #pageComponent
37077
38286
  [title]="lbl.pageTitle"
38287
+ [notificationCount]="notificationService.unreadCount()"
37078
38288
  [avatarInitials]="userService.avatarInitials()"
37079
38289
  [avatarType]="userService.avatarType()"
38290
+ (notificationClick)="handleNotificationClick()"
37080
38291
  (refresh)="handleRefresh($event)"
37081
38292
  >
37082
38293
  @if (pageComponent.isOffline()) {
@@ -37137,5 +38348,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
37137
38348
  * Generated bundle index. Do not edit.
37138
38349
  */
37139
38350
 
37140
- export { AcceptInvitePageComponent, ActionCommentComponent, ActionLikeComponent, AvatarUploadPageComponent, BaseModalService, ContentRowComponent, CreateAccountPageComponent, DEFAULT_SERVICE_PAGE_LABELS, DsAppIconComponent, DsAvatarWithBadgeComponent, DsLogoComponent, DsMobileAccessSheetComponent, DsMobileActionListItemComponent, DsMobileActionsBottomSheetComponent, DsMobileAddGroupTenantsModalComponent, DsMobileAppLoadingComponent, DsMobileAttachmentPreviewComponent, DsMobileBookingConfirmationWrapperComponent, DsMobileBookingModalComponent, DsMobileBookingModalService, DsMobileBookingSummaryComponent, DsMobileBottomSheetHeaderComponent, DsMobileBottomSheetService, DsMobileBottomSheetWrapperComponent, DsMobileCapacitySheetComponent, DsMobileCardInlineBannerComponent, DsMobileCardInlineComponent, DsMobileCardInlineContactComponent, DsMobileCardInlineFileComponent, DsMobileChatModalComponent, DsMobileChatModalService, DsMobileActionsBottomSheetComponent as DsMobileCommentActionsBottomSheetComponent, DsMobileCommentComponent, DsMobileCommunityAdminPickerComponent, DsMobileCommunityAdminsModalComponent, DsMobileConfirmationSheetComponent, DsMobileContactListItemComponent, DsMobileContentComponent, DsMobileCreateGroupModalComponent, DsMobileDropdownComponent, DsMobileEditGroupModalComponent, DsMobileEmptyStateComponent, DsMobileFabComponent, DsMobileFacilityArchiveConfirmationComponent, DsMobileFacilityCreationConfirmationWrapperComponent, DsMobileFacilityCreationModalComponent, DsMobileFacilityCreationModalService, DsMobileFacilityDeleteConfirmationComponent, DsMobileFacilityDetailModalComponent, DsMobileFacilityDetailModalService, DsMobileFileAttachmentComponent, DsMobileGlassSpinnerComponent, DsMobileGroupAvatarStackComponent, DsMobileGroupMembersModalComponent, DsMobileHandbookDetailModalComponent, DsMobileHandbookDetailModalService, DsMobileHandbookFolderComponent, DsMobileHandbookFolderMiniComponent, DsMobileHeaderContentComponent, DsMobileHeaderContentTileComponent, DsMobileIllustrationComponent, DsMobileImagePlaceholderComponent, DsMobileInlinePhotoComponent, DsMobileInlineTabsComponent, DsMobileInteractiveListItemBookingComponent, DsMobileInteractiveListItemInquiryComponent, DsMobileInteractiveListItemMessageComponent, DsMobileInteractiveListItemPostComponent, DsMobileLightboxImageComponent as DsMobileLightboxComponent, DsMobileLightboxFooterComponent, DsMobileLightboxHeaderComponent, DsMobileLightboxImageComponent, DsMobileLightboxImageWithDescriptionComponent, DsMobileLightboxPdfComponent, DsMobileLightboxService, DsMobileListItemComponent, DsMobileListItemStaticComponent, DsMobileListSearchComponent, DsMobileLoaderOverlayComponent, DsMobileLongPressDirective, DsMobileMediaActionsPanelComponent, DsMobileMessageBubbleComponent, DsMobileMessageComposerComponent, DsMobileModalBaseComponent, DsMobileModalService, DsMobileNewInquiryModalComponent, DsMobileNewInquiryModalService, DsMobileOfflineBannerComponent, DsMobilePageDetailsComponent, DsMobilePageMainComponent, DsMobilePillComponent, DsMobileActionsBottomSheetComponent as DsMobilePostActionsBottomSheetComponent, DsMobilePostComposerComponent, DsMobilePostCreateBottomSheetComponent, DsMobilePostDetailModalComponent, DsMobilePostDetailModalService, DsMobilePriceSheetComponent, DsMobileProfileActionsSheetComponent, DsMobilePromptBottomSheetComponent, DsMobilePropertyBannerComponent, DsMobileRichTextEditorComponent, DsMobileSectionComponent, DsMobileServiceVendorModalService, DsMobileServiceVendorSheetComponent, DsMobileSwiperComponent, DsMobileSwiperWithNavComponent, DsMobileSystemMessageBannerComponent, DsMobileTabBarComponent, DsMobileTabsComponent, DsMobileTenantPickerModalComponent, DsMobileWhenCanBookSheetComponent, DsMobileWhoCanBookSheetComponent, DsTextInputComponent, FamilyAccessPageComponent, FamilyAccessService, InquiriesService, InviteSuccessPageComponent, MediaPickerService, MobileBookingPageComponent, MobileCommunityPageComponent, MobileHandbookPageComponent, MobileHomePageComponent, MobileInquiriesPageComponent, MobileInquiryDetailPageComponent, MobileModalBase, MobilePageBase, MobilePostDetailPageComponent, MobileTabsExampleComponent, PageLoadingService, PostActionsComponent, PostAttachmentsComponent, PostContentComponent, PostCreatePageComponent, PostMediaComponent, PostPdfAttachmentComponent, PostTextComponent, PostsService, SectionHeaderComponent, ServicesPageComponent, SignInPageComponent, SignInToAcceptPageComponent, TenantChatPageComponent, TileContentComponent, TileIconComponent, TileLabelComponent, TileValueComponent, TrackingPermissionService, UserService, WhitelabelDemoModalComponent, WhitelabelDemoModalService, WhitelabelService, customBackTransition, customPageTransition };
38351
+ export { AcceptInvitePageComponent, ActionCommentComponent, ActionLikeComponent, AvatarUploadPageComponent, BaseModalService, ContentRowComponent, CreateAccountPageComponent, DEFAULT_SERVICE_PAGE_LABELS, DsAppIconComponent, DsAvatarWithBadgeComponent, DsLogoComponent, DsMobileAccessSheetComponent, DsMobileActionListItemComponent, DsMobileActionsBottomSheetComponent, DsMobileAddGroupTenantsModalComponent, DsMobileAppLoadingComponent, DsMobileAttachmentPreviewComponent, DsMobileBookingConfirmationWrapperComponent, DsMobileBookingModalComponent, DsMobileBookingModalService, DsMobileBookingSummaryComponent, DsMobileBottomSheetHeaderComponent, DsMobileBottomSheetService, DsMobileBottomSheetWrapperComponent, DsMobileCapacitySheetComponent, DsMobileCardInlineBannerComponent, DsMobileCardInlineComponent, DsMobileCardInlineContactComponent, DsMobileCardInlineFileComponent, DsMobileChatModalComponent, DsMobileChatModalService, DsMobileActionsBottomSheetComponent as DsMobileCommentActionsBottomSheetComponent, DsMobileCommentComponent, DsMobileCommunityAdminPickerComponent, DsMobileCommunityAdminsModalComponent, DsMobileConfirmationSheetComponent, DsMobileContactListItemComponent, DsMobileContentComponent, DsMobileCountBadgeComponent, DsMobileCreateGroupModalComponent, DsMobileDropdownComponent, DsMobileEditGroupModalComponent, DsMobileEmptyStateComponent, DsMobileFabComponent, DsMobileFacilityArchiveConfirmationComponent, DsMobileFacilityCreationConfirmationWrapperComponent, DsMobileFacilityCreationModalComponent, DsMobileFacilityCreationModalService, DsMobileFacilityDeleteConfirmationComponent, DsMobileFacilityDetailModalComponent, DsMobileFacilityDetailModalService, DsMobileFileAttachmentComponent, DsMobileGlassSpinnerComponent, DsMobileGroupAvatarStackComponent, DsMobileGroupMembersModalComponent, DsMobileHandbookDetailModalComponent, DsMobileHandbookDetailModalService, DsMobileHandbookFolderComponent, DsMobileHandbookFolderMiniComponent, DsMobileHeaderContentComponent, DsMobileHeaderContentTileComponent, DsMobileIllustrationComponent, DsMobileImagePlaceholderComponent, DsMobileInlinePhotoComponent, DsMobileInlineTabsComponent, DsMobileInteractiveListItemBookingComponent, DsMobileInteractiveListItemInquiryComponent, DsMobileInteractiveListItemMessageComponent, DsMobileInteractiveListItemPostComponent, DsMobileLightboxImageComponent as DsMobileLightboxComponent, DsMobileLightboxFooterComponent, DsMobileLightboxHeaderComponent, DsMobileLightboxImageComponent, DsMobileLightboxImageWithDescriptionComponent, DsMobileLightboxPdfComponent, DsMobileLightboxService, DsMobileListItemComponent, DsMobileListItemStaticComponent, DsMobileListSearchComponent, DsMobileLoaderOverlayComponent, DsMobileLongPressDirective, DsMobileMediaActionsPanelComponent, DsMobileMessageBubbleComponent, DsMobileMessageComposerComponent, DsMobileModalBaseComponent, DsMobileModalService, DsMobileNewInquiryModalComponent, DsMobileNewInquiryModalService, DsMobileNotificationButtonComponent, DsMobileNotificationModalComponent, DsMobileNotificationModalService, DsMobileNotificationPromptComponent, DsMobileOfflineBannerComponent, DsMobilePageDetailsComponent, DsMobilePageMainComponent, DsMobilePillComponent, DsMobileActionsBottomSheetComponent as DsMobilePostActionsBottomSheetComponent, DsMobilePostComposerComponent, DsMobilePostCreateBottomSheetComponent, DsMobilePostDetailModalComponent, DsMobilePostDetailModalService, DsMobilePriceSheetComponent, DsMobileProfileActionsSheetComponent, DsMobilePromptBottomSheetComponent, DsMobilePropertyBannerComponent, DsMobileRichTextEditorComponent, DsMobileSectionComponent, DsMobileServiceVendorModalService, DsMobileServiceVendorSheetComponent, DsMobileSwiperComponent, DsMobileSwiperWithNavComponent, DsMobileSystemMessageBannerComponent, DsMobileTabBarComponent, DsMobileTabsComponent, DsMobileTenantPickerModalComponent, DsMobileWhenCanBookSheetComponent, DsMobileWhoCanBookSheetComponent, DsTextInputComponent, FamilyAccessPageComponent, FamilyAccessService, InquiriesService, InviteSuccessPageComponent, MediaPickerService, MobileBookingPageComponent, MobileCommunityPageComponent, MobileHandbookPageComponent, MobileHomePageComponent, MobileInquiriesPageComponent, MobileInquiryDetailPageComponent, MobileModalBase, MobilePageBase, MobilePostDetailPageComponent, MobileTabsExampleComponent, NOTIFICATION_ICON_MAP, NotificationPromptService, NotificationService, PageLoadingService, PostActionsComponent, PostAttachmentsComponent, PostContentComponent, PostCreatePageComponent, PostMediaComponent, PostPdfAttachmentComponent, PostTextComponent, PostsService, RelativeTimePipe, SAMPLE_NOTIFICATIONS, SectionHeaderComponent, ServicesPageComponent, SignInPageComponent, SignInToAcceptPageComponent, TenantChatPageComponent, TileContentComponent, TileIconComponent, TileLabelComponent, TileValueComponent, TrackingPermissionService, UserService, VENDOR_MODAL_SERVICE, WhitelabelDemoModalComponent, WhitelabelDemoModalService, WhitelabelService, customBackTransition, customPageTransition, dateBucket };
37141
38352
  //# sourceMappingURL=propbinder-mobile-design.mjs.map