@propbinder/mobile-design 0.0.1 → 0.0.21

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,11 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, computed, Directive, EventEmitter, HostListener, Output, Input, inject, PLATFORM_ID, signal, output, Component, Injectable, ViewChild, model, ElementRef, CUSTOM_ELEMENTS_SCHEMA, createComponent } from '@angular/core';
2
+ import { input, computed, Directive, EventEmitter, HostListener, Output, Input, inject, PLATFORM_ID, signal, output, Component, Injectable, ViewChild, model, ElementRef, CUSTOM_ELEMENTS_SCHEMA, createComponent, effect, ViewEncapsulation } from '@angular/core';
3
3
  import * as i1$2 from '@angular/common';
4
4
  import { isPlatformBrowser, CommonModule } from '@angular/common';
5
5
  import * as i1$1 from '@angular/router';
6
6
  import { Router } from '@angular/router';
7
7
  import * as i1 from '@ionic/angular/standalone';
8
- import { IonHeader, IonToolbar, IonTitle, IonContent, IonButtons, Platform, ModalController, IonRefresher, IonRefresherContent, IonTabs, IonTabBar, IonTabButton, IonLabel, IonTab, IonSpinner } from '@ionic/angular/standalone';
8
+ import { IonHeader, IonToolbar, IonTitle, IonContent, IonButtons, Platform, ModalController, IonRefresher, IonRefresherContent, IonTabs, IonTabBar, IonTabButton, IonLabel, IonTab, IonSpinner, IonButton } from '@ionic/angular/standalone';
9
9
  import { ImpactStyle, Haptics } from '@capacitor/haptics';
10
10
  import { DsIconButtonComponent, DsIconComponent, DsButtonComponent, DsAvatarComponent, DsShapeIndicatorComponent } from '@propbinder/design-system';
11
11
  import * as i2 from '@angular/forms';
@@ -18,6 +18,7 @@ import { Share } from '@capacitor/share';
18
18
  import Swiper from 'swiper';
19
19
  import { Filesystem, Directory } from '@capacitor/filesystem';
20
20
  import { Browser } from '@capacitor/browser';
21
+ import { createAnimation } from '@ionic/core';
21
22
 
22
23
  /**
23
24
  * MobilePageBase
@@ -4419,7 +4420,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
4419
4420
  * />
4420
4421
  * ```
4421
4422
  */
4422
- class DsMobileTabsComponent {
4423
+ let DsMobileTabsComponent$1 = class DsMobileTabsComponent {
4423
4424
  elementRef;
4424
4425
  // Inputs
4425
4426
  tabs = [];
@@ -4555,8 +4556,8 @@ class DsMobileTabsComponent {
4555
4556
  </ion-tab-bar>
4556
4557
  </ion-tabs>
4557
4558
  `, isInline: true, styles: [":host{display:block;height:100vh;height:100dvh}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}ion-tabs{height:100%;background:var(--color-brand-secondary)}.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:calc(var(--ion-safe-area-bottom, 34px) - 4px)}@media (max-width: 767px){ion-tab-bar[slot=bottom]{position:fixed;bottom:0;left:0;right:0;z-index:100}}@media (max-width: 767px){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:env(safe-area-inset-bottom,0px)}}@media (display-mode: standalone){ion-tab-bar[slot=bottom]{padding-bottom:env(safe-area-inset-bottom,0px)!important}}@media (min-width: 768px){ion-tab-bar[slot=top]{--background: var(--color-brand-secondary);position:relative;display:flex;align-items:center;padding:12px 24px;height:64px;max-width:none}.ds-tab-bar__logo{display:flex;position:absolute;left:24px;align-items:center}.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-brand-base);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-brand-base);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-brand-base);border-radius:1000px}ion-tab-button ion-label{font-size:var(--font-size-xs);font-weight:400;letter-spacing:-.3px;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:auto;flex:0 0 auto;--color: rgba(255, 255, 255, .7);--color-selected: white;background:#ffffff1a;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{--color: white;--color-selected: white}.ds-tab-button.tab-selected{background:var(--color-background-brand);--color-selected: 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:#ffffff4d;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$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.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: 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"] }] });
4558
- }
4559
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobileTabsComponent, decorators: [{
4559
+ };
4560
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobileTabsComponent$1, decorators: [{
4560
4561
  type: Component,
4561
4562
  args: [{ selector: 'ds-mobile-tabs', standalone: true, imports: [
4562
4563
  CommonModule,
@@ -8284,11 +8285,4312 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
8284
8285
 
8285
8286
  // Mobile Page Components
8286
8287
 
8288
+ /**
8289
+ * User service for managing current user data globally
8290
+ */
8291
+ class UserService {
8292
+ // User avatar configuration
8293
+ _avatarInitials = signal('LM', ...(ngDevMode ? [{ debugName: "_avatarInitials" }] : []));
8294
+ _avatarType = signal('initials', ...(ngDevMode ? [{ debugName: "_avatarType" }] : []));
8295
+ _avatarSrc = signal('', ...(ngDevMode ? [{ debugName: "_avatarSrc" }] : []));
8296
+ // Readonly computed values
8297
+ avatarInitials = this._avatarInitials.asReadonly();
8298
+ avatarType = this._avatarType.asReadonly();
8299
+ avatarSrc = this._avatarSrc.asReadonly();
8300
+ /**
8301
+ * Update avatar configuration
8302
+ */
8303
+ setAvatarInitials(initials) {
8304
+ this._avatarInitials.set(initials);
8305
+ }
8306
+ setAvatarType(type) {
8307
+ this._avatarType.set(type);
8308
+ }
8309
+ setAvatarSrc(src) {
8310
+ this._avatarSrc.set(src);
8311
+ }
8312
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: UserService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
8313
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: UserService, providedIn: 'root' });
8314
+ }
8315
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: UserService, decorators: [{
8316
+ type: Injectable,
8317
+ args: [{
8318
+ providedIn: 'root'
8319
+ }]
8320
+ }] });
8321
+
8322
+ class MobileCommunityPageComponent {
8323
+ router;
8324
+ route;
8325
+ bottomSheet;
8326
+ lightbox;
8327
+ postModal;
8328
+ userService;
8329
+ // Store user-created posts
8330
+ userPosts = signal([
8331
+ {
8332
+ id: 'user-post-1',
8333
+ authorName: 'Lars Mikkelsen',
8334
+ authorRole: 'Dig',
8335
+ timestamp: '5m siden',
8336
+ avatarType: 'initials',
8337
+ avatarInitials: 'LM',
8338
+ content: 'Dette er mit første opslag! Ser frem til at forbinde med alle i bygningen. 🏠',
8339
+ isLiked: false,
8340
+ likeCount: 3,
8341
+ commentCount: 1
8342
+ }
8343
+ ], ...(ngDevMode ? [{ debugName: "userPosts" }] : []));
8344
+ // Flag to control whether static demo posts are shown
8345
+ // Set to false to see the empty state
8346
+ showStaticPosts = signal(true, ...(ngDevMode ? [{ debugName: "showStaticPosts" }] : []));
8347
+ // Computed to check if there are any posts to display
8348
+ hasAnyPosts = computed(() => {
8349
+ return this.userPosts().length > 0 || this.showStaticPosts();
8350
+ }, ...(ngDevMode ? [{ debugName: "hasAnyPosts" }] : []));
8351
+ constructor(router, route, bottomSheet, lightbox, postModal, userService) {
8352
+ this.router = router;
8353
+ this.route = route;
8354
+ this.bottomSheet = bottomSheet;
8355
+ this.lightbox = lightbox;
8356
+ this.postModal = postModal;
8357
+ this.userService = userService;
8358
+ }
8359
+ handleRefresh(event) {
8360
+ console.log('Pull-to-refresh triggered');
8361
+ setTimeout(() => {
8362
+ console.log('Refresh complete');
8363
+ event.target.complete();
8364
+ }, 1000);
8365
+ }
8366
+ /**
8367
+ * Open post detail modal
8368
+ * This provides a better UX than route navigation:
8369
+ * - Maintains scroll position
8370
+ * - Native iOS-style modal feel
8371
+ * - Proper close button that works
8372
+ */
8373
+ async openPost(postId, focusComment = false) {
8374
+ console.log('[Community] ===== openPost called =====', postId, 'Focus comment:', focusComment);
8375
+ // Map post ID to post data (in real app, fetch from service)
8376
+ const postDataMap = {
8377
+ '1': {
8378
+ postId: '1',
8379
+ authorName: 'Anders Jensen',
8380
+ authorRole: 'Lejer',
8381
+ timestamp: '2t siden',
8382
+ avatarType: 'photo',
8383
+ avatarSrc: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop&crop=face',
8384
+ content: 'Lige flyttet ind i min nye lejlighed! Udlejeren var super hjælpsom gennem hele processen. Virkelig begejstret for at være en del af dette fællesskab! 🏠',
8385
+ isLiked: false,
8386
+ likeCount: 42,
8387
+ commentCount: 12,
8388
+ comments: [
8389
+ {
8390
+ authorName: 'Mette Larsen',
8391
+ authorRole: 'Lejer',
8392
+ timestamp: '1t siden',
8393
+ avatarInitials: 'ML',
8394
+ content: 'Velkommen til fællesskabet!',
8395
+ likeCount: 5,
8396
+ isOwnComment: false
8397
+ },
8398
+ {
8399
+ authorName: 'Lars Mikkelsen',
8400
+ authorRole: 'Dig',
8401
+ timestamp: 'Lige nu',
8402
+ avatarInitials: 'LM',
8403
+ content: 'Vxhknbh',
8404
+ likeCount: 0,
8405
+ isOwnComment: true
8406
+ }
8407
+ ]
8408
+ },
8409
+ '2': {
8410
+ postId: '2',
8411
+ authorName: 'Sophie Andersen',
8412
+ authorRole: 'Lejer',
8413
+ timestamp: '4t siden',
8414
+ avatarInitials: 'SA',
8415
+ content: 'Se denne smukke udsigt fra min altan! Morgenkaffe har aldrig smagt så godt ☕️',
8416
+ imageSrc: '/Assets/Dummy-photos/balcony-view.jpg',
8417
+ imageAlt: 'Altanudsigt',
8418
+ isLiked: true,
8419
+ likeCount: 156,
8420
+ commentCount: 34,
8421
+ comments: [
8422
+ {
8423
+ authorName: 'Anders Jensen',
8424
+ authorRole: 'Lejer',
8425
+ timestamp: '3t siden',
8426
+ avatarInitials: 'AJ',
8427
+ content: 'Wow, den udsigt er fantastisk! Hvilken etage er du på?',
8428
+ likeCount: 12,
8429
+ isOwnComment: false
8430
+ },
8431
+ {
8432
+ authorName: 'Thomas Hansen',
8433
+ authorRole: 'Lejer',
8434
+ timestamp: '3t siden',
8435
+ avatarInitials: 'TH',
8436
+ content: 'Smuk! Jeg kan også se byens silhuet fra min lejlighed 🌆',
8437
+ isLiked: true,
8438
+ likeCount: 8,
8439
+ isOwnComment: false
8440
+ },
8441
+ {
8442
+ authorName: 'Lars Mikkelsen',
8443
+ authorRole: 'Dig',
8444
+ timestamp: 'Lige nu',
8445
+ avatarInitials: 'LM',
8446
+ content: 'Vxhknbh',
8447
+ likeCount: 0,
8448
+ isOwnComment: true
8449
+ }
8450
+ ]
8451
+ },
8452
+ '3': {
8453
+ postId: '3',
8454
+ authorName: 'Thomas Hansen',
8455
+ authorRole: 'Lejer',
8456
+ timestamp: '1d siden',
8457
+ avatarType: 'photo',
8458
+ avatarSrc: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face',
8459
+ content: 'Kender nogen et fælles fitnesscenter i nærheden? Leder efter anbefalinger til gode træningscentre i området. 🏋️',
8460
+ isLiked: false,
8461
+ likeCount: 23,
8462
+ commentCount: 45,
8463
+ comments: []
8464
+ },
8465
+ '4': {
8466
+ postId: '4',
8467
+ authorName: 'Karen Nielsen',
8468
+ authorRole: 'Ejendomsadministrator',
8469
+ timestamp: '2d siden',
8470
+ avatarInitials: 'KN',
8471
+ content: '📢 Påmindelse: Bygningsvedligeholdelse planlagt til denne lørdag fra kl. 9 til 14. Vandet vil være midlertidigt lukket. Vær venlig at planlægge derefter!',
8472
+ isLiked: false,
8473
+ likeCount: 89,
8474
+ commentCount: 67,
8475
+ comments: []
8476
+ },
8477
+ '5': {
8478
+ postId: '5',
8479
+ authorName: 'Emma Petersen',
8480
+ authorRole: 'Lejer',
8481
+ timestamp: '3d siden',
8482
+ avatarType: 'photo',
8483
+ avatarSrc: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&h=100&fit=crop&crop=face',
8484
+ content: 'Arrangerer en fælles BBQ næste weekend! Alle er inviteret. Tag din yndlingsret med til at dele. Lad os lære hinanden bedre at kende! 🍔🌭',
8485
+ isLiked: true,
8486
+ likeCount: 124,
8487
+ commentCount: 89,
8488
+ comments: []
8489
+ }
8490
+ };
8491
+ const postData = postDataMap[postId];
8492
+ if (postData) {
8493
+ // Add focusComment flag to postData
8494
+ await this.postModal.open({ ...postData, focusComment });
8495
+ }
8496
+ }
8497
+ /**
8498
+ * Open user-created post detail modal
8499
+ */
8500
+ async openUserPost(index, focusComment = false) {
8501
+ const posts = this.userPosts();
8502
+ const post = posts[index];
8503
+ if (post) {
8504
+ console.log('[Community] Opening user post modal:', index, 'Focus comment:', focusComment);
8505
+ // Convert user post to modal format with empty comments array
8506
+ const postData = {
8507
+ ...post,
8508
+ postId: `user-${index}`,
8509
+ comments: [] // User posts don't have comments yet
8510
+ };
8511
+ await this.postModal.open({ ...postData, focusComment });
8512
+ }
8513
+ }
8514
+ async openPostCreator() {
8515
+ // Open the post creator as a bottom sheet modal
8516
+ // Using 95% initial height to maximize keyboard auto-open chances
8517
+ const sheet = await this.bottomSheet.create({
8518
+ component: DsMobilePostCreateBottomSheetComponent,
8519
+ componentProps: {
8520
+ // This helps the component know it should auto-focus
8521
+ autoFocus: true
8522
+ },
8523
+ breakpoints: [0, 0.95, 1],
8524
+ initialBreakpoint: 0.95,
8525
+ handle: true
8526
+ });
8527
+ // Handle the result when the sheet is dismissed
8528
+ const result = await sheet.onWillDismiss();
8529
+ if (result.role === 'post' && result.data) {
8530
+ console.log('New post created:', result.data);
8531
+ // Create a new post object
8532
+ const newPost = {
8533
+ id: `user-post-${Date.now()}`, // Generate unique ID
8534
+ authorName: 'Lars Mikkelsen', // Current user name
8535
+ authorRole: 'Dig',
8536
+ timestamp: 'Lige nu',
8537
+ avatarType: this.userService.avatarType(),
8538
+ avatarSrc: this.userService.avatarSrc(),
8539
+ avatarInitials: this.userService.avatarInitials(),
8540
+ content: result.data.content,
8541
+ imageSrc: result.data.images && result.data.images.length > 0 ? result.data.images[0] : undefined,
8542
+ imageAlt: result.data.images && result.data.images.length > 0 ? 'Slået billede op' : undefined,
8543
+ isLiked: false,
8544
+ likeCount: 0,
8545
+ commentCount: 0
8546
+ };
8547
+ // Add to the beginning of the posts array
8548
+ this.userPosts.update(posts => [newPost, ...posts]);
8549
+ }
8550
+ }
8551
+ /**
8552
+ * Open an image in the lightbox viewer
8553
+ * Prevents the post click event from firing
8554
+ */
8555
+ async openImageLightbox(imageSrc, title, description, event) {
8556
+ console.log('[Community] Opening lightbox for image:', imageSrc);
8557
+ // Prevent the post card click event from firing
8558
+ event.stopPropagation();
8559
+ const authorMeta = {
8560
+ name: 'Sophie Andersen',
8561
+ role: 'Lejer',
8562
+ avatarInitials: 'SA',
8563
+ timestamp: '4t siden'
8564
+ };
8565
+ // Open the lightbox with the image
8566
+ await this.lightbox.open({
8567
+ images: [
8568
+ {
8569
+ type: 'image',
8570
+ src: imageSrc,
8571
+ alt: title,
8572
+ title: title,
8573
+ description: description,
8574
+ isLiked: true,
8575
+ likeCount: 156,
8576
+ commentCount: 34
8577
+ }
8578
+ ],
8579
+ author: authorMeta,
8580
+ enableZoom: true,
8581
+ showControls: false, // Single image, no need for controls
8582
+ showInfo: true
8583
+ });
8584
+ }
8585
+ async openHouseRulesPdf() {
8586
+ console.log('[Community] Opening House Rules PDF');
8587
+ // Author metadata
8588
+ const authorMeta = {
8589
+ name: 'Karen Nielsen',
8590
+ role: 'Ejendomsadministrator',
8591
+ avatarInitials: 'KN',
8592
+ timestamp: '2d siden'
8593
+ };
8594
+ // Open the PDF lightbox
8595
+ // Use absolute path for production deployment (Vercel, etc.)
8596
+ await this.lightbox.openPdf({
8597
+ pdf: {
8598
+ type: 'pdf',
8599
+ src: '/Assets/House_Rules.pdf', // Capital A to match public/Assets folder structure
8600
+ title: 'House Rules',
8601
+ description: 'Building regulations and community guidelines',
8602
+ fileSize: 250880, // 245 KB in bytes
8603
+ pageCount: 8
8604
+ },
8605
+ author: authorMeta
8606
+ });
8607
+ }
8608
+ /**
8609
+ * Handle long press on a post to show action sheet
8610
+ */
8611
+ async handlePostLongPress(postIdOrIndex, isOwnPost) {
8612
+ console.log('[Community] Post long pressed:', postIdOrIndex, 'isOwn:', isOwnPost);
8613
+ const sheet = await this.bottomSheet.create({
8614
+ component: DsMobileActionsBottomSheetComponent,
8615
+ componentProps: {
8616
+ isOwnContent: isOwnPost
8617
+ },
8618
+ breakpoints: [0, 1],
8619
+ initialBreakpoint: 1,
8620
+ handle: true,
8621
+ backdropDismiss: true,
8622
+ cssClass: 'auto-height'
8623
+ });
8624
+ const result = await sheet.onWillDismiss();
8625
+ if (result.role === 'select' && result.data) {
8626
+ const action = result.data.action;
8627
+ switch (action) {
8628
+ case 'edit':
8629
+ console.log('Edit post:', postIdOrIndex);
8630
+ // Open the post create bottom sheet in edit mode
8631
+ let postContent = '';
8632
+ let postId = '';
8633
+ // Get post content based on postIdOrIndex
8634
+ if (typeof postIdOrIndex === 'number') {
8635
+ const post = this.userPosts()[postIdOrIndex];
8636
+ if (post) {
8637
+ postContent = post.content || '';
8638
+ postId = post.id || postIdOrIndex.toString();
8639
+ }
8640
+ }
8641
+ else {
8642
+ // For static posts, we'll need to determine content
8643
+ // For now, use a placeholder
8644
+ postId = postIdOrIndex.toString();
8645
+ postContent = 'Edit this post...';
8646
+ }
8647
+ // Open the bottom sheet in edit mode
8648
+ const editSheet = await this.bottomSheet.create({
8649
+ component: DsMobilePostCreateBottomSheetComponent,
8650
+ componentProps: {
8651
+ autoFocus: true,
8652
+ isEditMode: true,
8653
+ postId: postId,
8654
+ initialContent: postContent
8655
+ },
8656
+ breakpoints: [0, 0.95, 1],
8657
+ initialBreakpoint: 0.95,
8658
+ handle: true,
8659
+ backdropBlur: true,
8660
+ backdropOpacity: 0.6
8661
+ });
8662
+ // Handle the result when the sheet is dismissed
8663
+ const editResult = await editSheet.onWillDismiss();
8664
+ if (editResult.role === 'post' && editResult.data) {
8665
+ console.log('Post updated:', editResult.data);
8666
+ // Update the post in the array
8667
+ if (typeof postIdOrIndex === 'number') {
8668
+ const currentPosts = this.userPosts();
8669
+ const updatedPosts = currentPosts.map((post, index) => index === postIdOrIndex
8670
+ ? { ...post, content: editResult.data.content, timestamp: 'Just now' }
8671
+ : post);
8672
+ this.userPosts.set(updatedPosts);
8673
+ }
8674
+ }
8675
+ break;
8676
+ case 'delete':
8677
+ console.log('Delete post:', postIdOrIndex);
8678
+ if (confirm('Er du sikker på, at du vil slette dette opslag?')) {
8679
+ // If it's a user post (number index), remove it from the array
8680
+ if (typeof postIdOrIndex === 'number') {
8681
+ const currentPosts = this.userPosts();
8682
+ const updatedPosts = currentPosts.filter((_, index) => index !== postIdOrIndex);
8683
+ this.userPosts.set(updatedPosts);
8684
+ }
8685
+ // Otherwise it's a static post, just show confirmation
8686
+ else {
8687
+ alert('Opslag slettet!');
8688
+ }
8689
+ }
8690
+ break;
8691
+ case 'like':
8692
+ console.log('Like post:', postIdOrIndex);
8693
+ // Toggle like - in a real app, this would call an API
8694
+ alert('Opslag liket!');
8695
+ break;
8696
+ case 'reply':
8697
+ console.log('Reply to post:', postIdOrIndex);
8698
+ // Open the post detail modal with comment input focused
8699
+ if (typeof postIdOrIndex === 'number') {
8700
+ await this.openUserPost(postIdOrIndex, true);
8701
+ }
8702
+ else {
8703
+ await this.openPost(postIdOrIndex, true);
8704
+ }
8705
+ break;
8706
+ }
8707
+ }
8708
+ }
8709
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileCommunityPageComponent, deps: [{ token: i1$1.Router }, { token: i1$1.ActivatedRoute }, { token: DsMobileBottomSheetService }, { token: DsMobileLightboxService }, { token: DsMobilePostDetailModalService }, { token: UserService }], target: i0.ɵɵFactoryTarget.Component });
8710
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: MobileCommunityPageComponent, isStandalone: true, selector: "app-mobile-community-page", ngImport: i0, template: `
8711
+ <ds-mobile-page-main
8712
+ title="Fællesskab"
8713
+ [avatarInitials]="userService.avatarInitials()"
8714
+ [avatarType]="userService.avatarType()"
8715
+ (refresh)="handleRefresh($event)">
8716
+
8717
+ <!-- Post Composer in header-expandable -->
8718
+ <ds-mobile-post-composer
8719
+ header-content
8720
+ [avatarInitials]="userService.avatarInitials()"
8721
+ [avatarType]="userService.avatarType()"
8722
+ [avatarSrc]="userService.avatarSrc()"
8723
+ (composerClick)="openPostCreator()"
8724
+ />
8725
+
8726
+ <ds-mobile-content>
8727
+ <div class="post-feed">
8728
+ <!-- Pinned Posts Section -->
8729
+ <div class="pinned-posts-section">
8730
+ <h2 class="section-headline">
8731
+ <ds-icon name="remixPushpinFill" size="16px" color="primary" />
8732
+ Fastgjorte opslag
8733
+ </h2>
8734
+
8735
+ <!-- Pinned: Maintenance Announcement -->
8736
+ <ds-mobile-interactive-list-item-post
8737
+ [authorName]="'Karen Nielsen'"
8738
+ [authorRole]="'Ejendomsadministrator'"
8739
+ [timestamp]="'2d siden'"
8740
+ [avatarInitials]="'KN'"
8741
+ [showBadge]="true"
8742
+ [clickable]="true"
8743
+ (postClick)="openPost('4')"
8744
+ (commentClick)="openPost('4', true)"
8745
+ (longPress)="handlePostLongPress('4', false)">
8746
+
8747
+ <post-content>
8748
+ <post-text>📢 Påmindelse: Bygningsvedligeholdelse planlagt til denne lørdag fra kl. 9 til 14. Vandet vil være midlertidigt lukket. Vær venlig at planlægge derefter!</post-text>
8749
+
8750
+ <post-attachments>
8751
+ <post-pdf-attachment
8752
+ [fileName]="'Husregler.pdf'"
8753
+ [fileSize]="'245 KB'"
8754
+ (pdfClick)="openHouseRulesPdf()">
8755
+ </post-pdf-attachment>
8756
+ </post-attachments>
8757
+ </post-content>
8758
+
8759
+ <post-actions>
8760
+ <action-like [count]="89" />
8761
+ <action-comment [count]="67" (commentClick)="openPost('4', true)" />
8762
+ </post-actions>
8763
+ </ds-mobile-interactive-list-item-post>
8764
+ </div>
8765
+
8766
+ <!-- All Posts Section -->
8767
+ <h2 class="section-headline">Alle opslag</h2>
8768
+
8769
+ @if (hasAnyPosts()) {
8770
+ <div class="post-list-wrapper">
8771
+ <!-- User Created Posts -->
8772
+ @for (post of userPosts(); track $index) {
8773
+ <ds-mobile-interactive-list-item-post
8774
+ [authorName]="post.authorName"
8775
+ [authorRole]="post.authorRole"
8776
+ [timestamp]="post.timestamp"
8777
+ [avatarType]="post.avatarType"
8778
+ [avatarSrc]="post.avatarSrc"
8779
+ [avatarInitials]="post.avatarInitials"
8780
+ [clickable]="true"
8781
+ (postClick)="openUserPost($index)"
8782
+ (commentClick)="openUserPost($index, true)"
8783
+ (longPress)="handlePostLongPress($index, post.authorRole === 'Dig')">
8784
+
8785
+ <post-content>
8786
+ @if (post.content) {
8787
+ <post-text>{{ post.content }}</post-text>
8788
+ }
8789
+ @if (post.imageSrc) {
8790
+ <post-media>
8791
+ <img
8792
+ [src]="post.imageSrc"
8793
+ [alt]="post.imageAlt || 'Posted image'"
8794
+ class="clickable-image"
8795
+ (click)="openImageLightbox(post.imageSrc, post.imageAlt || 'Posted image', post.content, $event)"
8796
+ />
8797
+ </post-media>
8798
+ }
8799
+ </post-content>
8800
+
8801
+ <post-actions>
8802
+ <action-like [count]="post.likeCount" [active]="post.isLiked" />
8803
+ <action-comment [count]="post.commentCount" (commentClick)="openUserPost($index, true)" />
8804
+ </post-actions>
8805
+ </ds-mobile-interactive-list-item-post>
8806
+ }
8807
+
8808
+ <!-- Post 1: Text only -->
8809
+ <ds-mobile-interactive-list-item-post
8810
+ [authorName]="'Anders Jensen'"
8811
+ [authorRole]="'Lejer'"
8812
+ [timestamp]="'2t siden'"
8813
+ [avatarType]="'photo'"
8814
+ [avatarSrc]="'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop&crop=face'"
8815
+ [clickable]="true"
8816
+ (postClick)="openPost('1')"
8817
+ (commentClick)="openPost('1', true)"
8818
+ (longPress)="handlePostLongPress('1', false)">
8819
+
8820
+ <post-content>
8821
+ <post-text>Lige flyttet ind i min nye lejlighed! Udlejeren var super hjælpsom gennem hele processen. Virkelig begejstret for at være en del af dette fællesskab! 🏠</post-text>
8822
+ </post-content>
8823
+
8824
+ <post-actions>
8825
+ <action-like [count]="42" />
8826
+ <action-comment [count]="12" (commentClick)="openPost('1', true)" />
8827
+ </post-actions>
8828
+ </ds-mobile-interactive-list-item-post>
8829
+
8830
+ <!-- Post 2: With multiple images (grid layout) -->
8831
+ <ds-mobile-interactive-list-item-post
8832
+ [authorName]="'Sophie Andersen'"
8833
+ [authorRole]="'Lejer'"
8834
+ [timestamp]="'4t siden'"
8835
+ [avatarInitials]="'SA'"
8836
+ [clickable]="true"
8837
+ (postClick)="openPost('2')"
8838
+ (commentClick)="openPost('2', true)"
8839
+ (longPress)="handlePostLongPress('2', false)">
8840
+
8841
+ <post-content>
8842
+ <post-text>Jeg har taget nogle billeder af vores smukke ejendom i løbet af det sidste par måneder. Fra altanudsigten om morgenen til den nye trappe og fællesområderne. Elsker virkelig at bo her! 🏡✨</post-text>
8843
+ <ds-mobile-inline-photo
8844
+ [images]="[
8845
+ '/Assets/Dummy-photos/balcony-view.jpg',
8846
+ '/Assets/Dummy-photos/staircase.jpg',
8847
+ '/Assets/Dummy-photos/park.jpg',
8848
+ '/Assets/Dummy-photos/yard.jpg'
8849
+ ]"
8850
+ [author]="{
8851
+ name: 'Sophie Andersen',
8852
+ role: 'Lejer',
8853
+ avatarInitials: 'SA',
8854
+ avatarType: 'initials',
8855
+ timestamp: '4t siden'
8856
+ }"
8857
+ />
8858
+ </post-content>
8859
+
8860
+ <post-actions>
8861
+ <action-like [active]="true" [count]="156" />
8862
+ <action-comment [count]="34" (commentClick)="openPost('2', true)" />
8863
+ </post-actions>
8864
+ </ds-mobile-interactive-list-item-post>
8865
+
8866
+ <!-- Post 3: Question -->
8867
+ <ds-mobile-interactive-list-item-post
8868
+ [authorName]="'Thomas Hansen'"
8869
+ [authorRole]="'Lejer'"
8870
+ [timestamp]="'1d siden'"
8871
+ [avatarType]="'photo'"
8872
+ [avatarSrc]="'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face'"
8873
+ [clickable]="true"
8874
+ (postClick)="openPost('3')"
8875
+ (commentClick)="openPost('3', true)"
8876
+ (longPress)="handlePostLongPress('3', false)">
8877
+
8878
+ <post-content>
8879
+ <post-text>Kender nogen et fælles fitnesscenter i nærheden? Leder efter anbefalinger til gode træningscentre i området. 🏋️</post-text>
8880
+ </post-content>
8881
+
8882
+ <post-actions>
8883
+ <action-like [count]="23" />
8884
+ <action-comment [count]="45" (commentClick)="openPost('3', true)" />
8885
+ </post-actions>
8886
+ </ds-mobile-interactive-list-item-post>
8887
+
8888
+ <!-- Post 3.5: Property Manager showcase with 6+ photos -->
8889
+ <ds-mobile-interactive-list-item-post
8890
+ [authorName]="'Karen Nielsen'"
8891
+ [authorRole]="'Ejendomsadministrator'"
8892
+ [timestamp]="'2d siden'"
8893
+ [avatarInitials]="'KN'"
8894
+ [showBadge]="true"
8895
+ [clickable]="true"
8896
+ (postClick)="openPost('3.5')"
8897
+ (commentClick)="openPost('3.5', true)"
8898
+ (longPress)="handlePostLongPress('3.5', false)">
8899
+
8900
+ <post-content>
8901
+ <post-text>Her er et kig på vores nyligt renoverede fællesområder! Vi har opgraderet postkasserne, trappeafsatsen og gårdområdet. Der er også blevet ansat en ny vicevært. Glæd dig over forbedringerne! 🏢✨</post-text>
8902
+ <ds-mobile-inline-photo
8903
+ [images]="[
8904
+ '/Assets/Dummy-photos/mailboxes.jpg',
8905
+ '/Assets/Dummy-photos/staircase.jpg',
8906
+ '/Assets/Dummy-photos/yard.jpg',
8907
+ '/Assets/Dummy-photos/park.jpg',
8908
+ '/Assets/Dummy-photos/balcony-view.jpg',
8909
+ '/Assets/Dummy-photos/handyman.jpg'
8910
+ ]"
8911
+ [author]="{
8912
+ name: 'Karen Nielsen',
8913
+ role: 'Ejendomsadministrator',
8914
+ avatarInitials: 'KN',
8915
+ avatarType: 'initials',
8916
+ timestamp: '2d siden'
8917
+ }"
8918
+ [maxVisible]="5"
8919
+ />
8920
+ </post-content>
8921
+
8922
+ <post-actions>
8923
+ <action-like [count]="234" />
8924
+ <action-comment [count]="89" (commentClick)="openPost('3.5', true)" />
8925
+ </post-actions>
8926
+ </ds-mobile-interactive-list-item-post>
8927
+
8928
+ <!-- Post 5: Event -->
8929
+ <ds-mobile-interactive-list-item-post
8930
+ [authorName]="'Emma Petersen'"
8931
+ [authorRole]="'Lejer'"
8932
+ [timestamp]="'3d siden'"
8933
+ [avatarType]="'photo'"
8934
+ [avatarSrc]="'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&h=100&fit=crop&crop=face'"
8935
+ [clickable]="true"
8936
+ (postClick)="openPost('5')"
8937
+ (commentClick)="openPost('5', true)"
8938
+ (longPress)="handlePostLongPress('5', false)">
8939
+
8940
+ <post-content>
8941
+ <post-text>Arrangerer en fælles BBQ næste weekend! Alle er inviteret. Tag din yndlingsret med til at dele. Lad os lære hinanden bedre at kende! 🍔🌭</post-text>
8942
+ </post-content>
8943
+
8944
+ <post-actions>
8945
+ <action-like [active]="true" [count]="124" />
8946
+ <action-comment [count]="89" (commentClick)="openPost('5', true)" />
8947
+ </post-actions>
8948
+ </ds-mobile-interactive-list-item-post>
8949
+ </div>
8950
+ } @else {
8951
+ <!-- Empty State -->
8952
+ <div class="community-empty-state">
8953
+ <img
8954
+ src="/Assets/Empty state-chat.png"
8955
+ alt="Ingen opslag endnu"
8956
+ class="empty-state-image"
8957
+ />
8958
+ <h3 class="empty-state-title">Ingen opslag endnu</h3>
8959
+ <p class="empty-state-description">Vær den første til at dele noget med dit fællesskab</p>
8960
+ </div>
8961
+ }
8962
+ </div>
8963
+ </ds-mobile-content>
8964
+ </ds-mobile-page-main>
8965
+ `, isInline: true, styles: [".post-feed{display:flex;flex-direction:column;max-width:640px}.post-list-wrapper{display:flex;flex-direction:column}.pinned-posts-section{margin:-12px -12px 12px;padding:0 12px 12px;box-shadow:var(--box-shadow-sm);border-radius:16px;border:1px solid var(--border-color-default)}.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}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance"], outputs: ["avatarClick", "refresh", "scroll"] }, { kind: "component", type: DsMobileContentComponent, selector: "ds-mobile-content", inputs: ["layout"] }, { kind: "component", type: DsMobileInteractiveListItemPostComponent, selector: "ds-mobile-interactive-list-item-post", inputs: ["authorName", "authorRole", "timestamp", "avatarInitials", "avatarType", "avatarSrc", "avatarIconName", "showBadge", "variant", "clickable"], 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: 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: PostPdfAttachmentComponent, selector: "post-pdf-attachment", inputs: ["fileName", "fileSize"], outputs: ["pdfClick"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsMobileInlinePhotoComponent, selector: "ds-mobile-inline-photo", inputs: ["images", "author", "maxVisible"], outputs: ["photoClick"] }] });
8966
+ }
8967
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileCommunityPageComponent, decorators: [{
8968
+ type: Component,
8969
+ args: [{ selector: 'app-mobile-community-page', standalone: true, imports: [
8970
+ DsMobilePageMainComponent,
8971
+ DsMobileContentComponent,
8972
+ DsMobileInteractiveListItemPostComponent,
8973
+ DsMobilePostComposerComponent,
8974
+ PostContentComponent,
8975
+ PostTextComponent,
8976
+ PostMediaComponent,
8977
+ PostAttachmentsComponent,
8978
+ PostActionsComponent,
8979
+ ActionLikeComponent,
8980
+ ActionCommentComponent,
8981
+ PostPdfAttachmentComponent,
8982
+ DsIconComponent,
8983
+ DsMobileInlinePhotoComponent
8984
+ ], template: `
8985
+ <ds-mobile-page-main
8986
+ title="Fællesskab"
8987
+ [avatarInitials]="userService.avatarInitials()"
8988
+ [avatarType]="userService.avatarType()"
8989
+ (refresh)="handleRefresh($event)">
8990
+
8991
+ <!-- Post Composer in header-expandable -->
8992
+ <ds-mobile-post-composer
8993
+ header-content
8994
+ [avatarInitials]="userService.avatarInitials()"
8995
+ [avatarType]="userService.avatarType()"
8996
+ [avatarSrc]="userService.avatarSrc()"
8997
+ (composerClick)="openPostCreator()"
8998
+ />
8999
+
9000
+ <ds-mobile-content>
9001
+ <div class="post-feed">
9002
+ <!-- Pinned Posts Section -->
9003
+ <div class="pinned-posts-section">
9004
+ <h2 class="section-headline">
9005
+ <ds-icon name="remixPushpinFill" size="16px" color="primary" />
9006
+ Fastgjorte opslag
9007
+ </h2>
9008
+
9009
+ <!-- Pinned: Maintenance Announcement -->
9010
+ <ds-mobile-interactive-list-item-post
9011
+ [authorName]="'Karen Nielsen'"
9012
+ [authorRole]="'Ejendomsadministrator'"
9013
+ [timestamp]="'2d siden'"
9014
+ [avatarInitials]="'KN'"
9015
+ [showBadge]="true"
9016
+ [clickable]="true"
9017
+ (postClick)="openPost('4')"
9018
+ (commentClick)="openPost('4', true)"
9019
+ (longPress)="handlePostLongPress('4', false)">
9020
+
9021
+ <post-content>
9022
+ <post-text>📢 Påmindelse: Bygningsvedligeholdelse planlagt til denne lørdag fra kl. 9 til 14. Vandet vil være midlertidigt lukket. Vær venlig at planlægge derefter!</post-text>
9023
+
9024
+ <post-attachments>
9025
+ <post-pdf-attachment
9026
+ [fileName]="'Husregler.pdf'"
9027
+ [fileSize]="'245 KB'"
9028
+ (pdfClick)="openHouseRulesPdf()">
9029
+ </post-pdf-attachment>
9030
+ </post-attachments>
9031
+ </post-content>
9032
+
9033
+ <post-actions>
9034
+ <action-like [count]="89" />
9035
+ <action-comment [count]="67" (commentClick)="openPost('4', true)" />
9036
+ </post-actions>
9037
+ </ds-mobile-interactive-list-item-post>
9038
+ </div>
9039
+
9040
+ <!-- All Posts Section -->
9041
+ <h2 class="section-headline">Alle opslag</h2>
9042
+
9043
+ @if (hasAnyPosts()) {
9044
+ <div class="post-list-wrapper">
9045
+ <!-- User Created Posts -->
9046
+ @for (post of userPosts(); track $index) {
9047
+ <ds-mobile-interactive-list-item-post
9048
+ [authorName]="post.authorName"
9049
+ [authorRole]="post.authorRole"
9050
+ [timestamp]="post.timestamp"
9051
+ [avatarType]="post.avatarType"
9052
+ [avatarSrc]="post.avatarSrc"
9053
+ [avatarInitials]="post.avatarInitials"
9054
+ [clickable]="true"
9055
+ (postClick)="openUserPost($index)"
9056
+ (commentClick)="openUserPost($index, true)"
9057
+ (longPress)="handlePostLongPress($index, post.authorRole === 'Dig')">
9058
+
9059
+ <post-content>
9060
+ @if (post.content) {
9061
+ <post-text>{{ post.content }}</post-text>
9062
+ }
9063
+ @if (post.imageSrc) {
9064
+ <post-media>
9065
+ <img
9066
+ [src]="post.imageSrc"
9067
+ [alt]="post.imageAlt || 'Posted image'"
9068
+ class="clickable-image"
9069
+ (click)="openImageLightbox(post.imageSrc, post.imageAlt || 'Posted image', post.content, $event)"
9070
+ />
9071
+ </post-media>
9072
+ }
9073
+ </post-content>
9074
+
9075
+ <post-actions>
9076
+ <action-like [count]="post.likeCount" [active]="post.isLiked" />
9077
+ <action-comment [count]="post.commentCount" (commentClick)="openUserPost($index, true)" />
9078
+ </post-actions>
9079
+ </ds-mobile-interactive-list-item-post>
9080
+ }
9081
+
9082
+ <!-- Post 1: Text only -->
9083
+ <ds-mobile-interactive-list-item-post
9084
+ [authorName]="'Anders Jensen'"
9085
+ [authorRole]="'Lejer'"
9086
+ [timestamp]="'2t siden'"
9087
+ [avatarType]="'photo'"
9088
+ [avatarSrc]="'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop&crop=face'"
9089
+ [clickable]="true"
9090
+ (postClick)="openPost('1')"
9091
+ (commentClick)="openPost('1', true)"
9092
+ (longPress)="handlePostLongPress('1', false)">
9093
+
9094
+ <post-content>
9095
+ <post-text>Lige flyttet ind i min nye lejlighed! Udlejeren var super hjælpsom gennem hele processen. Virkelig begejstret for at være en del af dette fællesskab! 🏠</post-text>
9096
+ </post-content>
9097
+
9098
+ <post-actions>
9099
+ <action-like [count]="42" />
9100
+ <action-comment [count]="12" (commentClick)="openPost('1', true)" />
9101
+ </post-actions>
9102
+ </ds-mobile-interactive-list-item-post>
9103
+
9104
+ <!-- Post 2: With multiple images (grid layout) -->
9105
+ <ds-mobile-interactive-list-item-post
9106
+ [authorName]="'Sophie Andersen'"
9107
+ [authorRole]="'Lejer'"
9108
+ [timestamp]="'4t siden'"
9109
+ [avatarInitials]="'SA'"
9110
+ [clickable]="true"
9111
+ (postClick)="openPost('2')"
9112
+ (commentClick)="openPost('2', true)"
9113
+ (longPress)="handlePostLongPress('2', false)">
9114
+
9115
+ <post-content>
9116
+ <post-text>Jeg har taget nogle billeder af vores smukke ejendom i løbet af det sidste par måneder. Fra altanudsigten om morgenen til den nye trappe og fællesområderne. Elsker virkelig at bo her! 🏡✨</post-text>
9117
+ <ds-mobile-inline-photo
9118
+ [images]="[
9119
+ '/Assets/Dummy-photos/balcony-view.jpg',
9120
+ '/Assets/Dummy-photos/staircase.jpg',
9121
+ '/Assets/Dummy-photos/park.jpg',
9122
+ '/Assets/Dummy-photos/yard.jpg'
9123
+ ]"
9124
+ [author]="{
9125
+ name: 'Sophie Andersen',
9126
+ role: 'Lejer',
9127
+ avatarInitials: 'SA',
9128
+ avatarType: 'initials',
9129
+ timestamp: '4t siden'
9130
+ }"
9131
+ />
9132
+ </post-content>
9133
+
9134
+ <post-actions>
9135
+ <action-like [active]="true" [count]="156" />
9136
+ <action-comment [count]="34" (commentClick)="openPost('2', true)" />
9137
+ </post-actions>
9138
+ </ds-mobile-interactive-list-item-post>
9139
+
9140
+ <!-- Post 3: Question -->
9141
+ <ds-mobile-interactive-list-item-post
9142
+ [authorName]="'Thomas Hansen'"
9143
+ [authorRole]="'Lejer'"
9144
+ [timestamp]="'1d siden'"
9145
+ [avatarType]="'photo'"
9146
+ [avatarSrc]="'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face'"
9147
+ [clickable]="true"
9148
+ (postClick)="openPost('3')"
9149
+ (commentClick)="openPost('3', true)"
9150
+ (longPress)="handlePostLongPress('3', false)">
9151
+
9152
+ <post-content>
9153
+ <post-text>Kender nogen et fælles fitnesscenter i nærheden? Leder efter anbefalinger til gode træningscentre i området. 🏋️</post-text>
9154
+ </post-content>
9155
+
9156
+ <post-actions>
9157
+ <action-like [count]="23" />
9158
+ <action-comment [count]="45" (commentClick)="openPost('3', true)" />
9159
+ </post-actions>
9160
+ </ds-mobile-interactive-list-item-post>
9161
+
9162
+ <!-- Post 3.5: Property Manager showcase with 6+ photos -->
9163
+ <ds-mobile-interactive-list-item-post
9164
+ [authorName]="'Karen Nielsen'"
9165
+ [authorRole]="'Ejendomsadministrator'"
9166
+ [timestamp]="'2d siden'"
9167
+ [avatarInitials]="'KN'"
9168
+ [showBadge]="true"
9169
+ [clickable]="true"
9170
+ (postClick)="openPost('3.5')"
9171
+ (commentClick)="openPost('3.5', true)"
9172
+ (longPress)="handlePostLongPress('3.5', false)">
9173
+
9174
+ <post-content>
9175
+ <post-text>Her er et kig på vores nyligt renoverede fællesområder! Vi har opgraderet postkasserne, trappeafsatsen og gårdområdet. Der er også blevet ansat en ny vicevært. Glæd dig over forbedringerne! 🏢✨</post-text>
9176
+ <ds-mobile-inline-photo
9177
+ [images]="[
9178
+ '/Assets/Dummy-photos/mailboxes.jpg',
9179
+ '/Assets/Dummy-photos/staircase.jpg',
9180
+ '/Assets/Dummy-photos/yard.jpg',
9181
+ '/Assets/Dummy-photos/park.jpg',
9182
+ '/Assets/Dummy-photos/balcony-view.jpg',
9183
+ '/Assets/Dummy-photos/handyman.jpg'
9184
+ ]"
9185
+ [author]="{
9186
+ name: 'Karen Nielsen',
9187
+ role: 'Ejendomsadministrator',
9188
+ avatarInitials: 'KN',
9189
+ avatarType: 'initials',
9190
+ timestamp: '2d siden'
9191
+ }"
9192
+ [maxVisible]="5"
9193
+ />
9194
+ </post-content>
9195
+
9196
+ <post-actions>
9197
+ <action-like [count]="234" />
9198
+ <action-comment [count]="89" (commentClick)="openPost('3.5', true)" />
9199
+ </post-actions>
9200
+ </ds-mobile-interactive-list-item-post>
9201
+
9202
+ <!-- Post 5: Event -->
9203
+ <ds-mobile-interactive-list-item-post
9204
+ [authorName]="'Emma Petersen'"
9205
+ [authorRole]="'Lejer'"
9206
+ [timestamp]="'3d siden'"
9207
+ [avatarType]="'photo'"
9208
+ [avatarSrc]="'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&h=100&fit=crop&crop=face'"
9209
+ [clickable]="true"
9210
+ (postClick)="openPost('5')"
9211
+ (commentClick)="openPost('5', true)"
9212
+ (longPress)="handlePostLongPress('5', false)">
9213
+
9214
+ <post-content>
9215
+ <post-text>Arrangerer en fælles BBQ næste weekend! Alle er inviteret. Tag din yndlingsret med til at dele. Lad os lære hinanden bedre at kende! 🍔🌭</post-text>
9216
+ </post-content>
9217
+
9218
+ <post-actions>
9219
+ <action-like [active]="true" [count]="124" />
9220
+ <action-comment [count]="89" (commentClick)="openPost('5', true)" />
9221
+ </post-actions>
9222
+ </ds-mobile-interactive-list-item-post>
9223
+ </div>
9224
+ } @else {
9225
+ <!-- Empty State -->
9226
+ <div class="community-empty-state">
9227
+ <img
9228
+ src="/Assets/Empty state-chat.png"
9229
+ alt="Ingen opslag endnu"
9230
+ class="empty-state-image"
9231
+ />
9232
+ <h3 class="empty-state-title">Ingen opslag endnu</h3>
9233
+ <p class="empty-state-description">Vær den første til at dele noget med dit fællesskab</p>
9234
+ </div>
9235
+ }
9236
+ </div>
9237
+ </ds-mobile-content>
9238
+ </ds-mobile-page-main>
9239
+ `, styles: [".post-feed{display:flex;flex-direction:column;max-width:640px}.post-list-wrapper{display:flex;flex-direction:column}.pinned-posts-section{margin:-12px -12px 12px;padding:0 12px 12px;box-shadow:var(--box-shadow-sm);border-radius:16px;border:1px solid var(--border-color-default)}.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}\n"] }]
9240
+ }], ctorParameters: () => [{ type: i1$1.Router }, { type: i1$1.ActivatedRoute }, { type: DsMobileBottomSheetService }, { type: DsMobileLightboxService }, { type: DsMobilePostDetailModalService }, { type: UserService }] });
9241
+
9242
+ class MobileHandbookPageComponent {
9243
+ userService;
9244
+ // Utilities folder data
9245
+ utilitiesItems = [
9246
+ {
9247
+ title: 'El',
9248
+ description: 'Hovedeltavle placeret i kælderrum B-12. Nødafbryderknap er ved hovedindgangen. Alle kredsløb er mærket.',
9249
+ contacts: [
9250
+ { name: 'ElektroTek ApS', initials: 'E', contactPerson: 'Lars Nielsen', phoneNumber: '+45 23 45 67 89' }
9251
+ ]
9252
+ },
9253
+ {
9254
+ title: 'Elektrisk diagram',
9255
+ description: 'Komplet diagram over bygningens elektriske installation med alle kredsløb og afbrydere.',
9256
+ attachments: [
9257
+ { name: 'Elektrisk_Diagram.pdf', type: 'pdf' }
9258
+ ]
9259
+ },
9260
+ {
9261
+ title: 'Vandforsyning',
9262
+ description: 'Hovedvandhane er placeret i kælderens tekniske rum. Individuelle lejlighedsafspærringer er i gangpanelerne. Vandtryk overvåges automatisk.',
9263
+ contacts: [
9264
+ { name: 'VVS Hansen', initials: 'V', contactPerson: 'Peter Hansen', phoneNumber: '+45 34 56 78 90' }
9265
+ ]
9266
+ },
9267
+ {
9268
+ title: 'Varmesystem',
9269
+ description: 'Fjernvarmetilslutning i kælder. Termostater i hver enhed kan justeres individuelt. Systemet vedligeholdes kvartalsvis af certificerede teknikere.',
9270
+ contacts: [
9271
+ { name: 'Varme Service A/S', initials: 'V', contactPerson: 'Maria Jensen', phoneNumber: '+45 45 67 89 01' }
9272
+ ]
9273
+ },
9274
+ {
9275
+ title: 'Varmeanlæg dokumentation',
9276
+ description: 'Teknisk dokumentation og vedligeholdelseshistorik for bygningens varmesystem.',
9277
+ attachments: [
9278
+ { name: 'Varmeplan.pdf', type: 'pdf' },
9279
+ { name: 'Vedligeholdelseslog.pdf', type: 'pdf' }
9280
+ ]
9281
+ },
9282
+ {
9283
+ title: 'Internet & TV',
9284
+ description: 'Fiberforbindelse i bygningen. Distributionspanel er i stueetageteknisk rum. Hver lejlighed har ethernet-stik i stue og soveværelser.',
9285
+ contacts: [
9286
+ { name: 'TeleCom Solutions', initials: 'T', contactPerson: 'Anders Petersen', phoneNumber: '+45 56 78 90 12' }
9287
+ ]
9288
+ },
9289
+ {
9290
+ title: 'Netværksopsætning guide',
9291
+ attachments: [
9292
+ { name: 'Netværksopsætning.pdf', type: 'pdf' }
9293
+ ]
9294
+ },
9295
+ {
9296
+ title: 'Affaldshåndtering',
9297
+ description: 'Affaldssorteringsstation placeret i gården. Afhentninger: Dagrenovation (man/tor), Genbrug (ons), Organisk (tir/fre). Storskrald kræver booking.',
9298
+ attachments: [
9299
+ { name: 'Affaldsretningslinjer.pdf', type: 'pdf' }
9300
+ ]
9301
+ }
9302
+ ];
9303
+ // Safety Equipment folder data
9304
+ sikkerhedsudstyrItems = [
9305
+ {
9306
+ title: 'Fælles områder og sikkerhed',
9307
+ description: 'Trappeopgange med nødbelysning og brandsikre døre. Postkasser placeret i indgangspartiet. Hold altid flugtveje fri.',
9308
+ images: [
9309
+ '/Assets/Dummy-photos/staircase.jpg',
9310
+ '/Assets/Dummy-photos/mailboxes.jpg'
9311
+ ]
9312
+ },
9313
+ {
9314
+ title: 'Hjertestarter (AED)',
9315
+ description: 'Automatisk hjertestarter placeret i stueetagen ved hovedindgangen. Tilgængelig 24/7. Ingen særlig uddannelse kræves - enheden guider dig gennem processen.',
9316
+ contacts: [
9317
+ { name: 'MediTech Service', initials: 'M', contactPerson: 'John Mortensen', phoneNumber: '+45 12 34 56 78' }
9318
+ ]
9319
+ },
9320
+ {
9321
+ title: 'Brandslukningsudstyr',
9322
+ description: 'Brandslukkere placeret på hver etage. Eftersyn udføres årligt. Brandalarm aktiveres automatisk ved røg.',
9323
+ attachments: [
9324
+ { name: 'Brandplan.pdf', type: 'pdf' }
9325
+ ]
9326
+ },
9327
+ {
9328
+ title: 'Alarmsystem',
9329
+ description: 'Adgangskontrol med kodesystem ved alle indgange. Kode ændres kvartalsvis. Ved indbrud kontakt straks politiet og ejendomsadministrationen.',
9330
+ contacts: [
9331
+ { name: 'SecureHome A/S', initials: 'S', contactPerson: 'Henrik Johansen', phoneNumber: '+45 98 76 54 32' }
9332
+ ]
9333
+ }
9334
+ ];
9335
+ // Service Contracts folder data
9336
+ serviceContractsItems = [
9337
+ {
9338
+ title: 'Rengøringsservice',
9339
+ description: 'Ugentlig rengøring af fællesarealer inklusiv indgangshal, trapper og elevatorer. Hovedrengøring kvartalsvis. Service leveres mandag-fredag, 6:00-9:00.',
9340
+ contacts: [
9341
+ { name: 'CleanCo Denmark', initials: 'C', contactPerson: 'Anne Kristensen', phoneNumber: '+45 89 01 23 45' }
9342
+ ]
9343
+ },
9344
+ {
9345
+ title: 'Rengøringskontrakt',
9346
+ attachments: [
9347
+ { name: 'Rengøringskontrakt_2024.pdf', type: 'pdf' },
9348
+ { name: 'Rengøringsplan.pdf', type: 'pdf' }
9349
+ ]
9350
+ },
9351
+ {
9352
+ title: 'Udendørs arealer',
9353
+ description: 'Fælles grønne områder med bede, siddepladser og terrasse. Beboere må frit benytte området. Respektér planterne og hold området pænt.',
9354
+ images: [
9355
+ '/Assets/Dummy-photos/park.jpg',
9356
+ '/Assets/Dummy-photos/yard.jpg'
9357
+ ]
9358
+ },
9359
+ {
9360
+ title: 'Havevedligeholdelse',
9361
+ description: 'Professionel havepleje inklusiv plæneklipning, hækklipning og blomsterbedvedligeholdelse. Vintersnefjerning inkluderet.',
9362
+ contacts: [
9363
+ { name: 'Green Gardens ApS', initials: 'G', contactPerson: 'Michael Olsen', phoneNumber: '+45 90 12 34 56' }
9364
+ ]
9365
+ },
9366
+ {
9367
+ title: 'Haveserviceaftale',
9368
+ attachments: [
9369
+ { name: 'Haveserviceaftale.pdf', type: 'pdf' }
9370
+ ]
9371
+ },
9372
+ {
9373
+ title: 'Vinduespolering',
9374
+ description: 'Professionel vinduespoleringsservice for alle udvendige vinduer to gange årligt - forår og efterår.',
9375
+ contacts: [
9376
+ { name: 'Crystal Clear Windows', initials: 'C', contactPerson: 'Lene Schmidt', phoneNumber: '+45 01 23 45 67' }
9377
+ ]
9378
+ },
9379
+ {
9380
+ title: 'Sikkerhedsservice',
9381
+ description: '24/7 overvågningsservice med alarmrespons. Direkte forbindelse til politi og brandvæsen.',
9382
+ contacts: [
9383
+ { name: 'SecureHome A/S', initials: 'S', contactPerson: 'Henrik Johansen', phoneNumber: '+45 12 34 56 78' }
9384
+ ]
9385
+ },
9386
+ {
9387
+ title: 'Sikkerhedskontrakt og procedurer',
9388
+ attachments: [
9389
+ { name: 'Sikkerhedskontrakt.pdf', type: 'pdf' },
9390
+ { name: 'Nødprocedurer.pdf', type: 'pdf' }
9391
+ ]
9392
+ }
9393
+ ];
9394
+ // Equipment folder data
9395
+ equipmentItems = [
9396
+ {
9397
+ title: 'Balkon udsigt',
9398
+ description: 'Eksempel på udsigt fra øverste etagers balkoner. Flere lejligheder har privat altan med fantastisk udsyn.',
9399
+ images: [
9400
+ '/Assets/Dummy-photos/balcony-view.jpg'
9401
+ ]
9402
+ },
9403
+ {
9404
+ title: 'Vaskerum',
9405
+ description: 'Fælles vaskerum med 4 vaskemaskiner og 4 tørretumblere. Bookingsystem tilgængeligt via beboerportal. Maskiner accepterer betalingskort. Åbningstider: 7:00-22:00.',
9406
+ contacts: [
9407
+ { name: 'WashTech Service', initials: 'W', contactPerson: 'Kirsten Berg', phoneNumber: '+45 34 56 78 90' }
9408
+ ]
9409
+ },
9410
+ {
9411
+ title: 'Vaskeri instruktioner',
9412
+ attachments: [
9413
+ { name: 'Vaskeinstruktioner.pdf', type: 'pdf' },
9414
+ { name: 'Bookingguide.pdf', type: 'pdf' }
9415
+ ]
9416
+ },
9417
+ {
9418
+ title: 'Vedligeholdelse og reparationer',
9419
+ description: 'Ved behov for reparationer eller vedligeholdelse i din lejlighed, kontakt vores hausmeister. Akutte problemer håndteres samme dag.',
9420
+ images: [
9421
+ '/Assets/Dummy-photos/handyman.jpg'
9422
+ ],
9423
+ contacts: [
9424
+ { name: 'Hausmeister Service', initials: 'H', contactPerson: 'Erik Sørensen', phoneNumber: '+45 56 78 90 12' }
9425
+ ]
9426
+ },
9427
+ {
9428
+ title: 'Værktøjsudlån',
9429
+ description: 'Basis håndværktøj tilgængeligt til beboerbrug. Kvittér for værktøj ved receptionen. Returnér inden for 48 timer.',
9430
+ attachments: [
9431
+ { name: 'Værktøjsliste.pdf', type: 'pdf' },
9432
+ { name: 'Udlånspolitik.pdf', type: 'pdf' }
9433
+ ]
9434
+ }
9435
+ ];
9436
+ constructor(userService) {
9437
+ this.userService = userService;
9438
+ }
9439
+ handleRefresh(event) {
9440
+ console.log('Pull-to-refresh triggered');
9441
+ setTimeout(() => {
9442
+ console.log('Refresh complete');
9443
+ event.target.complete();
9444
+ }, 1000);
9445
+ }
9446
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileHandbookPageComponent, deps: [{ token: UserService }], target: i0.ɵɵFactoryTarget.Component });
9447
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: MobileHandbookPageComponent, isStandalone: true, selector: "app-mobile-handbook-page", ngImport: i0, template: `
9448
+ <ds-mobile-page-main
9449
+ title="Håndbog"
9450
+ [avatarInitials]="userService.avatarInitials()"
9451
+ [avatarType]="userService.avatarType()"
9452
+ (refresh)="handleRefresh($event)">
9453
+
9454
+ <ds-mobile-content>
9455
+ <ds-mobile-content-section>
9456
+ <div class="folders-grid">
9457
+ <ds-mobile-handbook-folder
9458
+ [variant]="'pink'"
9459
+ [iconName]="'remixLightbulbLine'"
9460
+ [itemCount]="8"
9461
+ [label]="'Forsyninger'"
9462
+ [items]="utilitiesItems">
9463
+ </ds-mobile-handbook-folder>
9464
+
9465
+ <ds-mobile-handbook-folder
9466
+ [variant]="'success'"
9467
+ [iconName]="'remixKey2Line'"
9468
+ [itemCount]="4"
9469
+ [label]="'Sikkerhedsudstyr'"
9470
+ [items]="sikkerhedsudstyrItems">
9471
+ </ds-mobile-handbook-folder>
9472
+
9473
+ <ds-mobile-handbook-folder
9474
+ [variant]="'blue'"
9475
+ [iconName]="'remixFileList3Line'"
9476
+ [itemCount]="8"
9477
+ [label]="'Servicekontrakter'"
9478
+ [items]="serviceContractsItems">
9479
+ </ds-mobile-handbook-folder>
9480
+
9481
+ <ds-mobile-handbook-folder
9482
+ [variant]="'warning'"
9483
+ [iconName]="'remixToolsLine'"
9484
+ [itemCount]="5"
9485
+ [label]="'Udstyr'"
9486
+ [items]="equipmentItems">
9487
+ </ds-mobile-handbook-folder>
9488
+ </div>
9489
+ </ds-mobile-content-section>
9490
+ </ds-mobile-content>
9491
+ </ds-mobile-page-main>
9492
+ `, 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%}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance"], outputs: ["avatarClick", "refresh", "scroll"] }, { kind: "component", type: DsMobileContentComponent, selector: "ds-mobile-content", inputs: ["layout"] }, { kind: "component", type: DsMobileContentSectionComponent, selector: "ds-mobile-content-section" }, { kind: "component", type: DsMobileHandbookFolderComponent, selector: "ds-mobile-handbook-folder", inputs: ["variant", "iconName", "itemCount", "label", "items"] }] });
9493
+ }
9494
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileHandbookPageComponent, decorators: [{
9495
+ type: Component,
9496
+ args: [{ selector: 'app-mobile-handbook-page', standalone: true, imports: [
9497
+ DsMobilePageMainComponent,
9498
+ DsMobileContentComponent,
9499
+ DsMobileContentSectionComponent,
9500
+ SectionHeaderComponent,
9501
+ ContentRowComponent,
9502
+ DsMobileHandbookFolderComponent
9503
+ ], template: `
9504
+ <ds-mobile-page-main
9505
+ title="Håndbog"
9506
+ [avatarInitials]="userService.avatarInitials()"
9507
+ [avatarType]="userService.avatarType()"
9508
+ (refresh)="handleRefresh($event)">
9509
+
9510
+ <ds-mobile-content>
9511
+ <ds-mobile-content-section>
9512
+ <div class="folders-grid">
9513
+ <ds-mobile-handbook-folder
9514
+ [variant]="'pink'"
9515
+ [iconName]="'remixLightbulbLine'"
9516
+ [itemCount]="8"
9517
+ [label]="'Forsyninger'"
9518
+ [items]="utilitiesItems">
9519
+ </ds-mobile-handbook-folder>
9520
+
9521
+ <ds-mobile-handbook-folder
9522
+ [variant]="'success'"
9523
+ [iconName]="'remixKey2Line'"
9524
+ [itemCount]="4"
9525
+ [label]="'Sikkerhedsudstyr'"
9526
+ [items]="sikkerhedsudstyrItems">
9527
+ </ds-mobile-handbook-folder>
9528
+
9529
+ <ds-mobile-handbook-folder
9530
+ [variant]="'blue'"
9531
+ [iconName]="'remixFileList3Line'"
9532
+ [itemCount]="8"
9533
+ [label]="'Servicekontrakter'"
9534
+ [items]="serviceContractsItems">
9535
+ </ds-mobile-handbook-folder>
9536
+
9537
+ <ds-mobile-handbook-folder
9538
+ [variant]="'warning'"
9539
+ [iconName]="'remixToolsLine'"
9540
+ [itemCount]="5"
9541
+ [label]="'Udstyr'"
9542
+ [items]="equipmentItems">
9543
+ </ds-mobile-handbook-folder>
9544
+ </div>
9545
+ </ds-mobile-content-section>
9546
+ </ds-mobile-content>
9547
+ </ds-mobile-page-main>
9548
+ `, 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%}\n"] }]
9549
+ }], ctorParameters: () => [{ type: UserService }] });
9550
+
9551
+ class MobileHomePageComponent {
9552
+ navCtrl;
9553
+ userService;
9554
+ constructor(navCtrl, userService) {
9555
+ this.navCtrl = navCtrl;
9556
+ this.userService = userService;
9557
+ console.log('MobileHomePageComponent constructor');
9558
+ }
9559
+ handleRefresh(event) {
9560
+ console.log('Pull-to-refresh triggered');
9561
+ setTimeout(() => {
9562
+ console.log('Refresh complete');
9563
+ event.target.complete();
9564
+ }, 1000);
9565
+ }
9566
+ navigateToDetail() {
9567
+ // Navigation removed - home/detail page was deleted
9568
+ console.log('Navigate to detail (page removed)');
9569
+ }
9570
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileHomePageComponent, deps: [{ token: i1.NavController }, { token: UserService }], target: i0.ɵɵFactoryTarget.Component });
9571
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: MobileHomePageComponent, isStandalone: true, selector: "app-home-page", ngImport: i0, template: `
9572
+ <ds-mobile-page-main
9573
+ title="Hjem"
9574
+ headerTitle="Velkommen, Lars"
9575
+ headerSubtitle="Din lejebolig på et øjeblik."
9576
+ [avatarInitials]="userService.avatarInitials()"
9577
+ [avatarType]="userService.avatarType()"
9578
+ (refresh)="handleRefresh($event)">
9579
+
9580
+ <!-- Property info tiles in header -->
9581
+ <ds-mobile-header-content header-content>
9582
+ <ds-mobile-header-content-tile>
9583
+ <tile-icon>
9584
+ <ds-icon name="remixHome4Line" size="20px" color="#DFE4FF" />
9585
+ </tile-icon>
9586
+ <tile-content>
9587
+ <tile-label>Areal</tile-label>
9588
+ <tile-value>120 m²</tile-value>
9589
+ </tile-content>
9590
+ </ds-mobile-header-content-tile>
9591
+
9592
+ <ds-mobile-header-content-tile>
9593
+ <tile-icon>
9594
+ <ds-icon name="remixCollageLine" size="20px" color="#DFE4FF" />
9595
+ </tile-icon>
9596
+ <tile-content>
9597
+ <tile-label>Værelser</tile-label>
9598
+ <tile-value>3 værelser</tile-value>
9599
+ </tile-content>
9600
+ </ds-mobile-header-content-tile>
9601
+ </ds-mobile-header-content>
9602
+
9603
+ <!-- Main page content -->
9604
+ <ds-mobile-content>
9605
+ <ds-mobile-content-section>
9606
+ <section-header width="third"></section-header>
9607
+ <content-row>
9608
+ <div class="grey-box"></div>
9609
+ <div class="grey-box"></div>
9610
+ </content-row>
9611
+ <content-row>
9612
+ <div class="grey-box"></div>
9613
+ </content-row>
9614
+ </ds-mobile-content-section>
9615
+
9616
+ <!-- Purple box - clickable with brand background -->
9617
+ <content-row>
9618
+ <div class="grey-box clickable" (click)="navigateToDetail()"></div>
9619
+ </content-row>
9620
+
9621
+ <ds-mobile-content-section>
9622
+ <section-header width="half"></section-header>
9623
+ <content-row>
9624
+ <div class="grey-box"></div>
9625
+ <div class="grey-box"></div>
9626
+ <div class="grey-box"></div>
9627
+ </content-row>
9628
+ <content-row>
9629
+ <div class="grey-box"></div>
9630
+ <div class="grey-box"></div>
9631
+ </content-row>
9632
+ </ds-mobile-content-section>
9633
+
9634
+ <ds-mobile-content-section>
9635
+ <section-header width="third"></section-header>
9636
+ <content-row>
9637
+ <div class="grey-box"></div>
9638
+ </content-row>
9639
+ </ds-mobile-content-section>
9640
+
9641
+ <ds-mobile-content-section>
9642
+ <section-header width="half"></section-header>
9643
+ <content-row>
9644
+ <div class="grey-box"></div>
9645
+ <div class="grey-box"></div>
9646
+ </content-row>
9647
+ <content-row>
9648
+ <div class="grey-box"></div>
9649
+ <div class="grey-box"></div>
9650
+ <div class="grey-box"></div>
9651
+ </content-row>
9652
+ </ds-mobile-content-section>
9653
+
9654
+ <ds-mobile-content-section>
9655
+ <section-header width="third"></section-header>
9656
+ <content-row>
9657
+ <div class="grey-box"></div>
9658
+ <div class="grey-box"></div>
9659
+ </content-row>
9660
+ </ds-mobile-content-section>
9661
+
9662
+ <ds-mobile-content-section>
9663
+ <section-header width="half"></section-header>
9664
+ <content-row>
9665
+ <div class="grey-box"></div>
9666
+ </content-row>
9667
+ <content-row>
9668
+ <div class="grey-box"></div>
9669
+ <div class="grey-box"></div>
9670
+ </content-row>
9671
+ </ds-mobile-content-section>
9672
+
9673
+ <ds-mobile-content-section>
9674
+ <section-header width="third"></section-header>
9675
+ <content-row>
9676
+ <div class="grey-box"></div>
9677
+ <div class="grey-box"></div>
9678
+ <div class="grey-box"></div>
9679
+ </content-row>
9680
+ </ds-mobile-content-section>
9681
+ </ds-mobile-content>
9682
+ </ds-mobile-page-main>
9683
+ `, isInline: true, styles: [".grey-box{height:120px;border-radius:12px;background:var(--color-background-neutral-tertiary);flex:1}.grey-box.clickable{background:var(--color-background-brand);cursor:pointer;transition:transform var(--transition-duration-fast) var(--ease-smooth)}.grey-box.clickable:active{transform:scale(.98)}\n"], dependencies: [{ 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", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance"], outputs: ["avatarClick", "refresh", "scroll"] }, { kind: "component", type: DsMobileHeaderContentComponent, selector: "ds-mobile-header-content" }, { kind: "component", type: DsMobileHeaderContentTileComponent, selector: "ds-mobile-header-content-tile" }, { kind: "component", type: TileIconComponent, selector: "tile-icon" }, { kind: "component", type: TileContentComponent, selector: "tile-content" }, { kind: "component", type: TileLabelComponent, selector: "tile-label" }, { kind: "component", type: TileValueComponent, selector: "tile-value" }, { kind: "component", type: DsMobileContentComponent, selector: "ds-mobile-content", inputs: ["layout"] }, { kind: "component", type: DsMobileContentSectionComponent, selector: "ds-mobile-content-section" }, { kind: "component", type: SectionHeaderComponent, selector: "section-header", inputs: ["width"] }, { kind: "component", type: ContentRowComponent, selector: "content-row" }] });
9684
+ }
9685
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileHomePageComponent, decorators: [{
9686
+ type: Component,
9687
+ args: [{ selector: 'app-home-page', standalone: true, imports: [
9688
+ DsIconComponent,
9689
+ DsMobilePageMainComponent,
9690
+ DsMobileHeaderContentComponent,
9691
+ DsMobileHeaderContentTileComponent,
9692
+ TileIconComponent,
9693
+ TileContentComponent,
9694
+ TileLabelComponent,
9695
+ TileValueComponent,
9696
+ DsMobileContentComponent,
9697
+ DsMobileContentSectionComponent,
9698
+ SectionHeaderComponent,
9699
+ ContentRowComponent
9700
+ ], template: `
9701
+ <ds-mobile-page-main
9702
+ title="Hjem"
9703
+ headerTitle="Velkommen, Lars"
9704
+ headerSubtitle="Din lejebolig på et øjeblik."
9705
+ [avatarInitials]="userService.avatarInitials()"
9706
+ [avatarType]="userService.avatarType()"
9707
+ (refresh)="handleRefresh($event)">
9708
+
9709
+ <!-- Property info tiles in header -->
9710
+ <ds-mobile-header-content header-content>
9711
+ <ds-mobile-header-content-tile>
9712
+ <tile-icon>
9713
+ <ds-icon name="remixHome4Line" size="20px" color="#DFE4FF" />
9714
+ </tile-icon>
9715
+ <tile-content>
9716
+ <tile-label>Areal</tile-label>
9717
+ <tile-value>120 m²</tile-value>
9718
+ </tile-content>
9719
+ </ds-mobile-header-content-tile>
9720
+
9721
+ <ds-mobile-header-content-tile>
9722
+ <tile-icon>
9723
+ <ds-icon name="remixCollageLine" size="20px" color="#DFE4FF" />
9724
+ </tile-icon>
9725
+ <tile-content>
9726
+ <tile-label>Værelser</tile-label>
9727
+ <tile-value>3 værelser</tile-value>
9728
+ </tile-content>
9729
+ </ds-mobile-header-content-tile>
9730
+ </ds-mobile-header-content>
9731
+
9732
+ <!-- Main page content -->
9733
+ <ds-mobile-content>
9734
+ <ds-mobile-content-section>
9735
+ <section-header width="third"></section-header>
9736
+ <content-row>
9737
+ <div class="grey-box"></div>
9738
+ <div class="grey-box"></div>
9739
+ </content-row>
9740
+ <content-row>
9741
+ <div class="grey-box"></div>
9742
+ </content-row>
9743
+ </ds-mobile-content-section>
9744
+
9745
+ <!-- Purple box - clickable with brand background -->
9746
+ <content-row>
9747
+ <div class="grey-box clickable" (click)="navigateToDetail()"></div>
9748
+ </content-row>
9749
+
9750
+ <ds-mobile-content-section>
9751
+ <section-header width="half"></section-header>
9752
+ <content-row>
9753
+ <div class="grey-box"></div>
9754
+ <div class="grey-box"></div>
9755
+ <div class="grey-box"></div>
9756
+ </content-row>
9757
+ <content-row>
9758
+ <div class="grey-box"></div>
9759
+ <div class="grey-box"></div>
9760
+ </content-row>
9761
+ </ds-mobile-content-section>
9762
+
9763
+ <ds-mobile-content-section>
9764
+ <section-header width="third"></section-header>
9765
+ <content-row>
9766
+ <div class="grey-box"></div>
9767
+ </content-row>
9768
+ </ds-mobile-content-section>
9769
+
9770
+ <ds-mobile-content-section>
9771
+ <section-header width="half"></section-header>
9772
+ <content-row>
9773
+ <div class="grey-box"></div>
9774
+ <div class="grey-box"></div>
9775
+ </content-row>
9776
+ <content-row>
9777
+ <div class="grey-box"></div>
9778
+ <div class="grey-box"></div>
9779
+ <div class="grey-box"></div>
9780
+ </content-row>
9781
+ </ds-mobile-content-section>
9782
+
9783
+ <ds-mobile-content-section>
9784
+ <section-header width="third"></section-header>
9785
+ <content-row>
9786
+ <div class="grey-box"></div>
9787
+ <div class="grey-box"></div>
9788
+ </content-row>
9789
+ </ds-mobile-content-section>
9790
+
9791
+ <ds-mobile-content-section>
9792
+ <section-header width="half"></section-header>
9793
+ <content-row>
9794
+ <div class="grey-box"></div>
9795
+ </content-row>
9796
+ <content-row>
9797
+ <div class="grey-box"></div>
9798
+ <div class="grey-box"></div>
9799
+ </content-row>
9800
+ </ds-mobile-content-section>
9801
+
9802
+ <ds-mobile-content-section>
9803
+ <section-header width="third"></section-header>
9804
+ <content-row>
9805
+ <div class="grey-box"></div>
9806
+ <div class="grey-box"></div>
9807
+ <div class="grey-box"></div>
9808
+ </content-row>
9809
+ </ds-mobile-content-section>
9810
+ </ds-mobile-content>
9811
+ </ds-mobile-page-main>
9812
+ `, styles: [".grey-box{height:120px;border-radius:12px;background:var(--color-background-neutral-tertiary);flex:1}.grey-box.clickable{background:var(--color-background-brand);cursor:pointer;transition:transform var(--transition-duration-fast) var(--ease-smooth)}.grey-box.clickable:active{transform:scale(.98)}\n"] }]
9813
+ }], ctorParameters: () => [{ type: i1.NavController }, { type: UserService }] });
9814
+
9815
+ /**
9816
+ * Custom page transition - iOS-style push/pop
9817
+ *
9818
+ * FORWARD (navigating TO detail):
9819
+ * - Entering page (detail): slides in from RIGHT, z-index 10 (on top)
9820
+ * - Leaving page (list): slides LEFT slightly, z-index 9 (underneath)
9821
+ *
9822
+ * REVERSE (swipe back FROM detail):
9823
+ * - Entering page (list): slides in from LEFT, z-index 9 (underneath)
9824
+ * - Leaving page (detail): slides out to RIGHT, z-index 10 (on top)
9825
+ */
9826
+ const customPageTransition = (_, opts) => {
9827
+ const DURATION = 400;
9828
+ const isBackDirection = opts.direction === 'back';
9829
+ const rootTransition = createAnimation()
9830
+ .duration(opts.duration || DURATION)
9831
+ .easing('cubic-bezier(0.32,0.72,0,1)');
9832
+ // Entering page animation
9833
+ const enteringPage = createAnimation()
9834
+ .addElement(opts.enteringEl)
9835
+ .beforeRemoveClass('ion-page-invisible')
9836
+ .beforeStyles({
9837
+ 'z-index': isBackDirection ? '9' : '10',
9838
+ 'opacity': '1'
9839
+ })
9840
+ .fromTo('transform', isBackDirection ? 'translateX(-20%)' : 'translateX(100%)', 'translateX(0)');
9841
+ // Leaving page animation
9842
+ const leavingPage = createAnimation()
9843
+ .addElement(opts.leavingEl)
9844
+ .beforeStyles({
9845
+ 'z-index': isBackDirection ? '10' : '9'
9846
+ })
9847
+ .fromTo('transform', 'translateX(0)', isBackDirection ? 'translateX(100%)' : 'translateX(-20%)');
9848
+ rootTransition.addAnimation([enteringPage, leavingPage]);
9849
+ return rootTransition;
9850
+ };
9851
+ /**
9852
+ * Custom back transition - iOS style where page slides out to reveal page underneath
9853
+ * The entering page (inquiries) slides in from the LEFT underneath
9854
+ * The leaving page (detail) slides out to the RIGHT on top
9855
+ */
9856
+ const customBackTransition = (_, opts) => {
9857
+ const DURATION = 400;
9858
+ const rootTransition = createAnimation()
9859
+ .duration(opts.duration || DURATION)
9860
+ .easing('cubic-bezier(0.32,0.72,0,1)');
9861
+ // Entering page: underneath, sliding in from LEFT (-20% to 0)
9862
+ const enteringPage = createAnimation()
9863
+ .addElement(opts.enteringEl)
9864
+ .beforeRemoveClass('ion-page-invisible')
9865
+ .beforeStyles({
9866
+ 'z-index': '9',
9867
+ 'opacity': '1'
9868
+ })
9869
+ .fromTo('transform', 'translateX(-20%)', 'translateX(0)');
9870
+ // Leaving page: on top, sliding out to the RIGHT (0 to 100%)
9871
+ const leavingPage = createAnimation()
9872
+ .addElement(opts.leavingEl)
9873
+ .beforeStyles({
9874
+ 'z-index': '10',
9875
+ })
9876
+ .fromTo('transform', 'translateX(0)', 'translateX(100%)');
9877
+ rootTransition.addAnimation([enteringPage, leavingPage]);
9878
+ return rootTransition;
9879
+ };
9880
+
9881
+ /**
9882
+ * DsMobileTabBarComponent
9883
+ *
9884
+ * Pill-style tab bar for filtering/switching views
9885
+ * Used in the purple header section of pages
9886
+ *
9887
+ * @example
9888
+ * ```html
9889
+ * <ds-mobile-tab-bar
9890
+ * [tabs]="[
9891
+ * { id: 'all', label: 'All' },
9892
+ * { id: 'open', label: 'Open' },
9893
+ * { id: 'closed', label: 'Closed' }
9894
+ * ]"
9895
+ * [activeTab]="'all'"
9896
+ * (tabChange)="handleTabChange($event)">
9897
+ * </ds-mobile-tab-bar>
9898
+ * ```
9899
+ */
9900
+ class DsMobileTabBarComponent {
9901
+ /**
9902
+ * Array of tab items to display
9903
+ */
9904
+ tabs = input.required(...(ngDevMode ? [{ debugName: "tabs" }] : []));
9905
+ /**
9906
+ * Currently active tab ID
9907
+ */
9908
+ activeTab = input.required(...(ngDevMode ? [{ debugName: "activeTab" }] : []));
9909
+ /**
9910
+ * Emitted when a tab is clicked
9911
+ */
9912
+ tabChange = output();
9913
+ handleTabClick(tabId) {
9914
+ this.tabChange.emit(tabId);
9915
+ }
9916
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobileTabBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9917
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: DsMobileTabBarComponent, isStandalone: true, selector: "ds-mobile-tab-bar", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: true, transformFunction: null }, activeTab: { classPropertyName: "activeTab", publicName: "activeTab", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { tabChange: "tabChange" }, ngImport: i0, template: `
9918
+ <div class="filter-tabs">
9919
+ @for (tab of tabs(); track tab.id) {
9920
+ <button
9921
+ class="filter-tab"
9922
+ [class.active]="activeTab() === tab.id"
9923
+ (click)="handleTabClick(tab.id)"
9924
+ [attr.aria-label]="tab.label"
9925
+ [attr.aria-selected]="activeTab() === tab.id">
9926
+ {{ tab.label }}
9927
+ @if (tab.badge && tab.badge > 0) {
9928
+ <span class="tab-badge">{{ tab.badge }}</span>
9929
+ }
9930
+ </button>
9931
+ }
9932
+ </div>
9933
+ `, 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:600;color:#fff9;cursor:pointer;transition:all .2s ease;display:flex;align-items:center;gap:6px;white-space:nowrap}.filter-tab.active{background:var(--color-background-brand, #5d5fef);color:#fff}.filter-tab:hover:not(.active){background:#ffffff1a}.tab-badge{min-width:24px;height:16px;padding:0 6px;border-radius:10px;background:#fff3;color:#fff;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:#ffffff4d;color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
9934
+ }
9935
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobileTabBarComponent, decorators: [{
9936
+ type: Component,
9937
+ args: [{ selector: 'ds-mobile-tab-bar', standalone: true, imports: [CommonModule], template: `
9938
+ <div class="filter-tabs">
9939
+ @for (tab of tabs(); track tab.id) {
9940
+ <button
9941
+ class="filter-tab"
9942
+ [class.active]="activeTab() === tab.id"
9943
+ (click)="handleTabClick(tab.id)"
9944
+ [attr.aria-label]="tab.label"
9945
+ [attr.aria-selected]="activeTab() === tab.id">
9946
+ {{ tab.label }}
9947
+ @if (tab.badge && tab.badge > 0) {
9948
+ <span class="tab-badge">{{ tab.badge }}</span>
9949
+ }
9950
+ </button>
9951
+ }
9952
+ </div>
9953
+ `, 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:600;color:#fff9;cursor:pointer;transition:all .2s ease;display:flex;align-items:center;gap:6px;white-space:nowrap}.filter-tab.active{background:var(--color-background-brand, #5d5fef);color:#fff}.filter-tab:hover:not(.active){background:#ffffff1a}.tab-badge{min-width:24px;height:16px;padding:0 6px;border-radius:10px;background:#fff3;color:#fff;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:#ffffff4d;color:#fff}\n"] }]
9954
+ }], 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"] }] } });
9955
+
9956
+ class MobileInquiriesPageComponent {
9957
+ userService;
9958
+ navCtrl;
9959
+ constructor(userService, navCtrl) {
9960
+ this.userService = userService;
9961
+ this.navCtrl = navCtrl;
9962
+ }
9963
+ filterStatus = signal('all', ...(ngDevMode ? [{ debugName: "filterStatus" }] : []));
9964
+ tabItems = [
9965
+ { id: 'all', label: 'Alle' },
9966
+ { id: 'open', label: 'Åben' },
9967
+ { id: 'closed', label: 'Lukket' }
9968
+ ];
9969
+ inquiries = signal([
9970
+ {
9971
+ id: '1',
9972
+ title: 'Tørretumbler virker ikke',
9973
+ description: 'I de sidste tre dage har jeg oplevet vedvarende problemer med tørretumbleren. Den starter, men stopper efter få minutter.',
9974
+ status: 'open',
9975
+ timestamp: '12 dage siden',
9976
+ category: 'appliance'
9977
+ },
9978
+ {
9979
+ id: '2',
9980
+ title: 'Problem med vandtryk',
9981
+ description: 'Lavt vandtryk i badeværelseshåndvasken. Det er blevet gradvist værre i løbet af den sidste uge.',
9982
+ status: 'open',
9983
+ timestamp: '5 dage siden',
9984
+ category: 'plumbing'
9985
+ },
9986
+ {
9987
+ id: '3',
9988
+ title: 'Varme virker ikke ordentligt',
9989
+ description: 'Varmesystemet holder ikke den indstillede temperatur. Lejligheden er meget koldere, end den burde være.',
9990
+ status: 'closed',
9991
+ timestamp: '2 måneder siden',
9992
+ category: 'heating'
9993
+ }
9994
+ ], ...(ngDevMode ? [{ debugName: "inquiries" }] : []));
9995
+ // Computed signals that automatically update when dependencies change
9996
+ filteredInquiries = computed(() => {
9997
+ const all = this.inquiries();
9998
+ const status = this.filterStatus();
9999
+ if (status === 'all') {
10000
+ return all;
10001
+ }
10002
+ else if (status === 'open') {
10003
+ return all.filter(i => i.status === 'open');
10004
+ }
10005
+ else {
10006
+ return all.filter(i => i.status === 'closed');
10007
+ }
10008
+ }, ...(ngDevMode ? [{ debugName: "filteredInquiries" }] : []));
10009
+ openInquiries = computed(() => {
10010
+ return this.inquiries().filter(i => i.status === 'open');
10011
+ }, ...(ngDevMode ? [{ debugName: "openInquiries" }] : []));
10012
+ closedInquiries = computed(() => {
10013
+ return this.inquiries().filter(i => i.status === 'closed');
10014
+ }, ...(ngDevMode ? [{ debugName: "closedInquiries" }] : []));
10015
+ setFilter(status) {
10016
+ this.filterStatus.set(status);
10017
+ }
10018
+ getInquiryIcon(category) {
10019
+ return 'remixTodoLine';
10020
+ }
10021
+ openInquiryDetail(inquiryId) {
10022
+ console.log('Opening inquiry:', inquiryId);
10023
+ // Navigate to inquiry detail page with custom transition (absolute path outside tabs for animations)
10024
+ this.navCtrl.navigateForward([`/inquiry-detail/${inquiryId}`], {
10025
+ animation: customPageTransition
10026
+ });
10027
+ }
10028
+ showInquiryActions(inquiryId) {
10029
+ console.log('Showing actions for inquiry:', inquiryId);
10030
+ // Show bottom sheet with actions (edit, delete, etc.)
10031
+ }
10032
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileInquiriesPageComponent, deps: [{ token: UserService }, { token: i1.NavController }], target: i0.ɵɵFactoryTarget.Component });
10033
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: MobileInquiriesPageComponent, isStandalone: true, selector: "app-mobile-inquiries-page", host: { classAttribute: "ion-page" }, ngImport: i0, template: `
10034
+ <ds-mobile-page-main
10035
+ title="Henvendelser"
10036
+ [avatarInitials]="userService.avatarInitials()"
10037
+ [avatarType]="userService.avatarType()">
10038
+
10039
+ <!-- Filter tabs in header -->
10040
+ <div header-content>
10041
+ <ds-mobile-tab-bar
10042
+ [tabs]="tabItems"
10043
+ [activeTab]="filterStatus()"
10044
+ (tabChange)="setFilter($any($event))">
10045
+ </ds-mobile-tab-bar>
10046
+ </div>
10047
+
10048
+ <ds-mobile-content>
10049
+ <div class="inquiries-container">
10050
+ @if (filteredInquiries().length > 0) {
10051
+ <div class="inquiry-list-wrapper">
10052
+ @for (inquiry of filteredInquiries(); track inquiry.id; let idx = $index) {
10053
+ <ds-mobile-interactive-list-item-inquiry
10054
+ [title]="inquiry.title"
10055
+ [description]="inquiry.description"
10056
+ [status]="inquiry.status"
10057
+ [timestamp]="inquiry.timestamp"
10058
+ [iconName]="getInquiryIcon(inquiry.category)"
10059
+ [clickable]="true"
10060
+ [showChevron]="false"
10061
+ (inquiryClick)="openInquiryDetail(inquiry.id)"
10062
+ (longPress)="showInquiryActions(inquiry.id)">
10063
+ </ds-mobile-interactive-list-item-inquiry>
10064
+
10065
+ }
10066
+ </div>
10067
+ } @else {
10068
+ <!-- Empty state -->
10069
+ <div class="empty-state">
10070
+ <ds-icon name="remixInboxLine" size="48px" color="tertiary" />
10071
+ <h3 class="empty-state-title">Ingen henvendelser endnu</h3>
10072
+ <p class="empty-state-description">
10073
+ @if (filterStatus() === 'open') {
10074
+ Du har ingen åbne henvendelser
10075
+ } @else if (filterStatus() === 'closed') {
10076
+ Du har ingen lukkede henvendelser
10077
+ } @else {
10078
+ Du har ikke oprettet nogen henvendelser endnu
10079
+ }
10080
+ </p>
10081
+ </div>
10082
+ }
10083
+ </div>
10084
+ </ds-mobile-content>
10085
+ </ds-mobile-page-main>
10086
+ `, isInline: true, styles: [".inquiries-container{display:flex;flex-direction:column;max-width:640px}.inquiry-list-wrapper{display:flex;flex-direction:column}.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:16px 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", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance"], outputs: ["avatarClick", "refresh", "scroll"] }, { kind: "component", type: DsMobileContentComponent, selector: "ds-mobile-content", inputs: ["layout"] }, { kind: "component", type: DsMobileInteractiveListItemInquiryComponent, selector: "ds-mobile-interactive-list-item-inquiry", inputs: ["title", "description", "status", "statusLabel", "timestamp", "iconName", "iconColor", "variant", "clickable", "showChevron"], outputs: ["inquiryClick", "longPress"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsMobileTabBarComponent, selector: "ds-mobile-tab-bar", inputs: ["tabs", "activeTab"], outputs: ["tabChange"] }] });
10087
+ }
10088
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileInquiriesPageComponent, decorators: [{
10089
+ type: Component,
10090
+ args: [{ selector: 'app-mobile-inquiries-page', standalone: true, imports: [
10091
+ DsMobilePageMainComponent,
10092
+ DsMobileContentComponent,
10093
+ DsMobileInteractiveListItemInquiryComponent,
10094
+ DsIconComponent,
10095
+ DsMobileTabBarComponent
10096
+ ], host: {
10097
+ class: 'ion-page'
10098
+ }, template: `
10099
+ <ds-mobile-page-main
10100
+ title="Henvendelser"
10101
+ [avatarInitials]="userService.avatarInitials()"
10102
+ [avatarType]="userService.avatarType()">
10103
+
10104
+ <!-- Filter tabs in header -->
10105
+ <div header-content>
10106
+ <ds-mobile-tab-bar
10107
+ [tabs]="tabItems"
10108
+ [activeTab]="filterStatus()"
10109
+ (tabChange)="setFilter($any($event))">
10110
+ </ds-mobile-tab-bar>
10111
+ </div>
10112
+
10113
+ <ds-mobile-content>
10114
+ <div class="inquiries-container">
10115
+ @if (filteredInquiries().length > 0) {
10116
+ <div class="inquiry-list-wrapper">
10117
+ @for (inquiry of filteredInquiries(); track inquiry.id; let idx = $index) {
10118
+ <ds-mobile-interactive-list-item-inquiry
10119
+ [title]="inquiry.title"
10120
+ [description]="inquiry.description"
10121
+ [status]="inquiry.status"
10122
+ [timestamp]="inquiry.timestamp"
10123
+ [iconName]="getInquiryIcon(inquiry.category)"
10124
+ [clickable]="true"
10125
+ [showChevron]="false"
10126
+ (inquiryClick)="openInquiryDetail(inquiry.id)"
10127
+ (longPress)="showInquiryActions(inquiry.id)">
10128
+ </ds-mobile-interactive-list-item-inquiry>
10129
+
10130
+ }
10131
+ </div>
10132
+ } @else {
10133
+ <!-- Empty state -->
10134
+ <div class="empty-state">
10135
+ <ds-icon name="remixInboxLine" size="48px" color="tertiary" />
10136
+ <h3 class="empty-state-title">Ingen henvendelser endnu</h3>
10137
+ <p class="empty-state-description">
10138
+ @if (filterStatus() === 'open') {
10139
+ Du har ingen åbne henvendelser
10140
+ } @else if (filterStatus() === 'closed') {
10141
+ Du har ingen lukkede henvendelser
10142
+ } @else {
10143
+ Du har ikke oprettet nogen henvendelser endnu
10144
+ }
10145
+ </p>
10146
+ </div>
10147
+ }
10148
+ </div>
10149
+ </ds-mobile-content>
10150
+ </ds-mobile-page-main>
10151
+ `, styles: [".inquiries-container{display:flex;flex-direction:column;max-width:640px}.inquiry-list-wrapper{display:flex;flex-direction:column}.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:16px 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}\n"] }]
10152
+ }], ctorParameters: () => [{ type: UserService }, { type: i1.NavController }] });
10153
+
10154
+ /**
10155
+ * DsMobileListItemStaticComponent
10156
+ *
10157
+ * A read-only version of the interactive list item component.
10158
+ * Used for displaying static information without interaction.
10159
+ *
10160
+ * This component has the same structure as the interactive list item but without:
10161
+ * - Padding
10162
+ * - Rounded corners
10163
+ * - Hover states
10164
+ * - Click interactions
10165
+ * - Background fill (transparent)
10166
+ *
10167
+ * @example
10168
+ * ```html
10169
+ * <ds-mobile-list-item-static
10170
+ * [leadingSize]="'40px'">
10171
+ *
10172
+ * <div content-leading>
10173
+ * <ds-avatar initials="JD" />
10174
+ * </div>
10175
+ *
10176
+ * <div content-main>
10177
+ * <h3>Main Content</h3>
10178
+ * <p>Supporting text goes here...</p>
10179
+ * </div>
10180
+ *
10181
+ * <div content-trailing>
10182
+ * <span>Info</span>
10183
+ * </div>
10184
+ * </ds-mobile-list-item-static>
10185
+ * ```
10186
+ */
10187
+ class DsMobileListItemStaticComponent {
10188
+ /**
10189
+ * CSS size value for the leading content area (e.g., '32px', '40px', '48px')
10190
+ * Defaults to '32px' for standard list item avatars
10191
+ */
10192
+ leadingSize = input('32px', ...(ngDevMode ? [{ debugName: "leadingSize" }] : []));
10193
+ /**
10194
+ * Check if leading content slot has content
10195
+ */
10196
+ hasLeadingContent = computed(() => true, ...(ngDevMode ? [{ debugName: "hasLeadingContent" }] : [])); // Always render slot container for consistency
10197
+ /**
10198
+ * Check if trailing content slot has content
10199
+ */
10200
+ hasTrailingContent = computed(() => true, ...(ngDevMode ? [{ debugName: "hasTrailingContent" }] : [])); // Always render slot container for consistency
10201
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobileListItemStaticComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10202
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: DsMobileListItemStaticComponent, isStandalone: true, selector: "ds-mobile-list-item-static", inputs: { leadingSize: { classPropertyName: "leadingSize", publicName: "leadingSize", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.--leading-size": "leadingSize()" } }, ngImport: i0, template: `
10203
+ @if (hasLeadingContent()) {
10204
+ <div class="content-leading">
10205
+ <ng-content select="[content-leading]" />
10206
+ </div>
10207
+ }
10208
+
10209
+ <div class="content-main">
10210
+ <ng-content select="[content-main]" />
10211
+ <ng-content />
10212
+ </div>
10213
+
10214
+ @if (hasTrailingContent()) {
10215
+ <div class="content-trailing">
10216
+ <ng-content select="[content-trailing]" />
10217
+ </div>
10218
+ }
10219
+ `, isInline: true, styles: [":host{display:flex;flex-direction:row;align-items:flex-start;background:transparent;padding:0;gap:12px;position:relative;--leading-size: 32px}:host:after{content:\"\";position:absolute;bottom:-10px;left:calc(var(--leading-size) + 12px);right:0;height:1px;background:var(--border-color-default)}:host:last-child:after{display:none}.content-leading{flex-shrink:0;width:var(--leading-size);min-height:var(--leading-size);height:auto;display:flex;align-items:center;justify-content:center}.content-main{flex:1;min-width:0;min-height:var(--leading-size);display:flex;flex-direction:column;justify-content:center;align-items:flex-start;gap:8px}.content-trailing{flex-shrink:0;display:flex;align-items:flex-start}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
10220
+ }
10221
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobileListItemStaticComponent, decorators: [{
10222
+ type: Component,
10223
+ args: [{ selector: 'ds-mobile-list-item-static', standalone: true, imports: [CommonModule], host: {
10224
+ '[style.--leading-size]': 'leadingSize()'
10225
+ }, template: `
10226
+ @if (hasLeadingContent()) {
10227
+ <div class="content-leading">
10228
+ <ng-content select="[content-leading]" />
10229
+ </div>
10230
+ }
10231
+
10232
+ <div class="content-main">
10233
+ <ng-content select="[content-main]" />
10234
+ <ng-content />
10235
+ </div>
10236
+
10237
+ @if (hasTrailingContent()) {
10238
+ <div class="content-trailing">
10239
+ <ng-content select="[content-trailing]" />
10240
+ </div>
10241
+ }
10242
+ `, styles: [":host{display:flex;flex-direction:row;align-items:flex-start;background:transparent;padding:0;gap:12px;position:relative;--leading-size: 32px}:host:after{content:\"\";position:absolute;bottom:-10px;left:calc(var(--leading-size) + 12px);right:0;height:1px;background:var(--border-color-default)}:host:last-child:after{display:none}.content-leading{flex-shrink:0;width:var(--leading-size);min-height:var(--leading-size);height:auto;display:flex;align-items:center;justify-content:center}.content-main{flex:1;min-width:0;min-height:var(--leading-size);display:flex;flex-direction:column;justify-content:center;align-items:flex-start;gap:8px}.content-trailing{flex-shrink:0;display:flex;align-items:flex-start}\n"] }]
10243
+ }], propDecorators: { leadingSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "leadingSize", required: false }] }] } });
10244
+
10245
+ class MobileInquiryDetailPageComponent extends MobilePageBase {
10246
+ userService;
10247
+ navCtrl;
10248
+ elementRef;
10249
+ ionContent;
10250
+ // Platform detection
10251
+ platform = inject(Platform);
10252
+ // Computed property to check if running on native platform
10253
+ isNativePlatform = computed(() => this.platform.is('ios') ||
10254
+ this.platform.is('android') ||
10255
+ this.platform.is('capacitor'), ...(ngDevMode ? [{ debugName: "isNativePlatform" }] : []));
10256
+ inquiryTitle = 'Tørretumbler virker ikke';
10257
+ activeTab = signal('activity', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
10258
+ tabItems = [
10259
+ { id: 'activity', label: 'Aktivitet' },
10260
+ { id: 'messages', label: 'Beskeder', badge: 0 },
10261
+ { id: 'details', label: 'Detaljer' }
10262
+ ];
10263
+ activities = [
10264
+ {
10265
+ id: '1',
10266
+ type: 'assignment',
10267
+ actor: 'Martin Smith',
10268
+ actorInitials: 'MS',
10269
+ title: 'er blevet tildelt som din dedikerede tekniker.',
10270
+ timestamp: '2 dage siden',
10271
+ date: '28. feb 2025',
10272
+ iconName: 'remixUserAddLine'
10273
+ },
10274
+ {
10275
+ id: '2',
10276
+ type: 'assignment',
10277
+ actor: 'Ricki Meihlen',
10278
+ actorInitials: 'RM',
10279
+ title: 'er blevet tildelt til at håndtere din henvendelse.',
10280
+ timestamp: '8 dage siden',
10281
+ date: '22. feb 2025',
10282
+ iconName: 'remixUserLine'
10283
+ },
10284
+ {
10285
+ id: '3',
10286
+ type: 'creation',
10287
+ title: 'Henvendelse oprettet',
10288
+ timestamp: '8 dage siden',
10289
+ date: '22. feb 2025',
10290
+ iconName: 'remixAddCircleLine'
10291
+ }
10292
+ ];
10293
+ messageThreads = [
10294
+ {
10295
+ id: '1',
10296
+ senderName: 'Ove Hindborg',
10297
+ senderAvatar: '',
10298
+ senderInitials: 'OH',
10299
+ message: 'Dejligt at høre! Jeg venter på din teknikerbesøg tidsplan.',
10300
+ role: 'Sagsbehandler',
10301
+ timestamp: '2t siden',
10302
+ unread: true
10303
+ },
10304
+ {
10305
+ id: '2',
10306
+ senderName: 'Martin Smith',
10307
+ senderAvatar: '',
10308
+ senderInitials: 'MS',
10309
+ message: 'Dejligt at høre! Jeg venter på din teknikerbesøg tidsplan.',
10310
+ role: 'Tekniker',
10311
+ timestamp: '4t siden',
10312
+ unread: true
10313
+ }
10314
+ ];
10315
+ unreadMessagesCount = computed(() => {
10316
+ const count = this.messageThreads.filter(m => m.unread).length;
10317
+ // Update badge in tab items
10318
+ const messagesTab = this.tabItems.find(t => t.id === 'messages');
10319
+ if (messagesTab) {
10320
+ messagesTab.badge = count;
10321
+ }
10322
+ return count;
10323
+ }, ...(ngDevMode ? [{ debugName: "unreadMessagesCount" }] : []));
10324
+ constructor(userService, navCtrl, elementRef) {
10325
+ super();
10326
+ this.userService = userService;
10327
+ this.navCtrl = navCtrl;
10328
+ this.elementRef = elementRef;
10329
+ // Trigger initial badge update
10330
+ this.unreadMessagesCount();
10331
+ }
10332
+ ngAfterViewInit() {
10333
+ // Initial setup if needed
10334
+ }
10335
+ setActiveTab(tabId) {
10336
+ this.activeTab.set(tabId);
10337
+ }
10338
+ goBack() {
10339
+ this.navCtrl.back({ animation: customBackTransition });
10340
+ }
10341
+ handleScroll(event) {
10342
+ const scrollTop = event.detail.scrollTop;
10343
+ const threshold = 160;
10344
+ const fadeDistance = 200;
10345
+ const header = this.elementRef.nativeElement.querySelector('ion-header:not([collapse])');
10346
+ const headerExpandable = this.elementRef.nativeElement.querySelector('.header-expandable');
10347
+ // Show title in fixed header when scrolled past threshold
10348
+ if (scrollTop > threshold) {
10349
+ header?.classList.add('header-scrolled');
10350
+ }
10351
+ else {
10352
+ header?.classList.remove('header-scrolled');
10353
+ }
10354
+ // Fade out header-expandable content based on scroll
10355
+ if (headerExpandable) {
10356
+ const fadeProgress = Math.min(scrollTop / fadeDistance, 1);
10357
+ // Calculate opacity (1 to 0)
10358
+ const opacity = 1 - fadeProgress;
10359
+ // Calculate transform (0px to -20px upward)
10360
+ const translateY = fadeProgress * -20;
10361
+ // Apply styles
10362
+ headerExpandable.style.opacity = opacity.toString();
10363
+ headerExpandable.style.transform = `translateY(${translateY}px)`;
10364
+ }
10365
+ }
10366
+ handleRefresh(event) {
10367
+ console.log('Pull-to-refresh triggered');
10368
+ setTimeout(() => {
10369
+ console.log('Refresh complete');
10370
+ event.target.complete();
10371
+ }, 1000);
10372
+ }
10373
+ openMessage(messageId) {
10374
+ console.log('Opening message:', messageId);
10375
+ // Navigate to message thread detail
10376
+ }
10377
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileInquiryDetailPageComponent, deps: [{ token: UserService }, { token: i1.NavController }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
10378
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: MobileInquiryDetailPageComponent, isStandalone: true, selector: "app-mobile-inquiry-detail-page", host: { classAttribute: "ion-page" }, viewQueries: [{ propertyName: "ionContent", first: true, predicate: IonContent, descendants: true }], usesInheritance: true, ngImport: i0, template: `
10379
+ <!-- Fixed header at top -->
10380
+ <ion-header>
10381
+ <ion-toolbar>
10382
+ <div class="header-detail">
10383
+ <!-- Back Button -->
10384
+ <button class="back-button" (click)="goBack()" [attr.aria-label]="'Go back'">
10385
+ <ds-icon name="remixArrowLeftSLine" size="24px" color="white" />
10386
+ </button>
10387
+
10388
+ <!-- Title - fades in on scroll -->
10389
+ <ion-title class="header-detail__title">{{ inquiryTitle }}</ion-title>
10390
+ </div>
10391
+ </ion-toolbar>
10392
+ </ion-header>
10393
+
10394
+ <!-- Content with expandable header -->
10395
+ <ion-content [scrollEvents]="true" (ionScroll)="handleScroll($event)">
10396
+ <!-- Pull to refresh (only on native iOS/Android) -->
10397
+ @if (isNativePlatform()) {
10398
+ <ion-refresher
10399
+ slot="fixed"
10400
+ (ionRefresh)="handleRefresh($event)"
10401
+ [pullFactor]="0.4"
10402
+ [pullMin]="80"
10403
+ [pullMax]="240"
10404
+ closeDuration="600ms">
10405
+ <ion-refresher-content
10406
+ pullingIcon="chevron-down-circle-outline"
10407
+ refreshingSpinner="lines">
10408
+ </ion-refresher-content>
10409
+ </ion-refresher>
10410
+ }
10411
+
10412
+ <!-- Expandable header section (purple background) -->
10413
+ <div class="header-expandable">
10414
+ <div class="header-expandable-inner">
10415
+ <div class="header-expandable__text">
10416
+ <h1 class="header-expandable__title">{{ inquiryTitle }}</h1>
10417
+ </div>
10418
+
10419
+ <!-- Tabs in header -->
10420
+ <ds-mobile-tab-bar
10421
+ [tabs]="tabItems"
10422
+ [activeTab]="activeTab()"
10423
+ (tabChange)="setActiveTab($event)">
10424
+ </ds-mobile-tab-bar>
10425
+ </div>
10426
+ </div>
10427
+
10428
+ <!-- Content wrapper -->
10429
+ <div class="content-wrapper">
10430
+ <div class="content-inner">
10431
+ <!-- Activity Tab Content -->
10432
+ @if (activeTab() === 'activity') {
10433
+ <div class="activity-list">
10434
+ @for (activity of activities; track activity.id) {
10435
+ <div class="activity-item">
10436
+ @if (activity.actor) {
10437
+ <!-- Avatar with badge for actor activities -->
10438
+ <div class="avatar-wrapper">
10439
+ <ds-avatar
10440
+ [type]="'initials'"
10441
+ [initials]="activity.actorInitials || ''"
10442
+ size="md" />
10443
+
10444
+ <div class="avatar-badge">
10445
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 32" fill="none">
10446
+ <path d="M33.9862 5.51709H23.1724V8.82743H26.0413C26.2841 8.82743 26.4827 9.02606 26.4827 9.26881V12.7998C26.4827 13.0426 26.2841 13.2412 26.0413 13.2412H23.1724V14.3447H26.0413C26.2841 14.3447 26.4827 14.5433 26.4827 14.7861V18.3171C26.4827 18.5598 26.2841 18.7585 26.0413 18.7585H23.1724V19.8619H26.0413C26.2841 19.8619 26.4827 20.0605 26.4827 20.3033V23.8343C26.4827 24.0771 26.2841 24.2757 26.0413 24.2757H23.1724V26.2619C23.1724 26.7496 23.0267 27.2043 22.7773 27.5861H27.5862L32 31.9999V27.5861H33.9862C34.7167 27.5861 35.3103 26.9924 35.3103 26.2619V6.84123C35.3103 6.11075 34.7167 5.51709 33.9862 5.51709ZM32 23.8343C32 24.0771 31.8013 24.2757 31.5586 24.2757H28.0276C27.7848 24.2757 27.5862 24.0771 27.5862 23.8343V20.3033C27.5862 20.0605 27.7848 19.8619 28.0276 19.8619H31.5586C31.8013 19.8619 32 20.0605 32 20.3033V23.8343ZM32 18.3171C32 18.5598 31.8013 18.7585 31.5586 18.7585H28.0276C27.7848 18.7585 27.5862 18.5598 27.5862 18.3171V14.7861C27.5862 14.5433 27.7848 14.3447 28.0276 14.3447H31.5586C31.8013 14.3447 32 14.5433 32 14.7861V18.3171ZM32 12.7998C32 13.0426 31.8013 13.2412 31.5586 13.2412H28.0276C27.7848 13.2412 27.5862 13.0426 27.5862 12.7998V9.26881C27.5862 9.02606 27.7848 8.82743 28.0276 8.82743H31.5586C31.8013 8.82743 32 9.02606 32 9.26881V12.7998Z" fill="white"/>
10447
+ <path d="M20.7448 0H1.32414C0.593655 0 0 0.593655 0 1.32414V26.2621C0 26.9926 0.593655 27.5862 1.32414 27.5862H3.31034V32L7.72414 27.5862H20.7448C21.4753 27.5862 22.069 26.9926 22.069 26.2621V1.32414C22.069 0.593655 21.4753 0 20.7448 0ZM7.72414 23.8345C7.72414 24.0772 7.52552 24.2759 7.28276 24.2759H3.75172C3.50897 24.2759 3.31034 24.0772 3.31034 23.8345V20.3034C3.31034 20.0607 3.50897 19.8621 3.75172 19.8621H7.28276C7.52552 19.8621 7.72414 20.0607 7.72414 20.3034V23.8345ZM7.72414 18.3172C7.72414 18.56 7.52552 18.7586 7.28276 18.7586H3.75172C3.50897 18.7586 3.31034 18.56 3.31034 18.3172V14.7862C3.31034 14.5434 3.50897 14.3448 3.75172 14.3448H7.28276C7.52552 14.3448 7.72414 14.5434 7.72414 14.7862V18.3172ZM7.72414 12.8C7.72414 13.0428 7.52552 13.2414 7.28276 13.2414H3.75172C3.50897 13.2414 3.31034 13.0428 3.31034 12.8V9.26897C3.31034 9.02621 3.50897 8.82759 3.75172 8.82759H7.28276C7.52552 8.82759 7.72414 9.02621 7.72414 9.26897V12.8ZM7.72414 7.28276C7.72414 7.52552 7.52552 7.72414 7.28276 7.72414H3.75172C3.50897 7.72414 3.31034 7.52552 3.31034 7.28276V3.75172C3.31034 3.50897 3.50897 3.31034 3.75172 3.31034H7.28276C7.52552 3.31034 7.72414 3.50897 7.72414 3.75172V7.28276ZM13.2414 23.8345C13.2414 24.0772 13.0428 24.2759 12.8 24.2759H9.26897C9.02621 24.2759 8.82759 24.0772 8.82759 23.8345V20.3034C8.82759 20.0607 9.02621 19.8621 9.26897 19.8621H12.8C13.0428 19.8621 13.2414 20.0607 13.2414 20.3034V23.8345ZM13.2414 18.3172C13.2414 18.56 13.0428 18.7586 12.8 18.7586H9.26897C9.02621 18.7586 8.82759 18.56 8.82759 18.3172V14.7862C8.82759 14.5434 9.02621 14.3448 9.26897 14.3448H12.8C13.0428 14.3448 13.2414 14.5434 13.2414 14.7862V18.3172ZM13.2414 12.8C13.2414 13.0428 13.0428 13.2414 12.8 13.2414H9.26897C9.02621 13.2414 8.82759 13.0428 8.82759 12.8V9.26897C8.82759 9.02621 9.02621 8.82759 9.26897 8.82759H12.8C13.0428 8.82759 13.2414 9.02621 13.2414 9.26897V12.8ZM13.2414 6.84138V7.28276C13.2414 7.52552 13.0428 7.72414 12.8 7.72414H9.26897C9.02621 7.72414 8.82759 7.52552 8.82759 7.28276V3.75172C8.82759 3.50897 9.02621 3.31034 9.26897 3.31034H12.8C13.0428 3.31034 13.2414 3.50897 13.2414 3.75172V6.84138ZM18.7586 23.8345C18.7586 24.0772 18.56 24.2759 18.3172 24.2759H14.7862C14.5434 24.2759 14.3448 24.0772 14.3448 23.8345V20.3034C14.3448 20.0607 14.5434 19.8621 14.7862 19.8621H18.3172C18.56 19.8621 18.7586 20.0607 18.7586 20.3034V23.8345ZM18.7586 18.3172C18.7586 18.56 18.56 18.7586 18.3172 18.7586H14.7862C14.5434 18.7586 14.3448 18.56 14.3448 18.3172V14.7862C14.3448 14.5434 14.5434 14.3448 14.7862 14.3448H18.3172C18.56 14.3448 18.7586 14.5434 18.7586 14.7862V18.3172ZM18.7586 12.8C18.7586 13.0428 18.56 13.2414 18.3172 13.2414H14.7862C14.5434 13.2414 14.3448 13.0428 14.3448 12.8V9.26897C14.3448 9.02621 14.5434 8.82759 14.7862 8.82759H18.3172C18.56 8.82759 18.7586 9.02621 18.7586 9.26897V12.8ZM18.7586 5.51724V7.28276C18.7586 7.52552 18.56 7.72414 18.3172 7.72414H14.7862C14.5434 7.72414 14.3448 7.52552 14.3448 7.28276V3.75172C14.3448 3.50897 14.5434 3.31034 14.7862 3.31034H18.3172C18.56 3.31034 18.7586 3.50897 18.7586 3.75172V5.51724Z" fill="white"/>
10448
+ </svg>
10449
+ </div>
10450
+ </div>
10451
+ } @else {
10452
+ <!-- Icon wrapper for non-actor activities -->
10453
+ <div class="activity-icon-wrapper">
10454
+ <ds-icon
10455
+ [name]="activity.iconName"
10456
+ size="18px"
10457
+ color="secondary" />
10458
+ </div>
10459
+ }
10460
+
10461
+ <div class="activity-content">
10462
+ <p class="activity-title">
10463
+ @if (activity.actor) {
10464
+ <span class="actor-name">{{ activity.actor }}</span>
10465
+ <span class="activity-text"> {{ activity.title }}</span>
10466
+ } @else {
10467
+ <span class="actor-name">{{ activity.title }}</span>
10468
+ }
10469
+ </p>
10470
+ @if (activity.description) {
10471
+ <p class="activity-description">{{ activity.description }}</p>
10472
+ }
10473
+ <div class="activity-timestamp">
10474
+ <ds-icon name="remixCalendarLine" size="12px" color="--color-text-tertiary" />
10475
+ <span>{{ activity.date }}</span>
10476
+ </div>
10477
+ </div>
10478
+ </div>
10479
+ }
10480
+ </div>
10481
+ }
10482
+
10483
+ <!-- Messages Tab Content -->
10484
+ @if (activeTab() === 'messages') {
10485
+ <div class="messages-list">
10486
+ @for (message of messageThreads; track message.id) {
10487
+ <ds-mobile-interactive-list-item-message
10488
+ [senderName]="message.senderName"
10489
+ [senderRole]="message.role"
10490
+ [message]="message.message"
10491
+ [avatarInitials]="message.senderInitials"
10492
+ [unread]="message.unread"
10493
+ (messageClick)="openMessage(message.id)">
10494
+ </ds-mobile-interactive-list-item-message>
10495
+ }
10496
+ </div>
10497
+ }
10498
+
10499
+ <!-- Details Tab Content -->
10500
+ @if (activeTab() === 'details') {
10501
+ <div class="details-list">
10502
+ <!-- Assignee -->
10503
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
10504
+ <div content-leading>
10505
+ <ds-avatar
10506
+ [size]="'sm'"
10507
+ [type]="'initials'"
10508
+ [initials]="'R'" />
10509
+ </div>
10510
+ <div content-main>
10511
+ <div class="detail-label">Sagsbehandler</div>
10512
+ <div class="detail-value">Ricki Meihlen</div>
10513
+ </div>
10514
+ </ds-mobile-list-item-static>
10515
+
10516
+ <!-- Technician -->
10517
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
10518
+ <div content-leading>
10519
+ <ds-avatar
10520
+ [size]="'sm'"
10521
+ [type]="'initials'"
10522
+ [initials]="'M'" />
10523
+ </div>
10524
+ <div content-main>
10525
+ <div class="detail-label">Tekniker</div>
10526
+ <div class="detail-value">Martin Smith</div>
10527
+ </div>
10528
+ </ds-mobile-list-item-static>
10529
+
10530
+ <!-- Title -->
10531
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
10532
+ <div content-leading>
10533
+ <ds-icon name="remixTextBlock" size="20px" color="tertiary" />
10534
+ </div>
10535
+ <div content-main>
10536
+ <div class="detail-value">{{ inquiryTitle }}</div>
10537
+ </div>
10538
+ </ds-mobile-list-item-static>
10539
+
10540
+ <!-- Description -->
10541
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
10542
+ <div content-leading>
10543
+ <ds-icon name="remixAlignLeft" size="20px" color="tertiary" />
10544
+ </div>
10545
+ <div content-main>
10546
+ <div class="detail-value description-text">
10547
+ I de sidste tre dage har vi oplevet vedvarende problemer med tørretumbleren i vores lejlighed. På trods af at vi følger betjeningsvejledningen, fejler maskinen konsekvent i at fuldføre sine tørrecyklusser.
10548
+ </div>
10549
+ <div class="detail-tag">Husholdningsapparater</div>
10550
+ </div>
10551
+ </ds-mobile-list-item-static>
10552
+
10553
+ <!-- Photos -->
10554
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
10555
+ <div content-leading>
10556
+ <ds-icon name="remixCameraLine" size="20px" color="tertiary" />
10557
+ </div>
10558
+ <div content-main>
10559
+ <div class="photo-grid">
10560
+ <button class="photo-add">
10561
+ <ds-icon name="remixAddLine" size="20px" color="tertiary" />
10562
+ </button>
10563
+ <!-- Placeholder photos -->
10564
+ <div class="photo-item"></div>
10565
+ <div class="photo-item"></div>
10566
+ <div class="photo-item"></div>
10567
+ <div class="photo-item"></div>
10568
+ </div>
10569
+ </div>
10570
+ </ds-mobile-list-item-static>
10571
+ </div>
10572
+ }
10573
+ </div>
10574
+ </div>
10575
+ </ion-content>
10576
+ `, isInline: true, styles: ["ion-header{--background: var(--color-brand-secondary, #5d5fef);height:72px;min-height:72px}ion-toolbar{--background: var(--color-brand-secondary, #5d5fef);--color: white;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 72px;height:100%;min-height:72px}ion-toolbar ion-title{transition:transform .2s ease,opacity .2s ease!important}.header-detail{display:flex;align-items:center;gap:12px;padding:0 20px;height:100%}.back-button{width:36px;height:36px;border-radius:50%;background:#ffffff1a;border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background .2s ease;color:#fff;padding:0;flex-shrink:0}.back-button:hover,.back-button:active{background:#ffffff26}.header-detail__title{position:absolute;left:64px;transform:translateY(-100%);font-size:var(--font-size-base);font-weight:600;color:#fff;opacity:0!important;pointer-events:none;transition:transform .2s ease,opacity .2s ease!important;margin:0;padding:0;--color: white;text-align:left!important}.header-scrolled .header-detail__title{opacity:1!important;pointer-events:auto;transform:translateY(0)}@media (min-width: 768px){.header-detail{padding:16px 24px}}ion-content{--background: var(--color-brand-secondary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;border-radius:24px 24px 0 0;overflow:hidden}.plt-ios ion-content{--background: var(--color-background-neutral-primary)}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;display:flex;flex-direction:column}ion-refresher{z-index:0}ion-refresher-content{--color: white}.header-expandable{background:var(--color-brand-secondary);padding:32px 20px 24px;color:#fff;position:sticky;top:0;z-index:5;transition:opacity .1s ease-out,transform .1s ease-out}.header-expandable-inner{display:flex;flex-direction:column;gap:20px;max-width:640px}.header-expandable__text{margin-bottom:0}.header-expandable__title{font-family:Brockmann,sans-serif;font-size:var(--font-size-2xl);font-weight:600;line-height:1.2;letter-spacing:-.5px;margin:0;color:#fff}@media (min-width: 768px){.header-expandable{padding:40px var(--content-padding-md) 32px var(--content-padding-md)}.header-expandable__title{font-size:var(--font-size-3xl)}}@media (min-width: 992px){.header-expandable{padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.header-expandable{padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.header-expandable{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.header-expandable{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}.content-wrapper{background:var(--color-background-neutral-primary, white);border-radius:24px 24px 0 0;flex:1 1 auto;min-height:100%;position:relative;z-index:10;box-shadow:0 200vh 0 0 var(--color-background-neutral-primary)}.content-inner{padding:24px 20px 40px}@media (min-width: 768px){.content-inner{padding:32px}}.activity-list{display:flex;flex-direction:column;gap:32px}.activity-item{display:flex;gap:12px;position:relative}.activity-item:after{content:\"\";position:absolute;bottom:-16px;left:44px;right:8px;height:1px;background:var(--border-color-default, #e5e5e5)}.activity-item:last-child:after{display:none}.activity-icon-wrapper{width:32px;height:32px;border-radius:8px;background:var(--color-background-neutral-secondary, #f5f5f5);display:flex;align-items:center;justify-content:center;flex-shrink:0}.avatar-wrapper{position:relative;display:flex;align-items:start;justify-content:center;flex-shrink:0;width:32px;height:32px}.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;flex:1;min-width:0}.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}.messages-list{display:flex;flex-direction:column;gap:8px;margin:-8px}.details-list{display:flex;flex-direction:column;gap:20px}.detail-label{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-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-value.description-text{padding:8px 0}.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:flex;gap:8px;margin-top:8px;overflow-x:auto}.photo-add{width:80px;height:80px;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;flex-shrink:0}.photo-item{width:80px;height:80px;border-radius:12px;background:var(--color-background-neutral-secondary, #e5e5e5);flex-shrink:0}\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: 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: DsMobileTabBarComponent, selector: "ds-mobile-tab-bar", inputs: ["tabs", "activeTab"], outputs: ["tabChange"] }, { kind: "component", type: DsMobileListItemStaticComponent, selector: "ds-mobile-list-item-static", inputs: ["leadingSize"] }, { kind: "component", type: DsMobileInteractiveListItemMessageComponent, selector: "ds-mobile-interactive-list-item-message", inputs: ["senderName", "senderRole", "message", "avatarInitials", "avatarType", "avatarSrc", "unread", "clickable"], outputs: ["messageClick", "longPress"] }] });
10577
+ }
10578
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileInquiryDetailPageComponent, decorators: [{
10579
+ type: Component,
10580
+ args: [{ selector: 'app-mobile-inquiry-detail-page', standalone: true, imports: [
10581
+ CommonModule,
10582
+ IonHeader,
10583
+ IonToolbar,
10584
+ IonTitle,
10585
+ IonContent,
10586
+ IonRefresher,
10587
+ IonRefresherContent,
10588
+ DsIconComponent,
10589
+ DsAvatarComponent,
10590
+ DsMobileTabBarComponent,
10591
+ DsMobileListItemStaticComponent,
10592
+ DsMobileInteractiveListItemMessageComponent
10593
+ ], host: {
10594
+ class: 'ion-page'
10595
+ }, template: `
10596
+ <!-- Fixed header at top -->
10597
+ <ion-header>
10598
+ <ion-toolbar>
10599
+ <div class="header-detail">
10600
+ <!-- Back Button -->
10601
+ <button class="back-button" (click)="goBack()" [attr.aria-label]="'Go back'">
10602
+ <ds-icon name="remixArrowLeftSLine" size="24px" color="white" />
10603
+ </button>
10604
+
10605
+ <!-- Title - fades in on scroll -->
10606
+ <ion-title class="header-detail__title">{{ inquiryTitle }}</ion-title>
10607
+ </div>
10608
+ </ion-toolbar>
10609
+ </ion-header>
10610
+
10611
+ <!-- Content with expandable header -->
10612
+ <ion-content [scrollEvents]="true" (ionScroll)="handleScroll($event)">
10613
+ <!-- Pull to refresh (only on native iOS/Android) -->
10614
+ @if (isNativePlatform()) {
10615
+ <ion-refresher
10616
+ slot="fixed"
10617
+ (ionRefresh)="handleRefresh($event)"
10618
+ [pullFactor]="0.4"
10619
+ [pullMin]="80"
10620
+ [pullMax]="240"
10621
+ closeDuration="600ms">
10622
+ <ion-refresher-content
10623
+ pullingIcon="chevron-down-circle-outline"
10624
+ refreshingSpinner="lines">
10625
+ </ion-refresher-content>
10626
+ </ion-refresher>
10627
+ }
10628
+
10629
+ <!-- Expandable header section (purple background) -->
10630
+ <div class="header-expandable">
10631
+ <div class="header-expandable-inner">
10632
+ <div class="header-expandable__text">
10633
+ <h1 class="header-expandable__title">{{ inquiryTitle }}</h1>
10634
+ </div>
10635
+
10636
+ <!-- Tabs in header -->
10637
+ <ds-mobile-tab-bar
10638
+ [tabs]="tabItems"
10639
+ [activeTab]="activeTab()"
10640
+ (tabChange)="setActiveTab($event)">
10641
+ </ds-mobile-tab-bar>
10642
+ </div>
10643
+ </div>
10644
+
10645
+ <!-- Content wrapper -->
10646
+ <div class="content-wrapper">
10647
+ <div class="content-inner">
10648
+ <!-- Activity Tab Content -->
10649
+ @if (activeTab() === 'activity') {
10650
+ <div class="activity-list">
10651
+ @for (activity of activities; track activity.id) {
10652
+ <div class="activity-item">
10653
+ @if (activity.actor) {
10654
+ <!-- Avatar with badge for actor activities -->
10655
+ <div class="avatar-wrapper">
10656
+ <ds-avatar
10657
+ [type]="'initials'"
10658
+ [initials]="activity.actorInitials || ''"
10659
+ size="md" />
10660
+
10661
+ <div class="avatar-badge">
10662
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 32" fill="none">
10663
+ <path d="M33.9862 5.51709H23.1724V8.82743H26.0413C26.2841 8.82743 26.4827 9.02606 26.4827 9.26881V12.7998C26.4827 13.0426 26.2841 13.2412 26.0413 13.2412H23.1724V14.3447H26.0413C26.2841 14.3447 26.4827 14.5433 26.4827 14.7861V18.3171C26.4827 18.5598 26.2841 18.7585 26.0413 18.7585H23.1724V19.8619H26.0413C26.2841 19.8619 26.4827 20.0605 26.4827 20.3033V23.8343C26.4827 24.0771 26.2841 24.2757 26.0413 24.2757H23.1724V26.2619C23.1724 26.7496 23.0267 27.2043 22.7773 27.5861H27.5862L32 31.9999V27.5861H33.9862C34.7167 27.5861 35.3103 26.9924 35.3103 26.2619V6.84123C35.3103 6.11075 34.7167 5.51709 33.9862 5.51709ZM32 23.8343C32 24.0771 31.8013 24.2757 31.5586 24.2757H28.0276C27.7848 24.2757 27.5862 24.0771 27.5862 23.8343V20.3033C27.5862 20.0605 27.7848 19.8619 28.0276 19.8619H31.5586C31.8013 19.8619 32 20.0605 32 20.3033V23.8343ZM32 18.3171C32 18.5598 31.8013 18.7585 31.5586 18.7585H28.0276C27.7848 18.7585 27.5862 18.5598 27.5862 18.3171V14.7861C27.5862 14.5433 27.7848 14.3447 28.0276 14.3447H31.5586C31.8013 14.3447 32 14.5433 32 14.7861V18.3171ZM32 12.7998C32 13.0426 31.8013 13.2412 31.5586 13.2412H28.0276C27.7848 13.2412 27.5862 13.0426 27.5862 12.7998V9.26881C27.5862 9.02606 27.7848 8.82743 28.0276 8.82743H31.5586C31.8013 8.82743 32 9.02606 32 9.26881V12.7998Z" fill="white"/>
10664
+ <path d="M20.7448 0H1.32414C0.593655 0 0 0.593655 0 1.32414V26.2621C0 26.9926 0.593655 27.5862 1.32414 27.5862H3.31034V32L7.72414 27.5862H20.7448C21.4753 27.5862 22.069 26.9926 22.069 26.2621V1.32414C22.069 0.593655 21.4753 0 20.7448 0ZM7.72414 23.8345C7.72414 24.0772 7.52552 24.2759 7.28276 24.2759H3.75172C3.50897 24.2759 3.31034 24.0772 3.31034 23.8345V20.3034C3.31034 20.0607 3.50897 19.8621 3.75172 19.8621H7.28276C7.52552 19.8621 7.72414 20.0607 7.72414 20.3034V23.8345ZM7.72414 18.3172C7.72414 18.56 7.52552 18.7586 7.28276 18.7586H3.75172C3.50897 18.7586 3.31034 18.56 3.31034 18.3172V14.7862C3.31034 14.5434 3.50897 14.3448 3.75172 14.3448H7.28276C7.52552 14.3448 7.72414 14.5434 7.72414 14.7862V18.3172ZM7.72414 12.8C7.72414 13.0428 7.52552 13.2414 7.28276 13.2414H3.75172C3.50897 13.2414 3.31034 13.0428 3.31034 12.8V9.26897C3.31034 9.02621 3.50897 8.82759 3.75172 8.82759H7.28276C7.52552 8.82759 7.72414 9.02621 7.72414 9.26897V12.8ZM7.72414 7.28276C7.72414 7.52552 7.52552 7.72414 7.28276 7.72414H3.75172C3.50897 7.72414 3.31034 7.52552 3.31034 7.28276V3.75172C3.31034 3.50897 3.50897 3.31034 3.75172 3.31034H7.28276C7.52552 3.31034 7.72414 3.50897 7.72414 3.75172V7.28276ZM13.2414 23.8345C13.2414 24.0772 13.0428 24.2759 12.8 24.2759H9.26897C9.02621 24.2759 8.82759 24.0772 8.82759 23.8345V20.3034C8.82759 20.0607 9.02621 19.8621 9.26897 19.8621H12.8C13.0428 19.8621 13.2414 20.0607 13.2414 20.3034V23.8345ZM13.2414 18.3172C13.2414 18.56 13.0428 18.7586 12.8 18.7586H9.26897C9.02621 18.7586 8.82759 18.56 8.82759 18.3172V14.7862C8.82759 14.5434 9.02621 14.3448 9.26897 14.3448H12.8C13.0428 14.3448 13.2414 14.5434 13.2414 14.7862V18.3172ZM13.2414 12.8C13.2414 13.0428 13.0428 13.2414 12.8 13.2414H9.26897C9.02621 13.2414 8.82759 13.0428 8.82759 12.8V9.26897C8.82759 9.02621 9.02621 8.82759 9.26897 8.82759H12.8C13.0428 8.82759 13.2414 9.02621 13.2414 9.26897V12.8ZM13.2414 6.84138V7.28276C13.2414 7.52552 13.0428 7.72414 12.8 7.72414H9.26897C9.02621 7.72414 8.82759 7.52552 8.82759 7.28276V3.75172C8.82759 3.50897 9.02621 3.31034 9.26897 3.31034H12.8C13.0428 3.31034 13.2414 3.50897 13.2414 3.75172V6.84138ZM18.7586 23.8345C18.7586 24.0772 18.56 24.2759 18.3172 24.2759H14.7862C14.5434 24.2759 14.3448 24.0772 14.3448 23.8345V20.3034C14.3448 20.0607 14.5434 19.8621 14.7862 19.8621H18.3172C18.56 19.8621 18.7586 20.0607 18.7586 20.3034V23.8345ZM18.7586 18.3172C18.7586 18.56 18.56 18.7586 18.3172 18.7586H14.7862C14.5434 18.7586 14.3448 18.56 14.3448 18.3172V14.7862C14.3448 14.5434 14.5434 14.3448 14.7862 14.3448H18.3172C18.56 14.3448 18.7586 14.5434 18.7586 14.7862V18.3172ZM18.7586 12.8C18.7586 13.0428 18.56 13.2414 18.3172 13.2414H14.7862C14.5434 13.2414 14.3448 13.0428 14.3448 12.8V9.26897C14.3448 9.02621 14.5434 8.82759 14.7862 8.82759H18.3172C18.56 8.82759 18.7586 9.02621 18.7586 9.26897V12.8ZM18.7586 5.51724V7.28276C18.7586 7.52552 18.56 7.72414 18.3172 7.72414H14.7862C14.5434 7.72414 14.3448 7.52552 14.3448 7.28276V3.75172C14.3448 3.50897 14.5434 3.31034 14.7862 3.31034H18.3172C18.56 3.31034 18.7586 3.50897 18.7586 3.75172V5.51724Z" fill="white"/>
10665
+ </svg>
10666
+ </div>
10667
+ </div>
10668
+ } @else {
10669
+ <!-- Icon wrapper for non-actor activities -->
10670
+ <div class="activity-icon-wrapper">
10671
+ <ds-icon
10672
+ [name]="activity.iconName"
10673
+ size="18px"
10674
+ color="secondary" />
10675
+ </div>
10676
+ }
10677
+
10678
+ <div class="activity-content">
10679
+ <p class="activity-title">
10680
+ @if (activity.actor) {
10681
+ <span class="actor-name">{{ activity.actor }}</span>
10682
+ <span class="activity-text"> {{ activity.title }}</span>
10683
+ } @else {
10684
+ <span class="actor-name">{{ activity.title }}</span>
10685
+ }
10686
+ </p>
10687
+ @if (activity.description) {
10688
+ <p class="activity-description">{{ activity.description }}</p>
10689
+ }
10690
+ <div class="activity-timestamp">
10691
+ <ds-icon name="remixCalendarLine" size="12px" color="--color-text-tertiary" />
10692
+ <span>{{ activity.date }}</span>
10693
+ </div>
10694
+ </div>
10695
+ </div>
10696
+ }
10697
+ </div>
10698
+ }
10699
+
10700
+ <!-- Messages Tab Content -->
10701
+ @if (activeTab() === 'messages') {
10702
+ <div class="messages-list">
10703
+ @for (message of messageThreads; track message.id) {
10704
+ <ds-mobile-interactive-list-item-message
10705
+ [senderName]="message.senderName"
10706
+ [senderRole]="message.role"
10707
+ [message]="message.message"
10708
+ [avatarInitials]="message.senderInitials"
10709
+ [unread]="message.unread"
10710
+ (messageClick)="openMessage(message.id)">
10711
+ </ds-mobile-interactive-list-item-message>
10712
+ }
10713
+ </div>
10714
+ }
10715
+
10716
+ <!-- Details Tab Content -->
10717
+ @if (activeTab() === 'details') {
10718
+ <div class="details-list">
10719
+ <!-- Assignee -->
10720
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
10721
+ <div content-leading>
10722
+ <ds-avatar
10723
+ [size]="'sm'"
10724
+ [type]="'initials'"
10725
+ [initials]="'R'" />
10726
+ </div>
10727
+ <div content-main>
10728
+ <div class="detail-label">Sagsbehandler</div>
10729
+ <div class="detail-value">Ricki Meihlen</div>
10730
+ </div>
10731
+ </ds-mobile-list-item-static>
10732
+
10733
+ <!-- Technician -->
10734
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
10735
+ <div content-leading>
10736
+ <ds-avatar
10737
+ [size]="'sm'"
10738
+ [type]="'initials'"
10739
+ [initials]="'M'" />
10740
+ </div>
10741
+ <div content-main>
10742
+ <div class="detail-label">Tekniker</div>
10743
+ <div class="detail-value">Martin Smith</div>
10744
+ </div>
10745
+ </ds-mobile-list-item-static>
10746
+
10747
+ <!-- Title -->
10748
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
10749
+ <div content-leading>
10750
+ <ds-icon name="remixTextBlock" size="20px" color="tertiary" />
10751
+ </div>
10752
+ <div content-main>
10753
+ <div class="detail-value">{{ inquiryTitle }}</div>
10754
+ </div>
10755
+ </ds-mobile-list-item-static>
10756
+
10757
+ <!-- Description -->
10758
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
10759
+ <div content-leading>
10760
+ <ds-icon name="remixAlignLeft" size="20px" color="tertiary" />
10761
+ </div>
10762
+ <div content-main>
10763
+ <div class="detail-value description-text">
10764
+ I de sidste tre dage har vi oplevet vedvarende problemer med tørretumbleren i vores lejlighed. På trods af at vi følger betjeningsvejledningen, fejler maskinen konsekvent i at fuldføre sine tørrecyklusser.
10765
+ </div>
10766
+ <div class="detail-tag">Husholdningsapparater</div>
10767
+ </div>
10768
+ </ds-mobile-list-item-static>
10769
+
10770
+ <!-- Photos -->
10771
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
10772
+ <div content-leading>
10773
+ <ds-icon name="remixCameraLine" size="20px" color="tertiary" />
10774
+ </div>
10775
+ <div content-main>
10776
+ <div class="photo-grid">
10777
+ <button class="photo-add">
10778
+ <ds-icon name="remixAddLine" size="20px" color="tertiary" />
10779
+ </button>
10780
+ <!-- Placeholder photos -->
10781
+ <div class="photo-item"></div>
10782
+ <div class="photo-item"></div>
10783
+ <div class="photo-item"></div>
10784
+ <div class="photo-item"></div>
10785
+ </div>
10786
+ </div>
10787
+ </ds-mobile-list-item-static>
10788
+ </div>
10789
+ }
10790
+ </div>
10791
+ </div>
10792
+ </ion-content>
10793
+ `, styles: ["ion-header{--background: var(--color-brand-secondary, #5d5fef);height:72px;min-height:72px}ion-toolbar{--background: var(--color-brand-secondary, #5d5fef);--color: white;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 72px;height:100%;min-height:72px}ion-toolbar ion-title{transition:transform .2s ease,opacity .2s ease!important}.header-detail{display:flex;align-items:center;gap:12px;padding:0 20px;height:100%}.back-button{width:36px;height:36px;border-radius:50%;background:#ffffff1a;border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background .2s ease;color:#fff;padding:0;flex-shrink:0}.back-button:hover,.back-button:active{background:#ffffff26}.header-detail__title{position:absolute;left:64px;transform:translateY(-100%);font-size:var(--font-size-base);font-weight:600;color:#fff;opacity:0!important;pointer-events:none;transition:transform .2s ease,opacity .2s ease!important;margin:0;padding:0;--color: white;text-align:left!important}.header-scrolled .header-detail__title{opacity:1!important;pointer-events:auto;transform:translateY(0)}@media (min-width: 768px){.header-detail{padding:16px 24px}}ion-content{--background: var(--color-brand-secondary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;border-radius:24px 24px 0 0;overflow:hidden}.plt-ios ion-content{--background: var(--color-background-neutral-primary)}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;display:flex;flex-direction:column}ion-refresher{z-index:0}ion-refresher-content{--color: white}.header-expandable{background:var(--color-brand-secondary);padding:32px 20px 24px;color:#fff;position:sticky;top:0;z-index:5;transition:opacity .1s ease-out,transform .1s ease-out}.header-expandable-inner{display:flex;flex-direction:column;gap:20px;max-width:640px}.header-expandable__text{margin-bottom:0}.header-expandable__title{font-family:Brockmann,sans-serif;font-size:var(--font-size-2xl);font-weight:600;line-height:1.2;letter-spacing:-.5px;margin:0;color:#fff}@media (min-width: 768px){.header-expandable{padding:40px var(--content-padding-md) 32px var(--content-padding-md)}.header-expandable__title{font-size:var(--font-size-3xl)}}@media (min-width: 992px){.header-expandable{padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.header-expandable{padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.header-expandable{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.header-expandable{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}.content-wrapper{background:var(--color-background-neutral-primary, white);border-radius:24px 24px 0 0;flex:1 1 auto;min-height:100%;position:relative;z-index:10;box-shadow:0 200vh 0 0 var(--color-background-neutral-primary)}.content-inner{padding:24px 20px 40px}@media (min-width: 768px){.content-inner{padding:32px}}.activity-list{display:flex;flex-direction:column;gap:32px}.activity-item{display:flex;gap:12px;position:relative}.activity-item:after{content:\"\";position:absolute;bottom:-16px;left:44px;right:8px;height:1px;background:var(--border-color-default, #e5e5e5)}.activity-item:last-child:after{display:none}.activity-icon-wrapper{width:32px;height:32px;border-radius:8px;background:var(--color-background-neutral-secondary, #f5f5f5);display:flex;align-items:center;justify-content:center;flex-shrink:0}.avatar-wrapper{position:relative;display:flex;align-items:start;justify-content:center;flex-shrink:0;width:32px;height:32px}.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;flex:1;min-width:0}.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}.messages-list{display:flex;flex-direction:column;gap:8px;margin:-8px}.details-list{display:flex;flex-direction:column;gap:20px}.detail-label{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-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-value.description-text{padding:8px 0}.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:flex;gap:8px;margin-top:8px;overflow-x:auto}.photo-add{width:80px;height:80px;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;flex-shrink:0}.photo-item{width:80px;height:80px;border-radius:12px;background:var(--color-background-neutral-secondary, #e5e5e5);flex-shrink:0}\n"] }]
10794
+ }], ctorParameters: () => [{ type: UserService }, { type: i1.NavController }, { type: i0.ElementRef }], propDecorators: { ionContent: [{
10795
+ type: ViewChild,
10796
+ args: [IonContent]
10797
+ }] } });
10798
+
10799
+ /**
10800
+ * DsMobileTabsComponent
10801
+ *
10802
+ * Responsive tab navigation that adapts from mobile to desktop:
10803
+ * - Mobile (< 768px): Bottom tab bar with icons + labels
10804
+ * - Desktop (≥ 768px): Top navigation bar with logo, tabs, and avatar
10805
+ *
10806
+ * Wraps ion-tabs to maintain native routing functionality while
10807
+ * providing a responsive navigation experience with branding.
10808
+ *
10809
+ * @example
10810
+ * ```html
10811
+ * <ds-mobile-tabs
10812
+ * [tabs]="tabsConfig"
10813
+ * [avatarInitials]="'JD'"
10814
+ * (avatarClick)="handleAvatarClick()"
10815
+ * />
10816
+ * ```
10817
+ */
10818
+ class DsMobileTabsComponent {
10819
+ elementRef;
10820
+ platformId = inject(PLATFORM_ID);
10821
+ resizeListener;
10822
+ mutationObserver;
10823
+ // Inputs
10824
+ tabs = [];
10825
+ // Avatar inputs
10826
+ avatarType = 'initials';
10827
+ avatarInitials = 'U';
10828
+ avatarSrc = '';
10829
+ avatarIconName = 'remixUser3Line';
10830
+ // Outputs
10831
+ avatarClick = new EventEmitter();
10832
+ // Internal state
10833
+ activeTab = signal('', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
10834
+ isDesktop = signal(false, ...(ngDevMode ? [{ debugName: "isDesktop" }] : []));
10835
+ constructor(elementRef) {
10836
+ this.elementRef = elementRef;
10837
+ }
10838
+ ngOnInit() {
10839
+ console.log('DsMobileTabsComponent initialized');
10840
+ // Only run breakpoint detection in browser
10841
+ if (isPlatformBrowser(this.platformId)) {
10842
+ // Initial check
10843
+ this.checkBreakpoint();
10844
+ // Listen for window resize
10845
+ this.resizeListener = () => this.checkBreakpoint();
10846
+ window.addEventListener('resize', this.resizeListener);
10847
+ }
10848
+ }
10849
+ ngAfterViewInit() {
10850
+ // Initial removal
10851
+ this.removeTitleAttributes();
10852
+ // Set up mutation observer to continuously remove title attributes
10853
+ this.setupTitleRemovalObserver();
10854
+ }
10855
+ ngOnDestroy() {
10856
+ if (isPlatformBrowser(this.platformId) && this.resizeListener) {
10857
+ window.removeEventListener('resize', this.resizeListener);
10858
+ }
10859
+ if (this.mutationObserver) {
10860
+ this.mutationObserver.disconnect();
10861
+ }
10862
+ }
10863
+ setupTitleRemovalObserver() {
10864
+ const config = {
10865
+ attributes: true,
10866
+ attributeFilter: ['title'],
10867
+ subtree: true,
10868
+ childList: true
10869
+ };
10870
+ this.mutationObserver = new MutationObserver((mutations) => {
10871
+ mutations.forEach((mutation) => {
10872
+ if (mutation.type === 'attributes' && mutation.attributeName === 'title') {
10873
+ const target = mutation.target;
10874
+ if (target.tagName === 'ION-TAB-BUTTON' && target.hasAttribute('title')) {
10875
+ target.removeAttribute('title');
10876
+ }
10877
+ }
10878
+ });
10879
+ // Also do a sweep after any changes
10880
+ this.removeTitleAttributes();
10881
+ });
10882
+ this.mutationObserver.observe(this.elementRef.nativeElement, config);
10883
+ }
10884
+ removeTitleAttributes() {
10885
+ const tabButtons = this.elementRef.nativeElement.querySelectorAll('ion-tab-button');
10886
+ tabButtons.forEach((button) => {
10887
+ if (button.hasAttribute('title')) {
10888
+ button.removeAttribute('title');
10889
+ }
10890
+ // Also remove from the native button inside shadow DOM
10891
+ const nativeButton = button.shadowRoot?.querySelector('button');
10892
+ if (nativeButton?.hasAttribute('title')) {
10893
+ nativeButton.removeAttribute('title');
10894
+ }
10895
+ });
10896
+ }
10897
+ checkBreakpoint() {
10898
+ // 768px matches the @media (min-width: 768px) in CSS
10899
+ this.isDesktop.set(window.innerWidth >= 768);
10900
+ }
10901
+ trackByTabId(index, tab) {
10902
+ return tab.id;
10903
+ }
10904
+ isTabActive(tabId) {
10905
+ return this.activeTab() === tabId;
10906
+ }
10907
+ handleAvatarClick() {
10908
+ this.avatarClick.emit();
10909
+ }
10910
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobileTabsComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
10911
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: DsMobileTabsComponent, isStandalone: true, selector: "ds-mobile-tabs", inputs: { tabs: "tabs", avatarType: "avatarType", avatarInitials: "avatarInitials", avatarSrc: "avatarSrc", avatarIconName: "avatarIconName" }, outputs: { avatarClick: "avatarClick" }, ngImport: i0, template: `
10912
+ <ion-tabs>
10913
+ <ion-tab-bar
10914
+ [attr.slot]="isDesktop() ? 'top' : 'bottom'"
10915
+ class="ds-tab-bar"
10916
+ [class.ds-tab-bar--desktop]="isDesktop()">
10917
+
10918
+ <!-- Logo (desktop only, positioned via CSS) -->
10919
+ <div class="ds-tab-bar__logo">
10920
+ <svg class="logomark" width="32" height="28" viewBox="0 0 36 32" fill="none" xmlns="http://www.w3.org/2000/svg">
10921
+ <path d="M33.9862 5.51709H23.1724V8.82743H26.0413C26.2841 8.82743 26.4827 9.02606 26.4827 9.26881V12.7998C26.4827 13.0426 26.2841 13.2412 26.0413 13.2412H23.1724V14.3447H26.0413C26.2841 14.3447 26.4827 14.5433 26.4827 14.7861V18.3171C26.4827 18.5598 26.2841 18.7585 26.0413 18.7585H23.1724V19.8619H26.0413C26.2841 19.8619 26.4827 20.0605 26.4827 20.3033V23.8343C26.4827 24.0771 26.2841 24.2757 26.0413 24.2757H23.1724V26.2619C23.1724 26.7496 23.0267 27.2043 22.7773 27.5861H27.5862L32 31.9999V27.5861H33.9862C34.7167 27.5861 35.3103 26.9924 35.3103 26.2619V6.84123C35.3103 6.11075 34.7167 5.51709 33.9862 5.51709ZM32 23.8343C32 24.0771 31.8013 24.2757 31.5586 24.2757H28.0276C27.7848 24.2757 27.5862 24.0771 27.5862 23.8343V20.3033C27.5862 20.0605 27.7848 19.8619 28.0276 19.8619H31.5586C31.8013 19.8619 32 20.0605 32 20.3033V23.8343ZM32 18.3171C32 18.5598 31.8013 18.7585 31.5586 18.7585H28.0276C27.7848 18.7585 27.5862 18.5598 27.5862 18.3171V14.7861C27.5862 14.5433 27.7848 14.3447 28.0276 14.3447H31.5586C31.8013 14.3447 32 14.5433 32 14.7861V18.3171ZM32 12.7998C32 13.0426 31.8013 13.2412 31.5586 13.2412H28.0276C27.7848 13.2412 27.5862 13.0426 27.5862 12.7998V9.26881C27.5862 9.02606 27.7848 8.82743 28.0276 8.82743H31.5586C31.8013 8.82743 32 9.02606 32 9.26881V12.7998Z" fill="white"/>
10922
+ <path d="M20.7448 0H1.32414C0.593655 0 0 0.593655 0 1.32414V26.2621C0 26.9926 0.593655 27.5862 1.32414 27.5862H3.31034V32L7.72414 27.5862H20.7448C21.4753 27.5862 22.069 26.9926 22.069 26.2621V1.32414C22.069 0.593655 21.4753 0 20.7448 0ZM7.72414 23.8345C7.72414 24.0772 7.52552 24.2759 7.28276 24.2759H3.75172C3.50897 24.2759 3.31034 24.0772 3.31034 23.8345V20.3034C3.31034 20.0607 3.50897 19.8621 3.75172 19.8621H7.28276C7.52552 19.8621 7.72414 20.0607 7.72414 20.3034V23.8345ZM7.72414 18.3172C7.72414 18.56 7.52552 18.7586 7.28276 18.7586H3.75172C3.50897 18.7586 3.31034 18.56 3.31034 18.3172V14.7862C3.31034 14.5434 3.50897 14.3448 3.75172 14.3448H7.28276C7.52552 14.3448 7.72414 14.5434 7.72414 14.7862V18.3172ZM7.72414 12.8C7.72414 13.0428 7.52552 13.2414 7.28276 13.2414H3.75172C3.50897 13.2414 3.31034 13.0428 3.31034 12.8V9.26897C3.31034 9.02621 3.50897 8.82759 3.75172 8.82759H7.28276C7.52552 8.82759 7.72414 9.02621 7.72414 9.26897V12.8ZM7.72414 7.28276C7.72414 7.52552 7.52552 7.72414 7.28276 7.72414H3.75172C3.50897 7.72414 3.31034 7.52552 3.31034 7.28276V3.75172C3.31034 3.50897 3.50897 3.31034 3.75172 3.31034H7.28276C7.52552 3.31034 7.72414 3.50897 7.72414 3.75172V7.28276ZM13.2414 23.8345C13.2414 24.0772 13.0428 24.2759 12.8 24.2759H9.26897C9.02621 24.2759 8.82759 24.0772 8.82759 23.8345V20.3034C8.82759 20.0607 9.02621 19.8621 9.26897 19.8621H12.8C13.0428 19.8621 13.2414 20.0607 13.2414 20.3034V23.8345ZM13.2414 18.3172C13.2414 18.56 13.0428 18.7586 12.8 18.7586H9.26897C9.02621 18.7586 8.82759 18.56 8.82759 18.3172V14.7862C8.82759 14.5434 9.02621 14.3448 9.26897 14.3448H12.8C13.0428 14.3448 13.2414 14.5434 13.2414 14.7862V18.3172ZM13.2414 12.8C13.2414 13.0428 13.0428 13.2414 12.8 13.2414H9.26897C9.02621 13.2414 8.82759 13.0428 8.82759 12.8V9.26897C8.82759 9.02621 9.02621 8.82759 9.26897 8.82759H12.8C13.0428 8.82759 13.2414 9.02621 13.2414 9.26897V12.8ZM13.2414 6.84138V7.28276C13.2414 7.52552 13.0428 7.72414 12.8 7.72414H9.26897C9.02621 7.72414 8.82759 7.52552 8.82759 7.28276V3.75172C8.82759 3.50897 9.02621 3.31034 9.26897 3.31034H12.8C13.0428 3.31034 13.2414 3.50897 13.2414 3.75172V6.84138ZM18.7586 23.8345C18.7586 24.0772 18.56 24.2759 18.3172 24.2759H14.7862C14.5434 24.2759 14.3448 24.0772 14.3448 23.8345V20.3034C14.3448 20.0607 14.5434 19.8621 14.7862 19.8621H18.3172C18.56 19.8621 18.7586 20.0607 18.7586 20.3034V23.8345ZM18.7586 18.3172C18.7586 18.56 18.56 18.7586 18.3172 18.7586H14.7862C14.5434 18.7586 14.3448 18.56 14.3448 18.3172V14.7862C14.3448 14.5434 14.5434 14.3448 14.7862 14.3448H18.3172C18.56 14.3448 18.7586 14.5434 18.7586 14.7862V18.3172ZM18.7586 12.8C18.7586 13.0428 18.56 13.2414 18.3172 13.2414H14.7862C14.5434 13.2414 14.3448 13.0428 14.3448 12.8V9.26897C14.3448 9.02621 14.5434 8.82759 14.7862 8.82759H18.3172C18.56 8.82759 18.7586 9.02621 18.7586 9.26897V12.8ZM18.7586 5.51724V7.28276C18.7586 7.52552 18.56 7.72414 18.3172 7.72414H14.7862C14.5434 7.72414 14.3448 7.52552 14.3448 7.28276V3.75172C14.3448 3.50897 14.5434 3.31034 14.7862 3.31034H18.3172C18.56 3.31034 18.7586 3.50897 18.7586 3.75172V5.51724Z" fill="white"/>
10923
+ </svg>
10924
+ </div>
10925
+
10926
+ <!-- Tab buttons container -->
10927
+ <div class="ds-tab-bar__tabs" *ngIf="tabs">
10928
+ <ion-tab-button
10929
+ *ngFor="let tab of tabs; trackBy: trackByTabId"
10930
+ [tab]="tab.id"
10931
+ [attr.data-icon]="tab.icon"
10932
+ [attr.data-icon-active]="tab.iconActive"
10933
+ [attr.aria-label]="tab.label"
10934
+ class="ds-tab-button ion-activatable"
10935
+ [class.tab-selected]="isTabActive(tab.id)">
10936
+ <div class="tab-icon-ripple"></div>
10937
+ <div class="tab-icon-wrapper">
10938
+ <ds-icon
10939
+ [name]="tab.icon"
10940
+ [size]="isDesktop() ? '20px' : '24px'"
10941
+ class="tab-icon-inactive"
10942
+ />
10943
+ <ds-icon
10944
+ [name]="tab.iconActive"
10945
+ [size]="isDesktop() ? '20px' : '24px'"
10946
+ class="tab-icon-active"
10947
+ />
10948
+ </div>
10949
+ <ion-label [attr.aria-hidden]="true">{{ tab.label }}</ion-label>
10950
+ </ion-tab-button>
10951
+ </div>
10952
+
10953
+ <!-- Avatar (desktop only, positioned via CSS) -->
10954
+ <div class="ds-tab-bar__actions">
10955
+ <ds-avatar
10956
+ [initials]="avatarInitials"
10957
+ [type]="avatarType"
10958
+ [src]="avatarSrc"
10959
+ [iconName]="avatarIconName"
10960
+ (click)="handleAvatarClick()"
10961
+ style="cursor: pointer;"
10962
+ />
10963
+ </div>
10964
+ </ion-tab-bar>
10965
+ </ion-tabs>
10966
+ `, isInline: true, styles: [":host{display:block;height:100vh;height:100dvh}ion-tabs{height:100%;background:var(--color-brand-secondary)}.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:calc(var(--ion-safe-area-bottom, 34px) - 4px)}@media (max-width: 767px){ion-tab-bar[slot=bottom]{position:fixed;bottom:0;left:0;right:0;z-index:100}}@media (max-width: 767px){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:env(safe-area-inset-bottom,0px)}}@media (display-mode: standalone){ion-tab-bar[slot=bottom]{padding-bottom:env(safe-area-inset-bottom,0px)!important}}@media (min-width: 768px){ion-tab-bar[slot=top]{--background: var(--color-brand-secondary);position:relative;display:flex;align-items:center;padding:12px 24px;height:64px;max-width:none}.ds-tab-bar__logo{display:flex;position:absolute;left:24px;align-items:center}.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-brand-base);display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:visible}.tab-icon-ripple{position:absolute;left:50%;top:50%;width:40px;height:40px;border-radius:50%;background:var(--color-brand-base);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-brand-base);border-radius:1000px}ion-tab-button ion-label{font-size:var(--font-size-xs);font-weight:400;letter-spacing:-.3px;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:auto;flex:0 0 auto;--color: rgba(255, 255, 255, .7);--color-selected: white;background:#ffffff1a;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{--color: white;--color-selected: white}.ds-tab-button.tab-selected{background:var(--color-background-brand);--color-selected: 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:#ffffff4d;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$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IonTabs, selector: "ion-tabs" }, { 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"] }] });
10967
+ }
10968
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobileTabsComponent, decorators: [{
10969
+ type: Component,
10970
+ args: [{ selector: 'ds-mobile-tabs', standalone: true, imports: [
10971
+ CommonModule,
10972
+ IonTabs,
10973
+ IonTabBar,
10974
+ IonTabButton,
10975
+ IonLabel,
10976
+ DsIconComponent,
10977
+ DsAvatarComponent
10978
+ ], template: `
10979
+ <ion-tabs>
10980
+ <ion-tab-bar
10981
+ [attr.slot]="isDesktop() ? 'top' : 'bottom'"
10982
+ class="ds-tab-bar"
10983
+ [class.ds-tab-bar--desktop]="isDesktop()">
10984
+
10985
+ <!-- Logo (desktop only, positioned via CSS) -->
10986
+ <div class="ds-tab-bar__logo">
10987
+ <svg class="logomark" width="32" height="28" viewBox="0 0 36 32" fill="none" xmlns="http://www.w3.org/2000/svg">
10988
+ <path d="M33.9862 5.51709H23.1724V8.82743H26.0413C26.2841 8.82743 26.4827 9.02606 26.4827 9.26881V12.7998C26.4827 13.0426 26.2841 13.2412 26.0413 13.2412H23.1724V14.3447H26.0413C26.2841 14.3447 26.4827 14.5433 26.4827 14.7861V18.3171C26.4827 18.5598 26.2841 18.7585 26.0413 18.7585H23.1724V19.8619H26.0413C26.2841 19.8619 26.4827 20.0605 26.4827 20.3033V23.8343C26.4827 24.0771 26.2841 24.2757 26.0413 24.2757H23.1724V26.2619C23.1724 26.7496 23.0267 27.2043 22.7773 27.5861H27.5862L32 31.9999V27.5861H33.9862C34.7167 27.5861 35.3103 26.9924 35.3103 26.2619V6.84123C35.3103 6.11075 34.7167 5.51709 33.9862 5.51709ZM32 23.8343C32 24.0771 31.8013 24.2757 31.5586 24.2757H28.0276C27.7848 24.2757 27.5862 24.0771 27.5862 23.8343V20.3033C27.5862 20.0605 27.7848 19.8619 28.0276 19.8619H31.5586C31.8013 19.8619 32 20.0605 32 20.3033V23.8343ZM32 18.3171C32 18.5598 31.8013 18.7585 31.5586 18.7585H28.0276C27.7848 18.7585 27.5862 18.5598 27.5862 18.3171V14.7861C27.5862 14.5433 27.7848 14.3447 28.0276 14.3447H31.5586C31.8013 14.3447 32 14.5433 32 14.7861V18.3171ZM32 12.7998C32 13.0426 31.8013 13.2412 31.5586 13.2412H28.0276C27.7848 13.2412 27.5862 13.0426 27.5862 12.7998V9.26881C27.5862 9.02606 27.7848 8.82743 28.0276 8.82743H31.5586C31.8013 8.82743 32 9.02606 32 9.26881V12.7998Z" fill="white"/>
10989
+ <path d="M20.7448 0H1.32414C0.593655 0 0 0.593655 0 1.32414V26.2621C0 26.9926 0.593655 27.5862 1.32414 27.5862H3.31034V32L7.72414 27.5862H20.7448C21.4753 27.5862 22.069 26.9926 22.069 26.2621V1.32414C22.069 0.593655 21.4753 0 20.7448 0ZM7.72414 23.8345C7.72414 24.0772 7.52552 24.2759 7.28276 24.2759H3.75172C3.50897 24.2759 3.31034 24.0772 3.31034 23.8345V20.3034C3.31034 20.0607 3.50897 19.8621 3.75172 19.8621H7.28276C7.52552 19.8621 7.72414 20.0607 7.72414 20.3034V23.8345ZM7.72414 18.3172C7.72414 18.56 7.52552 18.7586 7.28276 18.7586H3.75172C3.50897 18.7586 3.31034 18.56 3.31034 18.3172V14.7862C3.31034 14.5434 3.50897 14.3448 3.75172 14.3448H7.28276C7.52552 14.3448 7.72414 14.5434 7.72414 14.7862V18.3172ZM7.72414 12.8C7.72414 13.0428 7.52552 13.2414 7.28276 13.2414H3.75172C3.50897 13.2414 3.31034 13.0428 3.31034 12.8V9.26897C3.31034 9.02621 3.50897 8.82759 3.75172 8.82759H7.28276C7.52552 8.82759 7.72414 9.02621 7.72414 9.26897V12.8ZM7.72414 7.28276C7.72414 7.52552 7.52552 7.72414 7.28276 7.72414H3.75172C3.50897 7.72414 3.31034 7.52552 3.31034 7.28276V3.75172C3.31034 3.50897 3.50897 3.31034 3.75172 3.31034H7.28276C7.52552 3.31034 7.72414 3.50897 7.72414 3.75172V7.28276ZM13.2414 23.8345C13.2414 24.0772 13.0428 24.2759 12.8 24.2759H9.26897C9.02621 24.2759 8.82759 24.0772 8.82759 23.8345V20.3034C8.82759 20.0607 9.02621 19.8621 9.26897 19.8621H12.8C13.0428 19.8621 13.2414 20.0607 13.2414 20.3034V23.8345ZM13.2414 18.3172C13.2414 18.56 13.0428 18.7586 12.8 18.7586H9.26897C9.02621 18.7586 8.82759 18.56 8.82759 18.3172V14.7862C8.82759 14.5434 9.02621 14.3448 9.26897 14.3448H12.8C13.0428 14.3448 13.2414 14.5434 13.2414 14.7862V18.3172ZM13.2414 12.8C13.2414 13.0428 13.0428 13.2414 12.8 13.2414H9.26897C9.02621 13.2414 8.82759 13.0428 8.82759 12.8V9.26897C8.82759 9.02621 9.02621 8.82759 9.26897 8.82759H12.8C13.0428 8.82759 13.2414 9.02621 13.2414 9.26897V12.8ZM13.2414 6.84138V7.28276C13.2414 7.52552 13.0428 7.72414 12.8 7.72414H9.26897C9.02621 7.72414 8.82759 7.52552 8.82759 7.28276V3.75172C8.82759 3.50897 9.02621 3.31034 9.26897 3.31034H12.8C13.0428 3.31034 13.2414 3.50897 13.2414 3.75172V6.84138ZM18.7586 23.8345C18.7586 24.0772 18.56 24.2759 18.3172 24.2759H14.7862C14.5434 24.2759 14.3448 24.0772 14.3448 23.8345V20.3034C14.3448 20.0607 14.5434 19.8621 14.7862 19.8621H18.3172C18.56 19.8621 18.7586 20.0607 18.7586 20.3034V23.8345ZM18.7586 18.3172C18.7586 18.56 18.56 18.7586 18.3172 18.7586H14.7862C14.5434 18.7586 14.3448 18.56 14.3448 18.3172V14.7862C14.3448 14.5434 14.5434 14.3448 14.7862 14.3448H18.3172C18.56 14.3448 18.7586 14.5434 18.7586 14.7862V18.3172ZM18.7586 12.8C18.7586 13.0428 18.56 13.2414 18.3172 13.2414H14.7862C14.5434 13.2414 14.3448 13.0428 14.3448 12.8V9.26897C14.3448 9.02621 14.5434 8.82759 14.7862 8.82759H18.3172C18.56 8.82759 18.7586 9.02621 18.7586 9.26897V12.8ZM18.7586 5.51724V7.28276C18.7586 7.52552 18.56 7.72414 18.3172 7.72414H14.7862C14.5434 7.72414 14.3448 7.52552 14.3448 7.28276V3.75172C14.3448 3.50897 14.5434 3.31034 14.7862 3.31034H18.3172C18.56 3.31034 18.7586 3.50897 18.7586 3.75172V5.51724Z" fill="white"/>
10990
+ </svg>
10991
+ </div>
10992
+
10993
+ <!-- Tab buttons container -->
10994
+ <div class="ds-tab-bar__tabs" *ngIf="tabs">
10995
+ <ion-tab-button
10996
+ *ngFor="let tab of tabs; trackBy: trackByTabId"
10997
+ [tab]="tab.id"
10998
+ [attr.data-icon]="tab.icon"
10999
+ [attr.data-icon-active]="tab.iconActive"
11000
+ [attr.aria-label]="tab.label"
11001
+ class="ds-tab-button ion-activatable"
11002
+ [class.tab-selected]="isTabActive(tab.id)">
11003
+ <div class="tab-icon-ripple"></div>
11004
+ <div class="tab-icon-wrapper">
11005
+ <ds-icon
11006
+ [name]="tab.icon"
11007
+ [size]="isDesktop() ? '20px' : '24px'"
11008
+ class="tab-icon-inactive"
11009
+ />
11010
+ <ds-icon
11011
+ [name]="tab.iconActive"
11012
+ [size]="isDesktop() ? '20px' : '24px'"
11013
+ class="tab-icon-active"
11014
+ />
11015
+ </div>
11016
+ <ion-label [attr.aria-hidden]="true">{{ tab.label }}</ion-label>
11017
+ </ion-tab-button>
11018
+ </div>
11019
+
11020
+ <!-- Avatar (desktop only, positioned via CSS) -->
11021
+ <div class="ds-tab-bar__actions">
11022
+ <ds-avatar
11023
+ [initials]="avatarInitials"
11024
+ [type]="avatarType"
11025
+ [src]="avatarSrc"
11026
+ [iconName]="avatarIconName"
11027
+ (click)="handleAvatarClick()"
11028
+ style="cursor: pointer;"
11029
+ />
11030
+ </div>
11031
+ </ion-tab-bar>
11032
+ </ion-tabs>
11033
+ `, styles: [":host{display:block;height:100vh;height:100dvh}ion-tabs{height:100%;background:var(--color-brand-secondary)}.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:calc(var(--ion-safe-area-bottom, 34px) - 4px)}@media (max-width: 767px){ion-tab-bar[slot=bottom]{position:fixed;bottom:0;left:0;right:0;z-index:100}}@media (max-width: 767px){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:env(safe-area-inset-bottom,0px)}}@media (display-mode: standalone){ion-tab-bar[slot=bottom]{padding-bottom:env(safe-area-inset-bottom,0px)!important}}@media (min-width: 768px){ion-tab-bar[slot=top]{--background: var(--color-brand-secondary);position:relative;display:flex;align-items:center;padding:12px 24px;height:64px;max-width:none}.ds-tab-bar__logo{display:flex;position:absolute;left:24px;align-items:center}.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-brand-base);display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:visible}.tab-icon-ripple{position:absolute;left:50%;top:50%;width:40px;height:40px;border-radius:50%;background:var(--color-brand-base);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-brand-base);border-radius:1000px}ion-tab-button ion-label{font-size:var(--font-size-xs);font-weight:400;letter-spacing:-.3px;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:auto;flex:0 0 auto;--color: rgba(255, 255, 255, .7);--color-selected: white;background:#ffffff1a;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{--color: white;--color-selected: white}.ds-tab-button.tab-selected{background:var(--color-background-brand);--color-selected: 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:#ffffff4d;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"] }]
11034
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { tabs: [{
11035
+ type: Input
11036
+ }], avatarType: [{
11037
+ type: Input
11038
+ }], avatarInitials: [{
11039
+ type: Input
11040
+ }], avatarSrc: [{
11041
+ type: Input
11042
+ }], avatarIconName: [{
11043
+ type: Input
11044
+ }], avatarClick: [{
11045
+ type: Output
11046
+ }] } });
11047
+
11048
+ class MobileTabsExampleComponent {
11049
+ userService;
11050
+ modalController;
11051
+ router;
11052
+ constructor(userService, modalController, router) {
11053
+ this.userService = userService;
11054
+ this.modalController = modalController;
11055
+ this.router = router;
11056
+ console.log('MobileTabsExampleComponent constructor');
11057
+ }
11058
+ ngOnInit() {
11059
+ console.log('MobileTabsExampleComponent ngOnInit');
11060
+ // Configure user avatar globally - this is now the single source of truth
11061
+ this.userService.setAvatarInitials('LM');
11062
+ this.userService.setAvatarType('initials');
11063
+ }
11064
+ tabs = [
11065
+ {
11066
+ id: 'home',
11067
+ label: 'Hjem',
11068
+ route: 'home',
11069
+ icon: 'remixHomeSmile2Line',
11070
+ iconActive: 'remixHomeSmile2Fill'
11071
+ },
11072
+ {
11073
+ id: 'inquiries',
11074
+ label: 'Henvendelser',
11075
+ route: 'inquiries',
11076
+ icon: 'remixFileList3Line',
11077
+ iconActive: 'remixFileList3Fill'
11078
+ },
11079
+ {
11080
+ id: 'announcements',
11081
+ label: 'Fællesskab',
11082
+ route: 'announcements',
11083
+ icon: 'remixCommunityLine',
11084
+ iconActive: 'remixCommunityFill'
11085
+ },
11086
+ {
11087
+ id: 'handbook',
11088
+ label: 'Håndbog',
11089
+ route: 'handbook',
11090
+ icon: 'remixBook2Line',
11091
+ iconActive: 'remixBook2Fill'
11092
+ }
11093
+ ];
11094
+ async handleAvatarClick() {
11095
+ console.log('Avatar clicked - opening profile bottom sheet');
11096
+ const sheet = await this.modalController.create({
11097
+ component: DsMobileActionsBottomSheetComponent,
11098
+ componentProps: {
11099
+ customActionGroups: [
11100
+ {
11101
+ actions: [
11102
+ {
11103
+ action: 'profile',
11104
+ title: 'Min profil',
11105
+ icon: 'remixUser3Line',
11106
+ destructive: false
11107
+ },
11108
+ {
11109
+ action: 'settings',
11110
+ title: 'Indstillinger',
11111
+ icon: 'remixSettings3Line',
11112
+ destructive: false
11113
+ },
11114
+ {
11115
+ action: 'whitelabel-demo',
11116
+ title: 'Whitelabel Demo',
11117
+ icon: 'remixPaletteLine',
11118
+ destructive: false
11119
+ }
11120
+ ]
11121
+ },
11122
+ {
11123
+ actions: [
11124
+ {
11125
+ action: 'logout',
11126
+ title: 'Log ud',
11127
+ icon: 'remixLogoutBoxLine',
11128
+ destructive: true
11129
+ }
11130
+ ]
11131
+ }
11132
+ ]
11133
+ },
11134
+ // Auto-height: no breakpoints, no handle
11135
+ cssClass: 'ds-bottom-sheet auto-height'
11136
+ });
11137
+ await sheet.present();
11138
+ const result = await sheet.onWillDismiss();
11139
+ if (result.data?.action) {
11140
+ console.log('Profile action selected:', result.data.action);
11141
+ switch (result.data.action) {
11142
+ case 'logout':
11143
+ console.log('Logging out...');
11144
+ // TODO: Implement logout logic
11145
+ break;
11146
+ case 'profile':
11147
+ console.log('Opening profile...');
11148
+ // TODO: Navigate to profile page
11149
+ break;
11150
+ case 'settings':
11151
+ console.log('Opening settings...');
11152
+ // TODO: Navigate to settings page
11153
+ break;
11154
+ case 'whitelabel-demo':
11155
+ console.log('Opening whitelabel demo...');
11156
+ this.router.navigate(['/whitelabel-demo']);
11157
+ break;
11158
+ }
11159
+ }
11160
+ }
11161
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileTabsExampleComponent, deps: [{ token: UserService }, { token: i1.ModalController }, { token: i1$1.Router }], target: i0.ɵɵFactoryTarget.Component });
11162
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: MobileTabsExampleComponent, isStandalone: true, selector: "app-mobile-tabs-example", ngImport: i0, template: `
11163
+ <ds-mobile-tabs
11164
+ [tabs]="tabs"
11165
+ [avatarInitials]="userService.avatarInitials()"
11166
+ (avatarClick)="handleAvatarClick()"
11167
+ />
11168
+ `, isInline: true, styles: [":host{display:block;height:100vh;width:100vw;position:relative}\n"], dependencies: [{ kind: "component", type: DsMobileTabsComponent, selector: "ds-mobile-tabs", inputs: ["tabs", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName"], outputs: ["avatarClick"] }] });
11169
+ }
11170
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileTabsExampleComponent, decorators: [{
11171
+ type: Component,
11172
+ args: [{ selector: 'app-mobile-tabs-example', standalone: true, imports: [DsMobileTabsComponent], template: `
11173
+ <ds-mobile-tabs
11174
+ [tabs]="tabs"
11175
+ [avatarInitials]="userService.avatarInitials()"
11176
+ (avatarClick)="handleAvatarClick()"
11177
+ />
11178
+ `, styles: [":host{display:block;height:100vh;width:100vw;position:relative}\n"] }]
11179
+ }], ctorParameters: () => [{ type: UserService }, { type: i1.ModalController }, { type: i1$1.Router }] });
11180
+
11181
+ /**
11182
+ * PostCreatePageComponent
11183
+ *
11184
+ * Full-screen detail page for creating new posts in the community feed.
11185
+ * Features Threads-inspired interface with rich text editing capabilities.
11186
+ */
11187
+ class PostCreatePageComponent {
11188
+ router;
11189
+ route;
11190
+ userService;
11191
+ textareaInput;
11192
+ postContent = '';
11193
+ username = signal('Lars Mikkelsen', ...(ngDevMode ? [{ debugName: "username" }] : []));
11194
+ placeholder = signal("What's new?", ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
11195
+ // Edit mode state
11196
+ isEditMode = signal(false, ...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
11197
+ postId = signal(null, ...(ngDevMode ? [{ debugName: "postId" }] : []));
11198
+ pageTitle = signal('New post', ...(ngDevMode ? [{ debugName: "pageTitle" }] : []));
11199
+ submitButtonLabel = signal('Post', ...(ngDevMode ? [{ debugName: "submitButtonLabel" }] : []));
11200
+ constructor(router, route, userService) {
11201
+ this.router = router;
11202
+ this.route = route;
11203
+ this.userService = userService;
11204
+ }
11205
+ ngOnInit() {
11206
+ // Check for edit mode via query parameters
11207
+ this.route.queryParams.subscribe(params => {
11208
+ const editMode = params['edit'] === 'true';
11209
+ const postId = params['id'];
11210
+ const content = params['content'];
11211
+ if (editMode && postId) {
11212
+ this.isEditMode.set(true);
11213
+ this.postId.set(postId);
11214
+ this.pageTitle.set('Edit post');
11215
+ this.submitButtonLabel.set('Save');
11216
+ // Prefill content if provided
11217
+ if (content) {
11218
+ this.postContent = decodeURIComponent(content);
11219
+ }
11220
+ }
11221
+ else {
11222
+ // Reset to create mode
11223
+ this.isEditMode.set(false);
11224
+ this.postId.set(null);
11225
+ this.pageTitle.set('New post');
11226
+ this.submitButtonLabel.set('Post');
11227
+ this.postContent = '';
11228
+ }
11229
+ });
11230
+ }
11231
+ ngAfterViewInit() {
11232
+ // Focus the textarea after view initialization to trigger keyboard on mobile
11233
+ setTimeout(() => {
11234
+ this.textareaInput?.nativeElement.focus();
11235
+ }, 300);
11236
+ }
11237
+ handleInput() {
11238
+ // Handle text input changes
11239
+ }
11240
+ canPost() {
11241
+ return this.postContent.trim().length > 0;
11242
+ }
11243
+ handleCancel() {
11244
+ if (this.postContent.trim().length > 0) {
11245
+ // Show confirmation dialog
11246
+ const confirmed = confirm('Discard this post?');
11247
+ if (confirmed) {
11248
+ this.router.navigate(['/mobile-tabs-example/community']);
11249
+ }
11250
+ }
11251
+ else {
11252
+ this.router.navigate(['/mobile-tabs-example/community']);
11253
+ }
11254
+ }
11255
+ handlePost() {
11256
+ if (!this.canPost())
11257
+ return;
11258
+ if (this.isEditMode()) {
11259
+ console.log('Updating post:', this.postId(), this.postContent);
11260
+ // TODO: Implement post update logic
11261
+ // this.postService.updatePost(this.postId(), this.postContent).subscribe(() => {
11262
+ // this.router.navigate(['/mobile-tabs-example/community']);
11263
+ // });
11264
+ }
11265
+ else {
11266
+ console.log('Creating post:', this.postContent);
11267
+ // TODO: Implement post creation logic
11268
+ // this.postService.createPost(this.postContent).subscribe(() => {
11269
+ // this.router.navigate(['/mobile-tabs-example/community']);
11270
+ // });
11271
+ }
11272
+ // For now, just navigate back
11273
+ this.router.navigate(['/mobile-tabs-example/community']);
11274
+ }
11275
+ handleAddImage() {
11276
+ console.log('Add image');
11277
+ // TODO: Open image picker
11278
+ }
11279
+ handleAddEmoji() {
11280
+ console.log('Add emoji');
11281
+ // TODO: Open emoji picker
11282
+ }
11283
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: PostCreatePageComponent, deps: [{ token: i1$1.Router }, { token: i1$1.ActivatedRoute }, { token: UserService }], target: i0.ɵɵFactoryTarget.Component });
11284
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: PostCreatePageComponent, isStandalone: true, selector: "app-post-create-page", viewQueries: [{ propertyName: "textareaInput", first: true, predicate: ["textareaInput"], descendants: true }], ngImport: i0, template: `
11285
+ <ds-mobile-page-details
11286
+ [title]="pageTitle()"
11287
+ [backRoute]="'/mobile-tabs-example/announcements'"
11288
+ (back)="handleCancel()">
11289
+
11290
+ <div class="post-create-container">
11291
+ <div class="post-composer">
11292
+ <div class="post-composer__avatar">
11293
+ <ds-avatar
11294
+ [initials]="userService.avatarInitials()"
11295
+ [type]="userService.avatarType()"
11296
+ [src]="userService.avatarSrc()"
11297
+ size="md" />
11298
+ </div>
11299
+
11300
+ <div class="post-composer__main">
11301
+ <div class="post-composer__header">
11302
+ <span class="post-composer__username">{{ username() }}</span>
11303
+ </div>
11304
+
11305
+ <textarea
11306
+ #textareaInput
11307
+ class="post-composer__textarea"
11308
+ [(ngModel)]="postContent"
11309
+ [placeholder]="placeholder()"
11310
+ (input)="handleInput()">
11311
+ </textarea>
11312
+
11313
+ <div class="post-composer__actions">
11314
+ <div class="post-composer__action-btns">
11315
+ <button class="post-composer__action-btn" (click)="handleAddImage()">
11316
+ <ds-icon name="remixImageLine" size="22px" />
11317
+ </button>
11318
+ <button class="post-composer__action-btn" (click)="handleAddEmoji()">
11319
+ <ds-icon name="remixEmotionLine" size="22px" />
11320
+ </button>
11321
+ </div>
11322
+
11323
+ <ds-button
11324
+ variant="primary"
11325
+ size="md"
11326
+ [disabled]="!canPost()"
11327
+ (clicked)="handlePost()">
11328
+ {{ submitButtonLabel() }}
11329
+ </ds-button>
11330
+ </div>
11331
+ </div>
11332
+ </div>
11333
+ </div>
11334
+ </ds-mobile-page-details>
11335
+ `, isInline: true, styles: [".post-create-container{display:flex;flex-direction:column;height:100%;max-width:640px}.content{flex:1;overflow-y:auto;padding:16px}.post-composer{display:flex;gap:12px;margin-bottom:24px;align-items:flex-start}.post-composer__avatar{flex-shrink:0;padding-top:2px}.post-composer__main{flex:1;min-width:0;display:flex;flex-direction:column;gap:12px}.post-composer__header{display:flex;align-items:center;gap:8px;height:32px}.post-composer__username{font-family:Brockmann,sans-serif;font-size:15px;font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a)}.post-composer__textarea{width:100%;min-height:120px;border:none;outline:none;resize:none;font-family:Brockmann,sans-serif;font-size:15px;font-weight:400;line-height:22px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);background:transparent;padding:0}.post-composer__textarea::placeholder{color:var(--color-text-tertiary, #999999)}.post-composer__actions{display:flex;align-items:center;justify-content:space-between;gap:16px}.post-composer__action-btns{display:flex;align-items:center;gap:16px}.post-composer__action-btn{background:none;border:none;padding:0;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--color-text-secondary, #737373);transition:color .2s ease}.post-composer__action-btn:hover{color:var(--color-text-primary, #1a1a1a)}.thread-line{position:absolute;left:35px;top:60px;bottom:0;width:2px;background:var(--border-color-default)}@media (max-width: 768px){.content{padding:12px 16px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { 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: DsMobilePageDetailsComponent, selector: "ds-mobile-page-details", inputs: ["title", "backRoute"], outputs: ["back"] }] });
11336
+ }
11337
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: PostCreatePageComponent, decorators: [{
11338
+ type: Component,
11339
+ args: [{ selector: 'app-post-create-page', standalone: true, imports: [
11340
+ CommonModule,
11341
+ FormsModule,
11342
+ DsAvatarComponent,
11343
+ DsIconComponent,
11344
+ DsButtonComponent,
11345
+ DsMobilePageDetailsComponent
11346
+ ], template: `
11347
+ <ds-mobile-page-details
11348
+ [title]="pageTitle()"
11349
+ [backRoute]="'/mobile-tabs-example/announcements'"
11350
+ (back)="handleCancel()">
11351
+
11352
+ <div class="post-create-container">
11353
+ <div class="post-composer">
11354
+ <div class="post-composer__avatar">
11355
+ <ds-avatar
11356
+ [initials]="userService.avatarInitials()"
11357
+ [type]="userService.avatarType()"
11358
+ [src]="userService.avatarSrc()"
11359
+ size="md" />
11360
+ </div>
11361
+
11362
+ <div class="post-composer__main">
11363
+ <div class="post-composer__header">
11364
+ <span class="post-composer__username">{{ username() }}</span>
11365
+ </div>
11366
+
11367
+ <textarea
11368
+ #textareaInput
11369
+ class="post-composer__textarea"
11370
+ [(ngModel)]="postContent"
11371
+ [placeholder]="placeholder()"
11372
+ (input)="handleInput()">
11373
+ </textarea>
11374
+
11375
+ <div class="post-composer__actions">
11376
+ <div class="post-composer__action-btns">
11377
+ <button class="post-composer__action-btn" (click)="handleAddImage()">
11378
+ <ds-icon name="remixImageLine" size="22px" />
11379
+ </button>
11380
+ <button class="post-composer__action-btn" (click)="handleAddEmoji()">
11381
+ <ds-icon name="remixEmotionLine" size="22px" />
11382
+ </button>
11383
+ </div>
11384
+
11385
+ <ds-button
11386
+ variant="primary"
11387
+ size="md"
11388
+ [disabled]="!canPost()"
11389
+ (clicked)="handlePost()">
11390
+ {{ submitButtonLabel() }}
11391
+ </ds-button>
11392
+ </div>
11393
+ </div>
11394
+ </div>
11395
+ </div>
11396
+ </ds-mobile-page-details>
11397
+ `, styles: [".post-create-container{display:flex;flex-direction:column;height:100%;max-width:640px}.content{flex:1;overflow-y:auto;padding:16px}.post-composer{display:flex;gap:12px;margin-bottom:24px;align-items:flex-start}.post-composer__avatar{flex-shrink:0;padding-top:2px}.post-composer__main{flex:1;min-width:0;display:flex;flex-direction:column;gap:12px}.post-composer__header{display:flex;align-items:center;gap:8px;height:32px}.post-composer__username{font-family:Brockmann,sans-serif;font-size:15px;font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a)}.post-composer__textarea{width:100%;min-height:120px;border:none;outline:none;resize:none;font-family:Brockmann,sans-serif;font-size:15px;font-weight:400;line-height:22px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);background:transparent;padding:0}.post-composer__textarea::placeholder{color:var(--color-text-tertiary, #999999)}.post-composer__actions{display:flex;align-items:center;justify-content:space-between;gap:16px}.post-composer__action-btns{display:flex;align-items:center;gap:16px}.post-composer__action-btn{background:none;border:none;padding:0;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--color-text-secondary, #737373);transition:color .2s ease}.post-composer__action-btn:hover{color:var(--color-text-primary, #1a1a1a)}.thread-line{position:absolute;left:35px;top:60px;bottom:0;width:2px;background:var(--border-color-default)}@media (max-width: 768px){.content{padding:12px 16px}}\n"] }]
11398
+ }], ctorParameters: () => [{ type: i1$1.Router }, { type: i1$1.ActivatedRoute }, { type: UserService }], propDecorators: { textareaInput: [{
11399
+ type: ViewChild,
11400
+ args: ['textareaInput']
11401
+ }] } });
11402
+
11403
+ class MobilePostDetailPageComponent {
11404
+ lightbox;
11405
+ bottomSheet;
11406
+ repliesCount = 6;
11407
+ constructor(lightbox, bottomSheet) {
11408
+ this.lightbox = lightbox;
11409
+ this.bottomSheet = bottomSheet;
11410
+ }
11411
+ /**
11412
+ * Open an image in the lightbox viewer
11413
+ */
11414
+ openImageLightbox(imageSrc, title, description) {
11415
+ const authorMeta = {
11416
+ name: 'Sophie Andersen',
11417
+ role: 'Lejer',
11418
+ avatarInitials: 'SA',
11419
+ timestamp: '4t siden'
11420
+ };
11421
+ this.lightbox.open({
11422
+ images: [
11423
+ {
11424
+ type: 'image',
11425
+ src: imageSrc,
11426
+ alt: title,
11427
+ title: title,
11428
+ description: description,
11429
+ isLiked: true,
11430
+ likeCount: 156,
11431
+ commentCount: 34
11432
+ }
11433
+ ],
11434
+ author: authorMeta,
11435
+ enableZoom: true,
11436
+ showControls: false, // Single image, no need for controls
11437
+ showInfo: true
11438
+ });
11439
+ }
11440
+ /**
11441
+ * Handle long press on a comment to show action sheet
11442
+ */
11443
+ async handleCommentLongPress(authorName, content, isOwnComment) {
11444
+ const sheet = await this.bottomSheet.create({
11445
+ component: DsMobileActionsBottomSheetComponent,
11446
+ componentProps: {
11447
+ isOwnContent: isOwnComment
11448
+ },
11449
+ breakpoints: [0, 1],
11450
+ initialBreakpoint: 1,
11451
+ handle: true,
11452
+ backdropDismiss: true,
11453
+ cssClass: 'auto-height'
11454
+ });
11455
+ const result = await sheet.onWillDismiss();
11456
+ if (result.role === 'select' && result.data) {
11457
+ const action = result.data.action;
11458
+ switch (action) {
11459
+ case 'like':
11460
+ console.log('Like comment by', authorName);
11461
+ // Implement like logic
11462
+ break;
11463
+ case 'reply':
11464
+ console.log('Reply to comment by', authorName);
11465
+ // Implement reply logic
11466
+ break;
11467
+ case 'edit':
11468
+ console.log('Edit comment by', authorName);
11469
+ // Implement edit logic
11470
+ break;
11471
+ case 'delete':
11472
+ console.log('Delete comment by', authorName);
11473
+ // Implement delete logic (with confirmation)
11474
+ break;
11475
+ }
11476
+ }
11477
+ }
11478
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobilePostDetailPageComponent, deps: [{ token: DsMobileLightboxService }, { token: DsMobileBottomSheetService }], target: i0.ɵɵFactoryTarget.Component });
11479
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: MobilePostDetailPageComponent, isStandalone: true, selector: "app-mobile-post-detail-page", ngImport: i0, template: `
11480
+ <ds-mobile-page-details title="Opslag">
11481
+ <div class="post-detail-container">
11482
+ <!-- Post Section -->
11483
+ <div class="post-section">
11484
+ <ds-mobile-post-card
11485
+ [authorName]="'Sophie Andersen'"
11486
+ [authorRole]="'Lejer'"
11487
+ [timestamp]="'4t siden'"
11488
+ [avatarInitials]="'SA'"
11489
+ [variant]="'detail'">
11490
+
11491
+ <post-content class="no-indent">
11492
+ <post-text>Se denne smukke udsigt fra min altan! Morgenkaffe har aldrig smagt så godt ☕️</post-text>
11493
+ <post-media>
11494
+ <img
11495
+ src="/Assets/Dummy-photos/balcony-view.jpg"
11496
+ alt="Altanudsigt"
11497
+ class="clickable-image"
11498
+ (click)="openImageLightbox('/Assets/Dummy-photos/balcony-view.jpg', 'Altanudsigt', 'Morgenkaffe har aldrig smagt så godt ☕️')"
11499
+ />
11500
+ </post-media>
11501
+ </post-content>
11502
+
11503
+ <post-actions class="no-indent">
11504
+ <action-like [active]="true" [count]="156" />
11505
+ <action-comment [count]="34" />
11506
+ </post-actions>
11507
+ </ds-mobile-post-card>
11508
+ </div>
11509
+
11510
+ <!-- Comments Section -->
11511
+ <div class="comments-section">
11512
+ <h2 class="comments-header">{{ repliesCount }} svar</h2>
11513
+
11514
+ <div class="comments-list">
11515
+ <ds-mobile-comment
11516
+ [authorName]="'Anders Jensen'"
11517
+ [authorRole]="'Lejer'"
11518
+ [timestamp]="'3t siden'"
11519
+ [avatarInitials]="'AJ'"
11520
+ [content]="'Wow, den udsigt er fantastisk! Hvilken etage er du på?'"
11521
+ [likeCount]="12"
11522
+ [clickable]="true"
11523
+ [isOwnComment]="false"
11524
+ (longPress)="handleCommentLongPress('Anders Jensen', 'Wow, den udsigt er fantastisk! Hvilken etage er du på?', false)" />
11525
+
11526
+ <ds-mobile-comment
11527
+ [authorName]="'Thomas Hansen'"
11528
+ [authorRole]="'Lejer'"
11529
+ [timestamp]="'3t siden'"
11530
+ [avatarInitials]="'TH'"
11531
+ [content]="'Smuk! Jeg kan også se byens silhuet fra min lejlighed 🌆'"
11532
+ [isLiked]="true"
11533
+ [likeCount]="8"
11534
+ [clickable]="true"
11535
+ [isOwnComment]="true"
11536
+ (longPress)="handleCommentLongPress('Thomas Hansen', 'Smuk! Jeg kan også se byens silhuet fra min lejlighed 🌆', true)" />
11537
+
11538
+ <ds-mobile-comment
11539
+ [authorName]="'Emma Petersen'"
11540
+ [authorRole]="'Lejer'"
11541
+ [timestamp]="'2t siden'"
11542
+ [avatarInitials]="'EP'"
11543
+ [content]="'Dette er præcis derfor jeg elsker at bo her. Godt billede!'"
11544
+ [likeCount]="15"
11545
+ [clickable]="true"
11546
+ [isOwnComment]="false"
11547
+ (longPress)="handleCommentLongPress('Emma Petersen', 'Dette er præcis derfor jeg elsker at bo her. Godt billede!', false)" />
11548
+
11549
+ <ds-mobile-comment
11550
+ [authorName]="'Nikolaj Sørensen'"
11551
+ [authorRole]="'Lejer'"
11552
+ [timestamp]="'2t siden'"
11553
+ [avatarInitials]="'NS'"
11554
+ [content]="'Solnedgangene fra den vinkel må være utrolige'"
11555
+ [likeCount]="6"
11556
+ [clickable]="true"
11557
+ [isOwnComment]="false"
11558
+ (longPress)="handleCommentLongPress('Nikolaj Sørensen', 'Solnedgangene fra den vinkel må være utrolige', false)" />
11559
+
11560
+ <ds-mobile-comment
11561
+ [authorName]="'Mette Larsen'"
11562
+ [authorRole]="'Lejer'"
11563
+ [timestamp]="'1t siden'"
11564
+ [avatarInitials]="'ML'"
11565
+ [content]="'Giver mig lyst til at få min morgenkaffe på altanen også! ☕'"
11566
+ [likeCount]="9"
11567
+ [clickable]="true"
11568
+ [isOwnComment]="false"
11569
+ (longPress)="handleCommentLongPress('Mette Larsen', 'Giver mig lyst til at få min morgenkaffe på altanen også! ☕', false)" />
11570
+
11571
+ <ds-mobile-comment
11572
+ [authorName]="'Frederik Nielsen'"
11573
+ [authorRole]="'Lejer'"
11574
+ [timestamp]="'1t siden'"
11575
+ [avatarInitials]="'FN'"
11576
+ [content]="'Heldig! Min altan vender den anden vej, men stadig pæn'"
11577
+ [likeCount]="4"
11578
+ [clickable]="true"
11579
+ [isOwnComment]="false"
11580
+ (longPress)="handleCommentLongPress('Frederik Nielsen', 'Heldig! Min altan vender den anden vej, men stadig pæn', false)" />
11581
+
11582
+ <ds-mobile-comment
11583
+ [authorName]="'Caroline Jensen'"
11584
+ [authorRole]="'Lejer'"
11585
+ [timestamp]="'45m siden'"
11586
+ [avatarInitials]="'CJ'"
11587
+ [content]="'Denne bygning har den bedste udsigt i byen, uden tvivl'"
11588
+ [likeCount]="11"
11589
+ [clickable]="true"
11590
+ [isOwnComment]="false"
11591
+ (longPress)="handleCommentLongPress('Caroline Jensen', 'Denne bygning har den bedste udsigt i byen, uden tvivl', false)" />
11592
+
11593
+ <ds-mobile-comment
11594
+ [authorName]="'Anna Hansen'"
11595
+ [authorRole]="'Lejer'"
11596
+ [timestamp]="'30m siden'"
11597
+ [avatarInitials]="'AH'"
11598
+ [content]="'Jeg skal se din altan en dag! 😍'"
11599
+ [clickable]="true"
11600
+ [isOwnComment]="false"
11601
+ (longPress)="handleCommentLongPress('Anna Hansen', 'Jeg skal se din altan en dag! 😍', false)" />
11602
+ </div>
11603
+ </div>
11604
+ </div>
11605
+ </ds-mobile-page-details>
11606
+ `, isInline: true, styles: [".post-detail-container{display:flex;flex-direction:column;gap:16px;max-width:640px}.post-section{border-bottom:1px solid var(--border-color-default);padding-bottom:16px}.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}.comments-section{display:flex;flex-direction:column;margin-left:-8px;margin-right:-8px}.comments-header{font-family:Brockmann,sans-serif;font-size:18px;font-weight:600;line-height:24px;color:var(--color-text-primary, #1a1a1a);margin-bottom:16px;padding-left:8px;padding-right:8px}.comments-list{display:flex;flex-direction:column}\n"], dependencies: [{ kind: "component", type: DsMobilePageDetailsComponent, selector: "ds-mobile-page-details", inputs: ["title", "backRoute"], outputs: ["back"] }, { kind: "component", type: DsMobilePostCardComponent, selector: "ds-mobile-post-card", inputs: ["authorName", "authorRole", "timestamp", "avatarInitials", "avatarType", "avatarSrc", "avatarIconName", "showBadge", "variant", "clickable"], outputs: ["postClick", "commentClick", "longPress"] }, { kind: "component", type: PostContentComponent$1, selector: "post-content" }, { kind: "component", type: PostTextComponent$1, selector: "post-text" }, { kind: "component", type: PostMediaComponent$1, selector: "post-media" }, { kind: "component", type: PostActionsComponent$1, selector: "post-actions" }, { kind: "component", type: ActionLikeComponent$1, selector: "action-like", inputs: ["active", "count"], outputs: ["activeChange", "countChange", "likeClick"] }, { kind: "component", type: ActionCommentComponent$1, selector: "action-comment", inputs: ["count"], outputs: ["commentClick"] }, { kind: "component", type: DsMobileCommentComponent, selector: "ds-mobile-comment", inputs: ["authorName", "authorRole", "timestamp", "content", "avatarInitials", "avatarType", "clickable", "isOwnComment", "isLiked", "likeCount"], outputs: ["isLikedChange", "likeCountChange", "commentClick", "replyClick", "editClick", "longPress"] }] });
11607
+ }
11608
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobilePostDetailPageComponent, decorators: [{
11609
+ type: Component,
11610
+ args: [{ selector: 'app-mobile-post-detail-page', standalone: true, imports: [
11611
+ DsMobilePageDetailsComponent,
11612
+ DsMobilePostCardComponent,
11613
+ PostContentComponent$1,
11614
+ PostTextComponent$1,
11615
+ PostMediaComponent$1,
11616
+ PostActionsComponent$1,
11617
+ ActionLikeComponent$1,
11618
+ ActionCommentComponent$1,
11619
+ DsMobileCommentComponent
11620
+ ], template: `
11621
+ <ds-mobile-page-details title="Opslag">
11622
+ <div class="post-detail-container">
11623
+ <!-- Post Section -->
11624
+ <div class="post-section">
11625
+ <ds-mobile-post-card
11626
+ [authorName]="'Sophie Andersen'"
11627
+ [authorRole]="'Lejer'"
11628
+ [timestamp]="'4t siden'"
11629
+ [avatarInitials]="'SA'"
11630
+ [variant]="'detail'">
11631
+
11632
+ <post-content class="no-indent">
11633
+ <post-text>Se denne smukke udsigt fra min altan! Morgenkaffe har aldrig smagt så godt ☕️</post-text>
11634
+ <post-media>
11635
+ <img
11636
+ src="/Assets/Dummy-photos/balcony-view.jpg"
11637
+ alt="Altanudsigt"
11638
+ class="clickable-image"
11639
+ (click)="openImageLightbox('/Assets/Dummy-photos/balcony-view.jpg', 'Altanudsigt', 'Morgenkaffe har aldrig smagt så godt ☕️')"
11640
+ />
11641
+ </post-media>
11642
+ </post-content>
11643
+
11644
+ <post-actions class="no-indent">
11645
+ <action-like [active]="true" [count]="156" />
11646
+ <action-comment [count]="34" />
11647
+ </post-actions>
11648
+ </ds-mobile-post-card>
11649
+ </div>
11650
+
11651
+ <!-- Comments Section -->
11652
+ <div class="comments-section">
11653
+ <h2 class="comments-header">{{ repliesCount }} svar</h2>
11654
+
11655
+ <div class="comments-list">
11656
+ <ds-mobile-comment
11657
+ [authorName]="'Anders Jensen'"
11658
+ [authorRole]="'Lejer'"
11659
+ [timestamp]="'3t siden'"
11660
+ [avatarInitials]="'AJ'"
11661
+ [content]="'Wow, den udsigt er fantastisk! Hvilken etage er du på?'"
11662
+ [likeCount]="12"
11663
+ [clickable]="true"
11664
+ [isOwnComment]="false"
11665
+ (longPress)="handleCommentLongPress('Anders Jensen', 'Wow, den udsigt er fantastisk! Hvilken etage er du på?', false)" />
11666
+
11667
+ <ds-mobile-comment
11668
+ [authorName]="'Thomas Hansen'"
11669
+ [authorRole]="'Lejer'"
11670
+ [timestamp]="'3t siden'"
11671
+ [avatarInitials]="'TH'"
11672
+ [content]="'Smuk! Jeg kan også se byens silhuet fra min lejlighed 🌆'"
11673
+ [isLiked]="true"
11674
+ [likeCount]="8"
11675
+ [clickable]="true"
11676
+ [isOwnComment]="true"
11677
+ (longPress)="handleCommentLongPress('Thomas Hansen', 'Smuk! Jeg kan også se byens silhuet fra min lejlighed 🌆', true)" />
11678
+
11679
+ <ds-mobile-comment
11680
+ [authorName]="'Emma Petersen'"
11681
+ [authorRole]="'Lejer'"
11682
+ [timestamp]="'2t siden'"
11683
+ [avatarInitials]="'EP'"
11684
+ [content]="'Dette er præcis derfor jeg elsker at bo her. Godt billede!'"
11685
+ [likeCount]="15"
11686
+ [clickable]="true"
11687
+ [isOwnComment]="false"
11688
+ (longPress)="handleCommentLongPress('Emma Petersen', 'Dette er præcis derfor jeg elsker at bo her. Godt billede!', false)" />
11689
+
11690
+ <ds-mobile-comment
11691
+ [authorName]="'Nikolaj Sørensen'"
11692
+ [authorRole]="'Lejer'"
11693
+ [timestamp]="'2t siden'"
11694
+ [avatarInitials]="'NS'"
11695
+ [content]="'Solnedgangene fra den vinkel må være utrolige'"
11696
+ [likeCount]="6"
11697
+ [clickable]="true"
11698
+ [isOwnComment]="false"
11699
+ (longPress)="handleCommentLongPress('Nikolaj Sørensen', 'Solnedgangene fra den vinkel må være utrolige', false)" />
11700
+
11701
+ <ds-mobile-comment
11702
+ [authorName]="'Mette Larsen'"
11703
+ [authorRole]="'Lejer'"
11704
+ [timestamp]="'1t siden'"
11705
+ [avatarInitials]="'ML'"
11706
+ [content]="'Giver mig lyst til at få min morgenkaffe på altanen også! ☕'"
11707
+ [likeCount]="9"
11708
+ [clickable]="true"
11709
+ [isOwnComment]="false"
11710
+ (longPress)="handleCommentLongPress('Mette Larsen', 'Giver mig lyst til at få min morgenkaffe på altanen også! ☕', false)" />
11711
+
11712
+ <ds-mobile-comment
11713
+ [authorName]="'Frederik Nielsen'"
11714
+ [authorRole]="'Lejer'"
11715
+ [timestamp]="'1t siden'"
11716
+ [avatarInitials]="'FN'"
11717
+ [content]="'Heldig! Min altan vender den anden vej, men stadig pæn'"
11718
+ [likeCount]="4"
11719
+ [clickable]="true"
11720
+ [isOwnComment]="false"
11721
+ (longPress)="handleCommentLongPress('Frederik Nielsen', 'Heldig! Min altan vender den anden vej, men stadig pæn', false)" />
11722
+
11723
+ <ds-mobile-comment
11724
+ [authorName]="'Caroline Jensen'"
11725
+ [authorRole]="'Lejer'"
11726
+ [timestamp]="'45m siden'"
11727
+ [avatarInitials]="'CJ'"
11728
+ [content]="'Denne bygning har den bedste udsigt i byen, uden tvivl'"
11729
+ [likeCount]="11"
11730
+ [clickable]="true"
11731
+ [isOwnComment]="false"
11732
+ (longPress)="handleCommentLongPress('Caroline Jensen', 'Denne bygning har den bedste udsigt i byen, uden tvivl', false)" />
11733
+
11734
+ <ds-mobile-comment
11735
+ [authorName]="'Anna Hansen'"
11736
+ [authorRole]="'Lejer'"
11737
+ [timestamp]="'30m siden'"
11738
+ [avatarInitials]="'AH'"
11739
+ [content]="'Jeg skal se din altan en dag! 😍'"
11740
+ [clickable]="true"
11741
+ [isOwnComment]="false"
11742
+ (longPress)="handleCommentLongPress('Anna Hansen', 'Jeg skal se din altan en dag! 😍', false)" />
11743
+ </div>
11744
+ </div>
11745
+ </div>
11746
+ </ds-mobile-page-details>
11747
+ `, styles: [".post-detail-container{display:flex;flex-direction:column;gap:16px;max-width:640px}.post-section{border-bottom:1px solid var(--border-color-default);padding-bottom:16px}.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}.comments-section{display:flex;flex-direction:column;margin-left:-8px;margin-right:-8px}.comments-header{font-family:Brockmann,sans-serif;font-size:18px;font-weight:600;line-height:24px;color:var(--color-text-primary, #1a1a1a);margin-bottom:16px;padding-left:8px;padding-right:8px}.comments-list{display:flex;flex-direction:column}\n"] }]
11748
+ }], ctorParameters: () => [{ type: DsMobileLightboxService }, { type: DsMobileBottomSheetService }] });
11749
+
11750
+ const DEFAULT_CONFIG = {
11751
+ logoUrl: '/assets/logos/propbinder-logo.svg',
11752
+ logoMarkUrl: '/assets/logos/propbinder-logomark.svg',
11753
+ logoAlt: 'Propbinder',
11754
+ primaryColor: '#6B5FF5', // Propbinder brand purple (--color-background-brand)
11755
+ secondaryColor: '#221a4c', // Propbinder dark purple for headers (--color-brand-secondary)
11756
+ organizationName: 'Propbinder',
11757
+ organizationId: 'default'
11758
+ };
11759
+ /**
11760
+ * WhitelabelService
11761
+ *
11762
+ * Manages whitelabel configuration including logos and brand colors.
11763
+ * Automatically updates CSS custom properties when colors change.
11764
+ *
11765
+ * @example
11766
+ * Initialize with custom config:
11767
+ * ```typescript
11768
+ * whitelabelService.initialize({
11769
+ * logoUrl: '/assets/logos/acme-logo.svg',
11770
+ * logoMarkUrl: '/assets/logos/acme-mark.svg',
11771
+ * primaryColor: '#2563eb',
11772
+ * secondaryColor: '#3b82f6',
11773
+ * organizationName: 'Acme Corp'
11774
+ * });
11775
+ * ```
11776
+ *
11777
+ * Load from API:
11778
+ * ```typescript
11779
+ * await whitelabelService.loadFromApi('acme-corp');
11780
+ * ```
11781
+ */
11782
+ class WhitelabelService {
11783
+ _config = signal(DEFAULT_CONFIG, ...(ngDevMode ? [{ debugName: "_config" }] : []));
11784
+ // Readonly getters for accessing config values
11785
+ logoUrl = () => this._config().logoUrl;
11786
+ logoMarkUrl = () => this._config().logoMarkUrl;
11787
+ logoAlt = () => this._config().logoAlt;
11788
+ primaryColor = () => this._config().primaryColor;
11789
+ secondaryColor = () => this._config().secondaryColor;
11790
+ organizationName = () => this._config().organizationName;
11791
+ organizationId = () => this._config().organizationId;
11792
+ // Full config accessor
11793
+ config = this._config.asReadonly();
11794
+ constructor() {
11795
+ // Apply default colors on initialization
11796
+ this.applyColors(DEFAULT_CONFIG.primaryColor, DEFAULT_CONFIG.secondaryColor);
11797
+ // Watch for config changes and update CSS custom properties
11798
+ effect(() => {
11799
+ const config = this._config();
11800
+ this.applyColors(config.primaryColor, config.secondaryColor);
11801
+ });
11802
+ }
11803
+ /**
11804
+ * Initialize whitelabel configuration
11805
+ * Call this early in app initialization (app.config.ts or app.component.ts)
11806
+ */
11807
+ initialize(config) {
11808
+ this._config.update(current => ({
11809
+ ...current,
11810
+ ...config
11811
+ }));
11812
+ }
11813
+ /**
11814
+ * Load whitelabel config from API
11815
+ * Typically called on app startup based on subdomain, user tenant, etc.
11816
+ *
11817
+ * @param organizationId - The organization identifier (subdomain, tenant ID, etc.)
11818
+ */
11819
+ async loadFromApi(organizationId) {
11820
+ try {
11821
+ // Example API call structure
11822
+ // const response = await fetch(`/api/whitelabel/${organizationId || 'default'}`);
11823
+ // const config = await response.json();
11824
+ // this.initialize(config);
11825
+ console.log('Loading whitelabel config from API for:', organizationId);
11826
+ // Example: Different configs for different organizations
11827
+ if (organizationId === 'demo-client') {
11828
+ this.initialize({
11829
+ logoUrl: '/assets/logos/demo-logo.svg',
11830
+ logoMarkUrl: '/assets/logos/demo-mark.svg',
11831
+ logoAlt: 'Demo Client',
11832
+ primaryColor: '#2563eb', // Blue
11833
+ secondaryColor: '#3b82f6', // Lighter blue
11834
+ organizationName: 'Demo Client',
11835
+ organizationId: 'demo-client'
11836
+ });
11837
+ }
11838
+ // Add more organization-specific configs as needed
11839
+ }
11840
+ catch (error) {
11841
+ console.error('Failed to load whitelabel config:', error);
11842
+ // Fallback to defaults already set
11843
+ }
11844
+ }
11845
+ /**
11846
+ * Update config dynamically (e.g., when user switches organizations)
11847
+ */
11848
+ updateConfig(updates) {
11849
+ this.initialize(updates);
11850
+ }
11851
+ /**
11852
+ * Update only the brand colors
11853
+ */
11854
+ updateColors(primaryColor, secondaryColor) {
11855
+ this._config.update(current => ({
11856
+ ...current,
11857
+ primaryColor,
11858
+ secondaryColor
11859
+ }));
11860
+ }
11861
+ /**
11862
+ * Reset to default configuration
11863
+ */
11864
+ resetToDefault() {
11865
+ this._config.set(DEFAULT_CONFIG);
11866
+ }
11867
+ /**
11868
+ * Apply colors to CSS custom properties
11869
+ * This updates the actual CSS variables used throughout the app
11870
+ */
11871
+ applyColors(primaryColor, secondaryColor) {
11872
+ if (typeof document !== 'undefined') {
11873
+ const root = document.documentElement;
11874
+ root.style.setProperty('--color-background-brand', primaryColor);
11875
+ root.style.setProperty('--color-brand-secondary', secondaryColor);
11876
+ console.log('Applied whitelabel colors:', { primaryColor, secondaryColor });
11877
+ }
11878
+ }
11879
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
11880
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelService, providedIn: 'root' });
11881
+ }
11882
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelService, decorators: [{
11883
+ type: Injectable,
11884
+ args: [{
11885
+ providedIn: 'root'
11886
+ }]
11887
+ }], ctorParameters: () => [] });
11888
+
11889
+ /**
11890
+ * DsLogoComponent
11891
+ *
11892
+ * Displays the whitelabeled logo or logomark based on current configuration.
11893
+ * Automatically pulls logo assets from WhitelabelService.
11894
+ *
11895
+ * @example
11896
+ * Full logo in header:
11897
+ * ```html
11898
+ * <ds-logo variant="full" size="md" />
11899
+ * ```
11900
+ *
11901
+ * Logomark for compact spaces:
11902
+ * ```html
11903
+ * <ds-logo variant="mark" size="sm" />
11904
+ * ```
11905
+ */
11906
+ class DsLogoComponent {
11907
+ whitelabelService = inject(WhitelabelService);
11908
+ variant = 'full';
11909
+ size = 'md';
11910
+ customHeight;
11911
+ customWidth;
11912
+ logoSrc = computed(() => {
11913
+ return this.variant === 'full'
11914
+ ? this.whitelabelService.logoUrl()
11915
+ : this.whitelabelService.logoMarkUrl();
11916
+ }, ...(ngDevMode ? [{ debugName: "logoSrc" }] : []));
11917
+ logoAlt = computed(() => {
11918
+ const alt = this.whitelabelService.logoAlt();
11919
+ return this.variant === 'full' ? alt : `${alt} logo`;
11920
+ }, ...(ngDevMode ? [{ debugName: "logoAlt" }] : []));
11921
+ logoClasses = computed(() => {
11922
+ return `logo logo--${this.variant} logo--${this.size}`;
11923
+ }, ...(ngDevMode ? [{ debugName: "logoClasses" }] : []));
11924
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsLogoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11925
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: DsLogoComponent, isStandalone: true, selector: "ds-logo", inputs: { variant: "variant", size: "size", customHeight: "customHeight", customWidth: "customWidth" }, ngImport: i0, template: `
11926
+ <img
11927
+ [src]="logoSrc()"
11928
+ [alt]="logoAlt()"
11929
+ [class]="logoClasses()"
11930
+ [style.height.px]="customHeight"
11931
+ [style.width.px]="customWidth"
11932
+ />
11933
+ `, isInline: true, styles: [":host{display:inline-block;line-height:0}.logo{display:block;object-fit:contain}.logo--full.logo--sm{height:24px;width:auto}.logo--full.logo--md{height:32px;width:auto}.logo--full.logo--lg{height:40px;width:auto}.logo--full.logo--xl{height:48px;width:auto}.logo--mark.logo--sm{height:20px;width:20px}.logo--mark.logo--md{height:28px;width:28px}.logo--mark.logo--lg{height:36px;width:36px}.logo--mark.logo--xl{height:44px;width:44px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
11934
+ }
11935
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsLogoComponent, decorators: [{
11936
+ type: Component,
11937
+ args: [{ selector: 'ds-logo', standalone: true, imports: [CommonModule], template: `
11938
+ <img
11939
+ [src]="logoSrc()"
11940
+ [alt]="logoAlt()"
11941
+ [class]="logoClasses()"
11942
+ [style.height.px]="customHeight"
11943
+ [style.width.px]="customWidth"
11944
+ />
11945
+ `, styles: [":host{display:inline-block;line-height:0}.logo{display:block;object-fit:contain}.logo--full.logo--sm{height:24px;width:auto}.logo--full.logo--md{height:32px;width:auto}.logo--full.logo--lg{height:40px;width:auto}.logo--full.logo--xl{height:48px;width:auto}.logo--mark.logo--sm{height:20px;width:20px}.logo--mark.logo--md{height:28px;width:28px}.logo--mark.logo--lg{height:36px;width:36px}.logo--mark.logo--xl{height:44px;width:44px}\n"] }]
11946
+ }], propDecorators: { variant: [{
11947
+ type: Input
11948
+ }], size: [{
11949
+ type: Input
11950
+ }], customHeight: [{
11951
+ type: Input
11952
+ }], customWidth: [{
11953
+ type: Input
11954
+ }] } });
11955
+
11956
+ /**
11957
+ * DsAvatarWithBadgeComponent
11958
+ *
11959
+ * Displays an avatar with a logomark badge overlay.
11960
+ * Useful for showing user avatars with organization branding.
11961
+ *
11962
+ * @example
11963
+ * ```html
11964
+ * <ds-avatar-with-badge
11965
+ * [type]="'initials'"
11966
+ * [initials]="'JD'"
11967
+ * [size]="'lg'"
11968
+ * [badgePosition]="'bottom-right'"
11969
+ * />
11970
+ * ```
11971
+ */
11972
+ class DsAvatarWithBadgeComponent {
11973
+ // Avatar props
11974
+ type = 'initials';
11975
+ size = 'md';
11976
+ initials = '';
11977
+ src = '';
11978
+ iconName = 'remixUser3Fill';
11979
+ // Badge props
11980
+ showBadge = true;
11981
+ badgePosition = 'bottom-right';
11982
+ badgeClasses = computed(() => {
11983
+ return `avatar-badge avatar-badge--${this.badgePosition} avatar-badge--${this.size}`;
11984
+ }, ...(ngDevMode ? [{ debugName: "badgeClasses" }] : []));
11985
+ // Calculate badge logo size based on avatar size
11986
+ badgeLogoSize = computed(() => {
11987
+ const sizeMap = {
11988
+ xs: 10,
11989
+ sm: 14,
11990
+ md: 16,
11991
+ lg: 22,
11992
+ xl: 28
11993
+ };
11994
+ return sizeMap[this.size];
11995
+ }, ...(ngDevMode ? [{ debugName: "badgeLogoSize" }] : []));
11996
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsAvatarWithBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11997
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: DsAvatarWithBadgeComponent, isStandalone: true, selector: "ds-avatar-with-badge", inputs: { type: "type", size: "size", initials: "initials", src: "src", iconName: "iconName", showBadge: "showBadge", badgePosition: "badgePosition" }, ngImport: i0, template: `
11998
+ <div class="avatar-badge-container">
11999
+ <ds-avatar
12000
+ [type]="type"
12001
+ [size]="size"
12002
+ [initials]="initials"
12003
+ [src]="src"
12004
+ [iconName]="iconName"
12005
+ />
12006
+
12007
+ @if (showBadge) {
12008
+ <div [class]="badgeClasses()">
12009
+ <ds-logo
12010
+ variant="mark"
12011
+ [customHeight]="badgeLogoSize()"
12012
+ [customWidth]="badgeLogoSize()"
12013
+ />
12014
+ </div>
12015
+ }
12016
+ </div>
12017
+ `, isInline: true, styles: [":host{display:inline-block;position:relative}.avatar-badge-container{position:relative;display:inline-block}.avatar-badge{position:absolute;background:#fff;border-radius:50%;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 4px #0000001a}.avatar-badge--bottom-right{bottom:0;right:0}.avatar-badge--bottom-left{bottom:0;left:0}.avatar-badge--top-right{top:0;right:0}.avatar-badge--top-left{top:0;left:0}.avatar-badge--xs{width:16px;height:16px;padding:3px}.avatar-badge--sm{width:20px;height:20px;padding:3px}.avatar-badge--md{width:24px;height:24px;padding:4px}.avatar-badge--lg{width:32px;height:32px;padding:5px}.avatar-badge--xl{width:40px;height:40px;padding:6px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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"] }] });
12018
+ }
12019
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsAvatarWithBadgeComponent, decorators: [{
12020
+ type: Component,
12021
+ args: [{ selector: 'ds-avatar-with-badge', standalone: true, imports: [CommonModule, DsAvatarComponent, DsLogoComponent], encapsulation: ViewEncapsulation.Emulated, template: `
12022
+ <div class="avatar-badge-container">
12023
+ <ds-avatar
12024
+ [type]="type"
12025
+ [size]="size"
12026
+ [initials]="initials"
12027
+ [src]="src"
12028
+ [iconName]="iconName"
12029
+ />
12030
+
12031
+ @if (showBadge) {
12032
+ <div [class]="badgeClasses()">
12033
+ <ds-logo
12034
+ variant="mark"
12035
+ [customHeight]="badgeLogoSize()"
12036
+ [customWidth]="badgeLogoSize()"
12037
+ />
12038
+ </div>
12039
+ }
12040
+ </div>
12041
+ `, styles: [":host{display:inline-block;position:relative}.avatar-badge-container{position:relative;display:inline-block}.avatar-badge{position:absolute;background:#fff;border-radius:50%;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 4px #0000001a}.avatar-badge--bottom-right{bottom:0;right:0}.avatar-badge--bottom-left{bottom:0;left:0}.avatar-badge--top-right{top:0;right:0}.avatar-badge--top-left{top:0;left:0}.avatar-badge--xs{width:16px;height:16px;padding:3px}.avatar-badge--sm{width:20px;height:20px;padding:3px}.avatar-badge--md{width:24px;height:24px;padding:4px}.avatar-badge--lg{width:32px;height:32px;padding:5px}.avatar-badge--xl{width:40px;height:40px;padding:6px}\n"] }]
12042
+ }], propDecorators: { type: [{
12043
+ type: Input
12044
+ }], size: [{
12045
+ type: Input
12046
+ }], initials: [{
12047
+ type: Input
12048
+ }], src: [{
12049
+ type: Input
12050
+ }], iconName: [{
12051
+ type: Input
12052
+ }], showBadge: [{
12053
+ type: Input
12054
+ }], badgePosition: [{
12055
+ type: Input
12056
+ }] } });
12057
+
12058
+ /**
12059
+ * Whitelabel Demo Page
12060
+ *
12061
+ * Demonstrates the whitelabeling system with live color and logo switching.
12062
+ */
12063
+ class WhitelabelDemoPage {
12064
+ whitelabelService = inject(WhitelabelService);
12065
+ // Custom color inputs
12066
+ customPrimaryColor = '#6B5FF5';
12067
+ customSecondaryColor = '#221a4c';
12068
+ // Logo upload state
12069
+ uploadedFullLogoName = '';
12070
+ uploadedLogomarkName = '';
12071
+ ngOnInit() {
12072
+ console.log('Whitelabel Demo Page initialized');
12073
+ // Initialize custom colors with current values
12074
+ this.customPrimaryColor = this.whitelabelService.primaryColor();
12075
+ this.customSecondaryColor = this.whitelabelService.secondaryColor();
12076
+ }
12077
+ handleFullLogoUpload(event) {
12078
+ const input = event.target;
12079
+ if (input.files && input.files[0]) {
12080
+ const file = input.files[0];
12081
+ this.uploadedFullLogoName = file.name;
12082
+ // Convert to data URL
12083
+ const reader = new FileReader();
12084
+ reader.onload = (e) => {
12085
+ const dataUrl = e.target?.result;
12086
+ this.whitelabelService.updateConfig({
12087
+ logoUrl: dataUrl
12088
+ });
12089
+ };
12090
+ reader.readAsDataURL(file);
12091
+ }
12092
+ }
12093
+ handleLogomarkUpload(event) {
12094
+ const input = event.target;
12095
+ if (input.files && input.files[0]) {
12096
+ const file = input.files[0];
12097
+ this.uploadedLogomarkName = file.name;
12098
+ // Convert to data URL
12099
+ const reader = new FileReader();
12100
+ reader.onload = (e) => {
12101
+ const dataUrl = e.target?.result;
12102
+ this.whitelabelService.updateConfig({
12103
+ logoMarkUrl: dataUrl
12104
+ });
12105
+ };
12106
+ reader.readAsDataURL(file);
12107
+ }
12108
+ }
12109
+ resetFullLogo() {
12110
+ this.uploadedFullLogoName = '';
12111
+ this.whitelabelService.updateConfig({
12112
+ logoUrl: '/assets/logos/propbinder-logo.svg'
12113
+ });
12114
+ }
12115
+ resetLogomark() {
12116
+ this.uploadedLogomarkName = '';
12117
+ this.whitelabelService.updateConfig({
12118
+ logoMarkUrl: '/assets/logos/propbinder-logomark.svg'
12119
+ });
12120
+ }
12121
+ configJson() {
12122
+ return JSON.stringify(this.whitelabelService.config(), null, 2);
12123
+ }
12124
+ applyDefaultTheme() {
12125
+ this.whitelabelService.updateConfig({
12126
+ primaryColor: '#6B5FF5',
12127
+ secondaryColor: '#221a4c',
12128
+ organizationName: 'Propbinder',
12129
+ organizationId: 'default'
12130
+ });
12131
+ this.updateCustomColorInputs();
12132
+ }
12133
+ applyBlueTheme() {
12134
+ this.whitelabelService.updateConfig({
12135
+ primaryColor: '#2563eb',
12136
+ secondaryColor: '#3b82f6',
12137
+ organizationName: 'Blue Corp',
12138
+ organizationId: 'blue-corp'
12139
+ });
12140
+ this.updateCustomColorInputs();
12141
+ }
12142
+ applyGreenTheme() {
12143
+ this.whitelabelService.updateConfig({
12144
+ primaryColor: '#16a34a',
12145
+ secondaryColor: '#22c55e',
12146
+ organizationName: 'Green Industries',
12147
+ organizationId: 'green-industries'
12148
+ });
12149
+ this.updateCustomColorInputs();
12150
+ }
12151
+ applyOrangeTheme() {
12152
+ this.whitelabelService.updateConfig({
12153
+ primaryColor: '#ea580c',
12154
+ secondaryColor: '#f97316',
12155
+ organizationName: 'Orange Solutions',
12156
+ organizationId: 'orange-solutions'
12157
+ });
12158
+ this.updateCustomColorInputs();
12159
+ }
12160
+ applyCustomColors() {
12161
+ this.whitelabelService.updateColors(this.customPrimaryColor, this.customSecondaryColor);
12162
+ }
12163
+ updateCustomColorInputs() {
12164
+ this.customPrimaryColor = this.whitelabelService.primaryColor();
12165
+ this.customSecondaryColor = this.whitelabelService.secondaryColor();
12166
+ }
12167
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelDemoPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
12168
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: WhitelabelDemoPage, isStandalone: true, selector: "app-whitelabel-demo", ngImport: i0, template: `
12169
+ <ion-header>
12170
+ <ion-toolbar>
12171
+ <ion-title>Whitelabel Demo</ion-title>
12172
+ </ion-toolbar>
12173
+ </ion-header>
12174
+
12175
+ <ion-content>
12176
+ <div class="demo-container">
12177
+ <!-- Logo Section -->
12178
+ <div class="demo-section">
12179
+ <h2>Logos</h2>
12180
+ <div class="logo-showcase">
12181
+ <div>
12182
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Full Logo (md)</p>
12183
+ <ds-logo variant="full" size="md" />
12184
+ </div>
12185
+ <div>
12186
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Logomark (sm)</p>
12187
+ <ds-logo variant="mark" size="sm" />
12188
+ </div>
12189
+ <div>
12190
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Logomark (md)</p>
12191
+ <ds-logo variant="mark" size="md" />
12192
+ </div>
12193
+ <div>
12194
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Logomark (lg)</p>
12195
+ <ds-logo variant="mark" size="lg" />
12196
+ </div>
12197
+ </div>
12198
+
12199
+ <!-- Logo Upload Section -->
12200
+ <div class="logo-upload">
12201
+ <div class="upload-section">
12202
+ <h3>Upload Full Logo</h3>
12203
+ <div class="file-input-wrapper">
12204
+ <input
12205
+ #fullLogoInput
12206
+ type="file"
12207
+ accept="image/*"
12208
+ (change)="handleFullLogoUpload($event)"
12209
+ />
12210
+ <button class="upload-button" (click)="fullLogoInput.click()">
12211
+ Choose File
12212
+ </button>
12213
+ @if (uploadedFullLogoName) {
12214
+ <span class="file-name">{{ uploadedFullLogoName }}</span>
12215
+ }
12216
+ @if (uploadedFullLogoName) {
12217
+ <button class="reset-button" (click)="resetFullLogo()">Reset</button>
12218
+ }
12219
+ </div>
12220
+ </div>
12221
+
12222
+ <div class="upload-section">
12223
+ <h3>Upload Logomark</h3>
12224
+ <div class="file-input-wrapper">
12225
+ <input
12226
+ #logomarkInput
12227
+ type="file"
12228
+ accept="image/*"
12229
+ (change)="handleLogomarkUpload($event)"
12230
+ />
12231
+ <button class="upload-button" (click)="logomarkInput.click()">
12232
+ Choose File
12233
+ </button>
12234
+ @if (uploadedLogomarkName) {
12235
+ <span class="file-name">{{ uploadedLogomarkName }}</span>
12236
+ }
12237
+ @if (uploadedLogomarkName) {
12238
+ <button class="reset-button" (click)="resetLogomark()">Reset</button>
12239
+ }
12240
+ </div>
12241
+ </div>
12242
+
12243
+ <p style="font-size: 12px; color: #999; margin-top: 12px;">
12244
+ Tip: Use SVG or PNG files with transparent backgrounds for best results
12245
+ </p>
12246
+ </div>
12247
+ </div>
12248
+
12249
+ <!-- Avatar with Badge Section -->
12250
+ <div class="demo-section">
12251
+ <h2>Avatar with Logo Badge</h2>
12252
+ <div class="avatar-showcase">
12253
+ <div>
12254
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Small</p>
12255
+ <ds-avatar-with-badge
12256
+ [type]="'initials'"
12257
+ [initials]="'JD'"
12258
+ [size]="'sm'"
12259
+ [badgePosition]="'bottom-right'"
12260
+ />
12261
+ </div>
12262
+ <div>
12263
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Medium</p>
12264
+ <ds-avatar-with-badge
12265
+ [type]="'initials'"
12266
+ [initials]="'JD'"
12267
+ [size]="'md'"
12268
+ [badgePosition]="'bottom-right'"
12269
+ />
12270
+ </div>
12271
+ <div>
12272
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Large</p>
12273
+ <ds-avatar-with-badge
12274
+ [type]="'initials'"
12275
+ [initials]="'JD'"
12276
+ [size]="'lg'"
12277
+ [badgePosition]="'bottom-right'"
12278
+ />
12279
+ </div>
12280
+ <div>
12281
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">X-Large</p>
12282
+ <ds-avatar-with-badge
12283
+ [type]="'initials'"
12284
+ [initials]="'JD'"
12285
+ [size]="'xl'"
12286
+ [badgePosition]="'bottom-right'"
12287
+ />
12288
+ </div>
12289
+ </div>
12290
+ </div>
12291
+
12292
+ <!-- Color Section -->
12293
+ <div class="demo-section">
12294
+ <h2>Brand Colors</h2>
12295
+ <div class="color-demo">
12296
+ <p style="margin-bottom: 8px; color: #666;">Current brand colors:</p>
12297
+ <div class="color-swatch">
12298
+ <div class="swatch swatch--primary">
12299
+ Primary<br/>
12300
+ <span style="font-size: 11px; opacity: 0.9;">{{ whitelabelService.primaryColor() }}</span>
12301
+ </div>
12302
+ <div class="swatch swatch--secondary">
12303
+ Secondary<br/>
12304
+ <span style="font-size: 11px; opacity: 0.9;">{{ whitelabelService.secondaryColor() }}</span>
12305
+ </div>
12306
+ </div>
12307
+
12308
+ <!-- Custom Color Inputs -->
12309
+ <div class="color-inputs">
12310
+ <div class="color-input-row">
12311
+ <label>Primary:</label>
12312
+ <input
12313
+ type="color"
12314
+ [(ngModel)]="customPrimaryColor"
12315
+ (change)="applyCustomColors()"
12316
+ />
12317
+ <input
12318
+ type="text"
12319
+ [(ngModel)]="customPrimaryColor"
12320
+ (change)="applyCustomColors()"
12321
+ placeholder="#6B5FF5"
12322
+ />
12323
+ </div>
12324
+ <div class="color-input-row">
12325
+ <label>Secondary:</label>
12326
+ <input
12327
+ type="color"
12328
+ [(ngModel)]="customSecondaryColor"
12329
+ (change)="applyCustomColors()"
12330
+ />
12331
+ <input
12332
+ type="text"
12333
+ [(ngModel)]="customSecondaryColor"
12334
+ (change)="applyCustomColors()"
12335
+ placeholder="#221a4c"
12336
+ />
12337
+ </div>
12338
+ </div>
12339
+ </div>
12340
+ </div>
12341
+
12342
+ <!-- Theme Switcher -->
12343
+ <div class="demo-section">
12344
+ <h2>Switch Organization Theme</h2>
12345
+ <div class="button-group">
12346
+ <ion-button (click)="applyDefaultTheme()">
12347
+ Default (Purple)
12348
+ </ion-button>
12349
+ <ion-button (click)="applyBlueTheme()">
12350
+ Blue Theme
12351
+ </ion-button>
12352
+ <ion-button (click)="applyGreenTheme()">
12353
+ Green Theme
12354
+ </ion-button>
12355
+ <ion-button (click)="applyOrangeTheme()">
12356
+ Orange Theme
12357
+ </ion-button>
12358
+ </div>
12359
+ </div>
12360
+
12361
+ <!-- Current Config -->
12362
+ <div class="demo-section">
12363
+ <h2>Current Configuration</h2>
12364
+ <div class="current-config">
12365
+ <pre>{{ configJson() }}</pre>
12366
+ </div>
12367
+ </div>
12368
+ </div>
12369
+ </ion-content>
12370
+ `, isInline: true, styles: [":host{display:block}ion-toolbar{--background: var(--color-brand-secondary);--color: white}ion-content{--background: #f5f5f5}.demo-container{padding:20px;max-width:800px;margin:0 auto}.demo-section{margin-bottom:32px}.demo-section h2{margin-bottom:16px;font-size:20px;font-weight:600}.logo-showcase{display:flex;gap:24px;align-items:center;padding:24px;background:#fff;border-radius:8px}.avatar-showcase{display:flex;gap:16px;align-items:center;padding:24px;background:#fff;border-radius:8px;flex-wrap:wrap}.button-group{display:flex;gap:12px;flex-wrap:wrap}.color-demo{padding:24px;background:#fff;border-radius:8px}.color-swatch{display:flex;gap:16px;margin-top:16px}.swatch{flex:1;min-height:100px;border-radius:8px;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:600}.swatch--primary{background:var(--color-background-brand)}.swatch--secondary{background:var(--color-brand-secondary)}.current-config{padding:16px;background:#f5f5f5;border-radius:8px;margin-top:16px}.current-config pre{margin:0;font-size:12px;overflow-x:auto}.color-inputs{display:flex;flex-direction:column;gap:16px;margin-top:16px}.color-input-row{display:flex;align-items:center;gap:12px}.color-input-row label{min-width:100px;font-weight:600;font-size:14px}.color-input-row input[type=color]{width:50px;height:40px;border:none;border-radius:8px;cursor:pointer}.color-input-row input[type=text]{flex:1;padding:8px 12px;border:1px solid #ddd;border-radius:8px;font-family:monospace;font-size:14px}.logo-upload{margin-top:24px;padding:20px;background:#fff;border-radius:8px}.upload-section{margin-bottom:20px}.upload-section h3{font-size:16px;font-weight:600;margin-bottom:12px}.file-input-wrapper{display:flex;align-items:center;gap:12px}.file-input-wrapper input[type=file]{display:none}.upload-button{padding:10px 16px;background:var(--color-background-brand);color:#fff;border:none;border-radius:8px;cursor:pointer;font-weight:600;font-size:14px}.upload-button:hover{opacity:.9}.file-name{font-size:14px;color:#666}.reset-button{padding:8px 12px;background:#f5f5f5;color:#666;border:1px solid #ddd;border-radius:8px;cursor:pointer;font-size:14px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { 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: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: DsLogoComponent, selector: "ds-logo", inputs: ["variant", "size", "customHeight", "customWidth"] }, { kind: "component", type: DsAvatarWithBadgeComponent, selector: "ds-avatar-with-badge", inputs: ["type", "size", "initials", "src", "iconName", "showBadge", "badgePosition"] }] });
12371
+ }
12372
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelDemoPage, decorators: [{
12373
+ type: Component,
12374
+ args: [{ selector: 'app-whitelabel-demo', standalone: true, imports: [
12375
+ CommonModule,
12376
+ FormsModule,
12377
+ IonContent,
12378
+ IonHeader,
12379
+ IonToolbar,
12380
+ IonTitle,
12381
+ IonButton,
12382
+ DsLogoComponent,
12383
+ DsAvatarWithBadgeComponent
12384
+ ], template: `
12385
+ <ion-header>
12386
+ <ion-toolbar>
12387
+ <ion-title>Whitelabel Demo</ion-title>
12388
+ </ion-toolbar>
12389
+ </ion-header>
12390
+
12391
+ <ion-content>
12392
+ <div class="demo-container">
12393
+ <!-- Logo Section -->
12394
+ <div class="demo-section">
12395
+ <h2>Logos</h2>
12396
+ <div class="logo-showcase">
12397
+ <div>
12398
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Full Logo (md)</p>
12399
+ <ds-logo variant="full" size="md" />
12400
+ </div>
12401
+ <div>
12402
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Logomark (sm)</p>
12403
+ <ds-logo variant="mark" size="sm" />
12404
+ </div>
12405
+ <div>
12406
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Logomark (md)</p>
12407
+ <ds-logo variant="mark" size="md" />
12408
+ </div>
12409
+ <div>
12410
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Logomark (lg)</p>
12411
+ <ds-logo variant="mark" size="lg" />
12412
+ </div>
12413
+ </div>
12414
+
12415
+ <!-- Logo Upload Section -->
12416
+ <div class="logo-upload">
12417
+ <div class="upload-section">
12418
+ <h3>Upload Full Logo</h3>
12419
+ <div class="file-input-wrapper">
12420
+ <input
12421
+ #fullLogoInput
12422
+ type="file"
12423
+ accept="image/*"
12424
+ (change)="handleFullLogoUpload($event)"
12425
+ />
12426
+ <button class="upload-button" (click)="fullLogoInput.click()">
12427
+ Choose File
12428
+ </button>
12429
+ @if (uploadedFullLogoName) {
12430
+ <span class="file-name">{{ uploadedFullLogoName }}</span>
12431
+ }
12432
+ @if (uploadedFullLogoName) {
12433
+ <button class="reset-button" (click)="resetFullLogo()">Reset</button>
12434
+ }
12435
+ </div>
12436
+ </div>
12437
+
12438
+ <div class="upload-section">
12439
+ <h3>Upload Logomark</h3>
12440
+ <div class="file-input-wrapper">
12441
+ <input
12442
+ #logomarkInput
12443
+ type="file"
12444
+ accept="image/*"
12445
+ (change)="handleLogomarkUpload($event)"
12446
+ />
12447
+ <button class="upload-button" (click)="logomarkInput.click()">
12448
+ Choose File
12449
+ </button>
12450
+ @if (uploadedLogomarkName) {
12451
+ <span class="file-name">{{ uploadedLogomarkName }}</span>
12452
+ }
12453
+ @if (uploadedLogomarkName) {
12454
+ <button class="reset-button" (click)="resetLogomark()">Reset</button>
12455
+ }
12456
+ </div>
12457
+ </div>
12458
+
12459
+ <p style="font-size: 12px; color: #999; margin-top: 12px;">
12460
+ Tip: Use SVG or PNG files with transparent backgrounds for best results
12461
+ </p>
12462
+ </div>
12463
+ </div>
12464
+
12465
+ <!-- Avatar with Badge Section -->
12466
+ <div class="demo-section">
12467
+ <h2>Avatar with Logo Badge</h2>
12468
+ <div class="avatar-showcase">
12469
+ <div>
12470
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Small</p>
12471
+ <ds-avatar-with-badge
12472
+ [type]="'initials'"
12473
+ [initials]="'JD'"
12474
+ [size]="'sm'"
12475
+ [badgePosition]="'bottom-right'"
12476
+ />
12477
+ </div>
12478
+ <div>
12479
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Medium</p>
12480
+ <ds-avatar-with-badge
12481
+ [type]="'initials'"
12482
+ [initials]="'JD'"
12483
+ [size]="'md'"
12484
+ [badgePosition]="'bottom-right'"
12485
+ />
12486
+ </div>
12487
+ <div>
12488
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">Large</p>
12489
+ <ds-avatar-with-badge
12490
+ [type]="'initials'"
12491
+ [initials]="'JD'"
12492
+ [size]="'lg'"
12493
+ [badgePosition]="'bottom-right'"
12494
+ />
12495
+ </div>
12496
+ <div>
12497
+ <p style="margin-bottom: 8px; font-size: 12px; color: #666;">X-Large</p>
12498
+ <ds-avatar-with-badge
12499
+ [type]="'initials'"
12500
+ [initials]="'JD'"
12501
+ [size]="'xl'"
12502
+ [badgePosition]="'bottom-right'"
12503
+ />
12504
+ </div>
12505
+ </div>
12506
+ </div>
12507
+
12508
+ <!-- Color Section -->
12509
+ <div class="demo-section">
12510
+ <h2>Brand Colors</h2>
12511
+ <div class="color-demo">
12512
+ <p style="margin-bottom: 8px; color: #666;">Current brand colors:</p>
12513
+ <div class="color-swatch">
12514
+ <div class="swatch swatch--primary">
12515
+ Primary<br/>
12516
+ <span style="font-size: 11px; opacity: 0.9;">{{ whitelabelService.primaryColor() }}</span>
12517
+ </div>
12518
+ <div class="swatch swatch--secondary">
12519
+ Secondary<br/>
12520
+ <span style="font-size: 11px; opacity: 0.9;">{{ whitelabelService.secondaryColor() }}</span>
12521
+ </div>
12522
+ </div>
12523
+
12524
+ <!-- Custom Color Inputs -->
12525
+ <div class="color-inputs">
12526
+ <div class="color-input-row">
12527
+ <label>Primary:</label>
12528
+ <input
12529
+ type="color"
12530
+ [(ngModel)]="customPrimaryColor"
12531
+ (change)="applyCustomColors()"
12532
+ />
12533
+ <input
12534
+ type="text"
12535
+ [(ngModel)]="customPrimaryColor"
12536
+ (change)="applyCustomColors()"
12537
+ placeholder="#6B5FF5"
12538
+ />
12539
+ </div>
12540
+ <div class="color-input-row">
12541
+ <label>Secondary:</label>
12542
+ <input
12543
+ type="color"
12544
+ [(ngModel)]="customSecondaryColor"
12545
+ (change)="applyCustomColors()"
12546
+ />
12547
+ <input
12548
+ type="text"
12549
+ [(ngModel)]="customSecondaryColor"
12550
+ (change)="applyCustomColors()"
12551
+ placeholder="#221a4c"
12552
+ />
12553
+ </div>
12554
+ </div>
12555
+ </div>
12556
+ </div>
12557
+
12558
+ <!-- Theme Switcher -->
12559
+ <div class="demo-section">
12560
+ <h2>Switch Organization Theme</h2>
12561
+ <div class="button-group">
12562
+ <ion-button (click)="applyDefaultTheme()">
12563
+ Default (Purple)
12564
+ </ion-button>
12565
+ <ion-button (click)="applyBlueTheme()">
12566
+ Blue Theme
12567
+ </ion-button>
12568
+ <ion-button (click)="applyGreenTheme()">
12569
+ Green Theme
12570
+ </ion-button>
12571
+ <ion-button (click)="applyOrangeTheme()">
12572
+ Orange Theme
12573
+ </ion-button>
12574
+ </div>
12575
+ </div>
12576
+
12577
+ <!-- Current Config -->
12578
+ <div class="demo-section">
12579
+ <h2>Current Configuration</h2>
12580
+ <div class="current-config">
12581
+ <pre>{{ configJson() }}</pre>
12582
+ </div>
12583
+ </div>
12584
+ </div>
12585
+ </ion-content>
12586
+ `, styles: [":host{display:block}ion-toolbar{--background: var(--color-brand-secondary);--color: white}ion-content{--background: #f5f5f5}.demo-container{padding:20px;max-width:800px;margin:0 auto}.demo-section{margin-bottom:32px}.demo-section h2{margin-bottom:16px;font-size:20px;font-weight:600}.logo-showcase{display:flex;gap:24px;align-items:center;padding:24px;background:#fff;border-radius:8px}.avatar-showcase{display:flex;gap:16px;align-items:center;padding:24px;background:#fff;border-radius:8px;flex-wrap:wrap}.button-group{display:flex;gap:12px;flex-wrap:wrap}.color-demo{padding:24px;background:#fff;border-radius:8px}.color-swatch{display:flex;gap:16px;margin-top:16px}.swatch{flex:1;min-height:100px;border-radius:8px;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:600}.swatch--primary{background:var(--color-background-brand)}.swatch--secondary{background:var(--color-brand-secondary)}.current-config{padding:16px;background:#f5f5f5;border-radius:8px;margin-top:16px}.current-config pre{margin:0;font-size:12px;overflow-x:auto}.color-inputs{display:flex;flex-direction:column;gap:16px;margin-top:16px}.color-input-row{display:flex;align-items:center;gap:12px}.color-input-row label{min-width:100px;font-weight:600;font-size:14px}.color-input-row input[type=color]{width:50px;height:40px;border:none;border-radius:8px;cursor:pointer}.color-input-row input[type=text]{flex:1;padding:8px 12px;border:1px solid #ddd;border-radius:8px;font-family:monospace;font-size:14px}.logo-upload{margin-top:24px;padding:20px;background:#fff;border-radius:8px}.upload-section{margin-bottom:20px}.upload-section h3{font-size:16px;font-weight:600;margin-bottom:12px}.file-input-wrapper{display:flex;align-items:center;gap:12px}.file-input-wrapper input[type=file]{display:none}.upload-button{padding:10px 16px;background:var(--color-background-brand);color:#fff;border:none;border-radius:8px;cursor:pointer;font-weight:600;font-size:14px}.upload-button:hover{opacity:.9}.file-name{font-size:14px;color:#666}.reset-button{padding:8px 12px;background:#f5f5f5;color:#666;border:1px solid #ddd;border-radius:8px;cursor:pointer;font-size:14px}\n"] }]
12587
+ }] });
12588
+
8287
12589
  /* Auto-generated. Do not edit. */
8288
12590
 
8289
12591
  /**
8290
12592
  * Generated bundle index. Do not edit.
8291
12593
  */
8292
12594
 
8293
- export { ActionCommentComponent$1 as ActionCommentComponent, ActionLikeComponent$1 as ActionLikeComponent, ContentRowComponent, DsMobileActionsBottomSheetComponent, DsMobileAppLayoutComponent, DsMobileBottomSheetService, DsMobileActionsBottomSheetComponent as DsMobileCommentActionsBottomSheetComponent, DsMobileCommentComponent, DsMobileContactListItemComponent, DsMobileContentComponent, DsMobileContentSectionComponent, DsMobileHandbookFolderComponent, DsMobileHandbookFolderMiniComponent, DsMobileHeaderContentComponent, DsMobileHeaderContentTileComponent, DsMobileInlinePhotoComponent, DsMobileInteractiveListItemInquiryComponent, DsMobileInteractiveListItemMessageComponent, DsMobileInteractiveListItemPostComponent, DsMobileLightboxImageComponent as DsMobileLightboxComponent, DsMobileLightboxFooterComponent, DsMobileLightboxHeaderComponent, DsMobileLightboxImageComponent, DsMobileLightboxPdfComponent, DsMobileLightboxService, DsMobileListItemComponent, DsMobileLongPressDirective, DsMobileModalService, DsMobilePageDetailsComponent, DsMobilePageMainComponent, DsMobileActionsBottomSheetComponent as DsMobilePostActionsBottomSheetComponent, DsMobilePostCardComponent, DsMobilePostComposerComponent, DsMobilePostCreateBottomSheetComponent, DsMobilePostDetailModalComponent, DsMobilePostDetailModalService, DsMobileTabsComponent, MobilePageBase, PostActionsComponent$1 as PostActionsComponent, PostAttachmentsComponent$1 as PostAttachmentsComponent, PostContentComponent$1 as PostContentComponent, PostMediaComponent$1 as PostMediaComponent, PostPdfAttachmentComponent, PostTextComponent$1 as PostTextComponent, SectionHeaderComponent, TileContentComponent, TileIconComponent, TileLabelComponent, TileValueComponent };
12595
+ export { ActionCommentComponent$1 as ActionCommentComponent, ActionLikeComponent$1 as ActionLikeComponent, ContentRowComponent, DsMobileActionsBottomSheetComponent, DsMobileAppLayoutComponent, DsMobileBottomSheetService, DsMobileActionsBottomSheetComponent as DsMobileCommentActionsBottomSheetComponent, DsMobileCommentComponent, DsMobileContactListItemComponent, DsMobileContentComponent, DsMobileContentSectionComponent, DsMobileHandbookFolderComponent, DsMobileHandbookFolderMiniComponent, DsMobileHeaderContentComponent, DsMobileHeaderContentTileComponent, DsMobileInlinePhotoComponent, DsMobileInteractiveListItemInquiryComponent, DsMobileInteractiveListItemMessageComponent, DsMobileInteractiveListItemPostComponent, DsMobileLightboxImageComponent as DsMobileLightboxComponent, DsMobileLightboxFooterComponent, DsMobileLightboxHeaderComponent, DsMobileLightboxImageComponent, DsMobileLightboxPdfComponent, DsMobileLightboxService, DsMobileListItemComponent, DsMobileLongPressDirective, DsMobileModalService, DsMobilePageDetailsComponent, DsMobilePageMainComponent, DsMobileActionsBottomSheetComponent as DsMobilePostActionsBottomSheetComponent, DsMobilePostCardComponent, DsMobilePostComposerComponent, DsMobilePostCreateBottomSheetComponent, DsMobilePostDetailModalComponent, DsMobilePostDetailModalService, DsMobileTabsComponent$1 as DsMobileTabsComponent, MobileCommunityPageComponent, MobileHandbookPageComponent, MobileHomePageComponent, MobileInquiriesPageComponent, MobileInquiryDetailPageComponent, MobilePageBase, MobilePostDetailPageComponent, MobileTabsExampleComponent, PostActionsComponent$1 as PostActionsComponent, PostAttachmentsComponent$1 as PostAttachmentsComponent, PostContentComponent$1 as PostContentComponent, PostCreatePageComponent, PostMediaComponent$1 as PostMediaComponent, PostPdfAttachmentComponent, PostTextComponent$1 as PostTextComponent, SectionHeaderComponent, TileContentComponent, TileIconComponent, TileLabelComponent, TileValueComponent, UserService, WhitelabelDemoPage, customBackTransition, customPageTransition };
8294
12596
  //# sourceMappingURL=propbinder-mobile-design.mjs.map