@propbinder/mobile-design 0.2.8 → 0.2.9

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.
@@ -12082,7 +12082,7 @@ class DsMobileCardInlineFileComponent {
12082
12082
  */
12083
12083
  fileName = input('Document', ...(ngDevMode ? [{ debugName: "fileName" }] : []));
12084
12084
  /**
12085
- * File size display (e.g., "1.2 MB")
12085
+ * File size display (e.g., '1.2 MB')
12086
12086
  */
12087
12087
  fileSize = input('', ...(ngDevMode ? [{ debugName: "fileSize" }] : []));
12088
12088
  /**
@@ -12097,6 +12097,11 @@ class DsMobileCardInlineFileComponent {
12097
12097
  * - 'compact' - Reduced padding and row layout
12098
12098
  */
12099
12099
  layout = input('default', ...(ngDevMode ? [{ debugName: "layout" }] : []));
12100
+ /**
12101
+ * Optional URL to open when clicked
12102
+ * If provided, clicking the card will open this URL in a new tab
12103
+ */
12104
+ fileUrl = input(undefined, ...(ngDevMode ? [{ debugName: "fileUrl" }] : []));
12100
12105
  /**
12101
12106
  * Emits when the file attachment is clicked
12102
12107
  */
@@ -12105,7 +12110,9 @@ class DsMobileCardInlineFileComponent {
12105
12110
  * Get the appropriate icon name based on variant
12106
12111
  */
12107
12112
  getIconName() {
12108
- return this.variant() === 'pdf' ? 'remixFileTextLine' : 'remixAttachmentLine';
12113
+ return this.variant() === 'pdf'
12114
+ ? 'remixFileTextLine'
12115
+ : 'remixAttachmentLine';
12109
12116
  }
12110
12117
  /**
12111
12118
  * Get the file type label based on variant
@@ -12114,73 +12121,87 @@ class DsMobileCardInlineFileComponent {
12114
12121
  return this.variant() === 'pdf' ? 'PDF' : 'DOC';
12115
12122
  }
12116
12123
  handleClick() {
12124
+ const url = this.fileUrl();
12125
+ console.log(url);
12126
+ if (url) {
12127
+ window.open(url, '_blank', 'noopener,noreferrer');
12128
+ }
12117
12129
  this.fileClick.emit();
12118
12130
  }
12119
12131
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsMobileCardInlineFileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12120
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsMobileCardInlineFileComponent, isStandalone: true, selector: "ds-mobile-card-inline-file", inputs: { fileName: { classPropertyName: "fileName", publicName: "fileName", isSignal: true, isRequired: false, transformFunction: null }, fileSize: { classPropertyName: "fileSize", publicName: "fileSize", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fileClick: "fileClick" }, ngImport: i0, template: `
12121
- <ds-mobile-card-inline
12122
- [variant]="layout()"
12123
- (cardClick)="handleClick()">
12124
-
12125
- <div content-leading class="item-avatar" [class.pdf]="variant() === 'pdf'" [class.doc]="variant() === 'doc'">
12132
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsMobileCardInlineFileComponent, isStandalone: true, selector: "ds-mobile-card-inline-file", inputs: { fileName: { classPropertyName: "fileName", publicName: "fileName", isSignal: true, isRequired: false, transformFunction: null }, fileSize: { classPropertyName: "fileSize", publicName: "fileSize", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, fileUrl: { classPropertyName: "fileUrl", publicName: "fileUrl", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fileClick: "fileClick" }, ngImport: i0, template: `
12133
+ <ds-mobile-card-inline [variant]="layout()" (cardClick)="handleClick()">
12134
+ <div
12135
+ content-leading
12136
+ class="item-avatar"
12137
+ [class.pdf]="variant() === 'pdf'"
12138
+ [class.doc]="variant() === 'doc'"
12139
+ >
12126
12140
  <ds-avatar
12127
12141
  type="icon"
12128
12142
  [iconName]="getIconName()"
12129
12143
  [size]="layout() === 'compact' ? 'sm' : 'md'"
12130
12144
  />
12131
12145
  </div>
12132
-
12146
+
12133
12147
  <div content-main class="item-content">
12134
12148
  <div class="item-name">{{ fileName() }}</div>
12135
12149
  @if (fileSize()) {
12136
- <div class="item-meta">{{ getFileTypeLabel() }} · {{ fileSize() }}</div>
12150
+ <div class="item-meta">{{ getFileTypeLabel() }} · {{ fileSize() }}</div>
12137
12151
  } @else {
12138
- <div class="item-meta">{{ getFileTypeLabel() }}</div>
12152
+ <div class="item-meta">{{ getFileTypeLabel() }}</div>
12139
12153
  }
12140
12154
  </div>
12141
-
12142
- <ds-icon
12155
+
12156
+ <ds-icon
12143
12157
  content-trailing
12144
- name="remixArrowRightSLine"
12158
+ name="remixArrowRightSLine"
12145
12159
  size="20px"
12146
12160
  class="item-trailing"
12147
12161
  />
12148
12162
  </ds-mobile-card-inline>
12149
- `, isInline: true, styles: [".item-avatar.pdf ::ng-deep .avatar--icon{background-color:#ff5757!important}.item-avatar.doc ::ng-deep .avatar--icon{background-color:var(--color-blue-base, #3B82F6)!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: DsMobileCardInlineComponent, selector: "ds-mobile-card-inline", inputs: ["variant", "disabled"], outputs: ["cardClick"] }] });
12163
+ `, isInline: true, styles: [".item-avatar.pdf ::ng-deep .avatar--icon{background-color:#ff5757!important}.item-avatar.doc ::ng-deep .avatar--icon{background-color:var(--color-blue-base, #3b82f6)!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: DsMobileCardInlineComponent, selector: "ds-mobile-card-inline", inputs: ["variant", "disabled"], outputs: ["cardClick"] }] });
12150
12164
  }
12151
12165
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsMobileCardInlineFileComponent, decorators: [{
12152
12166
  type: Component,
12153
- args: [{ selector: 'ds-mobile-card-inline-file', standalone: true, imports: [CommonModule, DsAvatarComponent, DsIconComponent, DsMobileCardInlineComponent], template: `
12154
- <ds-mobile-card-inline
12155
- [variant]="layout()"
12156
- (cardClick)="handleClick()">
12157
-
12158
- <div content-leading class="item-avatar" [class.pdf]="variant() === 'pdf'" [class.doc]="variant() === 'doc'">
12167
+ args: [{ selector: 'ds-mobile-card-inline-file', standalone: true, imports: [
12168
+ CommonModule,
12169
+ DsAvatarComponent,
12170
+ DsIconComponent,
12171
+ DsMobileCardInlineComponent,
12172
+ ], template: `
12173
+ <ds-mobile-card-inline [variant]="layout()" (cardClick)="handleClick()">
12174
+ <div
12175
+ content-leading
12176
+ class="item-avatar"
12177
+ [class.pdf]="variant() === 'pdf'"
12178
+ [class.doc]="variant() === 'doc'"
12179
+ >
12159
12180
  <ds-avatar
12160
12181
  type="icon"
12161
12182
  [iconName]="getIconName()"
12162
12183
  [size]="layout() === 'compact' ? 'sm' : 'md'"
12163
12184
  />
12164
12185
  </div>
12165
-
12186
+
12166
12187
  <div content-main class="item-content">
12167
12188
  <div class="item-name">{{ fileName() }}</div>
12168
12189
  @if (fileSize()) {
12169
- <div class="item-meta">{{ getFileTypeLabel() }} · {{ fileSize() }}</div>
12190
+ <div class="item-meta">{{ getFileTypeLabel() }} · {{ fileSize() }}</div>
12170
12191
  } @else {
12171
- <div class="item-meta">{{ getFileTypeLabel() }}</div>
12192
+ <div class="item-meta">{{ getFileTypeLabel() }}</div>
12172
12193
  }
12173
12194
  </div>
12174
-
12175
- <ds-icon
12195
+
12196
+ <ds-icon
12176
12197
  content-trailing
12177
- name="remixArrowRightSLine"
12198
+ name="remixArrowRightSLine"
12178
12199
  size="20px"
12179
12200
  class="item-trailing"
12180
12201
  />
12181
12202
  </ds-mobile-card-inline>
12182
- `, styles: [".item-avatar.pdf ::ng-deep .avatar--icon{background-color:#ff5757!important}.item-avatar.doc ::ng-deep .avatar--icon{background-color:var(--color-blue-base, #3B82F6)!important}\n"] }]
12183
- }], propDecorators: { fileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileName", required: false }] }], fileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileSize", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], fileClick: [{ type: i0.Output, args: ["fileClick"] }] } });
12203
+ `, styles: [".item-avatar.pdf ::ng-deep .avatar--icon{background-color:#ff5757!important}.item-avatar.doc ::ng-deep .avatar--icon{background-color:var(--color-blue-base, #3b82f6)!important}\n"] }]
12204
+ }], propDecorators: { fileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileName", required: false }] }], fileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileSize", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], fileUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileUrl", required: false }] }], fileClick: [{ type: i0.Output, args: ["fileClick"] }] } });
12184
12205
 
12185
12206
  /**
12186
12207
  * DsMobileSystemMessageBannerComponent
@@ -12384,7 +12405,7 @@ class DsMobileChatModalComponent {
12384
12405
  // Track keyboard state
12385
12406
  this.isKeyboardVisible = true;
12386
12407
  // Check if user is near bottom before auto-scrolling
12387
- this.isNearBottom().then(isNear => {
12408
+ this.isNearBottom().then((isNear) => {
12388
12409
  if (isNear) {
12389
12410
  // User is already viewing latest messages, maintain that position
12390
12411
  // Small delay to synchronize with keyboard animation
@@ -12443,7 +12464,7 @@ class DsMobileChatModalComponent {
12443
12464
  avatarType: this.currentUserAvatarType(),
12444
12465
  avatarSrc: this.currentUserAvatarSrc(),
12445
12466
  fileAttachments: event.attachments || [],
12446
- isNewMessage: true // Mark as new message to trigger animation
12467
+ isNewMessage: true, // Mark as new message to trigger animation
12447
12468
  };
12448
12469
  // Add message to list
12449
12470
  const updatedMessages = [...this.messages(), newMessage];
@@ -12451,7 +12472,7 @@ class DsMobileChatModalComponent {
12451
12472
  // Remove the isNewMessage flag after animation completes (using spring-bouncy duration ~600ms)
12452
12473
  setTimeout(() => {
12453
12474
  const msgs = this.messages();
12454
- const msgIndex = msgs.findIndex(m => m.id === newMessage.id);
12475
+ const msgIndex = msgs.findIndex((m) => m.id === newMessage.id);
12455
12476
  if (msgIndex !== -1) {
12456
12477
  msgs[msgIndex].isNewMessage = false;
12457
12478
  this.messages.set([...msgs]);
@@ -12508,7 +12529,10 @@ class DsMobileChatModalComponent {
12508
12529
  timestamp: this.formatMessageTimestamp(ownerMessage.timestamp),
12509
12530
  avatarInitials: ownerMessage.avatarInitials,
12510
12531
  avatarSrc: ownerMessage.avatarSrc,
12511
- avatarType: ownerMessage.avatarType === 'photo' || ownerMessage.avatarType === 'initials' ? ownerMessage.avatarType : undefined,
12532
+ avatarType: ownerMessage.avatarType === 'photo' ||
12533
+ ownerMessage.avatarType === 'initials'
12534
+ ? ownerMessage.avatarType
12535
+ : undefined,
12512
12536
  }
12513
12537
  : undefined;
12514
12538
  this.lightboxService.openImages({
@@ -12545,7 +12569,7 @@ class DsMobileChatModalComponent {
12545
12569
  requestAnimationFrame(() => {
12546
12570
  requestAnimationFrame(() => {
12547
12571
  setTimeout(() => {
12548
- this.isNearBottom().then(isNear => {
12572
+ this.isNearBottom().then((isNear) => {
12549
12573
  if (isNear) {
12550
12574
  // User is at bottom, maintain that position as composer expands
12551
12575
  this.scrollToBottom();
@@ -12565,7 +12589,7 @@ class DsMobileChatModalComponent {
12565
12589
  .then(() => {
12566
12590
  this.isKeyboardVisible = false;
12567
12591
  })
12568
- .catch(e => console.log('[ChatModal] Keyboard.hide() not available:', e));
12592
+ .catch((e) => console.log('[ChatModal] Keyboard.hide() not available:', e));
12569
12593
  }
12570
12594
  /**
12571
12595
  * Get file variant for card-inline-file component
@@ -12585,26 +12609,28 @@ class DsMobileChatModalComponent {
12585
12609
  */
12586
12610
  async handleImageClick(attachment, message) {
12587
12611
  await this.lightboxService.openImages({
12588
- images: [{
12612
+ images: [
12613
+ {
12589
12614
  type: 'image',
12590
12615
  src: attachment.src,
12591
12616
  title: attachment.name || 'Image',
12592
- alt: attachment.name || 'Chat image'
12593
- }],
12617
+ alt: attachment.name || 'Chat image',
12618
+ },
12619
+ ],
12594
12620
  author: {
12595
12621
  name: message.senderName,
12596
12622
  role: message.senderRole,
12597
12623
  avatarInitials: message.avatarInitials,
12598
12624
  avatarType: message.avatarType === 'initials' ? 'initials' : 'photo',
12599
12625
  avatarSrc: message.avatarSrc,
12600
- timestamp: this.formatMessageTimestamp(message.timestamp)
12626
+ timestamp: this.formatMessageTimestamp(message.timestamp),
12601
12627
  },
12602
12628
  initialIndex: 0,
12603
12629
  enableZoom: true,
12604
12630
  showControls: false,
12605
12631
  enableSwipe: false,
12606
12632
  showInfo: false,
12607
- showActions: false
12633
+ showActions: false,
12608
12634
  });
12609
12635
  }
12610
12636
  /**
@@ -12625,7 +12651,7 @@ class DsMobileChatModalComponent {
12625
12651
  .then(() => {
12626
12652
  this.isKeyboardVisible = false;
12627
12653
  })
12628
- .catch(e => console.log('[ChatModal] Keyboard.hide() not available:', e));
12654
+ .catch((e) => console.log('[ChatModal] Keyboard.hide() not available:', e));
12629
12655
  return; // Exit early, don't toggle timestamp
12630
12656
  }
12631
12657
  // Keyboard is hidden, proceed with timestamp toggle
@@ -12634,7 +12660,7 @@ class DsMobileChatModalComponent {
12634
12660
  clearTimeout(this.timestampTimeout);
12635
12661
  }
12636
12662
  // Toggle timestamp - if clicking same message, hide it; otherwise show new one
12637
- this.selectedMessageId.update(current => current === messageId ? null : messageId);
12663
+ this.selectedMessageId.update((current) => current === messageId ? null : messageId);
12638
12664
  // Auto-hide after 3 seconds if showing
12639
12665
  if (this.selectedMessageId() === messageId) {
12640
12666
  this.timestampTimeout = setTimeout(() => {
@@ -12649,7 +12675,7 @@ class DsMobileChatModalComponent {
12649
12675
  return date.toLocaleTimeString('da-DK', {
12650
12676
  hour: '2-digit',
12651
12677
  minute: '2-digit',
12652
- hour12: false
12678
+ hour12: false,
12653
12679
  });
12654
12680
  }
12655
12681
  /**
@@ -12672,11 +12698,12 @@ class DsMobileChatModalComponent {
12672
12698
  // 1. It's the first message
12673
12699
  // 2. More than threshold minutes have passed since last message
12674
12700
  // 3. Date changed (new day)
12675
- if (!currentGroup || this.shouldStartNewGroup(currentGroup.timestamp, messageDate, thresholdMinutes)) {
12701
+ if (!currentGroup ||
12702
+ this.shouldStartNewGroup(currentGroup.timestamp, messageDate, thresholdMinutes)) {
12676
12703
  currentGroup = {
12677
12704
  timestamp: messageDate,
12678
12705
  displayTimestamp: this.formatGroupTimestamp(messageDate),
12679
- messages: []
12706
+ messages: [],
12680
12707
  };
12681
12708
  groups.push(currentGroup);
12682
12709
  }
@@ -12710,7 +12737,7 @@ class DsMobileChatModalComponent {
12710
12737
  const timeStr = date.toLocaleTimeString('da-DK', {
12711
12738
  hour: '2-digit',
12712
12739
  minute: '2-digit',
12713
- hour12: false
12740
+ hour12: false,
12714
12741
  });
12715
12742
  // Today: "14:34"
12716
12743
  if (messageDate.getTime() === today.getTime()) {
@@ -12723,12 +12750,12 @@ class DsMobileChatModalComponent {
12723
12750
  // This week: "Mandag, 14:34"
12724
12751
  const daysAgo = Math.floor((today.getTime() - messageDate.getTime()) / (1000 * 60 * 60 * 24));
12725
12752
  if (daysAgo < 7) {
12726
- return date.toLocaleDateString('da-DK', { weekday: 'long' }) + `, ${timeStr}`;
12753
+ return (date.toLocaleDateString('da-DK', { weekday: 'long' }) + `, ${timeStr}`);
12727
12754
  }
12728
12755
  // Older: "15. jan, 14:34" or "20. dec. 2024, 14:34" if different year
12729
12756
  const dateFormat = {
12730
12757
  month: 'short',
12731
- day: 'numeric'
12758
+ day: 'numeric',
12732
12759
  };
12733
12760
  if (date.getFullYear() !== now.getFullYear()) {
12734
12761
  dateFormat.year = 'numeric';
@@ -12741,7 +12768,7 @@ class DsMobileChatModalComponent {
12741
12768
  * and calculates cluster positions for border radius styling
12742
12769
  */
12743
12770
  addDisplayMetadata(groups) {
12744
- return groups.map(group => {
12771
+ return groups.map((group) => {
12745
12772
  const messagesWithDisplay = [];
12746
12773
  for (let i = 0; i < group.messages.length; i++) {
12747
12774
  const currentMsg = group.messages[i];
@@ -12770,12 +12797,12 @@ class DsMobileChatModalComponent {
12770
12797
  messagesWithDisplay.push({
12771
12798
  ...currentMsg,
12772
12799
  showAvatar,
12773
- clusterPosition
12800
+ clusterPosition,
12774
12801
  });
12775
12802
  }
12776
12803
  return {
12777
12804
  ...group,
12778
- messages: messagesWithDisplay
12805
+ messages: messagesWithDisplay,
12779
12806
  };
12780
12807
  });
12781
12808
  }
@@ -12789,8 +12816,8 @@ class DsMobileChatModalComponent {
12789
12816
  [hasFixedBottom]="true"
12790
12817
  [enableKeyboardHandling]="true"
12791
12818
  (keyboardWillShow)="handleKeyboardShow($event)"
12792
- closeButtonLabel="Luk chat">
12793
-
12819
+ closeButtonLabel="Luk chat"
12820
+ >
12794
12821
  <!-- Header Avatar -->
12795
12822
  <ds-avatar-with-badge
12796
12823
  header-leading
@@ -12815,94 +12842,109 @@ class DsMobileChatModalComponent {
12815
12842
  <div class="chat-avatar-name">
12816
12843
  {{ participant().name }}
12817
12844
  @if (participant().verified) {
12818
- <ds-icon name="remixCheckboxCircleFill" size="24px" [style.color]="'var(--color-primary-base)'"></ds-icon>
12845
+ <ds-icon
12846
+ name="remixCheckboxCircleFill"
12847
+ size="24px"
12848
+ [style.color]="'var(--color-primary-base)'"
12849
+ ></ds-icon>
12819
12850
  }
12820
12851
  </div>
12821
12852
  @if (participant().role) {
12822
- <div class="chat-avatar-role">{{ participant().role }}</div>
12823
- }
12824
- @if (participant().lastActive) {
12825
- <div class="chat-avatar-meta">{{ participant().lastActive }}</div>
12853
+ <div class="chat-avatar-role">{{ participant().role }}</div>
12854
+ } @if (participant().lastActive) {
12855
+ <div class="chat-avatar-meta">{{ participant().lastActive }}</div>
12826
12856
  }
12827
12857
  </div>
12828
12858
  </div>
12829
12859
 
12830
12860
  <div class="messages-list">
12831
12861
  @if (messages().length === 0) {
12832
- <!-- Empty State - Timestamp and System Message -->
12833
- <div class="timestamp-header">
12834
- <span class="timestamp-text">{{ getInitialTimestamp() }}</span>
12835
- </div>
12836
-
12837
- <ds-mobile-system-message-banner
12838
- [message]="participant().name + ' har overtaget din henvendelse og vil kontakte dig snart.'"
12839
- [afterTimestamp]="true">
12840
- </ds-mobile-system-message-banner>
12841
- } @else {
12842
- @for (group of messagesWithDisplay(); track group.timestamp) {
12843
- <!-- Timestamp Header -->
12844
- <div class="timestamp-header">
12845
- <span class="timestamp-text">{{ group.displayTimestamp }}</span>
12846
- </div>
12847
-
12848
- <!-- System message example (shown after first timestamp) -->
12849
- @if ($first) {
12850
- <ds-mobile-system-message-banner
12851
- [message]="participant().name + ' har overtaget din henvendelse og vil kontakte dig snart.'"
12852
- [afterTimestamp]="true">
12853
- </ds-mobile-system-message-banner>
12854
- }
12855
-
12856
- @for (message of group.messages; track message.id) {
12857
- <!-- Only show bubble if has content -->
12858
- @if (message.content.trim()) {
12859
- <ds-mobile-message-bubble
12860
- [content]="message.content"
12861
- [isOwnMessage]="message.isOwnMessage"
12862
- [timestamp]="formatMessageTimestamp(message.timestamp)"
12863
- [showTimestamp]="selectedMessageId() === message.id"
12864
- [avatarInitials]="message.avatarInitials || ''"
12865
- [avatarType]="message.avatarType || 'initials'"
12866
- [avatarSrc]="message.avatarSrc || ''"
12867
- [showAvatar]="message.showAvatar"
12868
- [clusterPosition]="message.clusterPosition"
12869
- [attachments]="message.attachments"
12870
- [clickable]="true"
12871
- [isNewMessage]="message.isNewMessage || false"
12872
- (messageClick)="handleMessageClick(message.id)"
12873
- (attachmentClick)="handleAttachmentClick($event)"
12874
- (longPress)="handleMessageLongPress(message)">
12875
- </ds-mobile-message-bubble>
12876
- }
12877
-
12878
- <!-- File attachments displayed below message bubble -->
12879
- @if (message.fileAttachments && message.fileAttachments.length > 0) {
12880
- <div class="message-file-attachments" [class.own-message]="message.isOwnMessage">
12881
- @for (fileAttachment of message.fileAttachments; track fileAttachment.id) {
12882
- <!-- Show inline image preview for image attachments -->
12883
- @if (fileAttachment.type === 'image') {
12884
- <div class="message-image-attachment" (click)="handleImageClick(fileAttachment, message)">
12885
- <img
12886
- [src]="fileAttachment.src"
12887
- [alt]="fileAttachment.name || 'Image'"
12888
- class="inline-image">
12889
- </div>
12890
- } @else {
12891
- <!-- Show file card for non-image attachments -->
12892
- <ds-mobile-card-inline-file
12893
- [fileName]="fileAttachment.name || 'Unknown file'"
12894
- [fileSize]="fileAttachment.size || ''"
12895
- [variant]="getFileVariant(fileAttachment.type)"
12896
- [layout]="'compact'"
12897
- (fileClick)="handleFileAttachmentClick(fileAttachment)">
12898
- </ds-mobile-card-inline-file>
12899
- }
12900
- }
12901
- </div>
12902
- }
12903
- }
12904
- }
12862
+ <!-- Empty State - Timestamp and System Message -->
12863
+ <div class="timestamp-header">
12864
+ <span class="timestamp-text">{{ getInitialTimestamp() }}</span>
12865
+ </div>
12866
+
12867
+ <ds-mobile-system-message-banner
12868
+ [message]="
12869
+ participant().name +
12870
+ ' har overtaget din henvendelse og vil kontakte dig snart.'
12871
+ "
12872
+ [afterTimestamp]="true"
12873
+ >
12874
+ </ds-mobile-system-message-banner>
12875
+ } @else { @for (group of messagesWithDisplay(); track group.timestamp)
12876
+ {
12877
+ <!-- Timestamp Header -->
12878
+ <div class="timestamp-header">
12879
+ <span class="timestamp-text">{{ group.displayTimestamp }}</span>
12880
+ </div>
12881
+
12882
+ <!-- System message example (shown after first timestamp) -->
12883
+ @if ($first) {
12884
+ <ds-mobile-system-message-banner
12885
+ [message]="
12886
+ participant().name +
12887
+ ' har overtaget din henvendelse og vil kontakte dig snart.'
12888
+ "
12889
+ [afterTimestamp]="true"
12890
+ >
12891
+ </ds-mobile-system-message-banner>
12892
+ } @for (message of group.messages; track message.id) {
12893
+ <!-- Only show bubble if has content -->
12894
+ @if (message.content.trim()) {
12895
+ <ds-mobile-message-bubble
12896
+ [content]="message.content"
12897
+ [isOwnMessage]="message.isOwnMessage"
12898
+ [timestamp]="formatMessageTimestamp(message.timestamp)"
12899
+ [showTimestamp]="selectedMessageId() === message.id"
12900
+ [avatarInitials]="message.avatarInitials || ''"
12901
+ [avatarType]="message.avatarType || 'initials'"
12902
+ [avatarSrc]="message.avatarSrc || ''"
12903
+ [showAvatar]="message.showAvatar"
12904
+ [clusterPosition]="message.clusterPosition"
12905
+ [attachments]="message.attachments"
12906
+ [clickable]="true"
12907
+ [isNewMessage]="message.isNewMessage || false"
12908
+ (messageClick)="handleMessageClick(message.id)"
12909
+ (attachmentClick)="handleAttachmentClick($event)"
12910
+ (longPress)="handleMessageLongPress(message)"
12911
+ >
12912
+ </ds-mobile-message-bubble>
12905
12913
  }
12914
+
12915
+ <!-- File attachments displayed below message bubble -->
12916
+ @if (message.fileAttachments && message.fileAttachments.length > 0) {
12917
+ <div
12918
+ class="message-file-attachments"
12919
+ [class.own-message]="message.isOwnMessage"
12920
+ >
12921
+ @for (fileAttachment of message.fileAttachments; track
12922
+ fileAttachment.id) {
12923
+ <!-- Show inline image preview for image attachments -->
12924
+ @if (fileAttachment.type === 'image') {
12925
+ <div
12926
+ class="message-image-attachment"
12927
+ (click)="handleImageClick(fileAttachment, message)"
12928
+ >
12929
+ <img
12930
+ [src]="fileAttachment.src"
12931
+ [alt]="fileAttachment.name || 'Image'"
12932
+ class="inline-image"
12933
+ />
12934
+ </div>
12935
+ } @else {
12936
+ <!-- Show file card for non-image attachments -->
12937
+ <ds-mobile-card-inline-file
12938
+ [fileName]="fileAttachment.name || 'Unknown file'"
12939
+ [fileSize]="fileAttachment.size || ''"
12940
+ [variant]="getFileVariant(fileAttachment.type)"
12941
+ [layout]="'compact'"
12942
+ (fileClick)="handleFileAttachmentClick(fileAttachment)"
12943
+ >
12944
+ </ds-mobile-card-inline-file>
12945
+ } }
12946
+ </div>
12947
+ } } } }
12906
12948
  </div>
12907
12949
  </div>
12908
12950
 
@@ -12917,11 +12959,12 @@ class DsMobileChatModalComponent {
12917
12959
  [showAttachmentButton]="true"
12918
12960
  (messageSent)="handleMessageSent($event)"
12919
12961
  (attachmentClicked)="handleComposerAttachmentClick()"
12920
- (attachmentsChanged)="handleAttachmentsChanged()">
12962
+ (attachmentsChanged)="handleAttachmentsChanged()"
12963
+ >
12921
12964
  </ds-mobile-message-composer>
12922
12965
  </div>
12923
12966
  </ds-mobile-modal-base>
12924
- `, isInline: true, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-sm);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}.ghost-input-clean ::ng-deep .ds-input,.ghost-input-clean ::ng-deep .ds-textarea,.ghost-input-clean ::ng-deep .textarea-container{outline:none!important;border:none!important}.ghost-input-clean ::ng-deep .ds-input:hover,.ghost-input-clean ::ng-deep .ds-textarea:hover,.ghost-input-clean ::ng-deep .textarea-container:hover,.ghost-input-clean ::ng-deep .ds-input:focus,.ghost-input-clean ::ng-deep .ds-textarea:focus,.ghost-input-clean ::ng-deep .textarea-container:focus,.ghost-input-clean ::ng-deep .ds-input:focus-within,.ghost-input-clean ::ng-deep .ds-textarea:focus-within,.ghost-input-clean ::ng-deep .textarea-container:focus-within{outline:none!important;border:none!important;box-shadow:none!important}.ghost-input-clean ::ng-deep textarea{outline:none!important;border:none!important;box-shadow:none!important;resize:none!important}.ghost-input-clean ::ng-deep textarea:hover,.ghost-input-clean ::ng-deep textarea:focus{outline:none!important;border:none!important;box-shadow:none!important}\n", ":host ::ng-deep .modal-content-container{padding-top:0}.chat-messages-container{display:flex;flex-direction:column;width:100%}.chat-avatar-section{display:flex;flex-direction:column;align-items:center;gap:12px;padding:48px 20px 0;background:var(--color-background-neutral-primary, #ffffff)}.chat-avatar-info{display:flex;flex-direction:column;align-items:center;gap:4px}.chat-avatar-name{display:flex;align-items:center;gap:6px;font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a)}.chat-avatar-role{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;color:var(--color-text-secondary, #666666)}.chat-avatar-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:4px}.messages-list{display:flex;flex-direction:column;width:100%;padding:16px 20px 0;align-items:stretch}.messages-list ds-mobile-message-bubble{width:100%;display:flex}.timestamp-header{display:flex;justify-content:center;margin:16px 0 8px}.timestamp-text{font-family:Brockmann,sans-serif;font-size:12px;font-weight:400;color:var(--color-text-secondary);padding:4px 12px}.message-file-attachments{display:flex;flex-direction:column;gap:8px;margin-bottom:12px;padding:0 20px 0 60px;max-width:100%}.message-file-attachments.own-message{padding:0 0 0 96px;align-items:flex-end}.message-file-attachments ds-mobile-card-inline-file{max-width:280px;width:100%}.message-image-attachment{width:96px;height:96px;cursor:pointer;border-radius:12px;overflow:hidden;position:relative;transition:transform .2s ease;border:1px solid var(--border-color-default, #e5e5e5)}.message-image-attachment:active{transform:scale(.98)}.message-image-attachment .inline-image{width:100%;height:100%;display:block;-o-object-fit:cover;object-fit:cover}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsAvatarWithBadgeComponent, selector: "ds-avatar-with-badge", inputs: ["type", "size", "initials", "src", "iconName", "showBadge", "badgePosition"] }, { kind: "component", type: DsMobileMessageComposerComponent, selector: "ds-mobile-message-composer", inputs: ["avatarInitials", "avatarType", "avatarSrc", "placeholder", "sendButtonLabel", "attachmentButtonLabel", "showAttachmentButton", "editIndicatorText", "replyIndicatorText", "enableMentions", "mentionUsers", "autoFocus"], outputs: ["messageSent", "editCancelled", "replyCancelled", "mentionSelected", "attachmentClicked", "attachmentsChanged"] }, { kind: "component", type: DsMobileMessageBubbleComponent, selector: "ds-mobile-message-bubble", inputs: ["content", "isOwnMessage", "senderName", "timestamp", "showTimestamp", "avatarInitials", "avatarType", "avatarSrc", "showAvatar", "clusterPosition", "attachments", "clickable", "isNewMessage"], outputs: ["attachmentClick", "longPress", "messageClick"] }, { kind: "component", type: DsMobileModalBaseComponent, selector: "ds-mobile-modal-base", inputs: ["showHeader"] }, { kind: "component", type: DsMobileCardInlineFileComponent, selector: "ds-mobile-card-inline-file", inputs: ["fileName", "fileSize", "variant", "layout"], outputs: ["fileClick"] }, { kind: "component", type: DsMobileSystemMessageBannerComponent, selector: "ds-mobile-system-message-banner", inputs: ["message", "iconName", "afterTimestamp"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }] });
12967
+ `, isInline: true, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-sm);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}.ghost-input-clean ::ng-deep .ds-input,.ghost-input-clean ::ng-deep .ds-textarea,.ghost-input-clean ::ng-deep .textarea-container{outline:none!important;border:none!important}.ghost-input-clean ::ng-deep .ds-input:hover,.ghost-input-clean ::ng-deep .ds-textarea:hover,.ghost-input-clean ::ng-deep .textarea-container:hover,.ghost-input-clean ::ng-deep .ds-input:focus,.ghost-input-clean ::ng-deep .ds-textarea:focus,.ghost-input-clean ::ng-deep .textarea-container:focus,.ghost-input-clean ::ng-deep .ds-input:focus-within,.ghost-input-clean ::ng-deep .ds-textarea:focus-within,.ghost-input-clean ::ng-deep .textarea-container:focus-within{outline:none!important;border:none!important;box-shadow:none!important}.ghost-input-clean ::ng-deep textarea{outline:none!important;border:none!important;box-shadow:none!important;resize:none!important}.ghost-input-clean ::ng-deep textarea:hover,.ghost-input-clean ::ng-deep textarea:focus{outline:none!important;border:none!important;box-shadow:none!important}\n", ":host ::ng-deep .modal-content-container{padding-top:0}.chat-messages-container{display:flex;flex-direction:column;width:100%}.chat-avatar-section{display:flex;flex-direction:column;align-items:center;gap:12px;padding:48px 20px 0;background:var(--color-background-neutral-primary, #ffffff)}.chat-avatar-info{display:flex;flex-direction:column;align-items:center;gap:4px}.chat-avatar-name{display:flex;align-items:center;gap:6px;font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a)}.chat-avatar-role{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;color:var(--color-text-secondary, #666666)}.chat-avatar-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:4px}.messages-list{display:flex;flex-direction:column;width:100%;padding:16px 20px 0;align-items:stretch}.messages-list ds-mobile-message-bubble{width:100%;display:flex}.timestamp-header{display:flex;justify-content:center;margin:16px 0 8px}.timestamp-text{font-family:Brockmann,sans-serif;font-size:12px;font-weight:400;color:var(--color-text-secondary);padding:4px 12px}.message-file-attachments{display:flex;flex-direction:column;gap:8px;margin-bottom:12px;padding:0 20px 0 60px;max-width:100%}.message-file-attachments.own-message{padding:0 0 0 96px;align-items:flex-end}.message-file-attachments ds-mobile-card-inline-file{max-width:280px;width:100%}.message-image-attachment{width:96px;height:96px;cursor:pointer;border-radius:12px;overflow:hidden;position:relative;transition:transform .2s ease;border:1px solid var(--border-color-default, #e5e5e5)}.message-image-attachment:active{transform:scale(.98)}.message-image-attachment .inline-image{width:100%;height:100%;display:block;-o-object-fit:cover;object-fit:cover}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsAvatarWithBadgeComponent, selector: "ds-avatar-with-badge", inputs: ["type", "size", "initials", "src", "iconName", "showBadge", "badgePosition"] }, { kind: "component", type: DsMobileMessageComposerComponent, selector: "ds-mobile-message-composer", inputs: ["avatarInitials", "avatarType", "avatarSrc", "placeholder", "sendButtonLabel", "attachmentButtonLabel", "showAttachmentButton", "editIndicatorText", "replyIndicatorText", "enableMentions", "mentionUsers", "autoFocus"], outputs: ["messageSent", "editCancelled", "replyCancelled", "mentionSelected", "attachmentClicked", "attachmentsChanged"] }, { kind: "component", type: DsMobileMessageBubbleComponent, selector: "ds-mobile-message-bubble", inputs: ["content", "isOwnMessage", "senderName", "timestamp", "showTimestamp", "avatarInitials", "avatarType", "avatarSrc", "showAvatar", "clusterPosition", "attachments", "clickable", "isNewMessage"], outputs: ["attachmentClick", "longPress", "messageClick"] }, { kind: "component", type: DsMobileModalBaseComponent, selector: "ds-mobile-modal-base", inputs: ["showHeader"] }, { kind: "component", type: DsMobileCardInlineFileComponent, selector: "ds-mobile-card-inline-file", inputs: ["fileName", "fileSize", "variant", "layout", "fileUrl"], outputs: ["fileClick"] }, { kind: "component", type: DsMobileSystemMessageBannerComponent, selector: "ds-mobile-system-message-banner", inputs: ["message", "iconName", "afterTimestamp"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }] });
12925
12968
  }
12926
12969
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsMobileChatModalComponent, decorators: [{
12927
12970
  type: Component,
@@ -12933,8 +12976,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
12933
12976
  DsMobileModalBaseComponent,
12934
12977
  DsMobileCardInlineFileComponent,
12935
12978
  DsMobileSystemMessageBannerComponent,
12936
- DsMobileEmptyStateComponent,
12937
- DsIconComponent
12979
+ DsIconComponent,
12938
12980
  ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `
12939
12981
  <ds-mobile-modal-base
12940
12982
  [loading]="loading"
@@ -12944,8 +12986,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
12944
12986
  [hasFixedBottom]="true"
12945
12987
  [enableKeyboardHandling]="true"
12946
12988
  (keyboardWillShow)="handleKeyboardShow($event)"
12947
- closeButtonLabel="Luk chat">
12948
-
12989
+ closeButtonLabel="Luk chat"
12990
+ >
12949
12991
  <!-- Header Avatar -->
12950
12992
  <ds-avatar-with-badge
12951
12993
  header-leading
@@ -12970,94 +13012,109 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
12970
13012
  <div class="chat-avatar-name">
12971
13013
  {{ participant().name }}
12972
13014
  @if (participant().verified) {
12973
- <ds-icon name="remixCheckboxCircleFill" size="24px" [style.color]="'var(--color-primary-base)'"></ds-icon>
13015
+ <ds-icon
13016
+ name="remixCheckboxCircleFill"
13017
+ size="24px"
13018
+ [style.color]="'var(--color-primary-base)'"
13019
+ ></ds-icon>
12974
13020
  }
12975
13021
  </div>
12976
13022
  @if (participant().role) {
12977
- <div class="chat-avatar-role">{{ participant().role }}</div>
12978
- }
12979
- @if (participant().lastActive) {
12980
- <div class="chat-avatar-meta">{{ participant().lastActive }}</div>
13023
+ <div class="chat-avatar-role">{{ participant().role }}</div>
13024
+ } @if (participant().lastActive) {
13025
+ <div class="chat-avatar-meta">{{ participant().lastActive }}</div>
12981
13026
  }
12982
13027
  </div>
12983
13028
  </div>
12984
13029
 
12985
13030
  <div class="messages-list">
12986
13031
  @if (messages().length === 0) {
12987
- <!-- Empty State - Timestamp and System Message -->
12988
- <div class="timestamp-header">
12989
- <span class="timestamp-text">{{ getInitialTimestamp() }}</span>
12990
- </div>
12991
-
12992
- <ds-mobile-system-message-banner
12993
- [message]="participant().name + ' har overtaget din henvendelse og vil kontakte dig snart.'"
12994
- [afterTimestamp]="true">
12995
- </ds-mobile-system-message-banner>
12996
- } @else {
12997
- @for (group of messagesWithDisplay(); track group.timestamp) {
12998
- <!-- Timestamp Header -->
12999
- <div class="timestamp-header">
13000
- <span class="timestamp-text">{{ group.displayTimestamp }}</span>
13001
- </div>
13002
-
13003
- <!-- System message example (shown after first timestamp) -->
13004
- @if ($first) {
13005
- <ds-mobile-system-message-banner
13006
- [message]="participant().name + ' har overtaget din henvendelse og vil kontakte dig snart.'"
13007
- [afterTimestamp]="true">
13008
- </ds-mobile-system-message-banner>
13009
- }
13010
-
13011
- @for (message of group.messages; track message.id) {
13012
- <!-- Only show bubble if has content -->
13013
- @if (message.content.trim()) {
13014
- <ds-mobile-message-bubble
13015
- [content]="message.content"
13016
- [isOwnMessage]="message.isOwnMessage"
13017
- [timestamp]="formatMessageTimestamp(message.timestamp)"
13018
- [showTimestamp]="selectedMessageId() === message.id"
13019
- [avatarInitials]="message.avatarInitials || ''"
13020
- [avatarType]="message.avatarType || 'initials'"
13021
- [avatarSrc]="message.avatarSrc || ''"
13022
- [showAvatar]="message.showAvatar"
13023
- [clusterPosition]="message.clusterPosition"
13024
- [attachments]="message.attachments"
13025
- [clickable]="true"
13026
- [isNewMessage]="message.isNewMessage || false"
13027
- (messageClick)="handleMessageClick(message.id)"
13028
- (attachmentClick)="handleAttachmentClick($event)"
13029
- (longPress)="handleMessageLongPress(message)">
13030
- </ds-mobile-message-bubble>
13031
- }
13032
-
13033
- <!-- File attachments displayed below message bubble -->
13034
- @if (message.fileAttachments && message.fileAttachments.length > 0) {
13035
- <div class="message-file-attachments" [class.own-message]="message.isOwnMessage">
13036
- @for (fileAttachment of message.fileAttachments; track fileAttachment.id) {
13037
- <!-- Show inline image preview for image attachments -->
13038
- @if (fileAttachment.type === 'image') {
13039
- <div class="message-image-attachment" (click)="handleImageClick(fileAttachment, message)">
13040
- <img
13041
- [src]="fileAttachment.src"
13042
- [alt]="fileAttachment.name || 'Image'"
13043
- class="inline-image">
13044
- </div>
13045
- } @else {
13046
- <!-- Show file card for non-image attachments -->
13047
- <ds-mobile-card-inline-file
13048
- [fileName]="fileAttachment.name || 'Unknown file'"
13049
- [fileSize]="fileAttachment.size || ''"
13050
- [variant]="getFileVariant(fileAttachment.type)"
13051
- [layout]="'compact'"
13052
- (fileClick)="handleFileAttachmentClick(fileAttachment)">
13053
- </ds-mobile-card-inline-file>
13054
- }
13055
- }
13056
- </div>
13057
- }
13058
- }
13059
- }
13032
+ <!-- Empty State - Timestamp and System Message -->
13033
+ <div class="timestamp-header">
13034
+ <span class="timestamp-text">{{ getInitialTimestamp() }}</span>
13035
+ </div>
13036
+
13037
+ <ds-mobile-system-message-banner
13038
+ [message]="
13039
+ participant().name +
13040
+ ' har overtaget din henvendelse og vil kontakte dig snart.'
13041
+ "
13042
+ [afterTimestamp]="true"
13043
+ >
13044
+ </ds-mobile-system-message-banner>
13045
+ } @else { @for (group of messagesWithDisplay(); track group.timestamp)
13046
+ {
13047
+ <!-- Timestamp Header -->
13048
+ <div class="timestamp-header">
13049
+ <span class="timestamp-text">{{ group.displayTimestamp }}</span>
13050
+ </div>
13051
+
13052
+ <!-- System message example (shown after first timestamp) -->
13053
+ @if ($first) {
13054
+ <ds-mobile-system-message-banner
13055
+ [message]="
13056
+ participant().name +
13057
+ ' har overtaget din henvendelse og vil kontakte dig snart.'
13058
+ "
13059
+ [afterTimestamp]="true"
13060
+ >
13061
+ </ds-mobile-system-message-banner>
13062
+ } @for (message of group.messages; track message.id) {
13063
+ <!-- Only show bubble if has content -->
13064
+ @if (message.content.trim()) {
13065
+ <ds-mobile-message-bubble
13066
+ [content]="message.content"
13067
+ [isOwnMessage]="message.isOwnMessage"
13068
+ [timestamp]="formatMessageTimestamp(message.timestamp)"
13069
+ [showTimestamp]="selectedMessageId() === message.id"
13070
+ [avatarInitials]="message.avatarInitials || ''"
13071
+ [avatarType]="message.avatarType || 'initials'"
13072
+ [avatarSrc]="message.avatarSrc || ''"
13073
+ [showAvatar]="message.showAvatar"
13074
+ [clusterPosition]="message.clusterPosition"
13075
+ [attachments]="message.attachments"
13076
+ [clickable]="true"
13077
+ [isNewMessage]="message.isNewMessage || false"
13078
+ (messageClick)="handleMessageClick(message.id)"
13079
+ (attachmentClick)="handleAttachmentClick($event)"
13080
+ (longPress)="handleMessageLongPress(message)"
13081
+ >
13082
+ </ds-mobile-message-bubble>
13060
13083
  }
13084
+
13085
+ <!-- File attachments displayed below message bubble -->
13086
+ @if (message.fileAttachments && message.fileAttachments.length > 0) {
13087
+ <div
13088
+ class="message-file-attachments"
13089
+ [class.own-message]="message.isOwnMessage"
13090
+ >
13091
+ @for (fileAttachment of message.fileAttachments; track
13092
+ fileAttachment.id) {
13093
+ <!-- Show inline image preview for image attachments -->
13094
+ @if (fileAttachment.type === 'image') {
13095
+ <div
13096
+ class="message-image-attachment"
13097
+ (click)="handleImageClick(fileAttachment, message)"
13098
+ >
13099
+ <img
13100
+ [src]="fileAttachment.src"
13101
+ [alt]="fileAttachment.name || 'Image'"
13102
+ class="inline-image"
13103
+ />
13104
+ </div>
13105
+ } @else {
13106
+ <!-- Show file card for non-image attachments -->
13107
+ <ds-mobile-card-inline-file
13108
+ [fileName]="fileAttachment.name || 'Unknown file'"
13109
+ [fileSize]="fileAttachment.size || ''"
13110
+ [variant]="getFileVariant(fileAttachment.type)"
13111
+ [layout]="'compact'"
13112
+ (fileClick)="handleFileAttachmentClick(fileAttachment)"
13113
+ >
13114
+ </ds-mobile-card-inline-file>
13115
+ } }
13116
+ </div>
13117
+ } } } }
13061
13118
  </div>
13062
13119
  </div>
13063
13120
 
@@ -13072,7 +13129,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
13072
13129
  [showAttachmentButton]="true"
13073
13130
  (messageSent)="handleMessageSent($event)"
13074
13131
  (attachmentClicked)="handleComposerAttachmentClick()"
13075
- (attachmentsChanged)="handleAttachmentsChanged()">
13132
+ (attachmentsChanged)="handleAttachmentsChanged()"
13133
+ >
13076
13134
  </ds-mobile-message-composer>
13077
13135
  </div>
13078
13136
  </ds-mobile-modal-base>
@@ -13944,7 +14002,7 @@ class DsMobileHandbookDetailModalComponent {
13944
14002
  variant: 'light-purple',
13945
14003
  iconName: 'remixFolder3Line',
13946
14004
  itemCount: 0,
13947
- items: []
14005
+ items: [],
13948
14006
  }, ...(ngDevMode ? [{ debugName: "handbook" }] : []));
13949
14007
  ngOnInit() {
13950
14008
  // Initialize handbook data from input
@@ -13965,41 +14023,27 @@ class DsMobileHandbookDetailModalComponent {
13965
14023
  const hasImages = item.images && item.images.length > 0;
13966
14024
  const hasContacts = item.contacts && item.contacts.length > 0;
13967
14025
  const hasAttachments = item.attachments && item.attachments.length > 0;
13968
- // Case 1: Only one type of content - keep as is
13969
- const contentTypeCount = [hasImages, hasContacts, hasAttachments].filter(Boolean).length;
13970
- if (contentTypeCount <= 1) {
13971
- return [item];
13972
- }
13973
- // Case 2: Has images + contacts (allowed together) but no attachments
13974
- if (hasImages && hasContacts && !hasAttachments) {
13975
- return [item];
13976
- }
13977
- // Case 3: Violates rules - need to split
13978
- // First item: Text + Images (if present)
13979
- if (hasImages) {
14026
+ const hasContent = hasContacts || hasAttachments;
14027
+ // Case 1: Images AND other content -> Split Pictures from (Contacts + Attachments)
14028
+ if (hasImages && hasContent) {
14029
+ // Part 1: Images
13980
14030
  displayItems.push({
13981
14031
  title: item.title,
13982
14032
  description: item.description,
13983
- images: item.images
14033
+ images: item.images,
13984
14034
  });
13985
- }
13986
- // Second item: Text + Contacts (if present and no images shown yet)
13987
- if (hasContacts) {
14035
+ // Part 2: Contacts + Attachments
13988
14036
  displayItems.push({
13989
- title: item.title,
13990
- description: hasImages ? undefined : item.description, // Only show description if not shown before
13991
- contacts: item.contacts
13992
- });
13993
- }
13994
- // Third item: Text + Attachments (if present)
13995
- if (hasAttachments) {
13996
- displayItems.push({
13997
- title: item.title,
13998
- description: (!hasImages && !hasContacts) ? item.description : undefined, // Only show description if not shown before
13999
- attachments: item.attachments
14037
+ title: '', // No title for the continuation
14038
+ description: undefined,
14039
+ contacts: item.contacts,
14040
+ attachments: item.attachments,
14000
14041
  });
14042
+ return displayItems;
14001
14043
  }
14002
- return displayItems;
14044
+ // Case 2: No splitting needed (Images only, or No Images)
14045
+ // This allows Contacts and Attachments to coexist in one item
14046
+ return [item];
14003
14047
  }
14004
14048
  /**
14005
14049
  * Get all display items with enforced content structure rules applied
@@ -14025,7 +14069,7 @@ class DsMobileHandbookDetailModalComponent {
14025
14069
  */
14026
14070
  handleAttachmentClick(attachment) {
14027
14071
  console.log('Attachment clicked:', attachment);
14028
- // Implement attachment action (e.g., open file viewer, download, etc.)
14072
+ // Attachment action is now handled by DsMobileCardInlineFileComponent via fileUrl input
14029
14073
  }
14030
14074
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsMobileHandbookDetailModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14031
14075
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsMobileHandbookDetailModalComponent, isStandalone: true, selector: "ds-mobile-handbook-detail-modal", inputs: { handbookData: "handbookData", loading: "loading", error: "error" }, ngImport: i0, template: `
@@ -14034,88 +14078,90 @@ class DsMobileHandbookDetailModalComponent {
14034
14078
  [error]="error"
14035
14079
  [headerTitle]="handbook().title"
14036
14080
  [headerMeta]="handbook().itemCount + ' emner'"
14037
- closeButtonLabel="Luk">
14038
-
14081
+ closeButtonLabel="Luk"
14082
+ >
14039
14083
  <!-- Header Folder Icon -->
14040
14084
  <ds-mobile-handbook-folder-mini
14041
14085
  header-leading
14042
14086
  [variant]="handbook().variant"
14043
- [iconName]="handbook().iconName">
14087
+ [iconName]="handbook().iconName"
14088
+ >
14044
14089
  </ds-mobile-handbook-folder-mini>
14045
14090
 
14046
14091
  <!-- Content (main content slot) -->
14047
14092
  <div class="handbook-detail-container">
14048
- @if (handbook().items && handbook().items!.length > 0) {
14049
- @for (item of getDisplayItems(); track item.title + $index; let isLast = $last) {
14050
- <div class="handbook-item" [class.last-item]="isLast">
14051
- <div class="item-text-group">
14052
- <h2 class="item-title">{{ item.title }}</h2>
14053
-
14054
- @if (item.description) {
14055
- <p class="item-description">{{ item.description }}</p>
14056
- }
14057
- </div>
14058
-
14059
- <!-- Images -->
14060
- @if (item.images && item.images.length > 0) {
14061
- <ds-mobile-swiper [slideWidth]="item.images.length === 1 ? '100%' : '85%'" [gap]="16">
14062
- @for (image of item.images; track image) {
14063
- <div class="swiper-slide">
14064
- <img
14065
- [src]="image"
14066
- [alt]="item.title"
14067
- class="item-image"
14068
- />
14069
- </div>
14070
- }
14071
- </ds-mobile-swiper>
14072
- }
14073
-
14074
- <!-- Contacts -->
14075
- @if (item.contacts && item.contacts.length > 0) {
14076
- <div class="contacts-list">
14077
- @for (contact of item.contacts; track contact.name) {
14078
- <ds-mobile-card-inline-contact
14079
- [name]="contact.name"
14080
- [initials]="contact.initials"
14081
- [contactPerson]="contact.contactPerson || ''"
14082
- [phoneNumber]="contact.phoneNumber || ''"
14083
- [clickable]="true"
14084
- (contactClick)="handleContactClick(contact)">
14085
- </ds-mobile-card-inline-contact>
14086
- }
14087
- </div>
14088
- }
14089
-
14090
- <!-- Attachments -->
14091
- @if (item.attachments && item.attachments.length > 0) {
14092
- <div class="attachments-list">
14093
- @for (attachment of item.attachments; track attachment.name) {
14094
- <ds-mobile-card-inline-file
14095
- [fileName]="attachment.name"
14096
- [variant]="attachment.type === 'pdf' ? 'pdf' : 'doc'"
14097
- (fileClick)="handleAttachmentClick(attachment)">
14098
- </ds-mobile-card-inline-file>
14099
- }
14100
- </div>
14101
- }
14102
- </div>
14093
+ @if (handbook().items && handbook().items!.length > 0) { @for (item of
14094
+ getDisplayItems(); track item.title + $index; let isLast = $last) {
14095
+ <div class="handbook-item" [class.last-item]="isLast">
14096
+ <div class="item-text-group">
14097
+ @if (item.title) {
14098
+ <h2 class="item-title">{{ item.title }}</h2>
14099
+ } @if (item.description) {
14100
+ <p class="item-description" [innerHTML]="item.description"></p>
14101
+ }
14102
+ </div>
14103
+
14104
+ <!-- Images -->
14105
+ @if (item.images && item.images.length > 0) {
14106
+ <ds-mobile-swiper
14107
+ [slideWidth]="item.images.length === 1 ? '100%' : '85%'"
14108
+ [gap]="16"
14109
+ >
14110
+ @for (image of item.images; track image) {
14111
+ <div class="swiper-slide">
14112
+ <img [src]="image" [alt]="item.title" class="item-image" />
14113
+ </div>
14114
+ }
14115
+ </ds-mobile-swiper>
14116
+ }
14117
+
14118
+ <!-- Contacts -->
14119
+ @if (item.contacts && item.contacts.length > 0) {
14120
+ <div class="contacts-list">
14121
+ @for (contact of item.contacts; track contact.name) {
14122
+ <ds-mobile-card-inline-contact
14123
+ [name]="contact.name"
14124
+ [initials]="contact.initials"
14125
+ [contactPerson]="contact.contactPerson || ''"
14126
+ [phoneNumber]="contact.phoneNumber || ''"
14127
+ [clickable]="true"
14128
+ (contactClick)="handleContactClick(contact)"
14129
+ >
14130
+ </ds-mobile-card-inline-contact>
14103
14131
  }
14104
- } @else {
14105
- <!-- Empty State -->
14106
- <div class="handbook-empty-state">
14107
- <img
14108
- src="/Assets/Empty%20state-chat.png"
14109
- alt="No items yet"
14110
- class="empty-state-image"
14111
- />
14112
- <h3 class="empty-state-title">No items yet</h3>
14113
- <p class="empty-state-description">This folder is empty</p>
14114
14132
  </div>
14133
+ }
14134
+
14135
+ <!-- Attachments -->
14136
+ @if (item.attachments && item.attachments.length > 0) {
14137
+ <div class="attachments-list">
14138
+ @for (attachment of item.attachments; track attachment.name) {
14139
+ <ds-mobile-card-inline-file
14140
+ [fileName]="attachment.name"
14141
+ [variant]="attachment.type === 'pdf' ? 'pdf' : 'doc'"
14142
+ [fileUrl]="attachment.url"
14143
+ (fileClick)="handleAttachmentClick(attachment)"
14144
+ >
14145
+ </ds-mobile-card-inline-file>
14146
+ }
14147
+ </div>
14148
+ }
14149
+ </div>
14150
+ } } @else {
14151
+ <!-- Empty State -->
14152
+ <div class="handbook-empty-state">
14153
+ <img
14154
+ src="/Assets/Empty%20state-chat.png"
14155
+ alt="No items yet"
14156
+ class="empty-state-image"
14157
+ />
14158
+ <h3 class="empty-state-title">No items yet</h3>
14159
+ <p class="empty-state-description">This folder is empty</p>
14160
+ </div>
14115
14161
  }
14116
14162
  </div>
14117
14163
  </ds-mobile-modal-base>
14118
- `, isInline: true, styles: [".handbook-detail-container{display:flex;flex-direction:column;width:100%;flex:1}.handbook-item{width:100%;display:flex;flex-direction:column;gap:24px;padding:24px 20px;border-bottom:1px solid var(--border-color-default)}.handbook-item.last-item{border-bottom:none}.item-text-group{display:flex;flex-direction:column;gap:8px}.item-title{font-family:Brockmann,sans-serif;font-size:16px;font-weight:600;line-height:24px;color:var(--color-text-primary, #1a1a1a);margin:0}.item-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);line-height:20px;color:var(--color-text-primary, #1a1a1a);margin:0}.item-image{width:100%;max-width:100%;height:280px;-o-object-fit:cover;object-fit:cover;border-radius:12px;display:block}.contacts-list,.attachments-list{display:flex;flex-direction:column;gap:8px}.handbook-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}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a)}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373)}@supports (padding: env(safe-area-inset-bottom)){.handbook-detail-container{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsMobileHandbookFolderMiniComponent, selector: "ds-mobile-handbook-folder-mini", inputs: ["variant", "iconName"] }, { kind: "component", type: DsMobileCardInlineFileComponent, selector: "ds-mobile-card-inline-file", inputs: ["fileName", "fileSize", "variant", "layout"], outputs: ["fileClick"] }, { kind: "component", type: DsMobileCardInlineContactComponent, selector: "ds-mobile-card-inline-contact", inputs: ["name", "initials", "contactPerson", "phoneNumber", "layout", "clickable", "showChevron"], outputs: ["contactClick"] }, { kind: "component", type: DsMobileSwiperComponent, selector: "ds-mobile-swiper", inputs: ["slideWidth", "gap"] }, { kind: "component", type: DsMobileModalBaseComponent, selector: "ds-mobile-modal-base", inputs: ["showHeader"] }] });
14164
+ `, isInline: true, styles: [".handbook-detail-container{display:flex;flex-direction:column;width:100%;flex:1}.handbook-item{width:100%;display:flex;flex-direction:column;gap:24px;padding:24px 20px;border-bottom:1px solid var(--border-color-default)}.handbook-item.last-item{border-bottom:none}.item-text-group{display:flex;flex-direction:column;gap:8px}.item-title{font-family:Brockmann,sans-serif;font-size:16px;font-weight:600;line-height:24px;color:var(--color-text-primary, #1a1a1a);margin:0}.item-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);line-height:20px;color:var(--color-text-primary, #1a1a1a);margin:0}.item-image{width:100%;max-width:100%;height:280px;-o-object-fit:cover;object-fit:cover;border-radius:12px;display:block}.contacts-list,.attachments-list{display:flex;flex-direction:column;gap:8px}.handbook-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}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a)}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373)}@supports (padding: env(safe-area-inset-bottom)){.handbook-detail-container{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsMobileHandbookFolderMiniComponent, selector: "ds-mobile-handbook-folder-mini", inputs: ["variant", "iconName"] }, { kind: "component", type: DsMobileCardInlineFileComponent, selector: "ds-mobile-card-inline-file", inputs: ["fileName", "fileSize", "variant", "layout", "fileUrl"], outputs: ["fileClick"] }, { kind: "component", type: DsMobileCardInlineContactComponent, selector: "ds-mobile-card-inline-contact", inputs: ["name", "initials", "contactPerson", "phoneNumber", "layout", "clickable", "showChevron"], outputs: ["contactClick"] }, { kind: "component", type: DsMobileSwiperComponent, selector: "ds-mobile-swiper", inputs: ["slideWidth", "gap"] }, { kind: "component", type: DsMobileModalBaseComponent, selector: "ds-mobile-modal-base", inputs: ["showHeader"] }] });
14119
14165
  }
14120
14166
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsMobileHandbookDetailModalComponent, decorators: [{
14121
14167
  type: Component,
@@ -14125,91 +14171,93 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
14125
14171
  DsMobileCardInlineFileComponent,
14126
14172
  DsMobileCardInlineContactComponent,
14127
14173
  DsMobileSwiperComponent,
14128
- DsMobileModalBaseComponent
14174
+ DsMobileModalBaseComponent,
14129
14175
  ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `
14130
14176
  <ds-mobile-modal-base
14131
14177
  [loading]="loading"
14132
14178
  [error]="error"
14133
14179
  [headerTitle]="handbook().title"
14134
14180
  [headerMeta]="handbook().itemCount + ' emner'"
14135
- closeButtonLabel="Luk">
14136
-
14181
+ closeButtonLabel="Luk"
14182
+ >
14137
14183
  <!-- Header Folder Icon -->
14138
14184
  <ds-mobile-handbook-folder-mini
14139
14185
  header-leading
14140
14186
  [variant]="handbook().variant"
14141
- [iconName]="handbook().iconName">
14187
+ [iconName]="handbook().iconName"
14188
+ >
14142
14189
  </ds-mobile-handbook-folder-mini>
14143
14190
 
14144
14191
  <!-- Content (main content slot) -->
14145
14192
  <div class="handbook-detail-container">
14146
- @if (handbook().items && handbook().items!.length > 0) {
14147
- @for (item of getDisplayItems(); track item.title + $index; let isLast = $last) {
14148
- <div class="handbook-item" [class.last-item]="isLast">
14149
- <div class="item-text-group">
14150
- <h2 class="item-title">{{ item.title }}</h2>
14151
-
14152
- @if (item.description) {
14153
- <p class="item-description">{{ item.description }}</p>
14154
- }
14155
- </div>
14156
-
14157
- <!-- Images -->
14158
- @if (item.images && item.images.length > 0) {
14159
- <ds-mobile-swiper [slideWidth]="item.images.length === 1 ? '100%' : '85%'" [gap]="16">
14160
- @for (image of item.images; track image) {
14161
- <div class="swiper-slide">
14162
- <img
14163
- [src]="image"
14164
- [alt]="item.title"
14165
- class="item-image"
14166
- />
14167
- </div>
14168
- }
14169
- </ds-mobile-swiper>
14170
- }
14171
-
14172
- <!-- Contacts -->
14173
- @if (item.contacts && item.contacts.length > 0) {
14174
- <div class="contacts-list">
14175
- @for (contact of item.contacts; track contact.name) {
14176
- <ds-mobile-card-inline-contact
14177
- [name]="contact.name"
14178
- [initials]="contact.initials"
14179
- [contactPerson]="contact.contactPerson || ''"
14180
- [phoneNumber]="contact.phoneNumber || ''"
14181
- [clickable]="true"
14182
- (contactClick)="handleContactClick(contact)">
14183
- </ds-mobile-card-inline-contact>
14184
- }
14185
- </div>
14186
- }
14187
-
14188
- <!-- Attachments -->
14189
- @if (item.attachments && item.attachments.length > 0) {
14190
- <div class="attachments-list">
14191
- @for (attachment of item.attachments; track attachment.name) {
14192
- <ds-mobile-card-inline-file
14193
- [fileName]="attachment.name"
14194
- [variant]="attachment.type === 'pdf' ? 'pdf' : 'doc'"
14195
- (fileClick)="handleAttachmentClick(attachment)">
14196
- </ds-mobile-card-inline-file>
14197
- }
14198
- </div>
14199
- }
14200
- </div>
14193
+ @if (handbook().items && handbook().items!.length > 0) { @for (item of
14194
+ getDisplayItems(); track item.title + $index; let isLast = $last) {
14195
+ <div class="handbook-item" [class.last-item]="isLast">
14196
+ <div class="item-text-group">
14197
+ @if (item.title) {
14198
+ <h2 class="item-title">{{ item.title }}</h2>
14199
+ } @if (item.description) {
14200
+ <p class="item-description" [innerHTML]="item.description"></p>
14201
14201
  }
14202
- } @else {
14203
- <!-- Empty State -->
14204
- <div class="handbook-empty-state">
14205
- <img
14206
- src="/Assets/Empty%20state-chat.png"
14207
- alt="No items yet"
14208
- class="empty-state-image"
14209
- />
14210
- <h3 class="empty-state-title">No items yet</h3>
14211
- <p class="empty-state-description">This folder is empty</p>
14212
14202
  </div>
14203
+
14204
+ <!-- Images -->
14205
+ @if (item.images && item.images.length > 0) {
14206
+ <ds-mobile-swiper
14207
+ [slideWidth]="item.images.length === 1 ? '100%' : '85%'"
14208
+ [gap]="16"
14209
+ >
14210
+ @for (image of item.images; track image) {
14211
+ <div class="swiper-slide">
14212
+ <img [src]="image" [alt]="item.title" class="item-image" />
14213
+ </div>
14214
+ }
14215
+ </ds-mobile-swiper>
14216
+ }
14217
+
14218
+ <!-- Contacts -->
14219
+ @if (item.contacts && item.contacts.length > 0) {
14220
+ <div class="contacts-list">
14221
+ @for (contact of item.contacts; track contact.name) {
14222
+ <ds-mobile-card-inline-contact
14223
+ [name]="contact.name"
14224
+ [initials]="contact.initials"
14225
+ [contactPerson]="contact.contactPerson || ''"
14226
+ [phoneNumber]="contact.phoneNumber || ''"
14227
+ [clickable]="true"
14228
+ (contactClick)="handleContactClick(contact)"
14229
+ >
14230
+ </ds-mobile-card-inline-contact>
14231
+ }
14232
+ </div>
14233
+ }
14234
+
14235
+ <!-- Attachments -->
14236
+ @if (item.attachments && item.attachments.length > 0) {
14237
+ <div class="attachments-list">
14238
+ @for (attachment of item.attachments; track attachment.name) {
14239
+ <ds-mobile-card-inline-file
14240
+ [fileName]="attachment.name"
14241
+ [variant]="attachment.type === 'pdf' ? 'pdf' : 'doc'"
14242
+ [fileUrl]="attachment.url"
14243
+ (fileClick)="handleAttachmentClick(attachment)"
14244
+ >
14245
+ </ds-mobile-card-inline-file>
14246
+ }
14247
+ </div>
14248
+ }
14249
+ </div>
14250
+ } } @else {
14251
+ <!-- Empty State -->
14252
+ <div class="handbook-empty-state">
14253
+ <img
14254
+ src="/Assets/Empty%20state-chat.png"
14255
+ alt="No items yet"
14256
+ class="empty-state-image"
14257
+ />
14258
+ <h3 class="empty-state-title">No items yet</h3>
14259
+ <p class="empty-state-description">This folder is empty</p>
14260
+ </div>
14213
14261
  }
14214
14262
  </div>
14215
14263
  </ds-mobile-modal-base>
@@ -14373,7 +14421,7 @@ class DsMobileHandbookFolderComponent {
14373
14421
  'salmon-orange': 'salmon-orange',
14374
14422
  'orange': 'orange',
14375
14423
  'lime-green': 'lime-green',
14376
- 'grey': 'grey'
14424
+ 'grey': 'grey',
14377
14425
  };
14378
14426
  const colorName = variantMap[this.variant] || 'light-purple';
14379
14427
  return `var(--color-${colorName}-${suffix})`;
@@ -14422,11 +14470,11 @@ class DsMobileHandbookFolderComponent {
14422
14470
  variant: this.variant,
14423
14471
  iconName: this.iconName,
14424
14472
  itemCount: this.itemCount,
14425
- items: this.items
14473
+ items: this.items,
14426
14474
  };
14427
14475
  await this.handbookModal.open(handbookData, {
14428
14476
  loading: this.loading,
14429
- error: this.error
14477
+ error: this.error,
14430
14478
  });
14431
14479
  }
14432
14480
  /**
@@ -14442,55 +14490,43 @@ class DsMobileHandbookFolderComponent {
14442
14490
  }
14443
14491
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsMobileHandbookFolderComponent, deps: [{ token: DsMobileHandbookDetailModalService }], target: i0.ɵɵFactoryTarget.Component });
14444
14492
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsMobileHandbookFolderComponent, isStandalone: true, selector: "ds-mobile-handbook-folder", inputs: { variant: "variant", iconName: "iconName", itemCount: "itemCount", label: "label", items: "items", loading: "loading", error: "error" }, host: { listeners: { "mouseenter": "open()", "mouseleave": "close()", "touchstart": "onTouchStart($event)", "touchend": "onTouchEnd()", "touchcancel": "onTouchCancel()", "click": "onClick()" } }, ngImport: i0, template: `
14445
- <div class="folder-container"
14446
- [class.open]="isOpen()">
14493
+ <div class="folder-container" [class.open]="isOpen()">
14447
14494
  <!-- Folder Tab SVG -->
14448
- <svg
14449
- class="folder-tab"
14450
- width="101"
14451
- height="24"
14452
- viewBox="0 0 101 24"
14453
- fill="none"
14454
- xmlns="http://www.w3.org/2000/svg">
14455
- <path
14456
- d="M100.037 23.9999L100.5 24L0 24.0001V10.7646C0 4.80853 4.91797 -0.0234985 11 -0.0196688L66.4213 -0.0322266C69.3519 -0.0115886 72.197 1.20548 74.2473 3.29947L90.6765 20.0951C93.1218 22.5925 96.5417 23.9999 100.037 23.9999Z"
14457
- [attr.fill]="getColorVar('strong')"/>
14495
+ <svg class="folder-tab" width="101" height="24" viewBox="0 0 101 24" fill="none" xmlns="http://www.w3.org/2000/svg">
14496
+ <path
14497
+ d="M100.037 23.9999L100.5 24L0 24.0001V10.7646C0 4.80853 4.91797 -0.0234985 11 -0.0196688L66.4213 -0.0322266C69.3519 -0.0115886 72.197 1.20548 74.2473 3.29947L90.6765 20.0951C93.1218 22.5925 96.5417 23.9999 100.037 23.9999Z"
14498
+ [attr.fill]="getColorVar('strong')"
14499
+ />
14458
14500
  </svg>
14459
-
14501
+
14460
14502
  <!-- Folder Back -->
14461
14503
  <div class="folder-back" [style.background-color]="getColorVar('strong')">
14462
14504
  <!-- Page Sheets -->
14463
14505
  @for (sheet of getPageSheets(); track $index) {
14464
- <div class="page-sheet"></div>
14506
+ <div class="page-sheet"></div>
14465
14507
  }
14466
-
14508
+
14467
14509
  <!-- Folder Front -->
14468
- <div
14469
- class="folder-front"
14470
- [style.--border-color]="getColorVar('strong')"
14471
- [style.background-color]="getColorVar('base')">
14510
+ <div class="folder-front" [style.--border-color]="getColorVar('strong')" [style.background-color]="getColorVar('base')">
14472
14511
  <!-- Icon (Centered) -->
14473
14512
  <div class="folder-icon">
14474
- <ds-icon
14475
- [name]="iconName"
14476
- [size]="'32px'"
14477
- [style.color]="'white'" />
14513
+ <ds-icon [name]="iconName" [size]="'32px'" [style.color]="'white'" />
14478
14514
  </div>
14479
14515
  </div>
14480
14516
  </div>
14481
14517
  </div>
14482
-
14518
+
14483
14519
  <!-- Label and Item Count -->
14484
14520
  <div class="folder-label-container">
14485
14521
  <div class="folder-label ui-sm-semiBold">{{ label }}</div>
14486
14522
  <div class="item-count ui-sm-regular" [style.color]="'var(--color-text-secondary, #6b7280)'">
14487
14523
  @if (loading) {
14488
- <span class="loading-indicator">Loading...</span>
14524
+ <span class="loading-indicator">Loading...</span>
14489
14525
  } @else if (error) {
14490
- <span class="error-indicator" [style.color]="'var(--color-destructive-base)'">Error</span>
14526
+ <span class="error-indicator" [style.color]="'var(--color-destructive-base)'">Error</span>
14491
14527
  } @else {
14492
- <span>{{ itemCount }}</span>
14493
- <span class="item-count-label">emner</span>
14528
+ <span>{{ itemCount }}</span>
14529
+ <span class="item-count-label">emner</span>
14494
14530
  }
14495
14531
  </div>
14496
14532
  </div>
@@ -14499,55 +14535,43 @@ class DsMobileHandbookFolderComponent {
14499
14535
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsMobileHandbookFolderComponent, decorators: [{
14500
14536
  type: Component,
14501
14537
  args: [{ selector: 'ds-mobile-handbook-folder', standalone: true, imports: [CommonModule, DsIconComponent], template: `
14502
- <div class="folder-container"
14503
- [class.open]="isOpen()">
14538
+ <div class="folder-container" [class.open]="isOpen()">
14504
14539
  <!-- Folder Tab SVG -->
14505
- <svg
14506
- class="folder-tab"
14507
- width="101"
14508
- height="24"
14509
- viewBox="0 0 101 24"
14510
- fill="none"
14511
- xmlns="http://www.w3.org/2000/svg">
14512
- <path
14513
- d="M100.037 23.9999L100.5 24L0 24.0001V10.7646C0 4.80853 4.91797 -0.0234985 11 -0.0196688L66.4213 -0.0322266C69.3519 -0.0115886 72.197 1.20548 74.2473 3.29947L90.6765 20.0951C93.1218 22.5925 96.5417 23.9999 100.037 23.9999Z"
14514
- [attr.fill]="getColorVar('strong')"/>
14540
+ <svg class="folder-tab" width="101" height="24" viewBox="0 0 101 24" fill="none" xmlns="http://www.w3.org/2000/svg">
14541
+ <path
14542
+ d="M100.037 23.9999L100.5 24L0 24.0001V10.7646C0 4.80853 4.91797 -0.0234985 11 -0.0196688L66.4213 -0.0322266C69.3519 -0.0115886 72.197 1.20548 74.2473 3.29947L90.6765 20.0951C93.1218 22.5925 96.5417 23.9999 100.037 23.9999Z"
14543
+ [attr.fill]="getColorVar('strong')"
14544
+ />
14515
14545
  </svg>
14516
-
14546
+
14517
14547
  <!-- Folder Back -->
14518
14548
  <div class="folder-back" [style.background-color]="getColorVar('strong')">
14519
14549
  <!-- Page Sheets -->
14520
14550
  @for (sheet of getPageSheets(); track $index) {
14521
- <div class="page-sheet"></div>
14551
+ <div class="page-sheet"></div>
14522
14552
  }
14523
-
14553
+
14524
14554
  <!-- Folder Front -->
14525
- <div
14526
- class="folder-front"
14527
- [style.--border-color]="getColorVar('strong')"
14528
- [style.background-color]="getColorVar('base')">
14555
+ <div class="folder-front" [style.--border-color]="getColorVar('strong')" [style.background-color]="getColorVar('base')">
14529
14556
  <!-- Icon (Centered) -->
14530
14557
  <div class="folder-icon">
14531
- <ds-icon
14532
- [name]="iconName"
14533
- [size]="'32px'"
14534
- [style.color]="'white'" />
14558
+ <ds-icon [name]="iconName" [size]="'32px'" [style.color]="'white'" />
14535
14559
  </div>
14536
14560
  </div>
14537
14561
  </div>
14538
14562
  </div>
14539
-
14563
+
14540
14564
  <!-- Label and Item Count -->
14541
14565
  <div class="folder-label-container">
14542
14566
  <div class="folder-label ui-sm-semiBold">{{ label }}</div>
14543
14567
  <div class="item-count ui-sm-regular" [style.color]="'var(--color-text-secondary, #6b7280)'">
14544
14568
  @if (loading) {
14545
- <span class="loading-indicator">Loading...</span>
14569
+ <span class="loading-indicator">Loading...</span>
14546
14570
  } @else if (error) {
14547
- <span class="error-indicator" [style.color]="'var(--color-destructive-base)'">Error</span>
14571
+ <span class="error-indicator" [style.color]="'var(--color-destructive-base)'">Error</span>
14548
14572
  } @else {
14549
- <span>{{ itemCount }}</span>
14550
- <span class="item-count-label">emner</span>
14573
+ <span>{{ itemCount }}</span>
14574
+ <span class="item-count-label">emner</span>
14551
14575
  }
14552
14576
  </div>
14553
14577
  </div>
@@ -15916,7 +15940,7 @@ class MobileCommunityPageComponent {
15916
15940
  </div>
15917
15941
  </ds-mobile-content>
15918
15942
  </ds-mobile-page-main>
15919
- `, isInline: true, styles: [".post-feed,.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}ion-infinite-scroll{--color: var(--color-primary-surface)}ion-infinite-scroll-content{--color: var(--color-primary-surface)}ion-infinite-scroll-content::part(spinner){color:var(--color-primary-surface)}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "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: DsMobileCardInlineFileComponent, selector: "ds-mobile-card-inline-file", inputs: ["fileName", "fileSize", "variant", "layout"], outputs: ["fileClick"] }, { 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"] }, { kind: "component", type: IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }] });
15943
+ `, isInline: true, styles: [".post-feed,.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}ion-infinite-scroll{--color: var(--color-primary-surface)}ion-infinite-scroll-content{--color: var(--color-primary-surface)}ion-infinite-scroll-content::part(spinner){color:var(--color-primary-surface)}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "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: DsMobileCardInlineFileComponent, selector: "ds-mobile-card-inline-file", inputs: ["fileName", "fileSize", "variant", "layout", "fileUrl"], outputs: ["fileClick"] }, { 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"] }, { kind: "component", type: IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }] });
15920
15944
  }
15921
15945
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MobileCommunityPageComponent, decorators: [{
15922
15946
  type: Component,
@@ -16156,58 +16180,72 @@ class MobileHandbookPageComponent {
16156
16180
  title: 'El',
16157
16181
  description: 'Hovedeltavle placeret i kælderrum B-12. Nødafbryderknap er ved hovedindgangen. Alle kredsløb er mærket.',
16158
16182
  contacts: [
16159
- { name: 'ElektroTek ApS', initials: 'E', contactPerson: 'Lars Nielsen', phoneNumber: '+45 23 45 67 89' }
16160
- ]
16183
+ {
16184
+ name: 'ElektroTek ApS',
16185
+ initials: 'E',
16186
+ contactPerson: 'Lars Nielsen',
16187
+ phoneNumber: '+45 23 45 67 89',
16188
+ },
16189
+ ],
16161
16190
  },
16162
16191
  {
16163
16192
  title: 'Elektrisk diagram',
16164
16193
  description: 'Komplet diagram over bygningens elektriske installation med alle kredsløb og afbrydere.',
16165
- attachments: [
16166
- { name: 'Elektrisk_Diagram.pdf', type: 'pdf' }
16167
- ]
16194
+ attachments: [{ name: 'Elektrisk_Diagram.pdf', type: 'pdf' }],
16168
16195
  },
16169
16196
  {
16170
16197
  title: 'Vandforsyning',
16171
16198
  description: 'Hovedvandhane er placeret i kælderens tekniske rum. Individuelle lejlighedsafspærringer er i gangpanelerne. Vandtryk overvåges automatisk.',
16172
16199
  contacts: [
16173
- { name: 'VVS Hansen', initials: 'V', contactPerson: 'Peter Hansen', phoneNumber: '+45 34 56 78 90' }
16174
- ]
16200
+ {
16201
+ name: 'VVS Hansen',
16202
+ initials: 'V',
16203
+ contactPerson: 'Peter Hansen',
16204
+ phoneNumber: '+45 34 56 78 90',
16205
+ },
16206
+ ],
16175
16207
  },
16176
16208
  {
16177
16209
  title: 'Varmesystem',
16178
16210
  description: 'Fjernvarmetilslutning i kælder. Termostater i hver enhed kan justeres individuelt. Systemet vedligeholdes kvartalsvis af certificerede teknikere.',
16179
16211
  contacts: [
16180
- { name: 'Varme Service A/S', initials: 'V', contactPerson: 'Maria Jensen', phoneNumber: '+45 45 67 89 01' }
16181
- ]
16212
+ {
16213
+ name: 'Varme Service A/S',
16214
+ initials: 'V',
16215
+ contactPerson: 'Maria Jensen',
16216
+ phoneNumber: '+45 45 67 89 01',
16217
+ },
16218
+ ],
16182
16219
  },
16183
16220
  {
16184
16221
  title: 'Varmeanlæg dokumentation',
16185
16222
  description: 'Teknisk dokumentation og vedligeholdelseshistorik for bygningens varmesystem.',
16186
16223
  attachments: [
16187
16224
  { name: 'Varmeplan.pdf', type: 'pdf' },
16188
- { name: 'Vedligeholdelseslog.pdf', type: 'pdf' }
16189
- ]
16225
+ { name: 'Vedligeholdelseslog.pdf', type: 'pdf' },
16226
+ ],
16190
16227
  },
16191
16228
  {
16192
16229
  title: 'Internet & TV',
16193
16230
  description: 'Fiberforbindelse i bygningen. Distributionspanel er i stueetageteknisk rum. Hver lejlighed har ethernet-stik i stue og soveværelser.',
16194
16231
  contacts: [
16195
- { name: 'TeleCom Solutions', initials: 'T', contactPerson: 'Anders Petersen', phoneNumber: '+45 56 78 90 12' }
16196
- ]
16232
+ {
16233
+ name: 'TeleCom Solutions',
16234
+ initials: 'T',
16235
+ contactPerson: 'Anders Petersen',
16236
+ phoneNumber: '+45 56 78 90 12',
16237
+ },
16238
+ ],
16197
16239
  },
16198
16240
  {
16199
16241
  title: 'Netværksopsætning guide',
16200
- attachments: [
16201
- { name: 'Netværksopsætning.pdf', type: 'pdf' }
16202
- ]
16242
+ attachments: [{ name: 'Netværksopsætning.pdf', type: 'pdf' }],
16203
16243
  },
16204
16244
  {
16205
16245
  title: 'Affaldshåndtering',
16206
16246
  description: 'Affaldssorteringsstation placeret i gården. Afhentninger: Dagrenovation (man/tor), Genbrug (ons), Organisk (tir/fre). Storskrald kræver booking.',
16207
- attachments: [
16208
- { name: 'Affaldsretningslinjer.pdf', type: 'pdf' }
16209
- ]
16210
- }
16247
+ attachments: [{ name: 'Affaldsretningslinjer.pdf', type: 'pdf' }],
16248
+ },
16211
16249
  ];
16212
16250
  // Safety Equipment folder data
16213
16251
  sikkerhedsudstyrItems = [
@@ -16216,30 +16254,45 @@ class MobileHandbookPageComponent {
16216
16254
  description: 'Trappeopgange med nødbelysning og brandsikre døre. Postkasser placeret i indgangspartiet. Hold altid flugtveje fri.',
16217
16255
  images: [
16218
16256
  '/Assets/Dummy-photos/staircase.jpg',
16219
- '/Assets/Dummy-photos/mailboxes.jpg'
16220
- ]
16257
+ '/Assets/Dummy-photos/mailboxes.jpg',
16258
+ ],
16221
16259
  },
16222
16260
  {
16223
16261
  title: 'Hjertestarter (AED)',
16224
16262
  description: 'Automatisk hjertestarter placeret i stueetagen ved hovedindgangen. Tilgængelig 24/7. Ingen særlig uddannelse kræves - enheden guider dig gennem processen.',
16225
16263
  contacts: [
16226
- { name: 'MediTech Service', initials: 'M', contactPerson: 'John Mortensen', phoneNumber: '+45 12 34 56 78' }
16227
- ]
16264
+ {
16265
+ name: 'MediTech Service',
16266
+ initials: 'M',
16267
+ contactPerson: 'John Mortensen',
16268
+ phoneNumber: '+45 12 34 56 78',
16269
+ },
16270
+ ],
16271
+ attachments: [
16272
+ {
16273
+ name: 'Brandplan.pdf',
16274
+ type: 'pdf',
16275
+ url: 'https://portal-api.propbinder.com/Handbook/GetFile/50145267-d9a2-448b-a0e1-1af39d344818',
16276
+ },
16277
+ ],
16228
16278
  },
16229
16279
  {
16230
16280
  title: 'Brandslukningsudstyr',
16231
16281
  description: 'Brandslukkere placeret på hver etage. Eftersyn udføres årligt. Brandalarm aktiveres automatisk ved røg.',
16232
- attachments: [
16233
- { name: 'Brandplan.pdf', type: 'pdf' }
16234
- ]
16282
+ attachments: [{ name: 'Brandplan.pdf', type: 'pdf' }],
16235
16283
  },
16236
16284
  {
16237
16285
  title: 'Alarmsystem',
16238
16286
  description: 'Adgangskontrol med kodesystem ved alle indgange. Kode ændres kvartalsvis. Ved indbrud kontakt straks politiet og ejendomsadministrationen.',
16239
16287
  contacts: [
16240
- { name: 'SecureHome A/S', initials: 'S', contactPerson: 'Henrik Johansen', phoneNumber: '+45 98 76 54 32' }
16241
- ]
16242
- }
16288
+ {
16289
+ name: 'SecureHome A/S',
16290
+ initials: 'S',
16291
+ contactPerson: 'Henrik Johansen',
16292
+ phoneNumber: '+45 98 76 54 32',
16293
+ },
16294
+ ],
16295
+ },
16243
16296
  ];
16244
16297
  // Service Contracts folder data
16245
16298
  serviceContractsItems = [
@@ -16247,100 +16300,124 @@ class MobileHandbookPageComponent {
16247
16300
  title: 'Rengøringsservice',
16248
16301
  description: 'Ugentlig rengøring af fællesarealer inklusiv indgangshal, trapper og elevatorer. Hovedrengøring kvartalsvis. Service leveres mandag-fredag, 6:00-9:00.',
16249
16302
  contacts: [
16250
- { name: 'CleanCo Denmark', initials: 'C', contactPerson: 'Anne Kristensen', phoneNumber: '+45 89 01 23 45' }
16251
- ]
16303
+ {
16304
+ name: 'CleanCo Denmark',
16305
+ initials: 'C',
16306
+ contactPerson: 'Anne Kristensen',
16307
+ phoneNumber: '+45 89 01 23 45',
16308
+ },
16309
+ ],
16252
16310
  },
16253
16311
  {
16254
16312
  title: 'Rengøringskontrakt',
16255
16313
  attachments: [
16256
16314
  { name: 'Rengøringskontrakt_2024.pdf', type: 'pdf' },
16257
- { name: 'Rengøringsplan.pdf', type: 'pdf' }
16258
- ]
16315
+ { name: 'Rengøringsplan.pdf', type: 'pdf' },
16316
+ ],
16259
16317
  },
16260
16318
  {
16261
16319
  title: 'Udendørs arealer',
16262
16320
  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.',
16263
16321
  images: [
16264
16322
  '/Assets/Dummy-photos/park.jpg',
16265
- '/Assets/Dummy-photos/yard.jpg'
16266
- ]
16323
+ '/Assets/Dummy-photos/yard.jpg',
16324
+ ],
16267
16325
  },
16268
16326
  {
16269
16327
  title: 'Havevedligeholdelse',
16270
16328
  description: 'Professionel havepleje inklusiv plæneklipning, hækklipning og blomsterbedvedligeholdelse. Vintersnefjerning inkluderet.',
16271
16329
  contacts: [
16272
- { name: 'Green Gardens ApS', initials: 'G', contactPerson: 'Michael Olsen', phoneNumber: '+45 90 12 34 56' }
16273
- ]
16330
+ {
16331
+ name: 'Green Gardens ApS',
16332
+ initials: 'G',
16333
+ contactPerson: 'Michael Olsen',
16334
+ phoneNumber: '+45 90 12 34 56',
16335
+ },
16336
+ ],
16274
16337
  },
16275
16338
  {
16276
16339
  title: 'Haveserviceaftale',
16277
- attachments: [
16278
- { name: 'Haveserviceaftale.pdf', type: 'pdf' }
16279
- ]
16340
+ attachments: [{ name: 'Haveserviceaftale.pdf', type: 'pdf' }],
16280
16341
  },
16281
16342
  {
16282
16343
  title: 'Vinduespolering',
16283
16344
  description: 'Professionel vinduespoleringsservice for alle udvendige vinduer to gange årligt - forår og efterår.',
16284
16345
  contacts: [
16285
- { name: 'Crystal Clear Windows', initials: 'C', contactPerson: 'Lene Schmidt', phoneNumber: '+45 01 23 45 67' }
16286
- ]
16346
+ {
16347
+ name: 'Crystal Clear Windows',
16348
+ initials: 'C',
16349
+ contactPerson: 'Lene Schmidt',
16350
+ phoneNumber: '+45 01 23 45 67',
16351
+ },
16352
+ ],
16287
16353
  },
16288
16354
  {
16289
16355
  title: 'Sikkerhedsservice',
16290
16356
  description: '24/7 overvågningsservice med alarmrespons. Direkte forbindelse til politi og brandvæsen.',
16291
16357
  contacts: [
16292
- { name: 'SecureHome A/S', initials: 'S', contactPerson: 'Henrik Johansen', phoneNumber: '+45 12 34 56 78' }
16293
- ]
16358
+ {
16359
+ name: 'SecureHome A/S',
16360
+ initials: 'S',
16361
+ contactPerson: 'Henrik Johansen',
16362
+ phoneNumber: '+45 12 34 56 78',
16363
+ },
16364
+ ],
16294
16365
  },
16295
16366
  {
16296
16367
  title: 'Sikkerhedskontrakt og procedurer',
16297
16368
  attachments: [
16298
16369
  { name: 'Sikkerhedskontrakt.pdf', type: 'pdf' },
16299
- { name: 'Nødprocedurer.pdf', type: 'pdf' }
16300
- ]
16301
- }
16370
+ { name: 'Nødprocedurer.pdf', type: 'pdf' },
16371
+ ],
16372
+ },
16302
16373
  ];
16303
16374
  // Equipment folder data
16304
16375
  equipmentItems = [
16305
16376
  {
16306
16377
  title: 'Balkon udsigt',
16307
16378
  description: 'Eksempel på udsigt fra øverste etagers balkoner. Flere lejligheder har privat altan med fantastisk udsyn.',
16308
- images: [
16309
- '/Assets/Dummy-photos/balcony-view.jpg'
16310
- ]
16379
+ images: ['/Assets/Dummy-photos/balcony-view.jpg'],
16311
16380
  },
16312
16381
  {
16313
16382
  title: 'Vaskerum',
16314
16383
  description: 'Fælles vaskerum med 4 vaskemaskiner og 4 tørretumblere. Bookingsystem tilgængeligt via beboerportal. Maskiner accepterer betalingskort. Åbningstider: 7:00-22:00.',
16315
16384
  contacts: [
16316
- { name: 'WashTech Service', initials: 'W', contactPerson: 'Kirsten Berg', phoneNumber: '+45 34 56 78 90' }
16317
- ]
16385
+ {
16386
+ name: 'WashTech Service',
16387
+ initials: 'W',
16388
+ contactPerson: 'Kirsten Berg',
16389
+ phoneNumber: '+45 34 56 78 90',
16390
+ },
16391
+ ],
16318
16392
  },
16319
16393
  {
16320
16394
  title: 'Vaskeri instruktioner',
16321
16395
  attachments: [
16322
16396
  { name: 'Vaskeinstruktioner.pdf', type: 'pdf' },
16323
- { name: 'Bookingguide.pdf', type: 'pdf' }
16324
- ]
16397
+ { name: 'Bookingguide.pdf', type: 'pdf' },
16398
+ ],
16325
16399
  },
16326
16400
  {
16327
16401
  title: 'Vedligeholdelse og reparationer',
16328
16402
  description: 'Ved behov for reparationer eller vedligeholdelse i din lejlighed, kontakt vores hausmeister. Akutte problemer håndteres samme dag.',
16329
- images: [
16330
- '/Assets/Dummy-photos/handyman.jpg'
16331
- ],
16403
+ images: ['/Assets/Dummy-photos/handyman.jpg'],
16332
16404
  contacts: [
16333
- { name: 'Hausmeister Service', initials: 'H', contactPerson: 'Erik Sørensen', phoneNumber: '+45 56 78 90 12' }
16334
- ]
16405
+ {
16406
+ name: 'Hausmeister Service',
16407
+ initials: 'H',
16408
+ contactPerson: 'Erik Sørensen',
16409
+ phoneNumber: '+45 56 78 90 12',
16410
+ },
16411
+ ],
16335
16412
  },
16336
16413
  {
16337
16414
  title: 'Værktøjsudlån',
16338
16415
  description: 'Basis håndværktøj tilgængeligt til beboerbrug. Kvittér for værktøj ved receptionen. Returnér inden for 48 timer.',
16339
16416
  attachments: [
16340
16417
  { name: 'Værktøjsliste.pdf', type: 'pdf' },
16341
- { name: 'Udlånspolitik.pdf', type: 'pdf' }
16342
- ]
16343
- }
16418
+ { name: 'Udlånspolitik.pdf', type: 'pdf' },
16419
+ ],
16420
+ },
16344
16421
  ];
16345
16422
  constructor(userService) {
16346
16423
  this.userService = userService;
@@ -16358,8 +16435,8 @@ class MobileHandbookPageComponent {
16358
16435
  title="Håndbog"
16359
16436
  [avatarInitials]="userService.avatarInitials()"
16360
16437
  [avatarType]="userService.avatarType()"
16361
- (refresh)="handleRefresh($event)">
16362
-
16438
+ (refresh)="handleRefresh($event)"
16439
+ >
16363
16440
  <ds-mobile-content>
16364
16441
  <ds-mobile-content-section>
16365
16442
  <div class="folders-grid">
@@ -16368,7 +16445,8 @@ class MobileHandbookPageComponent {
16368
16445
  [iconName]="'remixLightbulbLine'"
16369
16446
  [itemCount]="8"
16370
16447
  [label]="'Forsyninger'"
16371
- [items]="utilitiesItems">
16448
+ [items]="utilitiesItems"
16449
+ >
16372
16450
  </ds-mobile-handbook-folder>
16373
16451
 
16374
16452
  <ds-mobile-handbook-folder
@@ -16376,7 +16454,8 @@ class MobileHandbookPageComponent {
16376
16454
  [iconName]="'remixKey2Line'"
16377
16455
  [itemCount]="4"
16378
16456
  [label]="'Sikkerhedsudstyr'"
16379
- [items]="sikkerhedsudstyrItems">
16457
+ [items]="sikkerhedsudstyrItems"
16458
+ >
16380
16459
  </ds-mobile-handbook-folder>
16381
16460
 
16382
16461
  <ds-mobile-handbook-folder
@@ -16384,7 +16463,8 @@ class MobileHandbookPageComponent {
16384
16463
  [iconName]="'remixFileList3Line'"
16385
16464
  [itemCount]="8"
16386
16465
  [label]="'Servicekontrakter'"
16387
- [items]="serviceContractsItems">
16466
+ [items]="serviceContractsItems"
16467
+ >
16388
16468
  </ds-mobile-handbook-folder>
16389
16469
 
16390
16470
  <ds-mobile-handbook-folder
@@ -16392,7 +16472,8 @@ class MobileHandbookPageComponent {
16392
16472
  [iconName]="'remixToolsLine'"
16393
16473
  [itemCount]="5"
16394
16474
  [label]="'Udstyr'"
16395
- [items]="equipmentItems">
16475
+ [items]="equipmentItems"
16476
+ >
16396
16477
  </ds-mobile-handbook-folder>
16397
16478
  </div>
16398
16479
  </ds-mobile-content-section>
@@ -16406,14 +16487,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
16406
16487
  DsMobilePageMainComponent,
16407
16488
  DsMobileContentComponent,
16408
16489
  DsMobileContentSectionComponent,
16409
- DsMobileHandbookFolderComponent
16490
+ DsMobileHandbookFolderComponent,
16410
16491
  ], template: `
16411
16492
  <ds-mobile-page-main
16412
16493
  title="Håndbog"
16413
16494
  [avatarInitials]="userService.avatarInitials()"
16414
16495
  [avatarType]="userService.avatarType()"
16415
- (refresh)="handleRefresh($event)">
16416
-
16496
+ (refresh)="handleRefresh($event)"
16497
+ >
16417
16498
  <ds-mobile-content>
16418
16499
  <ds-mobile-content-section>
16419
16500
  <div class="folders-grid">
@@ -16422,7 +16503,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
16422
16503
  [iconName]="'remixLightbulbLine'"
16423
16504
  [itemCount]="8"
16424
16505
  [label]="'Forsyninger'"
16425
- [items]="utilitiesItems">
16506
+ [items]="utilitiesItems"
16507
+ >
16426
16508
  </ds-mobile-handbook-folder>
16427
16509
 
16428
16510
  <ds-mobile-handbook-folder
@@ -16430,7 +16512,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
16430
16512
  [iconName]="'remixKey2Line'"
16431
16513
  [itemCount]="4"
16432
16514
  [label]="'Sikkerhedsudstyr'"
16433
- [items]="sikkerhedsudstyrItems">
16515
+ [items]="sikkerhedsudstyrItems"
16516
+ >
16434
16517
  </ds-mobile-handbook-folder>
16435
16518
 
16436
16519
  <ds-mobile-handbook-folder
@@ -16438,7 +16521,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
16438
16521
  [iconName]="'remixFileList3Line'"
16439
16522
  [itemCount]="8"
16440
16523
  [label]="'Servicekontrakter'"
16441
- [items]="serviceContractsItems">
16524
+ [items]="serviceContractsItems"
16525
+ >
16442
16526
  </ds-mobile-handbook-folder>
16443
16527
 
16444
16528
  <ds-mobile-handbook-folder
@@ -16446,7 +16530,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
16446
16530
  [iconName]="'remixToolsLine'"
16447
16531
  [itemCount]="5"
16448
16532
  [label]="'Udstyr'"
16449
- [items]="equipmentItems">
16533
+ [items]="equipmentItems"
16534
+ >
16450
16535
  </ds-mobile-handbook-folder>
16451
16536
  </div>
16452
16537
  </ds-mobile-content-section>
@@ -16623,7 +16708,7 @@ class MobileHomePageComponent {
16623
16708
  </div>
16624
16709
  </ds-mobile-content>
16625
16710
  </ds-mobile-page-main>
16626
- `, isInline: true, styles: [".section-headline{font-size:var(--font-size-sm);font-weight:600;color:var(--text-color-default-primary);margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px;padding:0}.pinned-posts-section{margin:-12px -12px 0;padding:0 12px 12px;box-shadow:var(--box-shadow-sm);border-radius:16px;border:1px solid var(--border-color-default)}.pinned-posts-section .section-headline{padding:16px 0}.content-section{display:flex;flex-direction:column;gap:12px}.items-container{display:flex;flex-direction:column;gap:4px}\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", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "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: DsMobileCardInlineContactComponent, selector: "ds-mobile-card-inline-contact", inputs: ["name", "initials", "contactPerson", "phoneNumber", "layout", "clickable", "showChevron"], outputs: ["contactClick"] }, { kind: "component", type: DsMobileCardInlineFileComponent, selector: "ds-mobile-card-inline-file", inputs: ["fileName", "fileSize", "variant", "layout"], outputs: ["fileClick"] }, { 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: PostContentComponent, selector: "post-content" }, { kind: "component", type: PostTextComponent, selector: "post-text" }, { 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"] }] });
16711
+ `, isInline: true, styles: [".section-headline{font-size:var(--font-size-sm);font-weight:600;color:var(--text-color-default-primary);margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px;padding:0}.pinned-posts-section{margin:-12px -12px 0;padding:0 12px 12px;box-shadow:var(--box-shadow-sm);border-radius:16px;border:1px solid var(--border-color-default)}.pinned-posts-section .section-headline{padding:16px 0}.content-section{display:flex;flex-direction:column;gap:12px}.items-container{display:flex;flex-direction:column;gap:4px}\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", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "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: DsMobileCardInlineContactComponent, selector: "ds-mobile-card-inline-contact", inputs: ["name", "initials", "contactPerson", "phoneNumber", "layout", "clickable", "showChevron"], outputs: ["contactClick"] }, { kind: "component", type: DsMobileCardInlineFileComponent, selector: "ds-mobile-card-inline-file", inputs: ["fileName", "fileSize", "variant", "layout", "fileUrl"], outputs: ["fileClick"] }, { 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: PostContentComponent, selector: "post-content" }, { kind: "component", type: PostTextComponent, selector: "post-text" }, { 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"] }] });
16627
16712
  }
16628
16713
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MobileHomePageComponent, decorators: [{
16629
16714
  type: Component,